Dloizides.Credits
1.0.1
dotnet add package Dloizides.Credits --version 1.0.1
NuGet\Install-Package Dloizides.Credits -Version 1.0.1
<PackageReference Include="Dloizides.Credits" Version="1.0.1" />
<PackageVersion Include="Dloizides.Credits" Version="1.0.1" />
<PackageReference Include="Dloizides.Credits" />
paket add Dloizides.Credits --version 1.0.1
#r "nuget: Dloizides.Credits, 1.0.1"
#:package Dloizides.Credits@1.0.1
#addin nuget:?package=Dloizides.Credits&version=1.0.1
#tool nuget:?package=Dloizides.Credits&version=1.0.1
Dloizides.Credits
Storage-agnostic per-tenant metering for multi-tenant SaaS. Four independent, composable caps over a tenant's billable units, extracted verbatim from PROOViD AMLService's screening-quota mechanics:
- Pre-paid credits — a
CreditBalancedecremented by aCreditCostPerScreenper unit. Cost0= not enforced (free);> 0= pay-per-use, blocked once the balance can't cover the next unit. - Monthly quota — fair-use / billing cap over the current calendar month (UTC).
- Rate limit — throughput / burst cap over a rolling 60-second window.
- Per-IP demo cap — a rolling 1-hour cap partitioned by client-IP hash, for a shared demo tenant.
All decision logic (allowed/blocked, window math, month-start calc, credit affordability) lives in the
package. The package owns no database — each product implements a small ICreditQuotaStore over
its own storage (EF Core, Dapper, anything). A second product gets metering in a handful of lines.
Install
dotnet add package Dloizides.Credits
Wire-up
// Program.cs
builder.Services.AddCreditsMetering(); // registers CreditQuotaService (scoped)
builder.Services.AddScoped<ICreditQuotaStore, MyEfStore>(); // your persistence, over your DbContext
Use
public MyController(CreditQuotaService metering) => _metering = metering;
// On the metered path:
var rate = await _metering.CheckRateAsync(tenantId, ct);
if (!rate.Allowed) return TooManyRequests(rate.RetryAfterSeconds);
var quota = await _metering.CheckQuotaAsync(tenantId, ct);
if (!quota.Allowed) return QuotaExceeded(quota.Used, quota.Limit);
var credit = await _metering.CheckCreditsAsync(tenantId, ct);
if (!credit.Allowed) return PaymentRequired(credit.Balance, credit.CostPerScreen);
// ... do the metered work ...
await _metering.ChargeCreditsAsync(tenantId, ct); // atomic; no-op when cost == 0
var usage = await _metering.GetUsageAsync(tenantId, ct); // GET /usage body
Public surface
CreditQuotaService
| Method | Returns | Meaning |
|---|---|---|
CheckQuotaAsync |
QuotaCheck |
Monthly quota. Null limit = unlimited. |
CheckRateAsync |
RateLimitCheck |
Per-minute burst cap (60s window). |
CheckIpRateAsync |
DemoIpRateCheck |
Per-IP demo cap (1h window); no-op unless set. |
CheckCreditsAsync |
CreditCheck |
Affordability; free when cost 0. |
ChargeCreditsAsync |
Task |
Atomic decrement; no-op when cost 0. |
GetUsageAsync |
MeteringUsage |
Full usage + credit position. |
SetCreditsAsync |
bool |
Top-up balance/cost (clamped ≥ 0). |
SetQuotaAsync / SetRateLimitAsync / SetDemoIpRateLimitAsync / SetMaxBatchRowsAsync |
bool |
Master config setters. |
ICreditQuotaStore (you implement)
Reads: GetMonthlyQuotaAsync, GetRateLimitPerMinuteAsync, GetDemoIpRateLimitPerHourAsync,
GetCreditSnapshotAsync, GetLimitsAsync, CountBillableUnitsSinceAsync,
CountBillableUnitsByIpSinceAsync. Writes: ChargeCreditsAsync (atomic), plus the five setters.
The store is responsible for counting only billable/externally-initiated units (e.g. excluding
internal system events via a source filter) and for making ChargeCreditsAsync a storage-side atomic
decrement guarded by cost > 0.
Result records
CreditCheck(Balance, CostPerScreen, Allowed), QuotaCheck(Limit, Used, Allowed),
RateLimitCheck(Limit, Recent, Allowed, RetryAfterSeconds),
DemoIpRateCheck(Limit, Recent, Allowed, RetryAfterSeconds),
MeteringUsage(...), CreditSnapshot(Balance, CostPerScreen),
TenantMeteringLimits(...).
License
MIT
| 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
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.1 | 587 | 7/7/2026 |