113 lines
4.1 KiB
PHP
113 lines
4.1 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Models\OperatorCasino;
|
|
use App\Models\OperatorSession;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Str;
|
|
|
|
class OperatorController extends Controller
|
|
{
|
|
// Supported game slugs
|
|
private const VALID_GAMES = ['dice', 'crash', 'mines', 'plinko'];
|
|
|
|
/**
|
|
* GET /operator/games
|
|
*
|
|
* Returns the full game catalog with thumbnail URLs and launch paths.
|
|
*/
|
|
public function games(Request $request)
|
|
{
|
|
$baseUrl = rtrim((string) config('games.game_base_url', config('app.url')), '/');
|
|
|
|
$games = collect(config('games.catalog', []))->map(fn ($g) => array_merge($g, [
|
|
'thumbnail_url' => "{$baseUrl}/assets/games/{$g['slug']}.png",
|
|
'launch_path' => "/{$g['slug']}",
|
|
]));
|
|
|
|
return response()->json(['games' => $games]);
|
|
}
|
|
|
|
/**
|
|
* POST /operator/launch
|
|
*
|
|
* Creates a new operator game session and returns the launch URL.
|
|
*/
|
|
public function launch(Request $request)
|
|
{
|
|
/** @var OperatorCasino $casino */
|
|
$casino = $request->attributes->get('operator_casino');
|
|
|
|
$data = $request->validate([
|
|
'player_id' => ['required', 'string', 'max:255'],
|
|
'balance' => ['required', 'numeric', 'min:0'],
|
|
'currency' => ['required', 'string', 'size:3'],
|
|
'game' => ['required', 'string', 'in:' . implode(',', self::VALID_GAMES)],
|
|
// license_key is consumed by middleware; allow it in the body without failing validation
|
|
'license_key' => ['sometimes', 'string'],
|
|
]);
|
|
|
|
// Generate server seed for provably-fair and store it encrypted
|
|
$serverSeed = bin2hex(random_bytes(32));
|
|
$serverSeedHash = hash('sha256', $serverSeed);
|
|
|
|
$token = (string) Str::uuid();
|
|
$expiresAt = now()->addHours(4);
|
|
|
|
OperatorSession::create([
|
|
'session_token' => $token,
|
|
'operator_casino_id' => $casino->id,
|
|
'player_id' => $data['player_id'],
|
|
'game_slug' => $data['game'],
|
|
'currency' => strtoupper($data['currency']),
|
|
'start_balance' => $data['balance'],
|
|
'current_balance' => $data['balance'],
|
|
'server_seed' => encrypt($serverSeed),
|
|
'server_seed_hash' => $serverSeedHash,
|
|
'status' => 'active',
|
|
'expires_at' => $expiresAt,
|
|
]);
|
|
|
|
$baseUrl = rtrim((string) config('games.game_base_url', config('app.url')), '/');
|
|
$launchUrl = "{$baseUrl}/{$data['game']}?session={$token}";
|
|
|
|
return response()->json([
|
|
'session_token' => $token,
|
|
'launch_url' => $launchUrl,
|
|
'server_seed_hash' => $serverSeedHash,
|
|
'expires_at' => $expiresAt->toIso8601String(),
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* GET /operator/session/{token}
|
|
*
|
|
* Returns the current state of a session — including the final balance delta.
|
|
* The casino should call this after the player's session ends.
|
|
*/
|
|
public function session(Request $request, string $token)
|
|
{
|
|
/** @var OperatorCasino $casino */
|
|
$casino = $request->attributes->get('operator_casino');
|
|
|
|
$session = OperatorSession::where('session_token', $token)
|
|
->where('operator_casino_id', $casino->id)
|
|
->firstOrFail();
|
|
|
|
$session->expireIfNeeded();
|
|
|
|
return response()->json([
|
|
'session_token' => $session->session_token,
|
|
'player_id' => $session->player_id,
|
|
'game' => $session->game_slug,
|
|
'currency' => $session->currency,
|
|
'start_balance' => (float) $session->start_balance,
|
|
'current_balance' => (float) $session->current_balance,
|
|
'balance_delta' => round((float) $session->current_balance - (float) $session->start_balance, 4),
|
|
'status' => $session->status,
|
|
'expires_at' => $session->expires_at->toIso8601String(),
|
|
]);
|
|
}
|
|
}
|