Model SaaS pricing
Declare provider-neutral plans with base prices, usage dimensions, free allowances, graduated tiers, minimum charges, labels, and explicit rounding.
@absolutejs/billingv0.6.0betaPlatform & InfraPure-function usage billing for Bun apps — declare a priced plan and compute host-portable invoices in integer micros.
Turns metered usage into invoices without float drift or vendor lock-in. createPlan declares a priced product — flat base fee, per-dimension unit prices, graduated tiers, free allowances — and computeInvoice is a pure function from a usage snapshot to line items and a total, all in integer micros. It pairs with @absolutejs/metering for usage collection and leaves Stripe, QuickBooks, or another invoicing integration to the host, so previewing an upcoming invoice, re-pricing a past period, or dry-running a plan change is a plain function call.
bun add @absolutejs/billingProvider-neutral pricing and invoice computation used by the hosted AbsoluteJS.ai platform.
@absolutejs/billing is the pure-function layer between @absolutejs/metering (which collects usage events) and an invoicing backend (Stripe, QuickBooks, an internal billing engine).
It does two things:
createPlan(...) — declares a priced product: optional flat
base fee, per-dimension unit prices, optional graduated tiers, optional per-dimension free allowances.
computeInvoice({ plan, period, tenant, usage }) — pure
function that turns a Usage snapshot into an Invoice with line items and a total.
All money math is done in integer micros (1 micro = 1/1,000,000 of a currency unit — the same denomination Stripe stores prices in internally). Float drift is structurally impossible: a $0.0002 per-request price is 200 and rounding policy is explicit.
A PricedDimension is one of three:
Flat per-unit — { perUnitMicros: 200, unit: 1 }
Tiered (graduated) — { tiers: [{ upTo: 1_000_000, perUnitMicros: 200 }, { upTo: Infinity, perUnitMicros: 100 }] }
Custom — { price: (chargedQuantity) => micros } (escape hatch for surge / caps / non-monotonic pricing)
Optional knobs:
freeTier — units subtracted before pricing
unit — divisor so bytesEgress priced as MB ↔ unit:
10241024
label — invoice line-item display name
Plan-level knobs:
basePriceMicros — flat fee per period
minimumChargeMicros — floor; an adjustment line item fills
any gap
The control plane needs to:
Preview an upcoming invoice before the period closes
Re-price a past period under a new plan ("what would this
customer have paid on the proposed enterprise tier?")
Dry-run plan changes before publishing them
A pure cost-model function makes all three trivial — no Stripe SDK, no side effects, no IO. The Stripe push (or QuickBooks export, or mailed-PDF generator) lives outside this package, in @absolutejs/billing-adapters/.
All prices are integer micros (1/1,000,000 of a currency unit — the same denomination Stripe stores internally), so float drift is structurally impossible and rounding policy is explicit.
Each dimension is flat per-unit, graduated tiers, or a custom price function — the escape hatch for surge pricing, caps, and other non-monotonic schemes.
Per-dimension free allowances subtract before pricing, a plan-level base fee charges per period, and minimumChargeMicros floors the total with an adjustment line item.
computeInvoice has no IO and no side effects, so you can preview invoices before the period closes, re-price history under a proposed plan, and dry-run changes before publishing.
Consumes Usage snapshots from @absolutejs/metering directly, and unit divisors let you price bytesEgress per MB or cpuMs per second without pre-converting.
Outcomes
Declare provider-neutral plans with base prices, usage dimensions, free allowances, graduated tiers, minimum charges, labels, and explicit rounding.
Preview, dry-run, and re-price usage snapshots without calling Stripe, QuickBooks, or another invoicing provider.
Hardening checklist
Follow in order
# @absolutejs/billing
import { createPlan, computeInvoice, formatMicros } from "@absolutejs/billing";
const plan = createPlan({
name: "pro",
currency: "usd",
basePriceMicros: 20_000_000, // $20/mo
pricedDimensions: {
requests: { perUnitMicros: 200, freeTier: 1_000_000 },
cpuMs: { perUnitMicros: 50, unit: 1000, freeTier: 60_000 * 60 * 10 },
bytesEgress: {
perUnitMicros: 100,
unit: 1024 * 1024,
freeTier: 100 * 1024 * 1024,
},
hibernationGbSeconds: { perUnitMicros: 5 },
},
});
const invoice = computeInvoice({
plan,
tenant: "acme",
period: { start, end },
usage, // a Usage from @absolutejs/metering
});
console.log(formatMicros(invoice.totalMicros, invoice.currency));
// "27.50 USD"Declare a plan with a base fee, per-dimension prices, and free allowances, then compute an invoice from a metering snapshot.
import {
createPlan,
computeInvoice,
formatMicros,
} from '@absolutejs/billing';
const plan = createPlan({
name: 'pro',
currency: 'usd',
basePriceMicros: 20_000_000, // $20/mo
pricedDimensions: {
requests: { perUnitMicros: 200, freeTier: 1_000_000 },
cpuMs: {
perUnitMicros: 50,
unit: 1000,
freeTier: 60_000 * 60 * 10,
},
bytesEgress: {
perUnitMicros: 100,
unit: 1024 * 1024,
freeTier: 100 * 1024 * 1024,
},
},
});
const invoice = computeInvoice({
plan,
tenant: 'acme',
period: { start, end },
usage, // a Usage from @absolutejs/metering
});
console.log(formatMicros(invoice.totalMicros, invoice.currency));
// "27.50 USD"Graduated tiers price each band separately — the first million requests at one rate, everything beyond at another.
const plan = createPlan({
name: 'scale',
currency: 'usd',
pricedDimensions: {
requests: {
tiers: [
{ upTo: 1_000_000, perUnitMicros: 200 },
{ upTo: Infinity, perUnitMicros: 100 },
],
},
},
});Search the declarations exported by the current package type files. Expand a symbol to inspect its source-backed signature.
@absolutejs/billing — cost-model substrate for the AbsoluteJS PaaS. Two pieces: - createPlan(...) — declarative pricing config: optional flat base fee + per-dimension unit prices, with optional graduated tiers and free-tier allowances per dimension. - computeInvoice({ plan, period, tenant, usage, currency? }) — pure function that turns a @absolutejs/metering-shaped Usage snapshot (or any record of metered numbers) into an Invoice of line items + total. All money math is done in integer micros (1
type Micros = number;@absolutejs/billingCurrent package surface
Import surface · click to copy