Base Path /api/subscription
â„šī¸ Subscription state is updated asynchronously via Stripe webhooks at POST /api/webhook/stripe. After checkout completion, allow a few seconds for the webhook to finalize the subscription.
GET

Get All Plans

â–ļ
GET /api/subscription/plans

Returns all active subscription plans ordered by plan.order. Each plan includes its available price options (monthly, quarterly, annual).

Response
200 — Plans list
{
  "success": true,
  "data": {
    "plans": [
      {
        "id": "plan_basic",
        "name": "Basic",
        "description": "Perfect for individuals getting started",
        "currency": "usd",
        "order": 1,
        "status": "active",
        "color": "#6366f1",
        "isTrialAllowed": true,
        "trialDays": 14,
        "settings": { "max_tools": 5 },
        "metadata": ["5 tool listings", "Basic analytics"],
        "planPrices": [
          {
            "id": "pp_basic_monthly",
            "priceId": "price_1ABC...",
            "name": "Monthly",
            "months": 1,
            "price": 1900,
            "discount": 0
          }
        ]
      }
    ]
  }
}
GET

Get Plan by ID

â–ļ
GET /api/subscription/plans/:id
Path Parameters
ParamTypeDescription
idstringPlan ID
Response
200 — Single plan
{ "success": true, "data": { "plan": { /* same shape as above */ } } }
404 — Not found
{ "success": false, "errorCode": "PLAN_NOT_FOUND", "message": "Plan not found." }
GET

My Subscription

🔒 Auth Required â–ļ
GET /api/subscription/

Returns the current user's active subscription, including plan details and current billing price. Returns null if no subscription exists.

Response
200 — Active subscription
{
  "success": true,
  "data": {
    "subscription": {
      "id": "sub_xyz",
      "stripeSubscriptionId": "sub_1ABC...",
      "status": "active",
      "amount": 1900,
      "currency": "usd",
      "periodStart": "2026-04-01T00:00:00.000Z",
      "periodEnd": "2026-05-01T00:00:00.000Z",
      "cancelAtPeriodEnd": false,
      "plan": { "id": "plan_basic", "name": "Basic", "settings": { "max_tools": 5 } },
      "currentPlanPrice": { "name": "Monthly", "months": 1, "price": 1900 }
    }
  }
}
POST

Create Checkout Session

🔒 Auth Required â–ļ
POST /api/subscription/checkout/:planId

Creates a Stripe Checkout session. Redirects the user to Stripe's hosted payment page. On success, Stripe redirects to {APP_ORIGIN}/subscription/success?session_id=....

Path Parameters
ParamTypeDescription
planIdstringThe plan ID to subscribe to
Response
201 — Checkout URL
{
  "success": true,
  "message": "Checkout session created.",
  "data": {
    "url": "https://checkout.stripe.com/pay/cs_test_...",
    "sessionId": "cs_test_abc123"
  }
}
POST

Direct Card Payment

🔒 Auth Required â–ļ
POST /api/subscription/pay/:planId

Creates a subscription using a Stripe PaymentMethod ID (tokenized by Stripe.js on the frontend). Card details are never sent to this server.

Path Parameters
ParamTypeDescription
planIdstringThe plan ID to subscribe to
Request Body
FieldTypeRequiredDescription
paymentMethodIdstringrequiredStripe PaymentMethod ID — must start with pm_
Response
201 — Subscription initiated
{
  "success": true,
  "message": "Subscription initiated. Confirm payment on the client.",
  "data": {
    "subscriptionId": "sub_1ABC...",
    "status": "incomplete",
    "clientSecret": "pi_abc_secret_xyz"
  }
}
// clientSecret is passed to stripe.confirmCardPayment() on the frontend for 3DS
POST

Cancel Subscription

🔒 Auth Required â–ļ
POST /api/subscription/cancel

Schedules the subscription to cancel at the end of the current billing period. Access continues until periodEnd. Does not immediately cut off access.

