Files
clinicpro/docs/api/subscription.md
T

9.0 KiB

Subscription API

مدیریت پنل‌های اشتراکی (Free / Basic / Professional).


GET /api/v1/subscription/plans

لیست پنل‌ها با دوره‌های فعال (عمومی — بدون auth).

پلن free: همه امکانات (patient_records, services, sms_panel, insurance) فعال‌اند؛ محدودیت‌هایش عددی‌اند — تعداد منشی (max_secretaries) و تعداد منبع (max_resources).

max_resources سقف منابع محیط است. مقدار -1 یعنی نامحدود. مقادیر شیپ‌شده: free = ۱، basic = ۳، professional = -1. اجرای این سقف در POST /api/v1/resource است — resource.md.

Response 200:

{
  "success": true,
  "data": [
    {
      "uuid": "...",
      "name": "free",
      "level": 0,
      "max_secretaries": 1,
      "max_resources": 1,
      "features": { "patient_records": true, "services": true, "sms_panel": true, "insurance": true },
      "active": true,
      "periods": []
    },
    {
      "uuid": "...",
      "name": "basic",
      "level": 1,
      "max_secretaries": 3,
      "max_resources": 3,
      "features": { "patient_records": true, "services": true, "sms_panel": false },
      "active": true,
      "periods": [
        {
          "uuid": "...",
          "plan_uuid": "...",
          "label": "یک ماهه",
          "duration_months": 1,
          "price_rials": 290000,
          "is_trial": false,
          "active": true,
          "sort_order": 1
        }
      ]
    }
  ]
}

GET /api/v1/subscription/my

اشتراک فعال کاربر جاری.

Permission: IS_AUTHENTICATED_FULLY

نکته: از نسخه فعلی، این endpoint برای ROLE_SECRETARY نیز کار می‌کند. منشی از طریق UserActiveContextRepository به db_uuid entity مربوطه (doctor یا clinic) دسترسی پیدا می‌کند و اشتراک همان entity برگردانده می‌شود.

Response 200:

{
  "success": true,
  "data": {
    "subscription": {
      "uuid": "...",
      "plan": { "name": "basic", "level": 1, "max_secretaries": 3, "max_resources": 3, "features": {...} },
      "period": { "label": "یک ماهه", "duration_months": 1, "price_rials": 290000 },
      "is_trial": false,
      "starts_at": 1718000000,
      "expires_at": 1720678400,
      "days_remaining": 30,
      "is_active": true
    },
    "used_trial": false,
    "effective_plan": { "name": "basic", "level": 1, "max_secretaries": 3, "max_resources": 3, "features": {...} }
  }
}

اگر اشتراک فعالی نداشت subscription برابر null است، اما effective_plan همیشه مقدار دارد: پلن اشتراک فعال، یا در نبود اشتراک، پلن پیش‌فرض free. فرانت‌اند برای تعیین دسترسی به امکانات (hasFeature) باید از effective_plan استفاده کند (نه subscription) تا کاربرانِ بدون اشتراک هم امکانات پلن free را داشته باشند. subscription/hasPlan صرفاً برای نمایش وضعیت اشتراک پولی است.

پاسخ کاهش‌یافته برای کاربرِ بدون مجوزِ subscription.view (2026-08)

پیش از این، منشیِ بدون این مجوز 403 می‌گرفت. نتیجه‌اش یک قفلِ دروغین در پنل بود: سایدبار هر آیتم feature-دار (پروندهٔ بیماران، بیمه) را با hasFeature() گیت می‌کند و بدون این پاسخ، features خالی می‌ماند و آیتم قفل و به صفحهٔ اشتراک هدایت می‌شد — حتی وقتی خودِ API آن قابلیت را به همان منشی می‌داد.

حالا پاسخ 200 است ولی فقط توانمندی‌های پلن را دارد:

{"success":true,"data":{
  "subscription": null,
  "used_trial": false,
  "effective_plan": { "features": { "patient_records": true, "…": true }, "max_secretaries": 1, "max_resources": 1 }
}}
  • subscription، used_trial و فیلدهای هویتی/سطحِ پلن (name, level, uuid, active, periods) در این حالت نمی‌آیند.
  • افشای تازه‌ای نیست: GET /subscription/plans عمومی است و همین features را (به‌همراه قیمت‌ها) برای همهٔ پلن‌ها می‌دهد.
  • سایر نقش‌ها و منشیِ دارای subscription.view همان پاسخ کامل بالا را می‌گیرند.

POST /api/v1/subscription/trial

فعال‌سازی تریال رایگان (یکبار برای هر entity).

