/** * Single source of truth for how long a PENDING_PAYMENT booking has to be paid for, * shared by TasksService (which auto-cancels bookings past this deadline) and * SeatsService (which extends the seat hold to cover exactly this window when a * booking/PNR is created — without this, the seat hold reverted to its original * short seat-selection TTL and could expire mid-payment, letting a second customer * grab the same seat). */ /** Maximum time (hours) a passenger has to pay after booking. */ export const MAX_PAYMENT_HOURS = 2; /** Minutes before departure: cutoff for new bookings and payment deadline. */ export const CUTOFF_MINUTES = 30; /** * How long a passenger is given to finish one provider payment session, once opened. * 5 minutes of actual paying (redirect → PIN/OTP → provider callback) + 1 minute of slack. */ export const PAYMENT_SESSION_MINUTES = 6; export const MIN_PAYMENT_WINDOW_MINUTES = 7; export const PAYMENT_SETTLE_MARGIN_SECONDS = 60; export function computePaymentDeadline( createdAt: Date, departureAt: Date, checkinMinutes: number = CUTOFF_MINUTES, ): Date { const maxDeadline = new Date(createdAt.getTime() + MAX_PAYMENT_HOURS * 60 * 60 * 1000); const cutoffDeadline = new Date(departureAt.getTime() - checkinMinutes * 60 * 1000); return maxDeadline < cutoffDeadline ? maxDeadline : cutoffDeadline; } export function canOpenPaymentSession( paymentDeadline: Date, now: Date = new Date(), ): boolean { return paymentDeadline.getTime() - now.getTime() >= MIN_PAYMENT_WINDOW_MINUTES * 60 * 1000; } export function computePaymentSessionExpiry( paymentDeadline: Date, now: Date = new Date(), ): Date { const sessionEnd = new Date(now.getTime() + PAYMENT_SESSION_MINUTES * 60 * 1000); return sessionEnd < paymentDeadline ? sessionEnd : paymentDeadline; }