/** * Catalog's provider for `finance.self-service-booking-source.runtime`. * * Finance owns the durable create command; catalog owns the draft, quote, and * hold a public caller built it from. This module is the seam between them: it * verifies that state, asks the owning vertical to derive a command, and later * spends the state inside Finance's transaction. * * Everything the command contains is derived here or by the vertical handler. * A public caller supplies only identifiers — never prices, booking numbers, * relationship ids, tax lines, or status. */ import type { AnyDrizzleDb } from "@voyant-travel/db"; import type { PostgresJsDatabase } from "drizzle-orm/postgres-js"; import type { OwnedBookingHandlerRegistry } from "./owned-handler.js"; /** Resolve or create the CRM person a booking is billed to. */ export type ResolveSelfServiceBillingPerson = (contact: { firstName: string | null; lastName: string | null; email: string | null; phone: string | null; }, provenance: { source: string; sourceRef: string; }) => Promise; export interface SelfServiceBookingSourceProviderDeps { /** Resolved per request, matching how other catalog jobs reach the registry. */ resolveOwnedHandlers(): OwnedBookingHandlerRegistry | Promise | undefined; /** * Optional CRM resolver. Only called once the whole party would pass the * create command's own validation — resolving persists a person before the * durable claim exists, so a party that would be rejected downstream must * never reach it, or every failed attempt orphans a CRM row. * * Without it an authenticated customer can still book (they already are the * billing party); a verified guest is rejected as `incomplete_draft`. */ resolveBillingPerson?: ResolveSelfServiceBillingPerson; /** * Runtime env for verifying the draft capability. Without it the capability * cannot be checked, so booking is refused rather than allowed. */ resolveEnv?(): Record; } /** * A draft or quote was spent between resolution and the create transaction. * Thrown rather than returned so the create rolls back. */ export declare class SelfServiceSourceConsumedError extends Error { readonly source: "draft" | "quote"; constructor(source: "draft" | "quote"); } type Rejection = { status: "rejected"; reason: string; }; export declare function createSelfServiceBookingSourceProvider(deps: SelfServiceBookingSourceProviderDeps): { resolveBookingSource(input: { db: PostgresJsDatabase; draftId: string; quoteId: string; caller: { personId?: string; verifiedEmail?: string; verifiedPhone?: string; }; /** Proves the caller is the one who built this draft. */ draftCapabilityToken?: string; }): Promise; holdExpiresAt: Date | null; }>; /** * Spend the draft and quote inside Finance's transaction. Reached only on * a first execution — an exact retry replays at the claim and skips this. */ consumeBookingSource(tx: AnyDrizzleDb, input: { draftId: string; quoteId: string; bookingId: string; }): Promise; }; export {};