# Subscription Packages API Documentation

Public pricing-page API. Each package unlocks one or more categories, with a limit on how many prompts a buyer can purchase from each. A package with one category is `single`; a package with several categories is a `combo`.

- **Base URL:** `http://localhost:8000/api`
- **Auth:** none (public). Only packages with `is_active = true` are returned.
- **Headers:** `Accept: application/json`

## How Packages Work

A package sets **how many prompts a buyer can purchase from each category**.

| Package | Categories and limits | `type` | `total_prompt_limit` |
|---------|-----------------------|--------|----------------------|
| Marketing Pack | Marketing → 100 | `single` | 100 |
| Creator Combo | Category 1 → 100, Category 2 → 50, Category 3 → 25 | `combo` | 175 |

- A **single** package has exactly one category.
- A **combo** package has two or more categories, each with its own limit.
- Each limit applies only to its own category. In the combo above, the buyer can purchase up to 50 prompts from Category 2, even if they haven't used any of Category 1's 100.
- Every category in a package has a limit of at least 1, and a category appears only once per package.

## Endpoints

| Method | Endpoint | Purpose |
|--------|----------|---------|
| GET | `/subscription-packages` | List all active packages (ordered by `sort_order`) |
| GET | `/subscription-packages/{slug}` | Single package by slug |

## Package Object

| Field | Type | Description |
|-------|------|-------------|
| `id` | int | |
| `title` | string | Package name, e.g. "Pro" |
| `slug` | string | URL-friendly unique key, generated from the title |
| `subtitle` | string\|null | Short tagline |
| `description` | string\|null | Longer description |
| `price` | number | Numeric price (`0` = free) |
| `price_formatted` | string | e.g. `৳499.00` or `Free` |
| `is_free` | bool | |
| `type` | string | `single` (one category) or `combo` (several categories) |
| `categories` | object[] | `{ id, name, slug, prompt_limit }` — prompts a buyer may purchase from each category, ordered by name |
| `total_prompt_limit` | int | Sum of all category `prompt_limit`s |
| `features` | string[] | Feature list, in display order |
| `is_popular` | bool | Highlight as "Most popular" |

## 1. List Packages

`GET /api/subscription-packages`

**200 OK**
```json
{
  "success": true,
  "data": [
    {
      "id": 1,
      "title": "Prompts Starter",
      "slug": "prompts-starter",
      "subtitle": "One category, 100 prompts",
      "description": "Purchase up to 100 prompts from the Prompts category.",
      "price": 499,
      "price_formatted": "৳499.00",
      "is_free": false,
      "type": "single",
      "categories": [
        { "id": 2, "name": "Prompts", "slug": "prompts", "prompt_limit": 100 }
      ],
      "total_prompt_limit": 100,
      "features": ["100 prompts from Prompts", "Lifetime access to purchased prompts"],
      "is_popular": false
    },
    {
      "id": 2,
      "title": "Creator Combo",
      "slug": "creator-combo",
      "subtitle": "Three categories in one package",
      "description": "Mix prompts from Prompts, Tutorials and Tools.",
      "price": 1299,
      "price_formatted": "৳1,299.00",
      "is_free": false,
      "type": "combo",
      "categories": [
        { "id": 2, "name": "Prompts", "slug": "prompts", "prompt_limit": 100 },
        { "id": 4, "name": "Tools", "slug": "tools", "prompt_limit": 50 },
        { "id": 1, "name": "Tutorials", "slug": "tutorials", "prompt_limit": 50 }
      ],
      "total_prompt_limit": 200,
      "features": ["100 prompts from Prompts", "50 prompts from Tutorials", "50 prompts from Tools"],
      "is_popular": true
    }
  ]
}
```

## 2. Get One Package

`GET /api/subscription-packages/creator-combo`

**200 OK**
```json
{ "success": true, "data": { "id": 2, "title": "Creator Combo", "slug": "creator-combo", "...": "same shape as above" } }
```

**404** – unknown or inactive slug
```json
{ "message": "No query results for model [App\\Models\\SubscriptionPackage]." }
```

With `APP_DEBUG=true` the 404 body also contains `exception`, `file`, `line` and `trace`; rely only on the status code and `message`.

## Frontend Usage

- Show a **Combo** badge when `type === "combo"`.
- List each entry in `categories` as "`prompt_limit` prompts from `name`", for example "100 prompts from Marketing".
- Show `total_prompt_limit` as the package's headline count, for example "175 prompts".
- Use `price_formatted` for display and `price` / `is_free` for logic.
- `categories` can be an empty array when an admin hasn't set limits on an older package yet. Hide those packages or show them without a limit list.
- Highlight packages where `is_popular` is `true`.

```ts
type PackageCategory = { id: number; name: string; slug: string; prompt_limit: number };

type SubscriptionPackage = {
  id: number;
  title: string;
  slug: string;
  subtitle: string | null;
  description: string | null;
  price: number;
  price_formatted: string;
  is_free: boolean;
  type: "single" | "combo";
  categories: PackageCategory[];
  total_prompt_limit: number;
  features: string[];
  is_popular: boolean;
};
```

## cURL

```bash
curl -H "Accept: application/json" http://localhost:8000/api/subscription-packages
curl -H "Accept: application/json" http://localhost:8000/api/subscription-packages/creator-combo
```

## Database

Table `subscription_packages`: `title`, `slug` (unique, auto-generated), `subtitle`, `description`, `price` (decimal 10,2), `features` (JSON array of strings), `is_popular`, `is_active`, `sort_order`, timestamps.

Table `category_subscription_package`: `subscription_package_id`, `category_id` (unique together), `prompt_limit` (unsigned int), timestamps.

## Managing Packages (Admin)

Admins manage packages in the dashboard under **Subscriptions** (`http://localhost:8000/subscriptions`):

- **Create / Edit:** title, subtitle, description, price, features (one per line), sort order, the Popular and Active switches, and **category prompt limits**: one row per category. Use **Add category** for a combo package.
- **Slug:** not entered by hand. It's generated from the title (`Creator Combo` → `creator-combo`, or `creator-combo-2` if taken) and changes only when the title changes.
- **Status:** click a package's status badge to make it active or inactive. Inactive packages are hidden from this API.
- **Delete:** asks for confirmation. Its category limits are removed with it.

Seeded packages (Prompts Starter, Creator Combo): `php artisan db:seed --class=SubscriptionPackageSeeder`.

## Changelog

- **2026-10-14:** Packages are now based on category limits. Removed `billing_cycle` and `billing_cycle_label`. Added `type`, `categories[].prompt_limit` and `total_prompt_limit`. The slug is now generated automatically.

## Code

- Model: `app/Models/SubscriptionPackage.php` (`categories()` relation, `isCombo()`, `totalPromptLimit()`, `uniqueSlug()`)
- Migration: `database/migrations/2026_10_14_120000_add_category_limits_to_subscription_packages.php`
- Admin page: `resources/views/pages/subscriptions/⚡index.blade.php`
- Controller: `app/Http/Controllers/Api/SubscriptionPackageController.php`
- Routes: `routes/api.php` · Tests: `tests/Feature/SubscriptionPackageApiTest.php`, `tests/Feature/SubscriptionPackageManagementTest.php`
