/** * Pure state computation over the charge log. * * This file is `state.ts` (not `derive.ts`) because gramio's `derive` * concept is reserved for ctx decoration (see `derive.ts` alongside, * which builds `ctx.payments`). The reducers here are bot-id and ctx * agnostic — they take `ChargeRecord[]` and a session-shaped object * and mutate the session in place. * * The charge log (`pay:charge:{chargeId}`) is the single source of truth * (CLAUDE.md §"Storage layout"). Session state is a cache for O(1) * runtime checks. This module owns the function that rebuilds the cache * from the log. * * Two surfaces: * * `deriveState(charges)` — pure, reduces a chronological list of * NON-refunded charges into the cache * shape. Used by reconcile and refund * (after marking the refunded charge). * * `applyCharge(session, …)` — incremental, mutates one session record * for a single new charge. Used by * `handlers.ts` on `successful_payment`. * * `revertCreditsForCharge(session, charge)` — targeted decrement * used by refund (vip/perks are full * re-derive territory; credits is not, * because consumption isn't in the log). * * Credits balance is **not fully derivable** from the log alone — the * log records grants, not consumption. `deriveState` therefore returns * `totalCreditsGranted` (sum) and callers decide whether to use it as * the balance (reconcile of fresh state) or to ignore (incremental). */ import type { ChargeRecord, DerivedPaymentsState, PaymentsSession } from "./types.js"; /** * Result of `deriveState`. `totalCreditsGranted` is informational — * subtract consumption (which isn't logged) for the real balance. */ export type DeriveResult = DerivedPaymentsState & { readonly totalCreditsGranted: number; }; /** * Pure reducer over a chronological list of charges. Caller is * responsible for filtering out refunded ones if they want * "current state after refunds" semantics. */ export declare const deriveState: (charges: ReadonlyArray) => DeriveResult; /** * Apply a single new charge to a session in place. Idempotent across * `chargeId` because the caller (handlers.ts) guards via the * `pay:idempotency:{chargeId}` sentinel before this fires; we don't * re-check here. * * - vip: replaces `session.pay.vip` (renewals update expiresAt; * tier upgrades replace rung) * - credits: increments `session.pay.credits` by the granted amount * (covers both pack purchases AND vip renewal grants) * - perks: set-if-absent on `session.pay.perks[key]` */ export declare const applyCharge: (session: { pay?: PaymentsSession; }, charge: ChargeRecord) => void; /** * Subtract a refunded charge's `creditsGranted` from the session * balance, clamping at zero. The "clamp at zero" semantics are * deliberate: a user may have already consumed credits when the refund * arrives, and going negative would let them buy back into a debt — we * refuse that and accept the asymmetry (the refunded Stars are already * leaving the bot's balance via `refundStarPayment` either way). */ export declare const revertCreditsForCharge: (session: { pay?: PaymentsSession; }, charge: ChargeRecord) => void; /** * Rebuild vip + perks (the fully-derivable parts of session.pay.*) * from a fresh chronological view of the charge log, **after** the * caller has marked any newly-refunded charges. Credits balance is * NOT touched — caller handles that explicitly via * `revertCreditsForCharge` for the targeted decrement semantics. * * Used by: * - refund.ts after `refundStarPayment` succeeds * - reconcile (admin command) for full state recovery */ export declare const rebuildVipAndPerks: (session: { pay?: PaymentsSession; }, charges: ReadonlyArray) => void; //# sourceMappingURL=state.d.ts.map