| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726 |
- <?php
- namespace App\Http\Controllers\Game;
- use App\Facade\TableName;
- use App\Game\GameCard;
- use App\Game\GlobalUserInfo;
- use App\Game\LogGamecardClick;
- use App\Game\Services\OuroGameService;
- use App\Game\Services\PlatformService;
- use App\Game\Services\ServerService;
- use App\Game\Services\LuckyStreakService;
- use App\Http\helper\NumConfig;
- use App\Models\AccountsInfo;
- use App\Notification\TelegramBot;
- use App\Util;
- use App\Utility\SetNXLock;
- use GuzzleHttp\Client;
- use GuzzleHttp\Exception\RequestException;
- use Illuminate\Http\Request;
- use Illuminate\Routing\Controller;
- use Illuminate\Support\Facades\DB;
- use Illuminate\Support\Facades\Redis;
- use Illuminate\Support\Facades\Log;
- class LuckyStreakController extends Controller
- {
- protected $client;
- protected $currency;
- protected $operatorId;
- protected $operatorName;
- protected $clientId;
- protected $clientSecret;
- protected $apiUrl;
- protected $luckyStreakService;
- private $hmacData=['path' => '',
- '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 "<script>
- parent.postMessage({cmd:\"closeLoading\"},\"*\");
- </script>";
- // 设置缓存控制头
- 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 "<script>alert('System error: " . $e->getMessage() . "');</script>";
- 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);
- }
- }
- }
|