Permission: IS_AUTHENTICATED_FULLY

Response 201: همان ساختار ClinicSubscription

Errors:

Code HTTP توضیح
ERR_TRIAL_ALREADY_USED 422 قبلاً از تریال استفاده شده
ERR_TRIAL_DISABLED 422 تریال غیرفعال است — یا SiteConfig: trial_enabled=0، یا پلن basic هیچ دورهٔ تریالِ active ندارد
ERR_FORBIDDEN_001 403 پروفایل doctor/clinic یافت نشد
ERR_NOT_FOUND_001 500 پلن basic وجود ندارد یا غیرفعال است (نصب ناقص)

POST /api/v1/subscription-payment

شروع پرداخت اشتراک.

Permission: IS_AUTHENTICATED_FULLY

Request Body:

{
  "gateway": "mellat",
  "amount_rials": 290000,
  "period_uuid": "uuid-of-subscription-period",
  "frontend_address": "https://example.com/payment-result"
}
فیلد نوع الزامی
gateway string (mellat|sep)
amount_rials integer
period_uuid string (UUID)
frontend_address string (URL)

Response 200:

{
  "success": true,
  "data": {
    "payment_uuid": "...",
    "redirect_url": "https://gateway.shaparak.ir/...",
    "order_id": "ORD-XXXXXXXXXXXXXXXX"
  }
}

GET /api/v1/subscription-payment/callback/{gateway}

callback درگاه پرداخت — پس از پرداخت موفق، ClinicSubscription به صورت خودکار ایجاد می‌شود (بر اساس period_uuid ذخیره‌شده در metadata پرداخت).


Admin Endpoints

GET /api/v1/admin/subscription/plans

Permission: ROLE_ADMIN — لیست همه پلن‌ها شامل غیرفعال‌ها (بر خلاف endpoint عمومی که فقط فعال‌ها را برمی‌گرداند). هر پلن فیلد active دارد و periods همیشه یک آرایه است (فقط دوره‌های فعال).

POST /api/v1/admin/subscription/plan

Permission: ROLE_ADMIN

{
  "name": "enterprise",
  "level": 3,
  "max_secretaries": 20,
  "max_resources": -1,
  "features": { "patient_records": true, "services": true, "sms_panel": true }
}

max_resources اختیاری است و پیش‌فرضش 1 — پلنِ ناشناخته نباید بی‌صدا نامحدود شود. مقدار -1 یعنی نامحدود. در PATCH هم همین فیلد پذیرفته می‌شود.

خطاها:

کد HTTP شرح
ERR_VALIDATION_001 422 name یا level ارسال نشده
ERR_VALIDATION_001 422 پلنی با این نام از قبل وجود دارد (نام یکتاست)

PATCH /api/v1/admin/subscription/plan/{uuid}

Permission: ROLE_ADMIN — ویرایش پنل (همه فیلدها اختیاری)

خطاها:

کد HTTP شرح
ERR_SUBSCRIPTION_NOT_FOUND 404 پلن یافت نشد
ERR_VALIDATION_001 422 نام جدید متعلق به پلن دیگری است

POST /api/v1/admin/subscription/period

Permission: ROLE_ADMIN

{
  "plan_uuid": "...",
  "label": "شش ماهه",
  "duration_months": 6,
  "price_rials": 1500000,
  "is_trial": false,
  "sort_order": 2
}

PATCH /api/v1/admin/subscription/period/{uuid}

Permission: ROLE_ADMIN — ویرایش دوره

DELETE /api/v1/admin/subscription/period/{uuid}

Permission: ROLE_ADMIN — غیرفعال کردن دوره (soft delete: active=false)

GET /api/v1/admin/subscription/report

Permission: ROLE_ADMIN

Query params: page, limit

{
  "success": true,
  "data": [
    {
      "uuid": "...",
      "entity_type": "clinic",
      "entity_id": 5,
      "is_trial": false,
      "starts_at": 1718000000,
      "expires_at": 1720678400,
      "plan_name": "basic",
      "plan_level": 1
    }
  ],
  "meta": { "totalRecords": 50, "totalPages": 3, "currentPage": 1 }
}

Error Codes

Code HTTP توضیح
ERR_SUBSCRIPTION_REQUIRED 403 قابلیت نیاز به پنل Basic+ دارد
ERR_TRIAL_ALREADY_USED 422 تریال قبلاً استفاده شده
ERR_TRIAL_DISABLED 422 تریال غیرفعال است
ERR_SUBSCRIPTION_NOT_FOUND 404 پنل یافت نشد