Blog

How to add subscription capacity add-ons in C# with Subscrio

Four base warehouse docks plus three two-dock packs provide ten total docks.

A warehouse uses DockFlow to coordinate dispatch across its loading docks. Its base subscription starts with four dispatch docks. It buys three capacity packs, each adding two docks. Later the purchase is reduced to one pack. That update must replace the quantity, not add another pack to the previous three.

In this guide we will add quantity-based subscription capacity add-ons in C# and verify the total after attachment, a quantity change, a repeated write, and detachment.

Each capacity pack is a quantity-based subscription add-on. Subscrio combines the base allowance with the purchased add-on quantity, so the application can read one effective capacity limit.

Define the customer promise

Purchased packs Calculation Total docks
None Base allowance 4
Three 4 + 3 × 2 10
One 4 + 1 × 2 6

Run the example

Get the complete runnable example. Install the .NET SDK and SQL Server Express LocalDB on Windows. The sample uses Windows integrated authentication and the MSSQLLocalDB instance.

git clone https://github.com/subscrio/samples.git
cd samples/examples/subscription-capacity-packs-csharp
sqllocaldb start MSSQLLocalDB
dotnet restore --locked-mode --source https://api.nuget.org/v3/index.json
dotnet run --no-restore

The C# runner uses SQL Server Express LocalDB on Windows with integrated authentication. Each run creates and removes its own disposable database.

The snippets below belong to that single program and run in order. Check compares a returned value with the expected value, prints it, and fails the run if they differ. The displayed output was captured from the sample.

Initialize Subscrio

The database helper supplies the LocalDB connection string. Install the schema before creating catalog records.

using var app = new Subscrio.Core.Subscrio(
    new SubscrioConfig
    {
        Database = new DatabaseConfig
        {
            ConnectionString = database.ConnectionString,
            DatabaseType = DatabaseType.SqlServer
        }
    }
);
await app.InstallSchemaAsync();

Create the product

DockFlow’s plans belong to one product. The runner creates a new LocalDB database for each run, so these readable keys do not collide with earlier examples.

await app.Products.CreateProductAsync(new("dockflow", "DockFlow"));

Define the feature

The dispatch-docks numeric feature represents the total docks a warehouse may use. It defaults to zero. The plan provides four and purchased packs will add to that allowance.

await app.Features.CreateFeatureAsync(
    new("dispatch-docks", "dispatch-docks", "numeric", "0")
);

Associate the feature

Use additive for add-ons so each purchased pack contributes to the base allowance. The most_generous subscription rule selects the largest eligible subscription total if the customer has more than one; this example uses one subscription.

await app.Products.AssociateFeatureAsync(
    "dockflow",
    "dispatch-docks",
    new FeatureResolutionOptions(
        AddonRule: "additive",
        SubscriptionRule: "most_generous"
    )
);

Create the plan

Create the Warehouse plan under DockFlow. Its base allowance will be four dispatch docks.

await app.Plans.CreatePlanAsync(new("dockflow", "warehouse", "warehouse"));

Set the plan value

The plan supplies 4 for dispatch-docks. The application will resolve it through the customer’s subscription.

await app.Plans.SetFeatureValueAsync("warehouse", "dispatch-docks", "4");

Create the billing cycle

The subscription will reference this monthly catalog record. Creating it does not collect payment.

await app.BillingCycles.CreateBillingCycleAsync(
    new("warehouse", "warehouse-monthly", "Monthly", "months", DurationValue: 1)
);

Create the customer

Create the customer who receives the agreement.

await app.Customers.CreateCustomerAsync(new("customer", "DockFlow demo customer"));

Assign the subscription

The agreement subscription connects customer to warehouse-monthly, which belongs to the Warehouse plan. It supplies four docks before any packs are attached.

await app.Subscriptions.CreateSubscriptionAsync(
    new("agreement", "customer", "warehouse-monthly")
);

Define the capacity add-on

The association explicitly uses additive composition. Each unit contributes two docks on top of the base four.

await app.Addons.CreateAddonAsync(
    new(
        "two-dock-pack",
        "dockflow",
        "Two dispatch docks",
        FeatureValues: new() { ["dispatch-docks"] = "2" }
    )
);
Check(
    "Base docks",
    await app.FeatureChecker.GetValueForCustomerAsync<int>(
        "customer",
        "dockflow",
        "dispatch-docks",
        0
    ),
    4
);

Captured output:

Base docks: 4

Set the purchased quantity to three

The allowance is four plus three times two.

await app.Subscriptions.AttachAddonAsync("agreement", "two-dock-pack", 3);
Check(
    "Three packs",
    await app.FeatureChecker.GetValueForCustomerAsync<int>(
        "customer",
        "dockflow",
        "dispatch-docks",
        0
    ),
    10
);

Captured output:

Three packs: 10

Update the purchased add-on quantity

The attachment method writes an absolute quantity. A repeated write of one must still resolve to six.

await app.Subscriptions.AttachAddonAsync("agreement", "two-dock-pack", 1);
Check(
    "One pack",
    await app.FeatureChecker.GetValueForCustomerAsync<int>(
        "customer",
        "dockflow",
        "dispatch-docks",
        0
    ),
    6
);
await app.Subscriptions.AttachAddonAsync("agreement", "two-dock-pack", 1);
Check(
    "One pack repeated",
    await app.FeatureChecker.GetValueForCustomerAsync<int>(
        "customer",
        "dockflow",
        "dispatch-docks",
        0
    ),
    6
);

Captured output:

One pack: 6
One pack repeated: 6

Remove the add-on and restore base capacity

Detachment restores the base allowance; it does not remove DockFlow records on the customer’s behalf.

await app.Subscriptions.DetachAddonAsync("agreement", "two-dock-pack");
Check(
    "Detached",
    await app.FeatureChecker.GetValueForCustomerAsync<int>(
        "customer",
        "dockflow",
        "dispatch-docks",
        0
    ),
    4
);

Captured output:

Detached: 4

Read the result

Attaching quantity three produces ten docks. Setting quantity one produces six; writing one again still produces six. Detaching the pack returns four. If the warehouse already uses more docks than its new allowance, deciding what to do with that existing work belongs to the application.

Run the complete C# example to reproduce the entitlement decisions and their expected results.

Written by

Jasen Fici

Founder, Subscrio

Bootstrapped founder. Built Velaro and StatusCast. Now building Subscrio, an entitlement engine for software products.

Screenshot preview