High-performance commission calculation engine supporting proportional and absolute commission models with tiered ranges, min/max constraints, automatic validation, binary search optimization, and ConditionalWeakTable caching. Zero allocation after initial normalization, 5M+ calculations/sec. Ideal for payment processing, affiliate marketing, broker fees.
$ dotnet add package Pandatech.CommissionCalculatorHigh-performance commission calculation engine for .NET 8+ supporting proportional and absolute commission models with tiered ranges, min/max constraints, and automatic validation.
dotnet add package Pandatech.CommissionCalculator
using CommissionCalculator.DTO;
var rule = new CommissionRule
{
CalculationType = CalculationType.Proportional,
DecimalPlace = 4,
CommissionRangeConfigs = new List<CommissionRangeConfigs>
{
new()
{
RangeStart = 0,
RangeEnd = 500,
Type = CommissionType.FlatRate,
CommissionAmount = 25,
MinCommission = 0,
MaxCommission = 0
},
new()
{
RangeStart = 500,
RangeEnd = 1000,
Type = CommissionType.Percentage,
CommissionAmount = 0.1m,
MinCommission = 70,
MaxCommission = 90
},
new()
{
RangeStart = 1000,
RangeEnd = 10000,
Type = CommissionType.Percentage,
CommissionAmount = 0.2m,
MinCommission = 250,
MaxCommission = 1500
},
new()
{
RangeStart = 10000,
RangeEnd = 0, // 0 = infinity
Type = CommissionType.FlatRate,
CommissionAmount = 2000,
MinCommission = 0,
MaxCommission = 0
}
}
};
Standard (principal-based):
decimal principal = 1000m;
decimal commission = Commission.ComputeCommission(principal, rule);
// Result: 25 (flat) + 70 (min enforced) = 95
Selector-based (different selection and application values):
decimal orderPrice = 2000m; // Amount to calculate commission on
decimal ticketCount = 3m; // Value to select the range
// Select range based on ticketCount, apply commission to orderPrice
decimal commission = Commission.ComputeCommission(orderPrice, ticketCount, rule);
Use case: When the value that determines which range to use (e.g., ticket quantity) differs from the amount you calculate commission on (e.g., order total).
Commission accumulates across tiers as the amount increases:
var proportional = new CommissionRule
{
CalculationType = CalculationType.Proportional,
CommissionRangeConfigs = new List<CommissionRangeConfigs>
{
new() { RangeStart = 0, RangeEnd = 100, Type = CommissionType.Percentage, CommissionAmount = 0.05m },
new() { RangeStart = 100, RangeEnd = 0, Type = CommissionType.Percentage, CommissionAmount = 0.03m }
}
};
// For $200:
// - First $100: $100 * 5% = $5
// - Next $100: $100 * 3% = $3
// Total: $8
Only the matching tier's commission applies:
var absolute = new CommissionRule
{
CalculationType = CalculationType.Absolute,
CommissionRangeConfigs = new List<CommissionRangeConfigs>
{
new() { RangeStart = 0, RangeEnd = 100, Type = CommissionType.FlatRate, CommissionAmount = 10 },
new() { RangeStart = 100, RangeEnd = 0, Type = CommissionType.FlatRate, CommissionAmount = 25 }
}
};
// For $200: $25 (uses second tier only)
| Property | Type | Description |
|---|---|---|
RangeStart | decimal | Start of range (inclusive) |
RangeEnd | decimal | End of range (exclusive), 0 = infinity |
Type | CommissionType | FlatRate or Percentage |
CommissionAmount | decimal | Commission value (flat amount or percentage like 0.1 for 10%) |
MinCommission | decimal | Minimum commission for this range |
MaxCommission | decimal | Maximum commission for this range, 0 = infinity |
CommissionAmount directly (min/max ignored)principal × CommissionAmount, clamped by min/maxFor safety, percentage commissions are limited to ±1000%:
// ✅ Valid: 10% = 0.1
CommissionAmount = 0.1m
// ❌ Invalid: 1500% = 15 (throws exception)
CommissionAmount = 15m
Validation occurs automatically on first use:
decimal commission = Commission.ComputeCommission(1000m, rule);
// Validates rule, caches normalized version, computes result
bool isValid = Commission.ValidateRule(rule);
✅ Required:
MaxCommission ≥ MinCommission (when MaxCommission ≠ 0)❌ Forbidden:
RangeStart and RangeEnd (except single range: both 0)Utility for validating commission period overlaps:
using CommissionCalculator.Helper;
using CommissionCalculator.DTO;
var period1 = new List<DateTimePair>
{
new(new DateTime(2024, 1, 1), new DateTime(2024, 1, 10))
};
var period2 = new List<DateTimePair>
{
new(new DateTime(2024, 1, 5), new DateTime(2024, 1, 15))
};
bool hasOverlap = DateTimeOverlapChecker.HasOverlap(period1, period2);
// Result: true
Performance characteristics:
First calculation: ~50μs (validation + normalization + computation)
Subsequent calculations: ~200ns (cache hit + binary search + math)
Memory: ~1KB per cached rule
var flatRule = new CommissionRule
{
CalculationType = CalculationType.Absolute,
CommissionRangeConfigs = new List<CommissionRangeConfigs>
{
new()
{
RangeStart = 0,
RangeEnd = 0, // Special case: both 0 for single range
Type = CommissionType.FlatRate,
CommissionAmount = 50,
MinCommission = 0,
MaxCommission = 0
}
}
};
// Any amount: $50 commission
var capped = new CommissionRule
{
CalculationType = CalculationType.Proportional,
CommissionRangeConfigs = new List<CommissionRangeConfigs>
{
new()
{
RangeStart = 0,
RangeEnd = 1000,
Type = CommissionType.Percentage,
CommissionAmount = 0.05m,
MinCommission = 10, // At least $10
MaxCommission = 50 // No more than $50 per tier
},
new()
{
RangeStart = 1000,
RangeEnd = 0,
Type = CommissionType.Percentage,
CommissionAmount = 0.02m,
MinCommission = 20,
MaxCommission = 0 // Unlimited on second tier
}
}
};
// Event ticketing: commission based on ticket count, applied to total price
var ticketRule = new CommissionRule
{
CalculationType = CalculationType.Absolute, // Required for selector-based
CommissionRangeConfigs = new List<CommissionRangeConfigs>
{
new() { RangeStart = 0, RangeEnd = 10, Type = CommissionType.Percentage, CommissionAmount = 0.1m },
new() { RangeStart = 10, RangeEnd = 0, Type = CommissionType.Percentage, CommissionAmount = 0.05m }
}
};
decimal orderTotal = 500m;
decimal ticketsSold = 15m;
// Uses second range (15 tickets), applies 5% to $500 order
decimal commission = Commission.ComputeCommission(orderTotal, ticketsSold, ticketRule);
// Result: $25
RangeEnd = 0 or MaxCommission = 0 to represent ∞RangeEnd is exclusive (range is [Start, End))DecimalPlace to control rounding (default: 4)// Higher commission for higher sales
CalculationType = CalculationType.Absolute
Ranges:
$0-$1000: 5%
$1000-$5000: 7%
$5000+: 10%
// Tiered fees that accumulate
CalculationType = CalculationType.Proportional
Ranges:
$0-$10,000: 2.9% + $0.30 min
$10,000+: 1.5%
// Fixed fee per transaction tier
CalculationType = CalculationType.Absolute
Ranges:
$0-$10,000: $9.99
$10,000-$100,000: $19.99
$100,000+: $49.99
MIT