576 lines
18 KiB
PHP
576 lines
18 KiB
PHP
<?php
|
||
const GET_WEB_CONFIG_URL = 'https://uniuser.jzvisa.com/home/Company/index/model/evus';
|
||
const CREATE_EVUS_ORDER_URL = 'https://uniuser.jzvisa.com/home/order/applyevus';
|
||
const UPDATE_EVUS_ORDER_URL = 'https://uniuser.jzvisa.com/home/order/updateevus';
|
||
const CREATE_ORDER_URL = 'https://uniuser.jzvisa.com/home/order/do_apply';
|
||
const NEWS_LIST = 'https://uniuser.jzvisa.com/home/news/lists';
|
||
const NEWS_DETAIL = 'https://uniuser.jzvisa.com/home/news/detail';
|
||
const YZM_URL = 'https://uniuser.jzvisa.com/home/sms/sendCode';
|
||
|
||
const GET_ORDER_URL = 'https://uniuser.jzvisa.com/home/order/orderList';
|
||
|
||
const INCOICETYPE = '1';
|
||
|
||
const MODEL = 'msg';
|
||
const BUCKET = 'gzevus';
|
||
const COUNTRY = 'EVUS登记';
|
||
const SOURCE = 'evus.acvisa.cn';
|
||
const PAY_MARK = 'evuscn';
|
||
const OSS_URL = 'https://files.jzvisa.com/' . BUCKET . '/';
|
||
|
||
/*esta 配置*/
|
||
const ESTA_COUNTRY = 'ESTA登记';
|
||
const PAY_ESTA_MARK = 'estacn';
|
||
const ESTA_MODEL = 'esta';
|
||
const ESTA_INCOICETYPE = '6';
|
||
const BASE_PATH = '/evus'; // 可后端模板注入
|
||
|
||
|
||
// 公共函数
|
||
function formDataToJson($fields, $postData, $kew_key, $checkField)
|
||
{
|
||
if (!isset($postData[$checkField]) || $postData[$checkField] != 1) {
|
||
foreach ($fields as $field) {
|
||
unset($postData[$field]);
|
||
}
|
||
return $postData;
|
||
}
|
||
|
||
$data = [];
|
||
foreach ($postData[$fields[0]] as $index => $value) {
|
||
$entry = [];
|
||
foreach ($fields as $field) {
|
||
$entry[$field] = isset($postData[$field][$index]) ? $postData[$field][$index] : null;
|
||
}
|
||
$data[] = $entry;
|
||
}
|
||
|
||
$postData[$kew_key] = json_encode($data, JSON_UNESCAPED_UNICODE);
|
||
foreach ($fields as $field) {
|
||
unset($postData[$field]);
|
||
}
|
||
|
||
return $postData;
|
||
}
|
||
|
||
function find_json($json_name, $field, $field_value)
|
||
{
|
||
$data = file_get_contents($json_name);
|
||
$data_arr = json_decode($data, true);
|
||
$found = array_filter($data_arr, function ($item) use ($field, $field_value) {
|
||
return $item[$field] === $field_value;
|
||
});
|
||
// 输出结果
|
||
if ($found) {
|
||
$found = array_values($found); // 获取过滤后的第一个元素
|
||
return $found;
|
||
} else {
|
||
return false;
|
||
}
|
||
}
|
||
|
||
function getNameById($json_name, $id)
|
||
{
|
||
$data = file_get_contents('./json/' . $json_name . '.json');
|
||
$data_arr = json_decode($data, true);
|
||
foreach ($data_arr as $item) {
|
||
if (array_key_exists('data', $item['data'])) {
|
||
foreach ($item['data']['data'] as $dataItem) {
|
||
if ($dataItem['id'] === $id) {
|
||
return $dataItem['name'];
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
|
||
function getIdByName($json_name, $id)
|
||
{
|
||
$data = file_get_contents('./json/' . $json_name . '.json');
|
||
$data_arr = json_decode($data, true);
|
||
foreach ($data_arr as $item) {
|
||
if (array_key_exists('data', $item['data'])) {
|
||
foreach ($item['data']['data'] as $dataItem) {
|
||
if ($dataItem['name'] === $id) {
|
||
return $dataItem['id'];
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
|
||
function isMobile()
|
||
{
|
||
$_SERVER['ALL_HTTP'] = isset($_SERVER['ALL_HTTP']) ? $_SERVER['ALL_HTTP'] : '';
|
||
|
||
if (preg_match('/(up.browser|up.link|mmp|symbian|smartphone|midp|wap|phone|iphone|ipad|ipod|android|xoom)/i',
|
||
strtolower($_SERVER['HTTP_USER_AGENT']))) {
|
||
return true;
|
||
}
|
||
if ((isset($_SERVER['HTTP_ACCEPT'])) and (strpos(strtolower($_SERVER['HTTP_ACCEPT']),
|
||
'application/vnd.wap.xhtml+xml') !== false)) {
|
||
return true;
|
||
}
|
||
if (isset($_SERVER['HTTP_X_WAP_PROFILE'])) {
|
||
return true;
|
||
}
|
||
if (isset($_SERVER['HTTP_PROFILE'])) {
|
||
return true;
|
||
}
|
||
$mobile_ua = strtolower(substr($_SERVER['HTTP_USER_AGENT'], 0, 4));
|
||
$mobile_agents = array(
|
||
'w3c ',
|
||
'acs-',
|
||
'alav',
|
||
'alca',
|
||
'amoi',
|
||
'audi',
|
||
'avan',
|
||
'benq',
|
||
'bird',
|
||
'blac',
|
||
'blaz',
|
||
'brew',
|
||
'cell',
|
||
'cldc',
|
||
'cmd-',
|
||
'dang',
|
||
'doco',
|
||
'eric',
|
||
'hipt',
|
||
'inno',
|
||
'ipaq',
|
||
'java',
|
||
'jigs',
|
||
'kddi',
|
||
'keji',
|
||
'leno',
|
||
'lg-c',
|
||
'lg-d',
|
||
'lg-g',
|
||
'lge-',
|
||
'maui',
|
||
'maxo',
|
||
'midp',
|
||
'mits',
|
||
'mmef',
|
||
'mobi',
|
||
'mot-',
|
||
'moto',
|
||
'mwbp',
|
||
'nec-',
|
||
'newt',
|
||
'noki',
|
||
'oper',
|
||
'palm',
|
||
'pana',
|
||
'pant',
|
||
'phil',
|
||
'play',
|
||
'port',
|
||
'prox',
|
||
'qwap',
|
||
'sage',
|
||
'sams',
|
||
'sany',
|
||
'sch-',
|
||
'sec-',
|
||
'send',
|
||
'seri',
|
||
'sgh-',
|
||
'shar',
|
||
'sie-',
|
||
'siem',
|
||
'smal',
|
||
'smar',
|
||
'sony',
|
||
'sph-',
|
||
'symb',
|
||
't-mo',
|
||
'teli',
|
||
'tim-',
|
||
'tosh',
|
||
'tsm-',
|
||
'upg1',
|
||
'upsi',
|
||
'vk-v',
|
||
'voda',
|
||
'wap-',
|
||
'wapa',
|
||
'wapi',
|
||
'wapp',
|
||
'wapr',
|
||
'webc',
|
||
'winw',
|
||
'winw',
|
||
'xda',
|
||
'xda-'
|
||
);
|
||
if (in_array($mobile_ua, $mobile_agents)) {
|
||
return true;
|
||
}
|
||
if (strpos(strtolower($_SERVER['ALL_HTTP']), 'operamini') !== false) {
|
||
return true;
|
||
}
|
||
// Pre-final check to reset everything if the user is on Windows
|
||
if (strpos(strtolower($_SERVER['HTTP_USER_AGENT']), 'windows') !== false) {
|
||
return false;
|
||
}
|
||
// But WP7 is also Windows, with a slightly different characteristic
|
||
if (strpos(strtolower($_SERVER['HTTP_USER_AGENT']), 'windows phone') !== false) {
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
function getOrderNumber()
|
||
{
|
||
return date('YmdHis') . mt_rand(10, 99);
|
||
}
|
||
|
||
function cmf_get_current_user_id()
|
||
{
|
||
$sessionUserId = session('user.id');
|
||
if (empty($sessionUserId)) {
|
||
return 0;
|
||
}
|
||
return $sessionUserId;
|
||
|
||
}
|
||
function getIP()
|
||
{
|
||
$realip = '';
|
||
$unknown = 'unknown';
|
||
if (isset($_SERVER)){
|
||
if(isset($_SERVER['HTTP_X_FORWARDED_FOR']) && !empty($_SERVER['HTTP_X_FORWARDED_FOR']) && strcasecmp($_SERVER['HTTP_X_FORWARDED_FOR'], $unknown)){
|
||
$arr = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
|
||
foreach($arr as $ip){
|
||
$ip = trim($ip);
|
||
if ($ip != 'unknown'){
|
||
$realip = $ip;
|
||
break;
|
||
}
|
||
}
|
||
}else if(isset($_SERVER['HTTP_CLIENT_IP']) && !empty($_SERVER['HTTP_CLIENT_IP']) && strcasecmp($_SERVER['HTTP_CLIENT_IP'], $unknown)){
|
||
$realip = $_SERVER['HTTP_CLIENT_IP'];
|
||
}else if(isset($_SERVER['REMOTE_ADDR']) && !empty($_SERVER['REMOTE_ADDR']) && strcasecmp($_SERVER['REMOTE_ADDR'], $unknown)){
|
||
$realip = $_SERVER['REMOTE_ADDR'];
|
||
}else{
|
||
$realip = $unknown;
|
||
}
|
||
}else{
|
||
if(getenv('HTTP_X_FORWARDED_FOR') && strcasecmp(getenv('HTTP_X_FORWARDED_FOR'), $unknown)){
|
||
$realip = getenv("HTTP_X_FORWARDED_FOR");
|
||
}else if(getenv('HTTP_CLIENT_IP') && strcasecmp(getenv('HTTP_CLIENT_IP'), $unknown)){
|
||
$realip = getenv("HTTP_CLIENT_IP");
|
||
}else if(getenv('REMOTE_ADDR') && strcasecmp(getenv('REMOTE_ADDR'), $unknown)){
|
||
$realip = getenv("REMOTE_ADDR");
|
||
}else{
|
||
$realip = $unknown;
|
||
}
|
||
}
|
||
$realip = preg_match("/[\d\.]{7,15}/", $realip, $matches) ? $matches[0] : $unknown;
|
||
return $realip;
|
||
}
|
||
function build_ocr_signature(string $appId, string $secret, ?int $timestamp = null): array
|
||
{
|
||
$timestamp = $timestamp ?? time();
|
||
$signString = $appId . "\n" . (string)$timestamp;
|
||
$signature = hash_hmac('sha256', $signString, $secret);
|
||
|
||
return [
|
||
'app_id' => $appId,
|
||
'timestamp' => (string)$timestamp,
|
||
'signature' => $signature,
|
||
'base_url' => config('app.OCR_BASE_URL'),
|
||
];
|
||
}
|
||
// 这是系统自动生成的公共文件
|
||
|
||
function arrayToJson($params, $values)
|
||
{
|
||
|
||
$datas = [];
|
||
$count = count($values[0]);
|
||
$valcount = count($params);
|
||
for ($i = 0; $i < $count; $i++) {
|
||
for ($j = 0; $j < $valcount; $j++) {
|
||
$data[$params[$j]] = $values[$j][$i];
|
||
}
|
||
$datas[] = $data;
|
||
}
|
||
if (empty($datas)) {
|
||
return '';
|
||
}
|
||
return json_encode($datas);
|
||
}
|
||
|
||
function getCate($val)
|
||
{
|
||
$cates = explode('-', $val);
|
||
if (count($cates) == 1) {
|
||
if ($val == 'CNMI') {
|
||
return 'CW/E2C';
|
||
}
|
||
} else {
|
||
preg_match_all('/[A-Z]+/', $val, $matches);
|
||
return $matches[0][0];
|
||
}
|
||
|
||
|
||
}
|
||
//非常给力的authcode加密函数,Discuz!经典代码(带详解)
|
||
function authcode($string, $operation = 'DECODE', $key = '20241021', $expiry = 0)
|
||
{
|
||
// 动态密匙长度,相同的明文会生成不同密文就是依靠动态密匙
|
||
$ckey_length = 4;
|
||
|
||
// 密匙
|
||
$key = md5($key ? $key : $GLOBALS['discuz_auth_key']);
|
||
|
||
// 密匙a会参与加解密
|
||
$keya = md5(substr($key, 0, 16));
|
||
// 密匙b会用来做数据完整性验证
|
||
$keyb = md5(substr($key, 16, 16));
|
||
// 密匙c用于变化生成的密文
|
||
$keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length) : substr(md5(microtime()), -$ckey_length)) : '';
|
||
// 参与运算的密匙
|
||
$cryptkey = $keya . md5($keya . $keyc);
|
||
$key_length = strlen($cryptkey);
|
||
// 明文,前10位用来保存时间戳,解密时验证数据有效性,10到26位用来保存$keyb(密匙b),
|
||
//解密时会通过这个密匙验证数据完整性
|
||
// 如果是解码的话,会从第$ckey_length位开始,因为密文前$ckey_length位保存 动态密匙,以保证解密正确
|
||
$string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0) . substr(md5($string . $keyb), 0, 16) . $string;
|
||
$string_length = strlen($string);
|
||
$result = '';
|
||
$box = range(0, 255);
|
||
$rndkey = array();
|
||
// 产生密匙簿
|
||
for ($i = 0; $i <= 255; $i++) {
|
||
$rndkey[$i] = ord($cryptkey[$i % $key_length]);
|
||
}
|
||
// 用固定的算法,打乱密匙簿,增加随机性,好像很复杂,实际上对并不会增加密文的强度
|
||
for ($j = $i = 0; $i < 256; $i++) {
|
||
$j = ($j + $box[$i] + $rndkey[$i]) % 256;
|
||
$tmp = $box[$i];
|
||
$box[$i] = $box[$j];
|
||
$box[$j] = $tmp;
|
||
}
|
||
// 核心加解密部分
|
||
for ($a = $j = $i = 0; $i < $string_length; $i++) {
|
||
$a = ($a + 1) % 256;
|
||
$j = ($j + $box[$a]) % 256;
|
||
$tmp = $box[$a];
|
||
$box[$a] = $box[$j];
|
||
$box[$j] = $tmp;
|
||
// 从密匙簿得出密匙进行异或,再转成字符
|
||
$result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
|
||
}
|
||
if ($operation == 'DECODE') {
|
||
// 验证数据有效性,请看未加密明文的格式
|
||
if ((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26) . $keyb), 0, 16)) {
|
||
return substr($result, 26);
|
||
} else {
|
||
return '';
|
||
}
|
||
} else {
|
||
// 把动态密匙保存在密文里,这也是为什么同样的明文,生产不同密文后能解密的原因
|
||
// 因为加密后的密文可能是一些特殊字符,复制过程可能会丢失,所以用base64编码
|
||
return $keyc . str_replace('=', '', base64_encode($result));
|
||
}
|
||
}
|
||
function getChineseCode($code)
|
||
{
|
||
$url = "http://code.mcdvisa.com/save.php?action=getcode&i=0&w={$code}";
|
||
$data = file_get_contents($url);
|
||
if (empty($data)){
|
||
return [
|
||
'code'=>0,
|
||
'data'=>[]
|
||
];
|
||
}
|
||
// 创建 DOMDocument 对象并加载 HTML
|
||
$dom = new DOMDocument();
|
||
libxml_use_internal_errors(true); // 抑制 HTML 解析错误
|
||
$dom->loadHTML($data);
|
||
libxml_clear_errors();
|
||
// 获取所有的 <li> 元素
|
||
$lis = $dom->getElementsByTagName('li');
|
||
$pattern = '/-?\d+(?:\.\d+)?/';
|
||
$datas=[];
|
||
foreach ($lis as $li) {
|
||
$text = $li->textContent;
|
||
preg_match_all($pattern, $text, $matches);
|
||
$datas[] = $matches[0][0];
|
||
}
|
||
return [
|
||
'code'=>200,
|
||
'data'=>$datas
|
||
];
|
||
}
|
||
/**
|
||
* 查询姓名电码。
|
||
* @param string $fullName 中文全名
|
||
* @param int $surnameLength 姓氏字数:单姓 1,复姓 2
|
||
* 依赖第三方网页结构;失败时抛出异常,由调用方转换为用户提示。
|
||
*/
|
||
function queryNameTelecode(string $fullName, int $surnameLength = 1): array
|
||
{
|
||
$fullName = trim($fullName);
|
||
$length = mb_strlen($fullName, 'UTF-8');
|
||
|
||
if (!preg_match('/^\p{Han}+$/u', $fullName) || $length > 8) {
|
||
throw new InvalidArgumentException('请输入不超过 8 个汉字的姓名');
|
||
}
|
||
|
||
if ($surnameLength < 1 || $surnameLength >= $length) {
|
||
throw new InvalidArgumentException('姓氏字数必须小于姓名总字数');
|
||
}
|
||
|
||
if (!extension_loaded('curl') || !extension_loaded('dom')) {
|
||
throw new RuntimeException('电码查询服务未就绪,请手动填写或联系管理员');
|
||
}
|
||
|
||
$url = 'https://apps.chasedream.com/chinese-commercial-code/';
|
||
// 两次请求共用一个时间预算,避免每次都等待完整超时时间。
|
||
$deadline = hrtime(true) + 15 * 1000000000;
|
||
$curl = curl_init($url);
|
||
|
||
if ($curl === false) {
|
||
throw new RuntimeException('无法初始化 cURL');
|
||
}
|
||
|
||
curl_setopt_array($curl, [
|
||
CURLOPT_RETURNTRANSFER => true,
|
||
CURLOPT_CONNECTTIMEOUT => 5,
|
||
CURLOPT_TIMEOUT => 15,
|
||
CURLOPT_COOKIEFILE => '', // 内存 Cookie,会话内复用
|
||
CURLOPT_USERAGENT => 'NameTelecodeLookup/1.0',
|
||
// 协商压缩响应并自动解压,减少两次查询的网络传输量。
|
||
CURLOPT_ENCODING => '',
|
||
]);
|
||
|
||
$request = static function () use ($curl, $deadline): string {
|
||
$remainingMs = (int) floor(($deadline - hrtime(true)) / 1000000);
|
||
if ($remainingMs <= 0) {
|
||
throw new RuntimeException('电码查询超时,请稍后重试或手动填写');
|
||
}
|
||
curl_setopt($curl, CURLOPT_TIMEOUT_MS, $remainingMs);
|
||
$html = curl_exec($curl);
|
||
|
||
if ($html === false) {
|
||
throw new RuntimeException('电码查询连接失败或超时,请稍后重试或手动填写');
|
||
}
|
||
|
||
$status = curl_getinfo($curl, CURLINFO_HTTP_CODE);
|
||
if ($status !== 200) {
|
||
throw new RuntimeException('电码查询服务暂时不可用,请稍后重试或手动填写');
|
||
}
|
||
|
||
if (trim($html) === '') {
|
||
throw new RuntimeException('电码查询返回空内容,请稍后重试或手动填写');
|
||
}
|
||
return $html;
|
||
};
|
||
|
||
$parse = static function (string $html): DOMXPath {
|
||
$previous = libxml_use_internal_errors(true);
|
||
|
||
try {
|
||
$dom = new DOMDocument();
|
||
if (!$dom->loadHTML('<?xml encoding="UTF-8">' . $html)) {
|
||
throw new RuntimeException('无法解析网页');
|
||
}
|
||
return new DOMXPath($dom);
|
||
} finally {
|
||
libxml_clear_errors();
|
||
libxml_use_internal_errors($previous);
|
||
}
|
||
};
|
||
|
||
try {
|
||
// 获取本次页面的动态隐藏字段。
|
||
$xpath = $parse($request());
|
||
$payload = [];
|
||
|
||
foreach ($xpath->query(
|
||
'//form[@id="form1"]//input[@type="hidden"][@name]'
|
||
) as $input) {
|
||
$payload[$input->getAttribute('name')] =
|
||
$input->getAttribute('value');
|
||
}
|
||
|
||
if (!isset($payload['__VIEWSTATE'])) {
|
||
throw new RuntimeException('查询页面结构发生变化');
|
||
}
|
||
|
||
$payload['txtInput'] = $fullName;
|
||
$payload['btnSearch'] = '查询';
|
||
|
||
curl_setopt_array($curl, [
|
||
CURLOPT_POST => true,
|
||
CURLOPT_POSTFIELDS => http_build_query($payload),
|
||
CURLOPT_REFERER => $url,
|
||
CURLOPT_HTTPHEADER => [
|
||
'Content-Type: application/x-www-form-urlencoded',
|
||
],
|
||
]);
|
||
|
||
$xpath = $parse($request());
|
||
$mapping = [];
|
||
|
||
// 只接受“一个汉字 + 一个四位电码”的结果行。
|
||
foreach ($xpath->query('//table[@id="grvDict"]//tr') as $row) {
|
||
$characters = [];
|
||
$codes = [];
|
||
|
||
foreach ($xpath->query('./td', $row) as $cell) {
|
||
$text = trim($cell->textContent);
|
||
|
||
if (preg_match('/^\p{Han}$/u', $text)) {
|
||
$characters[] = $text;
|
||
} elseif (preg_match('/^[0-9]{4}$/', $text)) {
|
||
$codes[] = $text;
|
||
}
|
||
}
|
||
|
||
if (count($characters) === 1 && count($codes) === 1) {
|
||
$character = $characters[0];
|
||
$code = $codes[0];
|
||
|
||
if (isset($mapping[$character])
|
||
&& $mapping[$character] !== $code) {
|
||
throw new RuntimeException("“{$character}”返回多个电码");
|
||
}
|
||
|
||
$mapping[$character] = $code;
|
||
}
|
||
}
|
||
|
||
$characters = preg_split('//u', $fullName, -1, PREG_SPLIT_NO_EMPTY);
|
||
$codes = [];
|
||
|
||
foreach ($characters as $character) {
|
||
if (!isset($mapping[$character])) {
|
||
throw new RuntimeException(
|
||
"未取得“{$character}”的电码,请人工核实或检查网页结构"
|
||
);
|
||
}
|
||
$codes[] = $mapping[$character];
|
||
}
|
||
|
||
return [
|
||
'full_name' => $fullName,
|
||
'surname' => mb_substr($fullName, 0, $surnameLength, 'UTF-8'),
|
||
'given_name' => mb_substr($fullName, $surnameLength, null, 'UTF-8'),
|
||
'surname_code' => implode('', array_slice($codes, 0, $surnameLength)),
|
||
'given_name_code' => implode('', array_slice($codes, $surnameLength)),
|
||
'full_code' => implode('', $codes),
|
||
'characters' => $mapping,
|
||
];
|
||
} finally {
|
||
curl_close($curl);
|
||
}
|
||
} |