Muonroi.Billing.Abstractions
1.0.0-alpha.16
dotnet add package Muonroi.Billing.Abstractions --version 1.0.0-alpha.16
NuGet\Install-Package Muonroi.Billing.Abstractions -Version 1.0.0-alpha.16
<PackageReference Include="Muonroi.Billing.Abstractions" Version="1.0.0-alpha.16" />
<PackageVersion Include="Muonroi.Billing.Abstractions" Version="1.0.0-alpha.16" />
<PackageReference Include="Muonroi.Billing.Abstractions" />
paket add Muonroi.Billing.Abstractions --version 1.0.0-alpha.16
#r "nuget: Muonroi.Billing.Abstractions, 1.0.0-alpha.16"
#:package Muonroi.Billing.Abstractions@1.0.0-alpha.16
#addin nuget:?package=Muonroi.Billing.Abstractions&version=1.0.0-alpha.16&prerelease
#tool nuget:?package=Muonroi.Billing.Abstractions&version=1.0.0-alpha.16&prerelease
Muonroi.Billing.Abstractions
Product-agnostic billing seam contracts for Muonroi multi-tenant applications — no payment-SDK dependency required.
This package defines the shared billing contracts that every Muonroi product line (PDF, rule-engine, storyflow) bills through using the same metered-dimension key (QuotaType). It ships a record-only default provider and a default usage aggregator that perform no external calls; a payment-processor adapter (e.g. Stripe) is a deferred, separate implementation wired behind the IBillingProvider seam. There is no runtime behavior in this package beyond the default providers — consuming packages depend only on the contracts and override them at host registration time.
Installation
dotnet add package Muonroi.Billing.Abstractions --prerelease
Quick Start
Register the default record-only rail
// Program.cs (or service registration in your host)
using Muonroi.Billing.Abstractions;
// Wires IBillingProvider → RecordOnlyBillingProvider
// and IUsageAggregator → UsageAggregator (TryAddSingleton — overridable).
// Prerequisite: ITenantQuotaStore must already be registered by the host.
builder.Services.AddRecordOnlyBilling();
Record a billable event
public class PdfRenderService(IBillingProvider billing)
{
public async Task RenderAsync(string tenantId, CancellationToken ct)
{
// ... render logic ...
await billing.RecordAsync(new BillableEvent(
TenantId: tenantId,
Dimension: QuotaType.PdfPages,
Quantity: 1,
OccurredAt: DateTimeOffset.UtcNow), ct);
}
}
Aggregate usage into a priced invoice preview
public class InvoiceService(IUsageAggregator aggregator)
{
public async Task<IReadOnlyList<UsageLineItem>> PreviewAsync(
string tenantId, CancellationToken ct)
{
var plan = new PricingPlan(
tier: TenantTier.Pro,
unitRates: new Dictionary<QuotaType, decimal>
{
[QuotaType.PdfPages] = 0.01m,
},
flatBaseAmount: 9.99m);
// Compute-only — no external call, no charge.
return await aggregator.AggregateAsync(
tenantId, plan,
periodStart: DateTime.UtcNow.AddMonths(-1),
periodEnd: DateTime.UtcNow, ct);
}
}
Implement a custom billing provider (e.g. Stripe adapter)
Register your adapter before calling AddRecordOnlyBilling; TryAddSingleton ensures the default does not overwrite it:
builder.Services.AddSingleton<IBillingProvider, StripeAdapter>();
builder.Services.AddRecordOnlyBilling(); // TryAdd — StripeAdapter wins
Features
IBillingProvider— fire-and-forgetRecordAsyncthat never throws to the caller; compute-onlyPreviewInvoiceAsyncthat never chargesIUsageAggregator— rolls per-tenant metered usage into deterministically pricedUsageLineItems via aPricingPlanBillableEvent— immutable record keyed onQuotaType(dimension), tenant ID, quantity, and timestampUsageLineItem— priced rollup record with aCreatefactory that computesAmount = Quantity * UnitRatePricingPlan— tier-scoped rate table (QuotaType → decimal) plus an optional flat base amount; missing dimensions price at0mRecordOnlyBillingProvider— thread-safe in-memory default; sink failures are logged and swallowed (never blocks callers); exposesRecordedEventsfor test assertionsUsageAggregator— default aggregator; readsITenantQuotaStore, emits per-dimension lines in deterministicQuotaTypeenum order, appends the flat-base line lastAddRecordOnlyBillingDI extension — wires both defaults viaTryAddSingletonso a real payment adapter registered first takes precedence
API Reference
| Type | Purpose |
|---|---|
IBillingProvider |
Primary billing seam: RecordAsync + PreviewInvoiceAsync |
IUsageAggregator |
Rolls metered usage into priced line items via a PricingPlan |
BillableEvent |
Immutable record for a single metered occurrence |
UsageLineItem |
Priced rollup for one dimension; Create factory computes amount |
UsageLineItem.FlatBaseDescription |
Canonical constant identifying the flat-base line item |
PricingPlan |
Rate table keyed by QuotaType + optional flat base; GetUnitRate returns 0m for unpriced dimensions |
RecordOnlyBillingProvider |
Default IBillingProvider; in-memory, non-blocking, testable via RecordedEvents |
UsageAggregator |
Default IUsageAggregator; depends on ITenantQuotaStore |
BillingServiceCollectionExtensions.AddRecordOnlyBilling |
Registers both defaults via TryAddSingleton |
Configuration
AddRecordOnlyBilling has no options class. The only host prerequisite is that ITenantQuotaStore is registered before UsageAggregator is resolved (the aggregator reads it to obtain per-tenant metered usage).
To override either seam, register your implementation before calling AddRecordOnlyBilling:
// Override the provider only
services.AddSingleton<IBillingProvider, MyPaymentAdapter>();
services.AddRecordOnlyBilling(); // UsageAggregator still wired; IBillingProvider not overwritten
Samples
No dedicated sample exists for this package yet. The record-only rail is wired inside the monetization host as part of Phase 17 — see Muonroi.Quota.Abstractions for the ITenantQuotaStore contract that UsageAggregator depends on.
Compatibility
- Target framework:
net8.0 - License: Apache-2.0 (OSS)
Related Packages
Muonroi.Quota.Abstractions— providesITenantQuotaStoreandQuotaType; required byUsageAggregatorMuonroi.Core.Abstractions— provides guard utilities (MGuard) used internallyMuonroi.Logging.Abstractions— providesIMLog<T>used byRecordOnlyBillingProviderfor non-silent error logging
License
Apache-2.0. See LICENSE-APACHE for details.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.3)
- Muonroi.Core.Abstractions (>= 1.0.0-alpha.16)
- Muonroi.Logging.Abstractions (>= 1.0.0-alpha.16)
- Muonroi.Quota.Abstractions (>= 1.0.0-alpha.16)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.0-alpha.16 | 48 | 6/22/2026 |