Comprehensive refactoring across 70 files (net -298 lines) improving type safety, error handling, and code organization: - Replace .passthrough()/.catchall(z.unknown()) with .strip() in all Zod schemas - Tighten Record<string, unknown> to bounded union types where possible - Replace throw new Error with domain-specific exceptions (OrderException, FulfillmentException, WhmcsOperationException, SalesforceOperationException, etc.) - Split AuthTokenService (625 lines) into TokenGeneratorService and TokenRefreshService with thin orchestrator - Deduplicate FreebitClientService with shared makeRequest() method - Add typed interfaces to WHMCS facade, order service, and fulfillment mapper - Externalize hardcoded config values to ConfigService with env fallbacks - Consolidate duplicate billing cycle enums into shared billingCycleSchema - Standardize logger usage (nestjs-pino @Inject(Logger) everywhere) - Move shared WHMCS number coercion helpers to whmcs-utils/schema.ts
62 lines
2.3 KiB
TypeScript
62 lines
2.3 KiB
TypeScript
/**
|
|
* Checkout Domain - Schemas
|
|
*
|
|
* Zod validation schemas for checkout flow.
|
|
* Supports authenticated checkout.
|
|
*/
|
|
|
|
import { z } from "zod";
|
|
import { CHECKOUT_ORDER_TYPE } from "./contract.js";
|
|
|
|
// ============================================================================
|
|
// Order Type Schema
|
|
// ============================================================================
|
|
|
|
const CHECKOUT_ORDER_TYPE_VALUES = Object.values(CHECKOUT_ORDER_TYPE) as [string, ...string[]];
|
|
|
|
/**
|
|
* Checkout order types - uses PascalCase to match Salesforce/BFF contracts
|
|
* @see packages/domain/orders/contract.ts ORDER_TYPE for canonical values
|
|
*/
|
|
export const checkoutOrderTypeSchema = z.enum(CHECKOUT_ORDER_TYPE_VALUES);
|
|
|
|
// ============================================================================
|
|
// 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: checkoutOrderTypeSchema,
|
|
planSku: z.string().min(1, "Plan SKU is required"),
|
|
planName: z.string().min(1, "Plan name is required"),
|
|
addonSkus: z.array(z.string()).default([]),
|
|
// Checkout configuration values are user-supplied key-value pairs (strings, numbers, booleans)
|
|
configuration: z
|
|
.record(z.string(), z.union([z.string(), z.number(), z.boolean(), z.null()]))
|
|
.default({}),
|
|
pricing: z.object({
|
|
monthlyTotal: z.number().nonnegative(),
|
|
oneTimeTotal: z.number().nonnegative(),
|
|
breakdown: z.array(priceBreakdownItemSchema).default([]),
|
|
}),
|
|
});
|
|
|
|
// ============================================================================
|
|
// Inferred Types
|
|
// ============================================================================
|
|
|
|
export type OrderType = z.infer<typeof checkoutOrderTypeSchema>;
|
|
export type PriceBreakdownItem = z.infer<typeof priceBreakdownItemSchema>;
|
|
export type CartItem = z.infer<typeof cartItemSchema>;
|