5.2 KiB
5.2 KiB
تسک ۱۱: پنل اشتراکی (Subscription Tiers)
توضیح
پیادهسازی سیستم پنلهای اشتراکی سهسطحی. ادمین دورهها و قیمتها را تعریف میکند. این تسک gate check برای تسکهای ۱۲، ۱۳، ۱۵ فراهم میکند.
نکته مهم: /api/v1/subscription-payment و callback آن از قبل در PaymentController موجود است.
در این تسک فقط باید بعد از callback موفق، ClinicSubscription ساخته شود + endpoint های Subscription خودش.
سطوح پنل
| Level | Name | منشی | patient_records | services |
|---|---|---|---|---|
| 0 | free | ۱ | ❌ | ❌ |
| 1 | basic | ۲ | ✅ | ✅ |
| 2 | professional | ۵ | ✅ | ✅ |
Endpoint ها
| متد | مسیر | Permission | توضیح |
|---|---|---|---|
| GET | /api/v1/subscription/plans |
public | لیست پنلها + دورهها + قیمت |
| GET | /api/v1/subscription/my |
doctor/clinic | اشتراک فعال + used_trial |
| POST | /api/v1/subscription/trial |
doctor/clinic | فعالسازی تریال Basic (یکبار) |
| GET | /api/v1/admin/subscription/plans |
ROLE_ADMIN | لیست مدیریت پنلها |
| POST | /api/v1/admin/subscription/plan |
ROLE_ADMIN | ایجاد پنل |
| PATCH | /api/v1/admin/subscription/plan/{uuid} |
ROLE_ADMIN | ویرایش پنل |
| POST | /api/v1/admin/subscription/period |
ROLE_ADMIN | افزودن دوره |
| PATCH | /api/v1/admin/subscription/period/{uuid} |
ROLE_ADMIN | ویرایش دوره/قیمت |
| DELETE | /api/v1/admin/subscription/period/{uuid} |
ROLE_ADMIN | غیرفعالسازی دوره |
| GET | /api/v1/admin/subscription/report |
ROLE_ADMIN | گزارش فروش + تریالها |
موجود (تغییر نمیکند):
POST /api/v1/subscription-payment— شروع پرداخت (body:{ period_uuid })GET|POST /api/v1/subscription-payment/callback/{gateway}— callback gateway
پیشنیازها
- تسک ۰۲ (Auth/JWT)
- تسک ۱۵-payment (PaymentController — موجود)
زمان تخمینی
۱۴ تا ۱۶ ساعت
نمونه Request
POST /api/v1/subscription/trial
{}
(body خالی — entity از JWT گرفته میشود)
POST /api/v1/admin/subscription/period
{
"plan_uuid": "uuid-of-basic-plan",
"label": "۶ ماهه",
"duration_months": 6,
"price_rials": 1200000,
"is_trial": false,
"sort_order": 3
}
POST /api/v1/subscription-payment (موجود)
{
"period_uuid": "uuid-of-selected-period",
"gateway": "mellat"
}
نمونه Response
GET /api/v1/subscription/plans
{
"success": true,
"data": [
{
"uuid": "...",
"name": "basic",
"level": 1,
"max_secretaries": 2,
"features": { "patient_records": true, "services": true, "sms_panel": false },
"periods": [
{ "uuid": "...", "label": "تریال ۱ ماهه", "duration_months": 1, "price_rials": 0, "is_trial": true },
{ "uuid": "...", "label": "۱ ماهه", "duration_months": 1, "price_rials": 250000, "is_trial": false },
{ "uuid": "...", "label": "۶ ماهه", "duration_months": 6, "price_rials": 1200000, "is_trial": false }
]
}
]
}
GET /api/v1/subscription/my
{
"success": true,
"data": {
"plan": { "name": "basic", "level": 1, "features": { "patient_records": true, "services": true } },
"period": { "label": "۶ ماهه", "duration_months": 6 },
"is_trial": false,
"starts_at": 1718000000,
"expires_at": 1733360000,
"used_trial": true,
"days_remaining": 42
}
}
وقتی اشتراک فعال ندارد:
{
"success": true,
"data": {
"plan": { "name": "free", "level": 0 },
"expires_at": null,
"used_trial": false
}
}
POST /api/v1/subscription/trial (موفق)
{
"success": true,
"data": {
"plan": "basic",
"starts_at": 1718000000,
"expires_at": 1720678400
}
}
POST /api/v1/subscription/trial (خطا — قبلاً استفاده شده)
{
"success": false,
"errors": [{ "code": "ERR_TRIAL_ALREADY_USED", "message": "قبلاً از تریال استفاده کردهاید" }]
}
قوانین تریال
- فقط برای پنل basic (level=1)
- هر entity یکبار — constraint
UNIQUE(entity_type, entity_id, is_trial)+ مقدارis_trial=1در DB - بدون پرداخت —
payment_id = null - بعد از انقضا → برگشت به Free (دادهها حفظ میشوند)
- ادمین میتواند تریال را کلاً غیرفعال کند:
SiteConfig.trial_enabled = false
قانون تمدید
expires_at جدید = max(expires_at فعلی, time()) + duration_months × 30 × 86400
یعنی اگر اشتراک هنوز منقضی نشده، تمدید از تاریخ انقضا محاسبه میشود (نه از now).
gate check در تسکهای بعدی
// SubscriptionService::hasFeature('patient_records') → bool
// false → $this->error(ErrorCodes::ERR_SUBSCRIPTION_REQUIRED, '...', 403)