Tenant Plans
B2B2C plan builder allowing tenants to create subscription plans for their end-users with feature bundling, pricing, and lifecycle management.
Tenant Plans enable SCRIPE's B2B2C model — the platform operator sells subscriptions to tenants (B2B), and those tenants can in turn create their own subscription plans for their end-users (B2C). This is the 'Tier 2' subscription system. Each tenant can define multiple plans with different features, pricing, and billing cycles.
B2B2C Subscription Model
The standard SCRIPE Entitlements module manages Tenant → Platform subscriptions (Tier 1). The Tenant Plans module adds Tier 2: User → Tenant subscriptions. This two-tier model enables SaaS resellers, multi-sided marketplaces, and platform operators who want to monetize their end-users.
TenantPlan Entity
A TenantPlan is created by a tenant admin and defines a subscription offering for their end-users. Plans are scoped to the creating tenant and are not visible to other tenants.
| Field | Type | Description |
|---|---|---|
| TenantId | Guid | The tenant that owns and manages this plan |
| Name | string(200) | Internal name — unique per tenant |
| DisplayNameEn / DisplayNameAr | string? | Localized display titles shown on pricing pages (up to 300 chars each) |
| Description | string? | Marketing description for the plan (pricing page copy, up to 2000 chars) |
| Tagline | string? | Short tagline (e.g. 'Best for growing teams', up to 200 chars) |
| Status | PlanStatus | Draft | Published | Archived — plan lifecycle state |
| IsPublic | bool | Whether this plan is visible on the tenant's public pricing page |
| IsRetired | bool | When true, no new subscribers allowed |
| IsActive | bool | Whether this plan is currently accepting subscribers |
| SortOrder | int | Display order on pricing pages (ascending) |
| BadgeText | string? | Pricing highlight badge text (e.g. 'Most Popular', 'Best Value') |
| Color | string? | Hex color for plan card (e.g. '#6366f1') |
| IconName | string? | Icon identifier for the plan card |
| MaxSubscribers | int? | Max concurrent active subscribers per plan. Null = unlimited |
| MaxUsers | int | Max users per subscription (-1 = unlimited) |
| AllowMonthly / AllowYearly / AllowLifetime / AllowTrial | bool | Billing cycle configuration toggles |
| IsSelfServiceEnabled | bool | Whether end-users can self-subscribe to this plan |
| IsContactSalesOnly | bool | Enterprise plan — requires contacting sales (no self-service) |
| TrialDays | int | Number of trial days before billing starts (0 = no trial) |
| TrialIsFree | bool | Whether the trial is 100% free (true) or discounted (false) |
| TrialDiscountPercent | int | Discount percentage during trial period (0–100) |
| GracePeriodDays | int | Grace period days after expiry before subscription suspension |
| FallbackPlanId | Guid? | Fallback plan when this plan is archived or a subscriber expires |
| TierLevel | int | Tier hierarchy (0 = Free, 1 = Basic, 2 = Pro, etc.) |
| CurrentVersion | int | Published version number — incremented on each publish action |
TenantPlanFeature Entity
Each TenantPlan can include a set of TenantPlanFeature records — key/value pairs that define what capabilities this plan provides to subscribers. These are free-form key/value strings defined by the tenant, not tied to the Entitlements feature catalog.
// Example TenantPlanFeature records for a "Pro" plan
[
{ "Key": "maxProjects", "Value": "50" },
{ "Key": "apiAccess", "Value": "true" },
{ "Key": "supportLevel", "Value": "priority" },
{ "Key": "storageGb", "Value": "100" }
]Plan Lifecycle
Plans are activated by setting IsActive = true. Deactivated plans prevent new subscribers but do not cancel existing subscriptions. Plans can be updated at any time — changes do not retroactively affect existing subscribers.
Tenant Context Required
The Tenant Plans module requires a valid tenant context. System admins (without an active tenant context) cannot access this page. The menu item is gated by RequiresTenantContext = true. System admins must use drill-down or impersonation to access tenant-level plan management.
API Endpoints
The TenantPlansController exposes 5 endpoints. All require the tenant_plans.* permission and a valid TenantId in the JWT context:
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| GET | /api/v1/tenant-plans | List plans for the current tenant (paginated) | 🔒 Required |
| GET | /api/v1/tenant-plans/{id} | Get plan details including features | 🔒 Required |
| POST | /api/v1/tenant-plans | Create a new tenant plan | 🔒 Required |
| PUT | /api/v1/tenant-plans/{id} | Update plan metadata or features | 🔒 Required |
| DELETE | /api/v1/tenant-plans/{id} | Soft-delete a plan | 🔒 Required |
Permissions
Tenant Plans use the following permission constants: tenant_plans.view, tenant_plans.create, tenant_plans.update, tenant_plans.delete. These must be granted to the tenant admin's role to allow plan management.
| Permission | Action |
|---|---|
| tenant_plans.view | View plan list and details |
| tenant_plans.create | Create new plans |
| tenant_plans.update | Edit plan name, status, features |
| tenant_plans.delete | Soft-delete a plan |