Files
starr-philproperties/apps/api/controllers/admin/documentation/tiers.md
T

8.8 KiB

Tiers Controller Documentation

File: controllers/admin/tiers.controller.js
Base URL: /api/admin/tiers
Guards: authenticate → requireAdmin() → adminLimiter


Table of Contents


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

{
  "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

{
  "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

{
  "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

{ "status": "success", "message": "Plan updated.", "data": { ... } }

Archive Plan

DELETE /api/admin/tiers/:id

Sets is_active = false then soft-deletes.

Response 200

{ "status": "success", "message": "Plan archived successfully." }

Bulk Archive Plans

POST /api/admin/tiers/bulk/archive

Request Body

{ "ids": [1, 2] }

Response 200

{
  "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

{ "status": "success", "message": "Plan restored successfully." }

Bulk Restore Plans

POST /api/admin/tiers/bulk/restore

Request Body

{ "ids": [1, 2] }

Response 200

{
  "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

{
  "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

{ "course_ids": [1, 2, 3] }

Pass [] to remove all courses from the plan.

Response 200

{ "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

{
  "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

{
  "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

{ "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

{
  "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

{
  "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.