where('is_active', (bool) $params['is_active']); } // 결제 주기 필터 if (! empty($params['billing_cycle'])) { $query->ofCycle($params['billing_cycle']); } // 검색 (이름, 코드, 설명) if (! empty($params['search'])) { $search = $params['search']; $query->where(function ($q) use ($search) { $q->where('name', 'like', "%{$search}%") ->orWhere('code', 'like', "%{$search}%") ->orWhere('description', 'like', "%{$search}%"); }); } // 정렬 $sortBy = $params['sort_by'] ?? 'price'; $sortDir = $params['sort_dir'] ?? 'asc'; $query->orderBy($sortBy, $sortDir); $perPage = $params['per_page'] ?? 20; return $query->paginate($perPage); } /** * 활성 요금제 목록 (공개용) */ public function active(): Collection { return Plan::active() ->orderBy('price', 'asc') ->get(); } /** * 요금제 상세 */ public function show(int $id): Plan { return Plan::query() ->withCount(['subscriptions as active_subscriptions_count' => function ($q) { $q->where('status', 'active'); }]) ->findOrFail($id); } // ========================================================================= // 요금제 생성/수정/삭제 // ========================================================================= /** * 요금제 생성 */ public function store(array $data): Plan { $userId = $this->apiUserId(); return Plan::create([ 'name' => $data['name'], 'code' => $data['code'], 'description' => $data['description'] ?? null, 'price' => $data['price'], 'billing_cycle' => $data['billing_cycle'] ?? Plan::BILLING_MONTHLY, 'features' => $data['features'] ?? null, 'is_active' => $data['is_active'] ?? true, 'created_by' => $userId, 'updated_by' => $userId, ]); } /** * 요금제 수정 */ public function update(int $id, array $data): Plan { $userId = $this->apiUserId(); $plan = Plan::findOrFail($id); $plan->fill([ 'name' => $data['name'] ?? $plan->name, 'code' => $data['code'] ?? $plan->code, 'description' => $data['description'] ?? $plan->description, 'price' => $data['price'] ?? $plan->price, 'billing_cycle' => $data['billing_cycle'] ?? $plan->billing_cycle, 'features' => $data['features'] ?? $plan->features, 'is_active' => $data['is_active'] ?? $plan->is_active, 'updated_by' => $userId, ]); $plan->save(); return $plan->fresh(); } /** * 요금제 삭제 */ public function destroy(int $id): bool { $userId = $this->apiUserId(); $plan = Plan::findOrFail($id); // 활성 구독이 있으면 삭제 불가 $activeCount = $plan->subscriptions() ->whereIn('status', ['active', 'pending']) ->count(); if ($activeCount > 0) { throw new \Symfony\Component\HttpKernel\Exception\BadRequestHttpException( __('error.plan.has_active_subscriptions') ); } $plan->deleted_by = $userId; $plan->save(); $plan->delete(); return true; } /** * 요금제 활성/비활성 토글 */ public function toggle(int $id): Plan { $userId = $this->apiUserId(); $plan = Plan::findOrFail($id); $plan->is_active = ! $plan->is_active; $plan->updated_by = $userId; $plan->save(); return $plan; } }