Response
200 — Cancellation scheduled
{
  "success": true,
  "message": "Subscription will be canceled at the end of the current billing period.",
  "data": { "cancelAtPeriodEnd": true }
}
POST

Reactivate Subscription

🔒 Auth Required â–ļ
POST /api/subscription/reactivate

Reverses a pending cancellation. Only valid when cancelAtPeriodEnd = true and the subscription has not yet been fully canceled.

Response
200 — Reactivated
{
  "success": true,
  "message": "Subscription reactivated. It will continue after the current billing period.",
  "data": { "cancelAtPeriodEnd": false }
}
POST

Upgrade Plan

🔒 Auth Required â–ļ
POST /api/subscription/upgrade/:planId

Initiates a plan upgrade. Proration is applied automatically via Stripe (always_invoice). The plan change is finalized asynchronously when the prorated invoice's webhook fires.

Path Parameters
ParamTypeDescription
planIdstringThe new plan ID to upgrade to
Response
200 — Upgrade initiated
{
  "success": true,
  "data": {
    "message": "Plan upgrade initiated. Prorated invoice will be charged.",
    "newPlanId": "plan_premium"
  }
}
GET

Poll Upgrade Status

🔒 Auth Required â–ļ
GET /api/subscription/upgrade/status

Polls whether a pending plan upgrade has been finalized by the Stripe webhook. Poll every few seconds after initiating an upgrade.

Response
200 — Pending
{
  "success": true,
  "data": {
    "upgradeStatus": "pending",
    "currentPlanId": "plan_basic",
    "pendingPlanId": "plan_premium"
  }
}
200 — Completed
{
  "success": true,
  "data": {
    "upgradeStatus": "completed",
    "currentPlanId": "plan_premium",
    "currentPlanName": "Premium",
    "pendingPlanId": null
  }
}
GET

Billing Portal

🔒 Auth Required â–ļ
GET /api/subscription/billing-portal

Creates a Stripe Customer Portal session URL. Redirect the user to this URL to let them manage payment methods, download invoices, and cancel subscriptions directly in Stripe's hosted UI.

Response
200 — Portal URL
{
  "success": true,
  "data": {
    "url": "https://billing.stripe.com/session/..."
  }
}
GET

My Invoices

🔒 Auth Required â–ļ
GET /api/subscription/invoices
Query Parameters
ParamTypeDefaultDescription
pagenumber1Page number
limitnumber10Results per page
Response
200 — Invoice list
{
  "success": true,
  "data": {
    "invoices": [
      {
        "id": "inv_abc",
        "invoiceId": "in_1ABC...",
        "amountPaid": 1900,
        "amountDue": 1900,
        "currency": "usd",
        "status": "paid",
        "pdfUrl": "https://pay.stripe.com/invoice/in_1ABC/pdf",
        "hostedInvoiceUrl": "https://invoice.stripe.com/i/acct_.../in_1ABC",
        "createdAt": "2026-04-01T00:00:00.000Z"
      }
    ],
    "pagination": { "total": 5, "page": 1, "limit": 10, "totalPages": 1 }
  }
}
GET

All Subscriptions

🛡 Admin Only â–ļ
GET /api/subscription/admin/all

Returns all subscriptions across all users with a summary metrics block. Powers the admin billing dashboard. No caching — always live data.

