import type Stripe from "stripe"; import { type PlanCatalog } from "./plans.js"; import type { Mirror, MirrorQuery } from "./mirror.js"; import type { WorkOSOrgAdapter } from "./adapters/workos-org.js"; /** Optional custom cursor persistence. By default the sync manages its own tiny * cursor table via the `query` executor — the app declares nothing. */ export interface CursorStore { get(source: string): Promise; set(source: string, cursor: string | null): Promise; } export interface BillingSyncOptions { adapter: WorkOSOrgAdapter; plans: PlanCatalog; /** DB executor (same one you pass to createMirror). The sync creates + uses * its own `billing_sync_cursors` table through it — no app schema needed. */ query: MirrorQuery; currency?: string; /** Override cursor persistence (advanced). Defaults to the query-backed table. */ cursor?: CursorStore; /** * Also poll PAYMENT events, as a catch-up sweep behind the webhook. * * Off by default: payments are the webhook's job (Stripe's recommendation, and * a late credit is a customer who paid and didn't get what they bought), and a * poller shadowing it every minute is duplicated infrastructure rather than a * safety net — Stripe already retries failed deliveries for three days. * * Turn it on for a LOW-FREQUENCY reconciliation run (a nightly cron) if you * want recovery from an endpoint that was disabled or misconfigured. Safe at * any frequency: the handlers are idempotent. */ reconcilePayments?: boolean; /** * An event whose handler failed every attempt and was SKIPPED to keep the sweep * moving. Defaults to `start`'s `onError`. * * Separate from `onError` because the two mean different things: `onError` is * "the sweep failed", which stops and retries; this is "the sweep carried on * WITHOUT this event", which nothing else will announce. That silence is the * characteristic failure of a poller — the same reason `onUsageFault` exists — * and it is what let a single unhandleable event stop mirroring for twelve days * with only a log line to show for it. */ onEventFault?: (source: "stripe" | "workos", event: { id: string; error: unknown; }) => void; /** Mirror of the WorkOS Organization (e.g. a workspaces table). */ orgMirror?: Mirror; /** Mirror of the WorkOS User (e.g. a users table). */ userMirror?: Mirror; hooks?: { /** Extra app-specific cleanup when a WorkOS user is deleted (the user * mirror row is already removed). */ onUserDeleted?(workosUserId: string): Promise; /** A subscription invoice failed to collect (dunning). The org's status is * already set to `past_due`; use this to notify the user / gate access. * Stripe Smart Retries + the card-updater keep retrying automatically. */ onPaymentFailed?(orgId: string): Promise; }; } export declare const PAYMENT_EVENT_TYPES: string[]; export declare const SYNC_EVENT_TYPES: string[]; export interface BillingSync { /** Poll + reconcile once. Use this from a serverless cron route. */ runOnce(): Promise<{ stripe: number; workos: number; }>; /** Start an in-process interval scheduler (for a long-lived server — e.g. * Next's instrumentation register()). Runs immediately, then every * intervalMs (default 60s), never overlapping. Returns a stop() fn. * For multi-instance deployments run it on one replica (or use runOnce via * a single external cron) to avoid duplicate polling. */ start(opts?: { intervalMs?: number; onError?: (e: unknown) => void; }): () => void; } export declare function createStripeEventHandler(opts: { adapter: WorkOSOrgAdapter; plans: PlanCatalog; currency?: string; hooks?: BillingSyncOptions["hooks"]; }): (event: Stripe.Event) => Promise; export declare function createBillingSync(opts: BillingSyncOptions): BillingSync; /** Web-standard (Request → Response) handler that runs one sync cycle — for a * serverless cron trigger. Framework-agnostic: mount in a Next route * (`export const GET = createSyncRoute(sync, { secret })`), Hono, Bun, etc. * If `secret` is set, requests must send it as `Authorization: Bearer ` * (or an `x-cron-secret` header). */ export declare function createSyncRoute(sync: BillingSync, opts?: { secret?: string; }): (request: Request) => Promise; //# sourceMappingURL=sync.d.ts.map