/** * Checkout Domain - Schemas * * Zod validation schemas for unified checkout flow. * Supports both authenticated and guest checkout. */ import { z } from "zod"; import { emailSchema, passwordSchema, nameSchema, phoneSchema, genderEnum, } from "../common/schema.js"; import { addressFormSchema } from "../customer/schema.js"; // ============================================================================ // ISO Date Schema // ============================================================================ const isoDateOnlySchema = z .string() .regex(/^\d{4}-\d{2}-\d{2}$/, "Enter a valid date (YYYY-MM-DD)") .refine(value => !Number.isNaN(Date.parse(value)), "Enter a valid date (YYYY-MM-DD)"); // ============================================================================ // Order Type Schema // ============================================================================ export const orderTypeSchema = z.enum(["INTERNET", "SIM", "VPN"]); // ============================================================================ // Price Breakdown Schema // ============================================================================ export const priceBreakdownItemSchema = z.object({ label: z.string(), sku: z.string().optional(), monthlyPrice: z.number().optional(), oneTimePrice: z.number().optional(), quantity: z.number().optional().default(1), }); // ============================================================================ // Cart Item Schema // ============================================================================ export const cartItemSchema = z.object({ orderType: orderTypeSchema, planSku: z.string().min(1, "Plan SKU is required"), planName: z.string().min(1, "Plan name is required"), addonSkus: z.array(z.string()).default([]), configuration: z.record(z.string(), z.unknown()).default({}), pricing: z.object({ monthlyTotal: z.number().nonnegative(), oneTimeTotal: z.number().nonnegative(), breakdown: z.array(priceBreakdownItemSchema).default([]), }), }); // ============================================================================ // Guest Info Schema // ============================================================================ export const guestInfoSchema = z.object({ email: emailSchema, firstName: nameSchema, lastName: nameSchema, phone: phoneSchema, phoneCountryCode: z.string().min(1, "Phone country code is required"), password: passwordSchema, dateOfBirth: isoDateOnlySchema.optional(), gender: genderEnum.optional(), }); // ============================================================================ // Checkout Register Request Schema // ============================================================================ export const checkoutRegisterRequestSchema = z.object({ email: emailSchema, firstName: nameSchema, lastName: nameSchema, phone: phoneSchema, phoneCountryCode: z.string().min(1, "Phone country code is required"), password: passwordSchema, address: addressFormSchema, dateOfBirth: isoDateOnlySchema.optional(), gender: genderEnum.optional(), acceptTerms: z.literal(true, { message: "You must accept the terms and conditions" }), marketingConsent: z.boolean().optional(), }); // ============================================================================ // Checkout Register Response Schema // ============================================================================ export const checkoutRegisterResponseSchema = z.object({ success: z.boolean(), user: z.object({ id: z.string(), email: z.string(), firstname: z.string(), lastname: z.string(), }), session: z.object({ expiresAt: z.string(), refreshExpiresAt: z.string(), }), sfAccountNumber: z.string().optional(), }); // ============================================================================ // Checkout Step Schema // ============================================================================ export const checkoutStepSchema = z.enum([ "account", "address", "availability", "payment", "review", ]); // ============================================================================ // Checkout State Schema (for Zustand store) // ============================================================================ export const checkoutStateSchema = z.object({ // Cart data cartItem: cartItemSchema.nullable(), // Guest info (pre-registration) guestInfo: guestInfoSchema.partial().nullable(), // Address address: addressFormSchema.nullable(), // Registration state registrationComplete: z.boolean(), userId: z.string().nullable(), // Payment state paymentMethodVerified: z.boolean(), // Checkout step currentStep: checkoutStepSchema, }); // ============================================================================ // Inferred Types // ============================================================================ export type OrderType = z.infer; export type PriceBreakdownItem = z.infer; export type CartItem = z.infer; export type GuestInfo = z.infer; export type CheckoutRegisterRequest = z.infer; export type CheckoutRegisterResponse = z.infer; export type CheckoutStep = z.infer; export type CheckoutState = z.infer;