import { METERS, meterDef } from "~/lib/meters"; import type { MeterId } from "~/lib/meters"; import { MeteringError } from "./record"; import type { BillingSettings } from "./quota"; /** Numbers from plan config and the price table, inlined into SQL; never user input. */ function sqlNumber(value: number): string { if (!Number.isFinite(value)) throw new MeteringError("Non-finite billing constant"); return String(value); } const MICRO_PER_CENT = 1e6; /** Overage microcents of every other priced counter meter this period, holds included. */ function otherCountersSql(settings: BillingSettings): string { const cases = METERS.filter((meter) => meter.kind !== "gauge" && meter.microCentsPerUnit > 0) .map((meter) => { const included = sqlNumber(settings.plan.included[meter.id] ?? 0); const price = sqlNumber(meter.microCentsPerUnit); return `WHEN '${meter.id}' THEN MAX(0, p.quantity - p.credit_covered + p.reserved - ${included}) * ${price}`; }) .join(" "); return `COALESCE((SELECT SUM(CASE p.meter ${cases} ELSE 0 END) FROM usage_periods p WHERE p.account_id = ?2 AND p.period = ?4 AND p.meter != ?3), 0)`; } /** Overage microcents of the held meter once this hold (?5) lands, less the prepaid credit still available. */ function heldMeterSql(settings: BillingSettings, meter: MeterId, headroom: string): string { const included = sqlNumber(settings.plan.included[meter] ?? 0); const price = sqlNumber(meterDef(meter)?.microCentsPerUnit ?? 0); return `COALESCE((SELECT MAX(0, p.quantity - p.credit_covered + p.reserved + ?5 - (${headroom}) - ${included}) * ${price} FROM usage_periods p WHERE p.account_id = ?2 AND p.period = ?4 AND p.meter = ?3), 0)`; } function gaugeSpendSql(settings: BillingSettings): string { const cases = METERS.filter((meter) => meter.kind === "gauge" && meter.microCentsPerUnit > 0) .map((meter) => { const included = sqlNumber(settings.plan.included[meter.id] ?? 0); return `WHEN '${meter.id}' THEN MAX(0, g.peak - ${included}) * ${sqlNumber(meter.microCentsPerUnit)}`; }) .join(" "); if (!cases) return "0"; return `COALESCE((SELECT SUM(CASE g.meter ${cases} ELSE 0 END) FROM usage_gauges g WHERE g.account_id = ?2 AND g.period = ?4), 0)`; } /** * The spending-limit guard for reserveUsage's insert (BILL-P1): the period's overage across every priced * meter, with this hold added to its own meter, must stay within the limit. It runs inside the same D1 * transaction that adds the hold, so concurrent holds on different meters see each other and cannot * jointly overshoot. Prepaid credit offsets only the held meter; other meters' holds count in full, * which errs on the strict side. Compared in microcents so the boundary is exact. */ export function spendGuard(settings: BillingSettings, meter: MeterId, headroom = "0"): string { if (!settings.plan.billsOverage || settings.spendingLimitCents === null) return ""; if ((meterDef(meter)?.microCentsPerUnit ?? 0) === 0) return ""; const limit = sqlNumber(settings.spendingLimitCents * MICRO_PER_CENT); return ` AND ${otherCountersSql(settings)} + ${heldMeterSql(settings, meter, headroom)} + ${gaugeSpendSql(settings)} <= ${limit}`; }