Editions

Named subscription plans with feature bundles, overflow policies, versioning, and rollout strategies.

Editions are named plans (e.g. Basic, Pro, Enterprise) that bundle feature values together. Each tenant subscribes to an edition, which determines their feature access. Editions support versioning with controlled rollout strategies for safe deployment of changes.

Edition Entity

An Edition is a named plan that bundles feature values. System editions are created by platform admins; retail editions are created by reseller tenants for their child tenants.

PropertyTypeDescription
IdGuidPrimary key (auto-generated)
NamestringUnique internal slug per platform (e.g. 'general-free', 'general-pro')
DisplayNameEn / DisplayNameArstring?Localized display titles shown in UI and pricing pages
Descriptionstring?Marketing description for the edition
Taglinestring?Short marketing tagline (e.g. 'Best for growing teams')
RecommendationLabelsstring?JSON array of badge labels (e.g. ['Best Value', 'Most Popular'])
TierLevelintHierarchical level (0 = Free, 1+ = premium) — used for upgrade/downgrade logic
IsSystembooltrue = system-defined platform plan. false = reseller-defined retail plan
IsRetiredboolWhen true, no new tenant assignments allowed (soft retirement)
EditionCategoryIdGuid?Optional grouping for the plan picker UI on the signup flow
FallbackEditionIdGuid?Downgrade target on subscription expiry. Null = tenant is suspended
OverflowPolicyOverflowPolicy enumBlock | Warn | Allow — governs behavior when resource counts exceed new limits after downgrade
AllowMonthly / AllowYearly / AllowLifetime / AllowTrialboolBilling cycle toggles — controls which billing modes are available for this edition
IsSelfServiceEnabledboolWhen true, tenants can self-subscribe via Stripe Checkout. False = sales-assisted only
IsContactSalesOnlyboolEnterprise plans where no Stripe Checkout is presented — sales contact required
IsFreebool (computed)Derived: true when no billing cycles AND no trial AND IsContactSalesOnly is false
TrialDurationDaysintLength of the trial period in days (0 = no trial). Range: 1–730
TrialIsFreebooltrue = 100% free trial. false = discounted trial (see TrialDiscountPercent)
TrialDiscountPercentintDiscount during trial (0–100). Only meaningful when TrialIsFree = false
GracePeriodDaysintDays after payment failure before subscription is suspended
MaxActiveSubscriptionsintGlobal capacity cap for this edition. -1 = unlimited
CurrentVersionintAuto-incremented version number — incremented each time features are published
ConnectCommissionRatedecimal?Stripe Connect platform commission rate. Null = use global default
StripeProductIdstring?Stripe product catalog ID — null until first sync
CreatedAt / UpdatedAtDateTimeAudit timestamps
Edition.cs (Domain Entity — simplified)C#
public class Edition : AuditableEntity<Guid>
{
    public string Name { get; set; } = null!;           // internal slug (e.g. "basic", "pro")
    public string? DisplayNameEn { get; set; }          // localized display name
    public string? DisplayNameAr { get; set; }
    public string? Description { get; set; }
    public string? Tagline { get; set; }                // short marketing tagline
    public string? RecommendationLabels { get; set; }   // JSON badge labels (e.g. ["Best Value"])
    public int TierLevel { get; set; }                  // 0 = Free, 1+ = premium
    public bool IsSystem { get; set; }
    public bool IsRetired { get; set; }                 // no new assignments allowed
    public Guid? EditionCategoryId { get; set; }        // grouping for signup plan picker
    public Guid? FallbackEditionId { get; set; }
    public OverflowPolicy OverflowPolicy { get; set; } = OverflowPolicy.Block;

    // Billing Toggles
    public bool AllowMonthly { get; set; } = true;
    public bool AllowYearly { get; set; } = true;
    public bool AllowLifetime { get; set; } = true;
    public bool AllowTrial { get; set; } = true;
    public bool IsSelfServiceEnabled { get; set; } = true;
    public bool IsContactSalesOnly { get; set; }

    // Computed: free when no billing types enabled
    public bool IsFree => !AllowTrial && !AllowMonthly && !AllowYearly && !AllowLifetime && !IsContactSalesOnly;

    // Trial Configuration
    [Range(1, 730)]
    public int TrialDurationDays { get; set; } = 14;
    public bool TrialIsFree { get; set; } = true;
    [Range(0, 100)]
    public int TrialDiscountPercent { get; set; } = 100;  // int, not decimal

    public int GracePeriodDays { get; set; } = 0;
    public int MaxActiveSubscriptions { get; set; } = -1;  // -1 = unlimited
    public int CurrentVersion { get; set; } = 1;
    public decimal? ConnectCommissionRate { get; set; }    // Stripe Connect commission (null = global default)
    public string? StripeProductId { get; set; }           // null until first catalog sync

    public virtual ICollection<EditionFeature> Features { get; set; } = new List<EditionFeature>();
    public virtual ICollection<EditionPrice> Prices { get; set; } = new List<EditionPrice>();
    public virtual ICollection<EditionVersion> Versions { get; set; } = new List<EditionVersion>();
    public ICollection<EditionPromotion> Promotions { get; set; } = new List<EditionPromotion>();
}

System vs Retail Editions

SCRIPE supports two types of editions: System editions created by platform admins visible to all tenants, and Retail editions created by reseller tenants for their child tenants only.

