import type Stripe from 'stripe'; import type * as BillingSettings from '../../db/tables/billingSettings.js'; import * as Db from '../../db/Db.js'; import type * as Scope from '../../Scope.js'; import type * as Store from '../../internal/Store.js'; import * as StripeCustomers from '../../db/tables/stripeCustomers.js'; /** * Whether the organization has any active billing source — the production * sponsorship gate's read. Storage stays one table per source; this resolver * is the union across them (Stripe today). * * @param db - The database. * @param orgId - The organization id (`org_…`). * @returns Whether any billing source is active. */ export declare function active(db: Db.Db, orgId: string, environment?: StripeCustomers.Record['environment']): Promise; /** * The organization's billing status derived from its stored billing source, * or undefined when it has none. The sponsorship gate maps non-`active` * statuses to distinct refusal reasons. * * @param db - The database. * @param orgId - The organization id (`org_…`). * @returns The stored status, or undefined. */ export declare function status(db: Db.Db, orgId: string, environment?: StripeCustomers.Record['environment']): Promise; /** * Converts a decimal currency string (e.g. `'0.50'`) to fee-token base units. * * @param amount - The decimal string; at most {@link Fees.tokenDecimals} fraction digits. * @returns The amount in base units. */ export declare function toBaseUnits(amount: string): bigint; /** * Converts fee-token base units to a decimal currency string with trailing * zeros trimmed (e.g. `350000n` → `'0.35'`). * * @param units - The amount in base units. * @returns The decimal string. */ export declare function fromBaseUnits(units: bigint): string; /** * Start of the limit window containing `now` (ISO 8601); `month` is the UTC * calendar month. * * @param period - The configured window. * @param now - Reference time; defaults to now. * @returns The window start. */ export declare function periodStart(period: BillingSettings.Record['period'], now?: Date): string; /** * A metered billing dimension: a Stripe meter plus its metered monthly price. * Each kind bills as a distinct invoice line item on one shared subscription. */ export type MeterKind = 'apiRequests' | 'feePayerSpend'; /** Descriptor for one metered dimension. */ export type MeterDescriptor = { /** Human-readable meter/product display name. */ displayName: string; /** Meter event name; the Stripe meter aggregates `sum(value)` per customer per period. */ eventName: string; /** Versioned price lookup key; changing economics ships a new key, never a mutated price. */ lookupKey: string; /** Price per meter unit in cents (`unit_amount_decimal`). */ unitAmountDecimal: string; }; /** * The metered dimensions and their unit economics. A price pins cents per meter * unit; changing them ships a new versioned `lookupKey`, never a mutated price. */ export declare const meters: { readonly apiRequests: { readonly displayName: 'API Requests'; readonly eventName: 'api_request_count'; readonly lookupKey: 'api-request-count-v1'; readonly unitAmountDecimal: '0.01'; }; readonly feePayerSpend: { readonly displayName: 'Sponsor Usage'; readonly eventName: 'fee_payer_spend'; readonly lookupKey: 'fee-payer-spend-v1'; readonly unitAmountDecimal: '0.0001'; }; }; /** * The metered dimensions billed in an environment. Production bills sponsorship * spend and request counts; sandbox bills request counts only. * * @param environment - The environment. * @returns The metered dimensions for that environment. */ export declare function kindsFor(environment: StripeCustomers.Record['environment']): readonly MeterKind[]; /** Back-compat: the fee-payer meter event name. */ export declare const meterEventName: "fee_payer_spend"; /** Back-compat: the fee-payer metered price lookup key. */ export declare const priceLookupKey: "fee-payer-spend-v1"; /** * Ensures the account-level metering fixtures exist for a kind: its meter and * metered monthly price. Idempotent by lookup (meter `event_name`, price * `lookup_key`) and memoized per `(client, kind)`. * * @param stripe - The Stripe client. * @param kind - The metered dimension; defaults to fee-payer spend. * @returns The fixture ids. */ export declare function ensureFixtures(stripe: Stripe, kind?: MeterKind): Promise; export declare namespace ensureFixtures { /** Account-level metering fixture ids. */ type Fixtures = { /** Spend meter id (`mtr_…`). */ meterId: string; /** Metered price id (`price_…`). */ priceId: string; }; } /** * Ensures the customer holds exactly one live subscription carrying one metered * item per requested {@link MeterKind}, creating the subscription or adding * missing items as needed. Metered usage bills the customer's default payment * method monthly. * * Fail-closed on more than one live managed subscription: duplicates would * double-bill the same meter usage, so the caller must reconcile manually. * * @param stripe - The Stripe client. * @param customerId - The Stripe customer id. * @param kinds - The metered dimensions to ensure; defaults to fee-payer spend. * @returns The per-kind creation state and whether a subscription was created. */ export declare function ensureSubscription(stripe: Stripe, customerId: string, kinds?: readonly MeterKind[]): Promise; export declare namespace ensureSubscription { /** Per-kind item reconciliation state. */ type ItemResult = { /** Whether this call added the item (a fresh item bills nothing before its start). */ created: boolean; }; /** Outcome of {@link ensureSubscription}. */ type Result = { /** Whether this call created the subscription (none was live before). */ created: boolean; /** Per-kind item state, keyed by {@link MeterKind}. */ items: Partial>; }; } /** More than one live managed subscription for a customer; requires manual reconcile. */ export declare class DuplicateSubscriptionError extends Error { constructor(customerId: string); } /** * Creates the metering job: each tick reports finalized, unreported billable * mainnet spend to the Stripe meter and marks rows reported. A host runtime * drives {@link createReporter.Reporter.tick} on a timer, like the * sponsorship finalizer. */ export declare function createReporter(options: createReporter.Options): createReporter.Reporter; export declare namespace createReporter { /** Options for {@link createReporter}. */ type Options = { /** Maximum rows reported per tick. @default 100 */ batchSize?: number | undefined; /** Chains whose spend is metered. @default mainnet */ chainIds?: readonly number[] | undefined; /** Database holding sponsorship rows, or a factory resolved per tick (Workers/Hyperdrive). */ db: Db.Source; /** Stripe client used to send meter events. */ stripe: Stripe; }; /** A scheduled metering job. */ type Reporter = { /** Runs one reporting pass. Failed rows stay unmarked and retry next tick. */ tick(): Promise<{ failed: number; reported: number; skipped: number; }>; }; } /** * Creates the request-metered billing job: each tick reports deduped billable * request counts to the `api_request_count` meter, per environment, using that * environment's Stripe client. Exactly-once from the request analytics stream * via a Postgres outbox with frozen counts: * * - Deduped counts come from `request_events` (`uniqExact(request_id)`), never * a raw `count()`, so queue retries never over-bill. * - Sandbox rows bill only when billing was active at request time * (`billing_active = 1`); production rows always bill. * - Each tick first re-sends any `pending` events (crash recovery, idempotent * on `identifier`), then seals new additive deltas for buckets older than the * settlement delay whose count has grown since the last report. * * A host runtime drives {@link createRequestUsageReporter.Reporter.tick} on a * timer, like the fee-payer meter reporter. */ export declare function createRequestUsageReporter(options: createRequestUsageReporter.Options): createRequestUsageReporter.Reporter; export declare namespace createRequestUsageReporter { /** Deduped billable request count for one `(org, hour)` bucket. */ type Count = { /** UTC hour start (ISO 8601). */ bucketStart: string; /** Deduped billable request count. */ count: number; /** Organization id (`org_…`). */ orgId: string; }; /** Options for {@link createRequestUsageReporter}. */ type Options = { /** Database holding the outbox/watermarks, or a factory resolved per tick (Workers/Hyperdrive). */ db: Db.Source; /** Clock seam for tests; defaults to `Date.now`. */ now?: (() => number) | undefined; /** Reads deduped billable counts per `(org, hour)` for an environment window. */ readCounts: (options: { environment: StripeCustomers.Record['environment']; from: string; to: string; }) => Promise; /** Sandbox (test-mode) Stripe client; omit to skip sandbox billing. */ sandboxStripe?: Stripe | undefined; /** Only seal buckets older than this. @default 15min */ settlementDelayMs?: number | undefined; /** Production Stripe client. */ stripe: Stripe; /** How far back a tick reconciles late deltas. @default 48h */ windowMs?: number | undefined; }; /** A scheduled request-metering job. */ type Reporter = { /** Reads the current retry backlog without sending meter events. */ pending(): Promise; /** Runs one reporting pass across production (and sandbox when configured). */ tick(): Promise<{ failed: number; reported: number; skipped: number; }>; }; /** Current durable retry backlog for one billing environment. */ type Pending = { count: number; environment: StripeCustomers.Record['environment']; oldestPendingAgeSeconds: number; }; } /** * Every organization's billing-active flag in an environment — the driver set * for reconciling cached snapshots against this table. Provider-agnostic seam: * callers reconcile against `{ orgId, active }`, never the Stripe source shape. * * @param db - The database. * @param environment - The environment to enumerate. * @returns One `{ orgId, active }` per billing source in the environment. */ export declare function listActive(db: Db.Db, environment: StripeCustomers.Record['environment']): Promise<{ active: boolean; orgId: string; }[]>; /** * Creates the snapshot reconciler: each tick re-stamps the `billingActive` * flag cached on API-key records from the authoritative billing table, so a * missed or failed webhook re-sync self-heals within a tick. A host runtime * drives {@link createKeyBillingSyncer.Syncer.tick} on a timer, like the meter * reporter. Off the request path, so the auth hot path never reads billing. * * Only environments where keys carry a billing snapshot are reconciled * (sandbox today); production keys carry none. */ export declare function createKeyBillingSyncer(options: createKeyBillingSyncer.Options): createKeyBillingSyncer.Syncer; export declare namespace createKeyBillingSyncer { /** Options for {@link createKeyBillingSyncer}. */ type Options = { /** Database holding billing sources, or a factory resolved per tick (Workers/Hyperdrive). */ db: Db.Source; /** Environments to reconcile. @default ['sandbox'] */ environments?: readonly StripeCustomers.Record['environment'][] | undefined; /** KV state store holding API-key records. */ kv: Store.State; /** Scope catalog accepted while reconciling API-key records. */ scopeCatalog?: Scope.Catalog | undefined; }; /** A scheduled snapshot-reconcile job. */ type Syncer = { /** Runs one reconcile pass, re-stamping drifted key records. */ tick(): Promise<{ checked: number; updated: number; }>; }; } //# sourceMappingURL=Billing.d.ts.map