barsa ce42664965 Add Checkout Registration Module and Enhance Public Contact Features
- Integrated CheckoutRegistrationModule into the application for handling checkout-related functionalities.
- Updated router configuration to include the new CheckoutRegistrationModule for API routing.
- Enhanced SalesforceAccountService with methods for account creation and email lookup to support checkout registration.
- Implemented public contact form functionality in SupportController, allowing unauthenticated users to submit inquiries.
- Added rate limiting to the public contact form to prevent spam submissions.
- Updated CatalogController and CheckoutController to allow public access for browsing and cart validation without authentication.
2025-12-17 14:07:22 +09:00

157 lines
5.3 KiB
TypeScript

/**
* 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", "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<typeof orderTypeSchema>;
export type PriceBreakdownItem = z.infer<typeof priceBreakdownItemSchema>;
export type CartItem = z.infer<typeof cartItemSchema>;
export type GuestInfo = z.infer<typeof guestInfoSchema>;
export type CheckoutRegisterRequest = z.infer<typeof checkoutRegisterRequestSchema>;
export type CheckoutRegisterResponse = z.infer<typeof checkoutRegisterResponseSchema>;
export type CheckoutStep = z.infer<typeof checkoutStepSchema>;
export type CheckoutState = z.infer<typeof checkoutStateSchema>;