Query Parameters
ParamTypeDefaultDescription
pagenumber1Page number
limitnumber20Results per page (max 200)
statusstring—active | past_due | canceled | trialing
searchstring—Filter by username or email (partial match)
Response
200 — Subscription list with summary
{
  "success": true,
  "data": {
    "summary": {
      "totalActive":    3,
      "monthlyRevenue": 377,   // USD, sum of active subscription amounts
      "pastDue":         1,
      "cancelled":       1
    },
    "subscriptions": [
      {
        "id":                   "sub_abc",
        "stripeSubscriptionId": "sub_1ABC...",
        "status":               "active",
        "amount":               29900,   // cents
        "currency":             "usd",
        "periodStart":          "2026-04-24T00:00:00.000Z",
        "periodEnd":            "2026-05-24T00:00:00.000Z",
        "cancelAtPeriodEnd":    false,
        "canceledAt":           null,
        "intervalId":           "1-month",
        "pendingPlanId":        null,
        "createdAt":            "2026-04-24T00:00:00.000Z",
        "user": {
          "id":       "usr_1",
          "username": "lawfirmABC",
          "email":    "abc@law.com",
          "avatar":   "https://..."
        },
        "plan": {
          "id":    "plan_enterprise",
          "name":  "Enterprise",
          "color": "#a855f7"
        }
      }
    ],
    "pagination": { "total": 5, "page": 1, "limit": 20, "totalPages": 1 }
  }
}
â„šī¸ summary is always computed across ALL subscriptions — it is not filtered by the status or search query params. The paginated list is filtered; the summary is not.
GET

All Plans (Admin)

🛡 Admin Only â–ļ
GET /api/subscription/plans/admin/all

Returns all plans regardless of status (including inactive / draft). Includes plan prices and live subscriber count per plan. No pagination — plan count is small.

Response
200 — All plans with subscriber counts
{
  "success": true,
  "data": {
    "plans": [
      {
        "id":              "plan_basic",
        "name":            "Basic",
        "description":    "For individuals",
        "currency":        "usd",
        "color":           "#6366f1",
        "status":          "active",
        "order":           1,
        "isTrialAllowed":  true,
        "trialDays":       14,
        "settings":        { "max_tools": 5 },
        "metadata":        ["5 tool listings", "Basic analytics"],
        "subscriberCount": 42,
        "planPrices": [
          {
            "id":       "pp_basic_monthly",
            "priceId": "price_1ABC...",
            "name":    "Monthly",
            "months":  1,
            "price":   2900,
            "discount": 0
          }
        ]
      }
    ]
  }
}
â„šī¸ Unlike the public GET /plans which only returns status = active, this endpoint returns all plans including inactive ones. Use it to manage plan visibility in the admin dashboard.
GET

All Invoices (Admin)

🛡 Admin Only â–ļ
GET /api/subscription/invoices/admin/all

Returns all invoices across all users. Includes user info and plan name via the subscription relation. Ordered by newest first.

Query Parameters
ParamTypeDefaultDescription
pagenumber1Page number
limitnumber20Results per page (max 200)
statusstring—paid | open | void | uncollectible
searchstring—Filter by username or email (partial match)
Response
200 — All invoices
{
  "success": true,
  "data": {
    "invoices": [
      {
        "id":                   "inv_abc",
        "invoiceId":            "in_1ABC...",
        "stripeSubscriptionId": "sub_1ABC...",
        "amountPaid":           29900,
        "amountDue":            29900,
        "currency":             "usd",
        "status":               "paid",
        "pdfUrl":               "https://pay.stripe.com/invoice/.../pdf",
        "hostedInvoiceUrl":     "https://invoice.stripe.com/i/...",
        "createdAt":            "2026-04-24T00:00:00.000Z",
        "user": {
          "id":       "usr_1",
          "username": "lawfirmABC",
          "email":    "abc@law.com",
          "avatar":   "https://..."
        },
        "subscription": {
          "plan": {
            "id":    "plan_enterprise",
            "name":  "Enterprise",
            "color": "#a855f7"
          }
        }
      }
    ],
    "pagination": { "total": 18, "page": 1, "limit": 20, "totalPages": 1 }
  }
}
PATCH

Update Plan Settings

🛡 Admin Only â–ļ
PATCH /api/subscription/plans/:id/settings
Path Parameters
ParamTypeDescription
idstringPlan ID
Request Body
FieldTypeRequiredDescription
max_toolsintegerrequiredMax tools allowed. -1 = unlimited
default_order_type"ASC" | "DESC"optionalDefault sort order for the plan
Response
200 — Updated plan
{
  "success": true,
  "message": "Plan settings updated.",
  "data": { "plan": { /* plan object with new settings */ } }
}