TypeCreated ByVisible ToCan SubscribeUse Case
System EditionPlatform SuperAdminAll tenantsAny tenantStandard platform plans (Basic, Pro, Enterprise)
Retail EditionReseller tenantCreator + child tenantsChild tenants onlyReseller creating custom plans for sub-tenants

Seeded System Editions

The platform seeds two standard system editions out of the box on startup via EditionSeeder, establishing default limits for features.

EditionDescriptionIncluded Features & Limits
FreeTierLevel = 0. Default fallback plan for all tenants upon subscription expiry. Permanent $0 plan.5 Admins, 3 Roles, 1 User Group, 0 child tenants, 256MB storage, 10MB upload limit. Two-Factor Authentication and External Auth are disabled.
StandardTierLevel = 1. Premium plan enabling advanced administrative and organization capabilities.50 Admins, 10 Roles, 5 User Groups, 5 child tenants, 5GB storage, 25MB upload limit. Two-Factor Authentication and External Auth are enabled.

Overflow Policy

When a tenant downgrades to an edition with lower limits, their existing resources may exceed the new limits. The OverflowPolicy determines what happens:

PolicyBehaviorWhen to Use
BlockPrevents downgrade if any feature would overflow. Returns 409 Conflict with details.Strict enforcement — tenant must clean up first
WarnAllows the downgrade but logs a warning. Existing resources above the new limit continue to work, but new ones are blocked.Soft enforcement — graceful transition period
AllowSilently allows the downgrade. No restrictions on existing resources.Lenient — existing resources are grandfathered
OverflowPolicy EnumC#
public enum OverflowPolicy
{
    /// <summary>Block downgrade if any resource exceeds the new limit</summary>
    Block = 0,
    
    /// <summary>Allow downgrade but warn; existing resources grandfathered</summary>
    Warn = 1,
    
    /// <summary>Silently allow downgrade; no enforcement on existing data</summary>
    Allow = 2,
}

Edition Features

Each edition contains a set of EditionFeature records that map features to their values within that plan. Features not explicitly set in an edition fall back to the Feature.DefaultValue.

PropertyTypeDescription
EditionIdGuidFK to the parent Edition
FeatureIdGuidFK to the Feature being configured
ValuestringThe feature value for this edition (interpreted by Feature.ValueType)
EditionFeature EntityC#
public class EditionFeature
{
    public Guid EditionId { get; set; }
    public Edition Edition { get; set; } = null!;
    
    public Guid FeatureId { get; set; }
    public Feature Feature { get; set; } = null!;
    
    public string Value { get; set; } = string.Empty;
    // Interpreted as: bool (Boolean), int (Numeric), or string (String)
}

Edition Versions

Edition Versions provide a versioning and rollout system for feature changes. Instead of modifying features directly, admins can create a new version (snapshot), choose a rollout strategy, and publish it.

PropertyTypeDescription
IdGuidVersion identifier
EditionIdGuidFK to parent Edition
VersionNumberintAuto-incrementing version number (1, 2, 3...)
FeaturesSnapshotJSONComplete snapshot of all feature values at this version
ChangeNotesstring?Description of what changed
RolloutStrategyenumHow to deploy: Immediate, Gradual, Manual
StatusenumDraft, Published, RolledBack
PublishedAtDateTime?When the version was published
PublishedByGuid?Who published the version
CreatedAtDateTimeWhen the version was created
Draft — Created, not applied
Publish
Published — Applied to subscribers
Rollback
Rolled Back — Reverted to previous

Rollout Strategies

When publishing an edition version, admins choose how the changes are deployed to subscribed tenants:

StrategyBehaviorUse Case
ImmediateAll subscribed tenants receive the new feature values instantly upon publishUrgent fixes, security patches, small changes
GradualTenants are migrated in batches (configurable batch size and delay)Large changes where you want to monitor impact
ManualTenants must be manually assigned the new version via APIControlled rollouts, beta programs, VIP tenants first

Apply Now vs Save as Version

SCRIPE provides two ways to update edition features, each suited for different scenarios:

Apply Now (Direct)Save as Version
SpeedInstant — changes take effect immediatelyRequires create → review → publish
Audit TrailLogged as a single mutationFull version history with change notes
RollbackMust manually revert changesOne-click rollback to any previous version
Rollout ControlAll-or-nothingImmediate, Gradual, or Manual per-tenant
Best ForUrgent fixes, small tweaksMajor plan updates, feature launches

API Endpoints

The Editions controller exposes 11 endpoints for managing editions, their features, and version lifecycle:

MethodEndpointDescriptionAuth
GET/api/v1/editionsList all editions (paginated, filterable)🔒 Required
GET/api/v1/editions/{id}Get edition details by ID🔒 Required
POST/api/v1/editionsCreate a new edition🔒 Required
PUT/api/v1/editions/{id}Update edition metadata🔒 Required
DELETE/api/v1/editions/{id}Soft-delete an edition🔒 Required
GET/api/v1/editions/{id}/featuresList features configured for this edition🔒 Required
PUT/api/v1/editions/{id}/featuresSet/update features for this edition🔒 Required
POST/api/v1/editions/{id}/features/applyApply feature changes immediately (no versioning)🔒 Required
GET/api/v1/editions/{id}/versionsList all versions for this edition🔒 Required
POST/api/v1/editions/{id}/versionsCreate a new draft version with feature snapshot🔒 Required
POST/api/v1/editions/{id}/versions/{vId}/publishPublish a draft version with chosen rollout strategy🔒 Required