import type Stripe from "stripe"; import { type BillingInterval, type PlanCatalog, type Sale } from "./plan-model.js"; export type { BillingInterval, Money, IntervalPrice, SeatTypeSpec, SeatTypeDisplay, PlanSpec, PlanDisplay, PlanLimits, PlanCatalog, PlanModel, NormalSeatType, Sells, Grant, Cap, CapWindow, CapCovers, Exhausted, Replenish, Sale, Quantities, BasketProblem, CycleWindow, Every, RateLimit, LedgerCoverage, ToolCapabilities, } from "./plan-model.js"; export { DEFAULT_SEAT_TYPE, definePlans, normalizePlan, normalizePlans, planModel, plansWhere, selfServePlans, toolCapabilities, ALL_TOOL_CAPABILITIES, defaultBasket, validateBasket, describeBasketProblem, grantFor, poolSizeOf, poolIsPerSeat, packSizeOf, exhaustedPolicy, capCovers, cycleWindowFor, rateWindowFor, rateLimitsOf, ledgerGaps, coverageNeededBy, } from "./plan-model.js"; export declare const lookupKeyFor: (plan: string, interval: BillingInterval, seatType?: string) => string; export interface EnsuredPrice { plan: string; interval: BillingInterval; /** Set only for seat-typed plans. */ seatType?: string; priceId: string; productId: string; amount: number; lookupKey: string; } /** Idempotently create/reconcile Stripe products + prices for the paid plans. * Returns the resolved price for every paid plan × interval. Free plans (both * prices 0) create no Stripe objects. Safe to call on every boot / first use. */ export declare function ensurePlans(plans: PlanCatalog, opts?: { currency?: string; taxBehavior?: Stripe.Price.TaxBehavior; /** * Sweep the account for managed prices/products this config no longer wants. * * It is a FULL account scan (every active price, every active product), so * it is not something to do on a request: `true` (the default) for an * explicit reconcile — a CLI/sync/boot call — and `"background"` on a hot * path, which starts the sweep without awaiting it. `false` skips it. */ archive?: boolean | "background"; }): Promise; export interface MigratedSubscription { subscriptionId: string; customerId: string | null; /** The price left behind, and the one adopted. */ from: string; to: string; quantity: number; /** Seat type for a seat-typed plan; absent for a flat plan. */ seatType?: string; } export interface MigrateSubscriptionsResult { /** What moved — or what WOULD move, when `dryRun`. */ migrated: MigratedSubscription[]; /** * Subscriptions found on a superseded price that turned out to need no change. * * NOT a count of every up-to-date subscriber: the search starts from the old * prices (which is what keeps it bounded), so a subscription already on the * current price is never visited. Zero here after a successful migration means * "nothing left on an old price", which is the answer that matters. */ alreadyCurrent: number; /** Old price ids that were searched. */ oldPrices: string[]; dryRun: boolean; } /** * Move live subscriptions from a plan's superseded prices onto its current ones. * * Run it after changing an amount (or the currency) in the plans config, when * the change is meant to apply to existing customers and not only to new ones. * It is idempotent: a subscription already on the current price is counted and * left alone, so re-running does nothing. * * `dryRun: true` reports exactly what would change without touching Stripe — * worth doing first, since the alternative is re-pricing real subscriptions. * * Proration defaults to `"none"`: the new amount takes effect at the next * renewal and nobody is charged (or credited) mid-cycle for the difference, * which is what a straightforward price change usually means. Pass * `"create_prorations"` or `"always_invoice"` deliberately. * * Only prices this library minted are considered (`metadata.managedBy`), so a * price attached by hand in the Dashboard is never moved out from under you. */ export declare function migrateSubscriptions(opts: { plans: PlanCatalog; plan: string; interval: BillingInterval; currency?: string; dryRun?: boolean; prorationBehavior?: Stripe.SubscriptionUpdateParams.ProrationBehavior; /** Stop after this many subscriptions. Omit for all of them. */ limit?: number; }): Promise; /** Resolved prices for one plans config: lookup_key → price id. */ export type PlanPrices = ReadonlyMap; /** * The price ids for `plans`, provisioning them on first use. * * Same guarantee as `ensurePlans` — the Stripe objects exist and match the * config — without paying for the check every time. Use this wherever a price id * is needed to serve a request; call `ensurePlans` directly when the point IS * the reconcile (a deploy hook, `billing sync`, a test). * * Look ids up with `lookupKeyFor(plan, interval, seatType)`. */ export declare function resolvePlanPrices(plans: PlanCatalog, opts?: { currency?: string; taxBehavior?: Stripe.Price.TaxBehavior; }): Promise; /** * Drop the memo, so the next `resolvePlanPrices` reconciles against Stripe again. * * Worth calling when a price id turns out to be stale — Stripe rejecting a * checkout because a price was archived in the dashboard is exactly the case the * TTL alone would leave broken for ten minutes. */ export declare function invalidatePlanPrices(): void; /** * Prime the price memo. TESTS ONLY. * * `resolvePlanPrices` provisions through `ensurePlans`, which WRITES to Stripe * and archives anything the passed catalogue doesn't mention — a partial config * once archived every real price in the test account that way. So a test that * wants the arithmetic downstream of price resolution stubs the map instead of * letting the reconcile run. Nothing in the library calls this. */ export declare function __setPlanPricesForTests(prices: PlanPrices): void; /** Resolve the current Stripe price id for a plan + interval (via lookup_key). * Returns null for a free/absent price. */ export declare function planPriceId(plan: string, interval: BillingInterval, seatType?: string): Promise; /** Reverse-map a Stripe price id → plan key (via the price's metadata). */ export declare function planForPriceId(priceId: string): Promise; /** Reverse-map a Stripe price id → its seat type (via metadata), or null for a * flat (non-seat-typed) price. */ export declare function seatTypeForPriceId(priceId: string): Promise; /** * What a subscription actually BOUGHT, by seat type — the number every seat * guard, pool size and per-seat grant is measured against. * * Read off the items' own price metadata, so it needs no catalogue and no * network. Null when nothing is quantified, so a reader falls back to the * active member count rather than to a pool of one seat. * * Lives here rather than in `sync.ts` because BOTH paths that learn about a * subscription have to record it: the poller/webhook, and `completeCheckout` — * which is the only moment the SIGNUP flow knows the subscription and the org * together, since the `customer.subscription.created` event fires before the * org exists to stamp on it. */ export declare function purchasedSeatsOf(sub: { items?: { data?: Array<{ price?: { metadata?: Record | null; } | null; quantity?: number | null; }>; } | null; }): Record | null; /** Seat limit for a plan (null = unlimited, undefined plan = null). */ /** Member limit for a plan (null = unlimited, unknown plan = null). */ export declare function seatLimit(plans: PlanCatalog, plan: string): number | null; /** Per-type seat cap, or null when that type is unlimited. This is what makes a * `max: 1` shared API seat mean something. */ export declare function seatTypeLimit(plans: PlanCatalog, plan: string, seatType: string): number | null; /** How a plan is sold, which decides whether any checkout path may accept it. */ export declare function planSale(plans: PlanCatalog, plan: string): Sale | null; /** * Credits to GRANT for `seatCount` members on a plan, per cycle. * * Now expressed over `grant`, so a plan whose allowance is an ENTITLEMENT * (`grant: none`, the default for everything but a credit-selling plan) returns * 0 — crediting it would discount that plan's own invoice. */ export declare function includedCredits(plans: PlanCatalog, plan: string, seatCount: number): number; /** Credits to GRANT given purchased counts per seat type. Falls back to the * member-count form for plans without seat types, so callers can use it * uniformly. */ export declare function includedCreditsByType(plans: PlanCatalog, plan: string | null, counts: Record): number; //# sourceMappingURL=plans.d.ts.map