business-as-code
Version:
Primitives for expressing business logic and processes as code
154 lines • 5.12 kB
TypeScript
/**
* Pricing — discriminated union with five factory variants.
*
* outcome — pay on delivery; tiers by complexity (S/M/L)
* subscription — recurring plan + optional metered overage
* perInvocation — flat per-call with included-tier ladder
* composite — one-time base + metered events
* percent-of — proportional charge against a realised basis
* (invoice amount, collected amount, transaction volume)
* with optional cap / floor
*
* Each factory returns a typed Pricing value with discriminator on .kind.
*/
import type { Currency, Money } from './types.js';
import type { SLATarget } from './sla.js';
export interface OutcomeTier {
id: string;
amount: bigint;
currency?: Currency;
description?: string;
}
export interface PerInvocationTier {
id: string;
amount: bigint;
/** Number of invocations included before per-tier billing applies. */
includedPerMonth?: number;
/** Per-invocation overage cost above included. */
overage?: bigint;
}
/**
* A single metered-billing line item. `description` is genuinely optional —
* callers may omit it from inline literals AND from the {@link Pricing}
* factory calls (`Pricing.subscription` / `Pricing.composite`).
*/
export interface MeteredEntry {
event: string;
amount: bigint;
description?: string;
}
/**
* Optional one-time base charge on a {@link Pricing.composite} plan. `description`
* is optional under `exactOptionalPropertyTypes`.
*/
export interface CompositeBase {
id: string;
amount: bigint;
description?: string;
}
/**
* Recurring plan portion of a {@link Pricing.subscription}. Shared between the
* `Pricing` discriminated union and the {@link Pricing.subscription} factory
* so the two shapes never drift.
*/
export interface SubscriptionPlan {
id: string;
amount: bigint;
currency: Currency;
interval: 'day' | 'week' | 'month' | 'quarter' | 'year';
}
/**
* Standard bases the {@link Pricing.percentOf} runtime knows how to resolve
* at settlement time. Adapters MAY accept arbitrary basis strings for
* domain-specific metering, but the canonical four cover the common cases:
*
* invoice-amount — face value of an outbound invoice
* collected-amount — funds actually received (post-settlement)
* transaction-volume — gross payment volume processed
* <custom string> — provider-defined; must be resolvable in the
* metering runtime
*/
export type PercentOfBasis = 'invoice-amount' | 'collected-amount' | 'transaction-volume' | (string & {});
export type Pricing = {
kind: 'outcome';
tiers: OutcomeTier[];
sla?: SLATarget;
} | {
kind: 'subscription';
plan: SubscriptionPlan;
metered?: MeteredEntry[];
sla?: SLATarget;
} | {
kind: 'per-invocation';
tiers: PerInvocationTier[];
} | {
kind: 'composite';
base: CompositeBase;
metered: MeteredEntry[];
} | {
kind: 'percent-of';
basis: PercentOfBasis;
/**
* Rate in basis points (1/100ths of a percent). Examples: `200` = 2%,
* `75` = 0.75%, `1000` = 10%.
*
* The metering runtime computes the charge as
* `(realised_basis * rateBasisPoints) / 10000`, then clamps the
* result by the optional `cap` / `floor` (when present).
*/
rateBasisPoints: number;
/** Optional upper bound on the per-event charge. */
cap?: Money;
/** Optional lower bound on the per-event charge. */
floor?: Money;
};
export declare const Pricing: {
outcome(opts: {
tiers: OutcomeTier[];
sla?: SLATarget;
}): Pricing;
subscription(opts: {
plan: SubscriptionPlan;
metered?: MeteredEntry[];
sla?: SLATarget;
}): Pricing;
perInvocation(opts: {
tiers: PerInvocationTier[];
}): Pricing;
composite(opts: {
base: CompositeBase;
metered: MeteredEntry[];
}): Pricing;
/**
* Percent-of-basis pricing — proportional charge against a realised
* basis (e.g. invoice amount, collected amount, transaction volume).
*
* The metering runtime resolves `basis` to a concrete bigint at
* settlement time, then computes the charge as
* `(realised_basis * rateBasisPoints) / 10000`, optionally clamped by
* `cap` / `floor`.
*
* @example AR Service: 2% of collected funds
* ```ts
* Pricing.percentOf({ basis: 'collected-amount', rateBasisPoints: 200 })
* ```
*
* @example Capped: 0.75% of transaction volume, max $50/event
* ```ts
* Pricing.percentOf({
* basis: 'transaction-volume',
* rateBasisPoints: 75,
* cap: { amount: 5000n, currency: 'USD' },
* })
* ```
*/
percentOf(opts: {
basis: PercentOfBasis;
rateBasisPoints: number;
cap?: Money;
floor?: Money;
}): Pricing;
};
/** Convenience: build a Money value from a bigint + currency. */
export declare const money: (amount: bigint, currency?: Currency) => Money;
//# sourceMappingURL=pricing.d.ts.map