Files
starr-philproperties/controllers/admin/documentation/tiers.md
T
kennethobsequio 439bb33f77 ready to test
Testing

Signed-off-by: Kenneth Obsequio <k80308392@gmail.com>
2026-06-22 10:06:58 +08:00

407 lines
8.8 KiB
Markdown

# Tiers Controller Documentation
**File:** `controllers/admin/tiers.controller.js`
**Base URL:** `/api/admin/tiers`
**Guards:** `authenticate → requireAdmin() → adminLimiter`
---
## Table of Contents
- [Tier Plans](#tier-plans)
- [Get All Plans](#get-all-plans)
- [Get Single Plan](#get-single-plan)
- [Create Plan](#create-plan)
- [Update Plan](#update-plan)
- [Archive Plan](#archive-plan)
- [Bulk Archive Plans](#bulk-archive-plans)
- [Restore Plan](#restore-plan)
- [Bulk Restore Plans](#bulk-restore-plans)
- [Get Plan Field Values](#get-plan-field-values)
- [Plan Courses](#plan-courses)
- [Get Plan Courses](#get-plan-courses)
- [Sync Plan Courses](#sync-plan-courses)
- [User Tiers](#user-tiers)
- [Get User Tiers](#get-user-tiers)
- [Grant Tier](#grant-tier)
- [Revoke Tier](#revoke-tier)
- [Payments](#payments)
- [Get All Payments](#get-all-payments)
- [Get Single Payment](#get-single-payment)
- [Get Payment Field Values](#get-payment-field-values)
---
## Notes
- Pending payments older than **60 minutes** are automatically expired before any payment list/detail call.
- Revoking a tier immediately creates a new `free` tier row for the user (auto-downgrade).
- `plan_id` is serialized as a string in create responses to avoid BigInt overflow in JS.
- A plan's `tier` field cannot be updated — only `label`, `duration_days`, `price`, `currency`, and `is_active`.
---
## Tier Plans
### Get All Plans
**`GET /api/admin/tiers`**
Returns paginated plans. Pass `?archived=true` to see soft-deleted plans instead.
### Query Parameters
| Parameter | Type | Required | Description |
|-----------|---------|----------|-------------|
| `archived` | boolean | No | `true` to show archived plans only |
| `page` | number | No | Default: `1` |
| `limit` | number | No | Default: `10` |
### Response `200`
```json
{
"status": "success",
"message": "Plans retrieved.",
"data": [...],
"pagination": { "page": 1, "limit": 10, "total": 4, "totalPages": 1 }
}
```
---
### Get Single Plan
**`GET /api/admin/tiers/:id`**
### Response `200`
```json
{
"status": "success",
"message": "Plan retrieved.",
"data": {
"plan_id": 1,
"tier": "premium",
"label": "Premium Monthly",
"duration_days": 30,
"price": "9.99",
"currency": "USD",
"is_active": true,
"createdAt": "...",
"updatedAt": "..."
}
}
```
---
### Create Plan
**`POST /api/admin/tiers`**
### Request Body
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `tier` | string | **Yes** | `premium` or `exclusive` |
| `label` | string | **Yes** | Human-readable plan name |
| `duration_days` | number | **Yes** | Access duration in days |
| `price` | number | **Yes** | Plan price (decimal) |
| `currency` | string | No | ISO 4217. Default: `USD` |
### Response `201`
```json
{
"status": "success",
"message": "Plan created.",
"data": { "plan_id": "5", "tier": "premium", ... }
}
```
---
### Update Plan
**`PUT /api/admin/tiers/:id`**
Only `label`, `duration_days`, `price`, `currency`, and `is_active` are updatable.
### Response `200`
```json
{ "status": "success", "message": "Plan updated.", "data": { ... } }
```
---
### Archive Plan
**`DELETE /api/admin/tiers/:id`**
Sets `is_active = false` then soft-deletes.
### Response `200`
```json
{ "status": "success", "message": "Plan archived successfully." }
```
---
### Bulk Archive Plans
**`POST /api/admin/tiers/bulk/archive`**
### Request Body
```json
{ "ids": [1, 2] }
```
### Response `200`
```json
{
"status": "success",
"message": "2 plan(s) archived successfully.",
"archived_ids": [1, 2],
"skipped_ids": []
}
```
---
### Restore Plan
**`POST /api/admin/tiers/:id/restore`**
Restores plan and sets `is_active = true`.
### Response `200`
```json
{ "status": "success", "message": "Plan restored successfully." }
```
---
### Bulk Restore Plans
**`POST /api/admin/tiers/bulk/restore`**
### Request Body
```json
{ "ids": [1, 2] }
```
### Response `200`
```json
{
"status": "success",
"message": "2 plan(s) restored successfully.",
"restored_ids": [1, 2],
"skipped_ids": []
}
```
---
### Get Plan Field Values
**`GET /api/admin/tiers/field-values`**
Returns distinct filterable field values for tier plans.
---
## Plan Courses
### Get Plan Courses
**`GET /api/admin/tiers/:id/courses`**
Returns the list of courses linked to this plan.
### Response `200`
```json
{
"status": "success",
"message": "Plan courses retrieved.",
"data": [
{ "course_id": 1, "title": "Intro to Python", "course_code": "PY-101", "subscription": "premium", "level": "beginner" }
]
}
```
---
### Sync Plan Courses
**`POST /api/admin/tiers/:id/courses`**
Replaces the full set of courses for this plan. Removes all existing assignments first, then inserts the new set. A course can only belong to one plan at a time — existing assignments to other plans are cleared automatically.
### Request Body
```json
{ "course_ids": [1, 2, 3] }
```
Pass `[]` to remove all courses from the plan.
### Response `200`
```json
{ "status": "success", "message": "Plan courses updated." }
```
---
## User Tiers
### Get User Tiers
**`GET /api/admin/tiers/users/:id/tiers`**
Returns the full tier history for a user, newest first. Includes `grantedByUser` and `revokedByUser` info.
### Response `200`
```json
{
"status": "success",
"message": "User tiers retrieved.",
"data": [
{
"tier_id": 3,
"user_id": 42,
"tier": "premium",
"status": "active",
"starts_at": "2026-06-01T00:00:00.000Z",
"expires_at": "2026-07-01T00:00:00.000Z",
"granted_by": 1,
"revoked_by": null,
"revoked_at": null,
"notes": "Trial promotion",
"grantedByUser": { "user_id": 1, "email": "admin@example.com" },
"revokedByUser": null
}
]
}
```
---
### Grant Tier
**`POST /api/admin/tiers/users/tiers/grant`**
- Expires all currently active tiers for the user before creating the new one.
- `expires_at` is calculated as `now + plan.duration_days`.
### Request Body
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `user_id` | number | **Yes** | Target user |
| `tier` | string | **Yes** | `premium` or `exclusive` |
| `plan_id` | number | **Yes** | Must match the plan's tier |
| `notes` | string | No | Optional admin note |
### Response `201`
```json
{
"status": "success",
"message": "Tier granted.",
"data": { "tier_id": 4, "user_id": 42, "tier": "premium", "status": "active", ... }
}
```
### Error Responses
| Status | Message |
|--------|---------|
| `400` | `user_id, tier, and plan_id are required.` |
| `400` | `Plan tier mismatch.` |
| `404` | `User not found.` |
| `404` | `Plan not found or inactive.` |
---
### Revoke Tier
**`PATCH /api/admin/tiers/users/tiers/:tid/revoke`**
- Sets the tier's status to `revoked` and records `revoked_by` / `revoked_at`.
- Automatically creates a new `free` tier row for the user (auto-downgrade note: `"Auto-downgrade after revoke."`).
### Response `200`
```json
{ "status": "success", "message": "Tier revoked. User downgraded to free." }
```
### Error Responses
| Status | Message |
|--------|---------|
| `400` | `Tier is not active.` |
| `404` | `Tier record not found.` |
---
## Payments
### Get All Payments
**`GET /api/admin/tiers/payments`**
Returns paginated payment records. Stale pending payments (>60 min old) are expired before the query runs.
### Query Parameters
| Parameter | Type | Required | Description |
|-----------|--------|----------|-------------|
| `page` | number | No | Default: `1` |
| `limit` | number | No | Default: `10` |
| `filters` | array | No | JSON filter array |
### Response `200`
```json
{
"status": "success",
"message": "Payments retrieved.",
"data": [
{
"payment_id": 1,
"user_id": 42,
"plan_id": 1,
"status": "completed",
"amount": "9.99",
"currency": "USD",
"promo_code": null,
"discount": "0.00",
"provider": "paypal",
"paid_at": "2026-06-01T10:00:00.000Z",
"user": { "user_id": 42, "email": "user@example.com" },
"plan": { "plan_id": 1, "label": "Premium Monthly", "tier": "premium", "duration_days": 30 }
}
],
"pagination": { ... }
}
```
---
### Get Single Payment
**`GET /api/admin/tiers/payments/:id`**
Returns full payment detail including `user`, `plan`, and `tier` associations.
### Response `200`
```json
{
"status": "success",
"message": "Payment retrieved.",
"data": {
"payment_id": 1,
"provider_payload": { "order_id": "...", "capture_id": "...", ... },
"user": { ... },
"plan": { ... },
"tier": { ... }
}
}
```
---
### Get Payment Field Values
**`GET /api/admin/tiers/payments/field-values`**
Returns distinct filterable field values for payments. `provider_payload` is excluded.