'',
'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);
}
}
}