docs(api): correct the subscription/my enforcement claim

The previous note said GET /api/v1/subscription/my has no gate because it
returns 200 with every permission off. That was wrong. It gates deliberately
with a degraded payload instead of a 403: without subscription.view the
response drops the active subscription and used_trial, and effective_plan
keeps only features, max_secretaries and max_resources — no plan identity,
no billing. Verified against the running app both ways.

The 403 it does not throw is the point: FeatureGate and useSubscription need
capability flags on every page, so a 403 would break the whole panel.

Also adds .claude/prompt/service-catalog-permission-gate.md for the one real
gap, with the per-route analysis that was previously deferred.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
hamed
2026-08-07 18:38:36 +03:30
co-authored by Claude Opus 5
parent ccd869e442
commit 1d10f8c907
2 changed files with 273 additions and 1 deletions
+3 -1
View File
@@ -156,7 +156,9 @@ Create a secretary for a doctor.
| `sms` | `SmsWalletController` (balance/charge/logs/settings). endpointهای admin (قالب/ارسال) همچنان `ROLE_ADMIN` | view/create/update |
| `appointment_settings` | `AppointmentSettingsController::denyDoctorAccess``SecretaryAccessChecker::canForDoctor` (اسکوپِ پزشکِ تخصیص‌یافته + توگل). `clinic_uuid` برای محیطِ کلینیک لازم است | view/update |
| `clinic_doctors` (فقط کلینیک) | `ClinicController::detachDoctor` (delete)، `ClinicDoctorPermissionController` (view/update)، `ClinicInvitationController` (create/view/update/delete) via `SecretaryAccessChecker::canForClinic` | view/create/update/delete |
| `subscription` | `SubscriptionController::trial` (create)، `PaymentController::initiateSubscription` (create). ⚠ خواندنِ `GET /api/v1/subscription/my` گِیت **ندارد** — با همهٔ مجوزها خاموش هم ۲۰۰ می‌دهد؛ چون FeatureGate و useSubscription همه‌جا صداش می‌زنند، بستنش نیاز به بررسی جداگانه دارد | create |
| `subscription` | `SubscriptionController::my` (view — **پاسخِ کاهش‌یافته**، نه ۴۰۳)، `trial` (create)، `PaymentController::initiateSubscription` (create) | view/create |
**استثنای `subscription.view` — پاسخِ کاهش‌یافته به‌جای ۴۰۳:** `GET /api/v1/subscription/my` عمداً ۴۰۳ نمی‌دهد. بدون این مجوز `subscription` و `used_trial` تهی برمی‌گردند و از `effective_plan` فقط `features` و `max_secretaries` و `max_resources` می‌ماند؛ با مجوز، پلنِ کامل (`uuid`, `name`, `level`, `active`) و اشتراکِ فعال هم می‌آید. دلیلش این است که `FeatureGate` و `useSubscription` در همهٔ صفحات به سقف‌ها و فلگ‌های قابلیت نیاز دارند؛ ۴۰۳ کل پنل را می‌شکست. اطلاعاتِ هویتی و مالیِ اشتراک پشت مجوز می‌ماند.
نقش‌های غیرمنشی (`ROLE_CLINIC`/`ROLE_DOCTOR`/`ROLE_ADMIN`) از این چک عبور می‌کنند (`canOrNonSecretary` برایشان `true`). منشیِ بدون رابطهٔ فعال/context هیچ مجوزی ندارد → همه‌چیز `403`.