client = new Client(); $this->currency = env('CONFIG_24680_CURRENCY'); $this->operatorId = env('LUCKYSTREAK_OPERATOR_ID', '526'); $this->operatorName = env('LUCKYSTREAK_OPERATOR_NAME', 'AEG'); $this->clientId = env('LUCKYSTREAK_CLIENT_ID', 'AEG_operator'); $this->clientSecret = env('LUCKYSTREAK_CLIENT_SECRET', 'X6vuibvYJI2Z9NglbzZE'); $this->apiUrl = env('LUCKYSTREAK_API_URL', 'https://integ.livepbt.com'); $this->hmacId = env('LUCKYSTREAK_HMAC_ID', 'AEG1'); $this->hmacUser = env('LUCKYSTREAK_HMAC_USER', 'AEGhmac1'); $this->hmacKey = env('LUCKYSTREAK_HMAC_KEY', 'WcVIR7szsyXNPstaY2JX'); } public function callSubApi($username,$request) { Util::WriteLog('luckystreak_validate','callsubapi'); $apiurl=ServerService::GetApiByGUID($username); try { // 获取当前请求的 GET 和 POST 数据 // $getData = $request->query(); // 获取 GET 数据 $postData = $request->post(); // 获取 POST 数据 $response = $this->client->post( $apiurl . $_SERVER['REQUEST_URI'], [ 'verify'=>false, // 'query' => $getData, // 传递 GET 数据 'form_params' => $postData, // 传递 POST 数据 ]); $res=json_decode($response->getBody(),true); Util::WriteLog('luckystreak_validate',$res); return $res; } catch (RequestException $e) { return $this->handleRequestException($e); } } private function handleRequestException(\Exception $e) { // TelegramBot::getDefault()->sendMsgWithEnv($e->getMessage().$e->getTraceAsString()); } /** * 获取授权令牌 * * @return string|null */ public function getAuthToken() { try { // 构建凭证字符串 $credentialsBase = $this->operatorId . ':' . $this->clientId . ':' . $this->clientSecret; $credentials = base64_encode($credentialsBase); // 请求授权令牌 $response = $this->client->post('https://integ-api-ids.livepbt.com/ids/connect/token', [ 'verify' => false, 'headers' => [ 'Content-Type' => 'application/x-www-form-urlencoded', 'Authorization'=>'Basic QUVHX29wZXJhdG9yOlg2dnVpYnZZSkkyWjlOZ2xielpF' ], 'form_params' => [ 'grant_type' => 'operator_authorization', 'scope' => 'operator offline_access', 'operator_name' => 'AEG' ] ]); $result = json_decode($response->getBody(), true); $token = $result['access_token'] ?? null; if ($token) { // 缓存令牌一段时间以减少API调用 $expiresIn = $result['expires_in'] ?? 3600; Redis::setex('luckystreak_token', $expiresIn - 60, $token); } return $token; } catch (\Exception $e) { Util::WriteLog('luckystreak_error', $e->getMessage()); return null; } } /** * 获取缓存的令牌或请求新令牌 * * @return string|null */ public function getCachedAuthToken() { // 尝试从缓存获取令牌 $token = Redis::get('luckystreak_token'); // 如果缓存中没有令牌或令牌已过期,请求新令牌 if (!$token) { $token = $this->getAuthToken(); } return $token; } /** * 获取游戏列表 * * @return array */ public function getGamesList() { try { // 获取授权令牌 $token = $this->getCachedAuthToken(); if (!$token) { throw new \Exception("Failed to get authorization token"); } // 调用游戏列表API $response = $this->client->post($this->apiUrl . '/lobby/api/v4/lobby/games', [ 'verify' => false, 'headers' => [ 'Authorization' => 'Bearer ' . $token, 'Content-Type' => 'application/json' ], 'json' => [ 'data' => [ 'Open' => true, 'GameTypes' => [], 'Currencies' => [] ] ] ]); return json_decode($response->getBody(), true); } catch (\Exception $e) { Util::WriteLog('luckystreak_error', $e->getMessage()); return ['error' => $e->getMessage()]; } } public function getJackpot() { try { // 获取授权令牌 $token = $this->getCachedAuthToken(); if (!$token) { throw new \Exception("Failed to get authorization token"); } // 调用游戏列表API $response = $this->client->post($this->apiUrl . '/lobby/api/v4/lobby/jackpots', [ 'verify' => false, 'headers' => [ 'Authorization' => 'Bearer ' . $token, 'Content-Type' => 'application/json' ], 'json' => [ ] ]); return json_decode($response->getBody(), true); } catch (\Exception $e) { Util::WriteLog('luckystreak_error', $e->getMessage()); return ['error' => $e->getMessage()]; } } /** * 获取游戏启动URL的HTML * * @param string $gameId * @param string $globalUid * @param string $ipAddress * @param string $language * @return string */ public function getLaunchURLHTML($gameId,$globalUid, $ipAddress, $language) { try { // 获取游戏类型(如果需要) $gameType = $this->getGameTypeById($gameId); // 使用GlobalUID作为AuthCode参数 // 使用正确的启动URL格式 $launchURL = "https://m.integ.livepbt.com/?PlayerName={$globalUid}&OperatorName=AEG&AuthCode={$globalUid}&GameId={$gameId}&GameType={$gameType}&LimitsGroupId=67efe1d102d4c16717303002"; // 添加语言参数 if ($language) { $launchURL .= "&language={$language}"; } // 返回HTML重定向脚本 return ""; } catch (\Exception $e) { Util::WriteLog('luckystreak_error', $e->getMessage()); return ""; } } /** * 根据游戏ID获取游戏类型 * * @param string $gameId * @return string|null */ private function getGameTypeById($gameId) { // 基本游戏类型映射 $gameTypes = [ '3' => 'Roulette', '4' => 'Blackjack', '2' => 'Baccarat', // 可以添加更多游戏类型映射 ]; return $gameTypes[$gameId] ?? null; } /** * 验证HMAC签名(用于出站请求) * * @param array $data * @param string $signature * @return bool */ public function verifyHmacSignature($data, $signature) { // 根据LuckyStreak API文档实现HMAC验证逻辑 // 按照文档附录B的规范实现 // 1. 获取完整请求URL中的路径部分(不包含域名和查询参数) $path = $data['path'] ?? ''; unset($data['path']); // 2. 获取请求方法 $method = $data['method'] ?? 'POST'; unset($data['method']); // 3. 获取请求时间戳 $timestamp = $data['timestamp'] ?? ''; if (empty($timestamp)) { return false; } unset($data['timestamp']); // 4. 获取随机nonce $nonce = $data['nonce'] ?? ''; if (empty($nonce)) { return false; } unset($data['nonce']); // 获取ext和hash $ext = ''; if (isset($data['ext'])) { $ext = $data['ext']; unset($data['ext']); } $hash = ''; if (isset($data['hash'])) { $hash = $data['hash']; unset($data['hash']); } // 主机名和端口 $host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : 'euapi.24680.org'; $port = '443'; // 假设使用HTTPS // 5. 构建规范化请求字符串 $normalized = "hawk.1.header\n" . $timestamp . "\n" . $nonce . "\n" . $method . "\n" . $path . "\n" . $host . "\n" . $port . "\n"; // 6. 如果有请求体,添加请求体内容的哈希 if (isset($data['body']) && !empty($data['body'])) { $body = $data['body']; // 如果是JSON字符串,尝试解码并重新编码,确保格式一致 if (is_string($body) && json_decode($body) !== null) { $bodyObj = json_decode($body, true); if (is_array($bodyObj)) { $body = json_encode($bodyObj); } } elseif (is_array($body)) { $body = json_encode($body); } // 如果已提供的hash值,使用它 if (!empty($hash)) { $normalized .= $hash . "\n"; } else { // 否则计算body的hash $bodyHash = base64_encode(hash('sha256', $body, true)); $normalized .= $bodyHash . "\n"; } } else { $normalized .= "\n"; // 如果没有请求体,添加空行 } // 7. 如果有ext,添加ext if (!empty($ext)) { $normalized .= $ext . "\n"; } else { $normalized .= ''; // 如果没有ext,添加空字符串 } // 8. 使用HMAC-SHA256生成签名 $calculatedSignature = base64_encode(hash_hmac('sha256', $normalized, $this->hmacKey, true)); // 9. 比较签名 // Util::WriteLog('luckystreak_hmac', [ // 'normalized' => $normalized, // 'calculated' => $calculatedSignature, // 'received' => $signature, // 'hmacKey' => $this->hmacKey // ]); return $signature === $calculatedSignature; } public function generateHmacSignature($data, $body = null) { // $data=['path' => '', // 'method' => '', // 'timestamp' => '', // 'nonce' => '']; // 生成时间戳(毫秒级) // $timestamp = (string)round(microtime(true) * 1000); // 生成随机nonce // $nonce = uniqid('', true); extract($data); // 主机名和端口 $host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : 'euapi.24680.org'; $port = '443'; // 假设使用HTTPS // 请求标识符 (根据文档使用hawk.1.response) $type = "hawk.1.response"; // 构建规范化请求字符串 $normalized = $type . "\n" . $timestamp . "\n" . $nonce . "\n" . $method . "\n" . $path . "\n" . $host . "\n" . $port . "\n"; // 如果有请求体,添加请求体的哈希 $bodyHash = ''; if (!empty($body)&&false) { // 如果是JSON字符串,尝试解码并重新编码,确保格式一致 if (is_string($body) && json_decode($body) !== null) { $bodyObj = json_decode($body, true); if (is_array($bodyObj)) { $body = json_encode($bodyObj); } } elseif (is_array($body)) { $body = json_encode($body); } $bodyHash = base64_encode(hash('sha256', $body, true)); $normalized .= $bodyHash . "\n"; } else { $normalized .= "\n"; // 如果没有请求体,添加空行 } // 添加ext(根据文档使用X-Request-Header-To-Protect:secret) $ext = "X-Request-Header-To-Protect:secret"; $normalized .= $ext ."\n"; // 使用HMAC-SHA256生成签名 $signature = base64_encode(hash_hmac('sha256', $normalized, $this->hmacKey, true)); // Util::WriteLog('luckystreak_hmac', [ // 'normalized' => $normalized, // 'signature' => $signature, // 'body' => $body, // 'bodyHash' => $bodyHash, // 'header'=>[ 'Server-Authorization' => 'hawk mac="' . $signature . '", ext="' . $ext . '"'] // ]); // 返回Server-Authorization头(根据文档) return [ 'Server-Authorization' => 'hawk mac="' . $signature . '", ext="' . $ext . '"' ]; } /** * 获取第三方游戏提供商游戏列表 * * @return array */ public function getProviderGamesList() { try { // 获取授权令牌 $token = $this->getCachedAuthToken(); if (!$token) { throw new \Exception("Failed to get authorization token"); } // 调用Provider游戏列表API $response = $this->client->post($this->apiUrl . '/lobby/api/v4/lobby/providergames', [ 'verify' => false, 'headers' => [ 'Authorization' => 'Bearer ' . $token, 'Content-Type' => 'application/json' ], 'json' => [ 'data' => [ 'Open' => true, 'GameTypes' => [], 'Currencies' => [] ] ] ]); return json_decode($response->getBody(), true); } catch (\Exception $e) { Util::WriteLog('luckystreak_error', $e->getMessage()); return ['error' => $e->getMessage()]; } } }