lotteryAdmin(); abort_if($admin === null, 401); try { $agentProfileService->assertActorMayCreatePlayer($admin); } catch (ValidationException $e) { return ApiMessage::errorResponse( $request, 'admin.player_create_capability_forbidden', ErrorCode::AdminForbidden->value, $e->errors(), 403, ); } $siteCode = (string) $request->validated('site_code'); if (! AdminSiteScope::siteCodeAllowed($admin, $siteCode)) { return ApiMessage::errorResponse($request, 'admin.player_create_site_forbidden', ErrorCode::AdminForbidden->value, null, 403); } $isNative = $request->filled('password'); $sitePlayerId = (string) ($request->validated('site_player_id') ?? ''); if ($isNative) { $sitePlayerId = $sitePlayerId !== '' ? $sitePlayerId : $this->generateNativeSitePlayerId($siteCode); } if ($sitePlayerId === '') { return ApiMessage::errorResponse($request, 'admin.player_site_player_id_required', ErrorCode::ValidationFailed->value, null, 422); } $exists = Player::query() ->where('site_code', $siteCode) ->where('site_player_id', $sitePlayerId) ->exists(); if ($exists) { return ApiMessage::errorResponse($request, 'admin.player_already_registered', ErrorCode::ValidationFailed->value, null, 422); } if ($isNative) { $username = trim((string) $request->validated('username', '')); if ($username === '') { return ApiMessage::errorResponse($request, 'admin.player_native_username_required', ErrorCode::ValidationFailed->value, null, 422); } $usernameTaken = Player::query() ->where('site_code', $siteCode) ->where('username', $username) ->where('auth_source', PlayerAuthSource::LOTTERY_NATIVE) ->exists(); if ($usernameTaken) { return ApiMessage::errorResponse($request, 'admin.player_username_taken', ErrorCode::ValidationFailed->value, null, 422); } } $agentNodeId = $admin->isSuperAdmin() ? $this->resolveAgentNodeIdForSuperAdmin($request->validated('agent_node_id'), $siteCode) : $admin->primaryAgentNodeId(); if ($agentNodeId === null) { return ApiMessage::errorResponse( $request, 'admin.player_create_agent_required', ErrorCode::ValidationFailed->value, null, 422, ); } if (! $admin->isSuperAdmin()) { $agent = AdminAgentScope::primaryAgentNode($admin); if ($agent === null || (int) $agentNodeId !== (int) $agent->id) { return ApiMessage::errorResponse($request, 'admin.player_create_agent_forbidden', ErrorCode::AdminForbidden->value, null, 403); } } $agent = AgentNode::query()->findOrFail($agentNodeId); $rebateRate = 0.0; $extraRebateRate = 0.0; if ($request->has('rebate_rate')) { $rebateRate = (float) $request->input('rebate_rate', 0) / 100; $extraRebateRate = (float) $request->input('extra_rebate_rate', 0) / 100; $rebateLimitValidator->assertPlayerRebateWithinAgent( $agent, $rebateRate, $extraRebateRate, ); } $creditLimit = $request->has('credit_limit') ? (int) $request->input('credit_limit', 0) : ($isNative ? 0 : 0); $player = DB::transaction(function () use ( $agent, $agentProfileService, $playerCreditService, $request, $isNative, $siteCode, $agentNodeId, $sitePlayerId, $creditLimit, $rebateRate, $extraRebateRate, ): Player { $agentProfileService->assertMayIncreasePlayerCredit($agent, $creditLimit); $player = Player::query()->create([ 'site_code' => $siteCode, 'agent_node_id' => $agentNodeId, 'site_player_id' => $sitePlayerId, 'auth_source' => $isNative ? PlayerAuthSource::LOTTERY_NATIVE : PlayerAuthSource::MAIN_SITE_SSO, 'funding_mode' => $isNative ? PlayerFundingMode::CREDIT : PlayerFundingMode::WALLET, 'username' => $isNative ? trim((string) $request->validated('username')) : $request->validated('username'), 'password_hash' => $isNative ? Hash::make((string) $request->validated('password')) : null, 'nickname' => $request->validated('nickname'), 'default_currency' => $request->validated('default_currency', 'NPR'), 'status' => $request->validated('status', 0), ]); if ($request->has('credit_limit') || $isNative) { $playerCreditService->upsertAccount($player, [ 'credit_limit' => $creditLimit, ]); } $agentProfileService->refreshAllocatedCredit($agent); if ($request->has('rebate_rate')) { DB::table('player_rebate_profiles')->insert([ 'player_id' => $player->id, 'game_type' => '*', 'inherit_from_agent' => false, 'rebate_rate' => $rebateRate, 'extra_rebate_rate' => $extraRebateRate, 'created_at' => now(), 'updated_at' => now(), ]); } return $player; }); return ApiResponse::success(PlayerApiPresenter::listItem($player))->setStatusCode(201); } private function resolveAgentNodeIdForSuperAdmin(mixed $requested, string $siteCode): ?int { if ($requested !== null && (int) $requested > 0) { return (int) $requested; } $siteId = AdminSite::query()->where('code', $siteCode)->value('id'); if ($siteId === null) { return null; } $rootId = AgentNode::query() ->where('admin_site_id', (int) $siteId) ->where('depth', 0) ->value('id'); return $rootId !== null ? (int) $rootId : null; } private function generateNativeSitePlayerId(string $siteCode): string { $prefix = strtoupper(substr(preg_replace('/[^A-Za-z]/', '', $siteCode) ?: 'LP', 0, 2)); $prefix = str_pad($prefix, 2, 'P'); do { $candidate = sprintf('%s%06d', $prefix, random_int(0, 999999)); $exists = Player::query() ->where('site_code', $siteCode) ->where('site_player_id', $candidate) ->exists(); } while ($exists); return $candidate; } }