/** * Single-use consumption of a verified storefront challenge. * * A verified challenge is a bearer credential: without consumption it would * authorize an unlimited number of bookings, and without binding it would * authorize a booking for a draft or a contact it was never verified against. * * Consumption is one conditional UPDATE so that concurrent callers cannot both * win, and it takes a transaction because it must commit atomically with * whatever it authorizes. For booking creation that means running inside the * create command, after the durable claim — an exact idempotent retry replays * the original booking without re-entering this path. */ import type { AnyDrizzleDb } from "@voyant-travel/db"; /** Purpose marking a challenge as authorizing one self-service booking create. */ export declare const STOREFRONT_VERIFICATION_BOOKING_CREATE_PURPOSE: "booking_create"; export interface ConsumeVerifiedChallengeInput { challengeId: string; /** Must equal the purpose the challenge was started with. */ purpose: string; /** The draft the challenge was bound to at start. */ subjectRef: string; /** * The normalized contact the challenge proved control of. Passing the * booking's billing contact here is what stops a challenge verified for one * address from authorizing a booking billed to another. */ destination: string; /** What is consuming it — the created booking id. */ consumedRef: string; now?: Date; consumptionWindowSeconds?: number; } export type ConsumeVerifiedChallengeResult = { status: "consumed"; destination: string; } | { status: "rejected"; }; /** * Spend a verified challenge, or report that it cannot be spent. * * Every condition lives in the UPDATE predicate rather than in a preceding * read, so there is no window between checking and spending. */ export declare function consumeVerifiedChallenge(tx: AnyDrizzleDb, input: ConsumeVerifiedChallengeInput): Promise; /** * Read the destination a challenge was verified for, without spending it. * * Applies the same binding predicate as consumption — purpose, subject, and * verification window — so a caller cannot learn the destination of a * challenge that could not authorize this booking anyway. Returns null when * the challenge is unusable, so the route reports "verification required" * rather than distinguishing why. */ export declare function peekVerifiedChallengeDestination(db: AnyDrizzleDb, input: { challengeId: string; purpose: string; subjectRef: string; now?: Date; consumptionWindowSeconds?: number; }): Promise<{ channel: "email" | "sms"; destination: string; } | null>;