using System.Collections.Generic;
private static readonly IDictionary<string, PlanPricingModel> _planPricingModels;
private static readonly decimal _taxRate;
private static readonly PricingModel _pricingModel;
public static void Main()
bool yearlyPayment = false;
bool includeSignupFee = false;
var price = CalculatePrice(plan, members, supporters, yearlyPayment, includeTax, includeSignupFee);
Console.WriteLine("The price is: {0:0,#}円", price);
var pricingModel = JsonConvert.DeserializeObject<PricingModel>(GetPricingJson());
_pricingModel = pricingModel;
_taxRate = pricingModel.TaxRate;
_planPricingModels = (pricingModel?.Plans ?? new Dictionary<string, PlanPricingModel>()).ToDictionary(p => p.Key, p => p.Value, StringComparer.OrdinalIgnoreCase);
public static decimal? CalculatePrice(string plan, int memberCount, int supporterCount, bool yearlyPayment, bool includeTax, bool includeSignupFee = false)
if (!_planPricingModels.TryGetValue(plan, out var model))
Console.WriteLine("plan not found");
PlanPricingUnitCosts pricingData;
if (model.Tiers is not null && model.Tiers.Any())
.OrderByDescending(t => t.MemberThreshold)
.FirstOrDefault(t => memberCount >= t.MemberThreshold);
tier ??= model.Tiers.OrderBy(t => t.MemberThreshold).First();
pricingData = yearlyPayment ? tier.YearlyUnitCosts : tier.MonthlyUnitCosts;
defaultMembers = tier.DefaultMembers ?? model.DefaultMembers;
pricingData = yearlyPayment ? model.YearlyUnitCosts : model.MonthlyUnitCosts;
defaultMembers = model.DefaultMembers;
var supportersForMembers = model.SupportersPerUser * Math.Max(defaultMembers, memberCount);
var extraMemberCount = Math.Max(defaultMembers, memberCount) - defaultMembers;
var extraSupporterCount = Math.Max(0, Math.Max(model.DefaultSupporters, supporterCount) - (model.DefaultSupporters + supportersForMembers));
var memberCost = (defaultMembers * pricingData.DefaultMemberUnitCost) + (extraMemberCount * pricingData.ExtraMemberUnitCost)
+ (model.DefaultSupporters * pricingData.DefaultSupporterUnitCost) + (extraSupporterCount * pricingData.ExtraSupporterUnitCost);
var planCostSubtotal = model.BasePrice + memberCost;
var periodSubtotal = yearlyPayment ? (planCostSubtotal * model.YearlyMultiplier) : planCostSubtotal;
decimal finalPrice = periodSubtotal;
finalPrice += CalculateInitialFee(plan, includeTax: true) ?? 0;
finalPrice = RemoveTax(finalPrice);
private static string GetPricingJson()
{"taxRate":10,"defaultSignupFeeInclTax":22000,"supportPlanFeeMonthly":36300,"supportPlanFeeYearly":33000,"plans":{"start":{"defaultMembers":15,"defaultSupporters":0,"monthlyUnitCosts":{"defaultMemberUnitCost":320,"extraMemberUnitCost":320,"defaultSupporterUnitCost":0,"extraSupporterUnitCost":160},"yearlyUnitCosts":{"defaultMemberUnitCost":320,"extraMemberUnitCost":320,"defaultSupporterUnitCost":0,"extraSupporterUnitCost":160},"yearlyMultiplier":10},"standard":{"defaultMembers":40,"defaultSupporters":0,"monthlyUnitCosts":{"defaultMemberUnitCost":270,"extraMemberUnitCost":270,"defaultSupporterUnitCost":0,"extraSupporterUnitCost":160},"yearlyUnitCosts":{"defaultMemberUnitCost":270,"extraMemberUnitCost":270,"defaultSupporterUnitCost":0,"extraSupporterUnitCost":160},"yearlyMultiplier":10},"premium":{"defaultMembers":100,"defaultSupporters":0,"monthlyUnitCosts":{"defaultMemberUnitCost":220,"extraMemberUnitCost":220,"defaultSupporterUnitCost":0,"extraSupporterUnitCost":160},"yearlyUnitCosts":{"defaultMemberUnitCost":220,"extraMemberUnitCost":220,"defaultSupporterUnitCost":0,"extraSupporterUnitCost":160},"yearlyMultiplier":10},"lite":{"defaultMembers":10,"defaultSupporters":0,"monthlyUnitCosts":{"defaultMemberUnitCost":390,"extraMemberUnitCost":390,"defaultSupporterUnitCost":0,"extraSupporterUnitCost":350},"yearlyUnitCosts":{"defaultMemberUnitCost":330,"extraMemberUnitCost":330,"defaultSupporterUnitCost":0,"extraSupporterUnitCost":290},"yearlyMultiplier":12},"basic":{"defaultMembers":10,"defaultSupporters":0,"monthlyUnitCosts":{"defaultMemberUnitCost":690,"extraMemberUnitCost":690,"defaultSupporterUnitCost":0,"extraSupporterUnitCost":350},"yearlyUnitCosts":{"defaultMemberUnitCost":580,"extraMemberUnitCost":580,"defaultSupporterUnitCost":0,"extraSupporterUnitCost":290},"yearlyMultiplier":12},"basic_plus":{"defaultMembers":10,"defaultSupporters":0,"supportersPerUser":1,"monthlyUnitCosts":{"defaultMemberUnitCost":690,"extraMemberUnitCost":690,"defaultSupporterUnitCost":0,"extraSupporterUnitCost":350},"yearlyUnitCosts":{"defaultMemberUnitCost":580,"extraMemberUnitCost":580,"defaultSupporterUnitCost":0,"extraSupporterUnitCost":290},"yearlyMultiplier":12},"advanced":{"defaultMembers":10,"defaultSupporters":0,"monthlyUnitCosts":{"defaultMemberUnitCost":890,"extraMemberUnitCost":890,"defaultSupporterUnitCost":0,"extraSupporterUnitCost":350},"yearlyUnitCosts":{"defaultMemberUnitCost":750,"extraMemberUnitCost":750,"defaultSupporterUnitCost":0,"extraSupporterUnitCost":290},"yearlyMultiplier":12},"regular":{"defaultMembers":15,"defaultSupporters":0,"yearlyMultiplier":12,"tiers":[{"memberThreshold":15,"defaultMembers":15,"monthlyUnitCosts":{"defaultMemberUnitCost":1320,"extraMemberUnitCost":690,"defaultSupporterUnitCost":0,"extraSupporterUnitCost":350},"yearlyUnitCosts":{"defaultMemberUnitCost":1100,"extraMemberUnitCost":580,"defaultSupporterUnitCost":0,"extraSupporterUnitCost":290}},{"memberThreshold":30,"defaultMembers":30,"monthlyUnitCosts":{"defaultMemberUnitCost":954,"extraMemberUnitCost":690,"defaultSupporterUnitCost":0,"extraSupporterUnitCost":350},"yearlyUnitCosts":{"defaultMemberUnitCost":795,"extraMemberUnitCost":580,"defaultSupporterUnitCost":0,"extraSupporterUnitCost":290}},{"memberThreshold":50,"defaultMembers":50,"monthlyUnitCosts":{"defaultMemberUnitCost":792,"extraMemberUnitCost":690,"defaultSupporterUnitCost":0,"extraSupporterUnitCost":350},"yearlyUnitCosts":{"defaultMemberUnitCost":660,"extraMemberUnitCost":580,"defaultSupporterUnitCost":0,"extraSupporterUnitCost":290}}]},"regular_plus":{"defaultMembers":15,"defaultSupporters":0,"supportersPerUser":1,"yearlyMultiplier":12,"tiers":[{"memberThreshold":15,"defaultMembers":15,"monthlyUnitCosts":{"defaultMemberUnitCost":1320,"extraMemberUnitCost":690,"defaultSupporterUnitCost":0,"extraSupporterUnitCost":350},"yearlyUnitCosts":{"defaultMemberUnitCost":1100,"extraMemberUnitCost":580,"defaultSupporterUnitCost":0,"extraSupporterUnitCost":290}},{"memberThreshold":30,"defaultMembers":30,"monthlyUnitCosts":{"defaultMemberUnitCost":954,"extraMemberUnitCost":690,"defaultSupporterUnitCost":0,"extraSupporterUnitCost":350},"yearlyUnitCosts":{"defaultMemberUnitCost":795,"extraMemberUnitCost":580,"defaultSupporterUnitCost":0,"extraSupporterUnitCost":290}},{"memberThreshold":50,"defaultMembers":50,"monthlyUnitCosts":{"defaultMemberUnitCost":792,"extraMemberUnitCost":690,"defaultSupporterUnitCost":0,"extraSupporterUnitCost":350},"yearlyUnitCosts":{"defaultMemberUnitCost":660,"extraMemberUnitCost":580,"defaultSupporterUnitCost":0,"extraSupporterUnitCost":290}}]},"elite":{"defaultMembers":15,"defaultSupporters":0,"yearlyMultiplier":12,"tiers":[{"memberThreshold":15,"defaultMembers":15,"monthlyUnitCosts":{"defaultMemberUnitCost":1704,"extraMemberUnitCost":890,"defaultSupporterUnitCost":0,"extraSupporterUnitCost":350},"yearlyUnitCosts":{"defaultMemberUnitCost":1420,"extraMemberUnitCost":750,"defaultSupporterUnitCost":0,"extraSupporterUnitCost":290}},{"memberThreshold":30,"defaultMembers":30,"monthlyUnitCosts":{"defaultMemberUnitCost":1236,"extraMemberUnitCost":890,"defaultSupporterUnitCost":0,"extraSupporterUnitCost":350},"yearlyUnitCosts":{"defaultMemberUnitCost":1030,"extraMemberUnitCost":750,"defaultSupporterUnitCost":0,"extraSupporterUnitCost":290}},{"memberThreshold":50,"defaultMembers":50,"monthlyUnitCosts":{"defaultMemberUnitCost":1020,"extraMemberUnitCost":890,"defaultSupporterUnitCost":0,"extraSupporterUnitCost":350},"yearlyUnitCosts":{"defaultMemberUnitCost":850,"extraMemberUnitCost":750,"defaultSupporterUnitCost":0,"extraSupporterUnitCost":290}}]},"elite_plus":{"defaultMembers":15,"defaultSupporters":0,"supportersPerUser":1,"yearlyMultiplier":12,"tiers":[{"memberThreshold":15,"defaultMembers":15,"monthlyUnitCosts":{"defaultMemberUnitCost":1704,"extraMemberUnitCost":890,"defaultSupporterUnitCost":0,"extraSupporterUnitCost":350},"yearlyUnitCosts":{"defaultMemberUnitCost":1420,"extraMemberUnitCost":750,"defaultSupporterUnitCost":0,"extraSupporterUnitCost":290}},{"memberThreshold":30,"defaultMembers":30,"monthlyUnitCosts":{"defaultMemberUnitCost":1236,"extraMemberUnitCost":890,"defaultSupporterUnitCost":0,"extraSupporterUnitCost":350},"yearlyUnitCosts":{"defaultMemberUnitCost":1030,"extraMemberUnitCost":750,"defaultSupporterUnitCost":0,"extraSupporterUnitCost":290}},{"memberThreshold":50,"defaultMembers":50,"monthlyUnitCosts":{"defaultMemberUnitCost":1020,"extraMemberUnitCost":890,"defaultSupporterUnitCost":0,"extraSupporterUnitCost":350},"yearlyUnitCosts":{"defaultMemberUnitCost":850,"extraMemberUnitCost":750,"defaultSupporterUnitCost":0,"extraSupporterUnitCost":290}}]}}}
public static decimal AddTax(decimal value)
value = Math.Round((value * (_taxRate + 100)) / 100m, 2, MidpointRounding.AwayFromZero);
public static decimal RemoveTax(decimal value)
value = Math.Round((value * 100) / (_taxRate + 100m), 2, MidpointRounding.ToZero);
public static decimal? CalculateInitialFee(string plan, bool includeTax)
if (!_planPricingModels.TryGetValue(plan, out var model))
if (model.SignupFeeInclTax.HasValue)
initialFee += model.SignupFeeInclTax.Value;
else if (_pricingModel.DefaultSignupFeeInclTax.HasValue)
initialFee += _pricingModel.DefaultSignupFeeInclTax.Value;
initialFee = RemoveTax(initialFee);
public static decimal CalculateSupportFee(bool yearlyPayment, bool includeTax)
decimal supportFee = yearlyPayment ? _pricingModel.SupportPlanFeeYearly : _pricingModel.SupportPlanFeeMonthly;
supportFee = RemoveTax(supportFee);
public class PricingModel
public IDictionary<string, PlanPricingModel> Plans { get; set; }
public decimal TaxRate { get; set; }
public decimal? DefaultSignupFeeInclTax { get; set; }
public decimal SupportPlanFeeMonthly { get; set; }
public decimal SupportPlanFeeYearly { get; set; }
public class PlanPricingModel
public decimal BasePrice { get; set; }
public int DefaultMembers { get; set; }
public int DefaultSupporters { get; set; }
public int SupportersPerUser { get; set; }
public PlanPricingUnitCosts MonthlyUnitCosts { get; set; }
public PlanPricingUnitCosts YearlyUnitCosts { get; set; }
public PlanPricingTiers[] Tiers { get; set; }
public decimal YearlyMultiplier { get; set; }
public decimal? SignupFeeInclTax { get; set; }
public class PlanPricingUnitCosts
public decimal DefaultMemberUnitCost { get; set; }
public decimal ExtraMemberUnitCost { get; set; }
public decimal DefaultSupporterUnitCost { get; set; }
public decimal ExtraSupporterUnitCost { get; set; }
public class PlanPricingTiers
public int MemberThreshold { get; set; }
public int? DefaultMembers { get; set; }
public PlanPricingUnitCosts MonthlyUnitCosts { get; set; }
public PlanPricingUnitCosts YearlyUnitCosts { get; set; }