'', 'method' => '', 'timestamp' => '', 'nonce' => '']; public function __construct(LuckyStreakService $luckyStreakService) { $this->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->luckyStreakService = $luckyStreakService; } /** * 验证请求的真实性 * * @param Request $request * @return bool */ private function verifyRequest(Request $request) { // 获取Authorization头 $authHeader = $request->header('Authorization'); // 如果没有Authorization头,拒绝请求 if (empty($authHeader)) { Util::WriteLog('luckystreak_hmac', 'No Authorization header found'); return true; } // 检查是否是Hawk认证格式 if (strpos($authHeader, 'hawk ') !== 0) { Util::WriteLog('luckystreak_hmac', 'Not a Hawk authentication format'); return false; } // 解析Hawk头信息 $matches = []; preg_match('/id="([^"]+)"/', $authHeader, $matches); $id = $matches[1] ?? ''; preg_match('/ts="([^"]+)"/', $authHeader, $matches); $timestamp = $matches[1] ?? ''; preg_match('/nonce="([^"]+)"/', $authHeader, $matches); $nonce = $matches[1] ?? ''; preg_match('/mac="([^"]+)"/', $authHeader, $matches); $mac = $matches[1] ?? ''; preg_match('/hash="([^"]+)"/', $authHeader, $matches); $hash = $matches[1] ?? ''; preg_match('/ext="([^"]+)"/', $authHeader, $matches); $ext = $matches[1] ?? ''; // 如果缺少必要参数,拒绝请求 if (empty($id) || empty($timestamp) || empty($nonce) || empty($mac)) { Util::WriteLog('luckystreak_hmac', 'Missing required Hawk parameters'); return false; } // Util::WriteLog('luckystreak_hmac', $authHeader); // 获取请求方法和路径 $method = $request->method(); $path = '/'.$request->path(); // 获取请求体 $body = $request->getContent(); // 构建验证数据 $data = [ 'path' => $path, 'method' => $method, 'timestamp' => $timestamp, 'nonce' => $nonce ]; if (!empty($body)) { $data['body'] = $body; } if (!empty($ext)) { $data['ext'] = $ext; } if (!empty($hash)) { $data['hash'] = $hash; } $this->hmacData=$data; // 使用LuckyStreakService验证签名 $isValid = $this->luckyStreakService->verifyHmacSignature($data, $mac); // Util::WriteLog('luckystreak_hmac', compact('isValid', 'data','hash','mac','ext')); return $isValid; } public function reponseHeader($request,$responseData,$status=200) { // 生成响应对象 $response = response()->json($responseData,$status); // 获取响应内容 $responseContent = json_encode($responseData); // 生成HMAC头 $hmacHeaders = $this->luckyStreakService->generateHmacSignature($this->hmacData, $responseContent); // 添加HMAC头到响应 foreach ($hmacHeaders as $header => $value) { $response->header($header, str_replace("\n", " ", $value)); } return $response; } /** * 验证玩家身份 * * @param Request $request * @return \Illuminate\Http\JsonResponse */ public function validate(Request $request) { $requestData = $request->all(); Util::WriteLog('luckystreak_validate', [$requestData,$request->header()]); // 验证请求的真实性 if (!$this->verifyRequest($request)) { return response()->json([ 'errors' => [ ['code' => 'AUTH_001', 'message' => 'Unauthorized request'] ] ], 401); } // 按照LuckyStreak API格式获取请求数据 if (isset($requestData['data'])) { $data = $requestData['data']; } else { $data = $requestData; } $authCode = $data['AuthorizationCode'] ?? null; $operatorName = $data['OperatorName'] ?? $this->operatorName; if (!$authCode) { return $this->reponseHeader($request,[ 'errors' => [ ['code' => 'AUTH_002', 'message' => 'Authorization code is required'] ] ], 400); } // 在这里我们用authCode作为用户名,因为LuckyStreak使用它来标识玩家 $username = $authCode; if(!ServerService::IsLocalUser($username)) { $data=$this->luckyStreakService->callSubApi($username, $request); return $this->reponseHeader($request,$data); } // 尝试查找用户 $user = GlobalUserInfo::getGameUserInfo('GlobalUID', $username); if (!$user) { return response()->json([ 'errors' => [ ['code' => 'AUTH_003', 'message' => 'Player not found'] ] ], 404); } // 获取用户余额 $balance = GlobalUserInfo::getScoreByUserID($user->UserID); // 获取当前时间,格式化为UTC格式 $currentTimeUtc = gmdate('Y-m-d\TH:i:s.u\Z'); // 准备响应数据 $responseData = [ 'data' => [ 'userName' => $username, 'currency' => $this->currency, 'language' => GlobalUserInfo::getLocale() . 'US', // 例如:enUS, esUS等 'timeZone' => null, 'nickname' => strlen($user->NickName)<6?$user->NickName.$user->NickName:$user->NickName, 'balance' => $balance, 'type' => null, 'subOperator' => null, 'balanceTimestamp' => $currentTimeUtc, 'lastUpdateDate' => $currentTimeUtc, 'additionalFields' => null, 'errorMessage' => null, 'isError' => false ], 'errors' => null ]; return $this->reponseHeader($request,$responseData); } /** * 获取玩家余额 * * @param Request $request * @return \Illuminate\Http\JsonResponse */ public function getBalance(Request $request) { $data = $request->all(); Util::WriteLog('luckystreak_validate', [$data,$request->header()]); // 验证请求的真实性 if (!$this->verifyRequest($request)) { return response()->json([ 'errors' => [ ['code' => 'AUTH_001', 'message' => 'Unauthorized request'] ] ], 401); } $data=$data['data']; $username = $data['username'] ?? null; if (!$username) { return response()->json([ 'errors' => [ ['code' => 'AUTH_002', 'message' => 'Username is required'] ] ], 400); } if(!ServerService::IsLocalUser($username)) { $data=$this->luckyStreakService->callSubApi($username, $request); return $this->reponseHeader($request,$data); } // 获取用户信息 $user = GlobalUserInfo::getGameUserInfo('GlobalUID', $username); if (!$user) { return response()->json([ 'errors' => [ ['code' => 'AUTH_003', 'message' => 'Player not found'] ] ], 404); } // 获取用户余额 $balance = GlobalUserInfo::getScoreByUserID($user->UserID); // 准备响应数据 $responseData = [ 'data' => [ 'errorCode' => 0, 'balance' => $balance, 'balanceTimestamp' => gmdate('Y-m-d\TH:i:s.u\Z'), 'currency' => $this->currency, 'refTransactionId' => $data['transactionRequestId'] ?? null, ] ]; return $this->reponseHeader($request,$responseData); } /** * 处理资金移动请求(投注和赢取) * * @param Request $request * @return \Illuminate\Http\JsonResponse */ public function moveFunds(Request $request) { $data = $request->all(); Util::WriteLog('luckystreak_validate', $data); // 验证请求的真实性 if (!$this->verifyRequest($request)) { return response()->json([ 'errors' => [ ['code' => 'AUTH_001', 'message' => 'Unauthorized request'] ] ], 401); } $data=$data['data']; // 获取请求参数 $username = $data['username'] ?? null; if(!ServerService::IsLocalUser($username)) { $data=$this->luckyStreakService->callSubApi($username, $request); return $this->reponseHeader($request,$data); } // 验证请求的真实性 if ($data['operatorId']!=$this->operatorId||$data['currency']!=$this->currency) { return response()->json([ 'errors' => [ ['code' => 'AUTH_001', 'message' => 'Unauthorized request'] ] ], 401); } $amount = isset($data['amount']) ? floatval($data['amount']) : 0; $transactionRequestId = $data['transactionRequestId'] ?? null; $direction = $data['direction'] ?? null; // Debit(投注) 或 Credit(赢取) $eventType = $data['eventType'] ?? null; // Bet, Win, Loss 等 $gameId = $data['gameId'] ?? null; $gameType = $data['gameType'] ?? null; $roundId = $data['roundId'] ?? null; $eventId = $data['eventId'] ?? null; // 验证必须的参数 if (!$username || !$transactionRequestId || !$direction || $amount === 0) { return response()->json([ 'errors' => [ ['code' => 'REQ_001', 'message' => 'Missing required parameters'] ] ], 400); } // 获取用户信息 $user = GlobalUserInfo::getGameUserInfo('GlobalUID', $username); if (!$user) { return response()->json([ 'errors' => [ ['code' => 'AUTH_003', 'message' => 'Player not found'] ] ], 404); } $userId = $user->UserID; // 检查是否已经处理过这个交易 $transactionKey = 'luckystreak_tx_' . $transactionRequestId; if (Redis::exists($transactionKey)) { // 交易已处理,返回上次的结果 $previousResult = json_decode(Redis::get($transactionKey), true); // 生成响应对象,添加Hawk头 return $this->reponseHeader($request,$previousResult); } // 获取当前余额 $currentBalance = GlobalUserInfo::getScoreByUserID($userId)*NumConfig::NUM_VALUE; $amountInCents = intval($amount * NumConfig::NUM_VALUE); // 处理不同方向的资金移动 if ($direction === 'Debit') { // 检查余额是否足够 if ($currentBalance < $amountInCents) { $errorResponse = [ 'errors' => [ ['code' => 'ERR-FUND-001', 'message' => 'Insufficient funds'] ] ]; return $this->reponseHeader($request,$errorResponse,400); } // 获取独占锁确保事务安全 $res = SetNXLock::getExclusiveLock('luckystreak_bet_' . $transactionRequestId, 86400); if (!$res) { // 已经处理过,返回之前的结果 return $this->getBalance($request); } // 扣除余额 DB::connection('write')->table('QPTreasureDB.dbo.GameScoreInfo') ->where('UserID', $userId) ->decrement('Score', $amountInCents); // 记录下注 PlatformService::platformBet('luckystreak', $gameId, $amountInCents, $userId); // 保存交易数据用于可能的取消 Redis::set('luckystreak_bet_amount_' . $transactionRequestId, $gameId.'_'.$amountInCents); Redis::expire('luckystreak_bet_amount_' . $transactionRequestId, 86400); // 更新当前余额 $currentBalance -= $amountInCents; } elseif ($direction === 'Credit') { // 获取独占锁确保事务安全 $res = SetNXLock::getExclusiveLock('luckystreak_win_' . $transactionRequestId, 86400); if (!$res) { // 已经处理过,返回之前的结果 return $this->getBalance($request); } // 原始下注金额,如果有的话 $originalBetKey = 'luckystreak_bet_amount_' . $eventId; $originalBet = Redis::exists($originalBetKey) ? intval(Redis::get($originalBetKey)) : 0; // 增加余额 DB::connection('write')->table('QPTreasureDB.dbo.GameScoreInfo') ->where('UserID', $userId) ->increment('Score', $amountInCents); // 记录赢取 PlatformService::platformWin( $userId, 'luckystreak', $gameId, $amountInCents, $originalBet, $currentBalance + $amountInCents ); // 更新当前余额 $currentBalance += $amountInCents; } else { $errorResponse = [ 'errors' => [ ['code' => 'DIR_001', 'message' => 'Invalid direction'] ] ]; return $this->reponseHeader($request,$errorResponse,400); } // 构造成功响应 $responseData = [ 'data' => [ 'refTransactionId' => $transactionRequestId, 'balanceTimestamp' => gmdate('Y-m-d\TH:i:s.u\Z'), 'balance' => $currentBalance/NumConfig::NUM_VALUE, 'currency' => $this->currency ] ]; // 缓存交易结果 Redis::set($transactionKey, json_encode($responseData)); Redis::expire($transactionKey, 86400); return $this->reponseHeader($request,$responseData); } /** * 处理资金移动中止请求 * * @param Request $request * @return \Illuminate\Http\JsonResponse */ public function abortMoveFunds(Request $request) { $data = $request->all(); Util::WriteLog('luckystreak_validate', $data); // 验证请求的真实性 if (!$this->verifyRequest($request)) { return response()->json([ 'errors' => [ ['code' => 'AUTH_001', 'message' => 'Unauthorized request'] ] ], 401); } $data=$data['data']; // 获取请求参数 $username = $data['username'] ?? null; if(!ServerService::IsLocalUser($username)) { $data=$this->luckyStreakService->callSubApi($username, $request); return $this->reponseHeader($request,$data); } $transactionRequestId = $data['transactionRequestId'] ?? null; $originalTransactionRequestId = $data['abortedTransactionRequestId'] ?? null; // 验证必须的参数 if (!$username || !$transactionRequestId || !$originalTransactionRequestId) { $errorResponse = [ 'errors' => [ ['code' => 'REQ_001', 'message' => 'Missing required parameters'] ] ]; return $this->reponseHeader($request,$errorResponse,400); } // 获取用户信息 $user = GlobalUserInfo::getGameUserInfo('GlobalUID', $username); if (!$user) { $errorResponse = [ 'errors' => [ ['code' => 'AUTH_003', 'message' => 'Player not found'] ] ]; return $this->reponseHeader($request,$errorResponse,404); } $userId = $user->UserID; // 检查是否已经处理过这个中止请求 $abortKey = 'luckystreak_abort_' . $transactionRequestId; if (Redis::exists($abortKey)) { // 中止请求已处理,返回上次的结果 $previousResult = json_decode(Redis::get($abortKey), true); return $this->reponseHeader($request,$previousResult); } // 获取原始交易金额 $originalBetKey = 'luckystreak_bet_amount_' . $originalTransactionRequestId; if (!Redis::exists($originalBetKey)) { // 找不到原始交易,返回错误 $errorResponse = [ 'errors' => [ ['code' => 'ABORT_001', 'message' => 'Original transaction not found'] ] ]; // 生成错误响应,添加Hawk头 return $this->reponseHeader($request,$errorResponse,404); } $originalBetKey=Redis::get($originalBetKey); $originalBetKey=explode('_',$originalBetKey); $gameId = $originalBetKey[0]; $originalAmount = intval($originalBetKey[1]); // 获取独占锁确保事务安全 $res = SetNXLock::getExclusiveLock($abortKey, 86400); if (!$res) { // 已经处理过,返回之前的结果 return $this->getBalance($request); } // 获取当前余额 $currentBalance = GlobalUserInfo::getScoreByUserID($userId)*NumConfig::NUM_VALUE; // 退还金额 DB::connection('write')->table('QPTreasureDB.dbo.GameScoreInfo') ->where('UserID', $userId) ->increment('Score', $originalAmount); // 记录取消交易 PlatformService::platformBet('luckystreak', $gameId, -$originalAmount,$userId); // 更新当前余额 $currentBalance += $originalAmount; // 构造成功响应 $responseData = [ 'data' => [ 'refTransactionId' => $transactionRequestId, 'balance' => $currentBalance / NumConfig::NUM_VALUE, 'currency' => $this->currency ] ]; // 缓存中止请求结果 Redis::set($abortKey, json_encode($responseData)); Redis::expire($abortKey, 86400); return $this->reponseHeader($request,$responseData); } /** * 游戏启动页面 * * @param Request $request * @return \Illuminate\Http\JsonResponse */ public function gameLunch(Request $request) { // 获取请求参数 $gid = $request->input('gid'); $user = $request->user(); $userId = $user->UserID; $globalUID = $user->GlobalUID; // 检查用户渠道权限 (如果需要) if ($user->Channel != 99 && $user->Channel != 44) { http_response_code(404); exit(); } // 找到游戏卡片 GameCard::$enableStateCheck = false; // 记录游戏点击 $gamecard = GameCard::where('gid', $gid)->where('brand', 'LuckyStreak')->first(); $gamecard->increment('play_num', 1); LogGamecardClick::recordClick($gamecard->id, $userId); // 检查用户当前游戏状态 $inGameId = OuroGameService::getUserInGame($userId, $globalUID); if ($inGameId != intval($gamecard->id)) { Util::WriteLog('24680game', compact('inGameId', 'gamecard', 'user')); } // 获取用户语言 $lang = GlobalUserInfo::getLocale(); $supportedLangs = ['en', 'da', 'de', 'es', 'fi', 'fr', 'it', 'pt', 'ru', 'zh','ar','ur']; if (!in_array($lang, $supportedLangs)) { $lang = 'en'; } // 关闭加载动画 echo ""; // 设置缓存控制头 header("Cache-Control: no-cache, no-store, must-revalidate"); header("Expires: 0"); try { // 使用服务获取游戏启动URL $htmlContent = $this->luckyStreakService->getLaunchURLHTML($gid, $globalUID, $request->ip(), $lang); echo $htmlContent; exit(); } catch (\Exception $e) { Util::WriteLog('luckystreak_error', $e->getMessage()); echo ""; exit(); } } /** * 获取游戏列表 * * @param Request $request * @return \Illuminate\Http\JsonResponse */ public function gameList(Request $request) { try { // 使用服务获取游戏列表 $result = $this->luckyStreakService->getGamesList(); return response()->json($result); } catch (\Exception $e) { Util::WriteLog('luckystreak_error', $e->getMessage()); return response()->json([ 'error' => $e->getMessage() ], 500); } } /** * 获取游戏列表 * * @param Request $request * @return \Illuminate\Http\JsonResponse */ public function jackpot(Request $request) { try { // 使用服务获取游戏列表 $result = $this->luckyStreakService->getJackpot(); return response()->json($result); } catch (\Exception $e) { Util::WriteLog('luckystreak_error', $e->getMessage()); return response()->json([ 'error' => $e->getMessage() ], 500); } } /** * 获取第三方游戏提供商游戏列表 * * @param Request $request * @return \Illuminate\Http\JsonResponse */ public function providerGameList(Request $request) { dd($this->luckyStreakService->getCachedAuthToken()); try { // 使用服务获取提供商游戏列表 $result = $this->luckyStreakService->getProviderGamesList(); return response()->json($result); } catch (\Exception $e) { Util::WriteLog('luckystreak_provider_error', $e->getMessage()); return response()->json([ 'error' => $e->getMessage() ], 500); } } }