import { z } from "zod"; export enum PaymentMethod { Card = "card", // INFO: for other future payment methods } function isValidLuhn(cardNumber: string): boolean { const digits = cardNumber.replace(/\D/g, ""); let sum = 0; let isEven = false; for (let i = digits.length - 1; i >= 0; i--) { let digit = parseInt(digits[i], 10); if (isEven) { digit *= 2; if (digit > 9) { digit -= 9; } } sum += digit; isEven = !isEven; } return sum % 10 === 0; } function isValidExpiry(expiryDate: string): boolean { const match = expiryDate.match(/^(0[1-9]|1[0-2])\/(\d{2})$/); if (!match) return false; const month = parseInt(match[1], 10); const year = parseInt(match[2], 10); const currentDate = new Date(); const currentYear = currentDate.getFullYear() % 100; // Get last 2 digits of year const maxYear = currentYear + 20; // Check if year is within valid range if (year < currentYear || year > maxYear) return false; // If it's the current year, check if the month is valid if (year === currentYear) { const currentMonth = currentDate.getMonth() + 1; // getMonth() returns 0-11 if (month < currentMonth) return false; } return true; } export const cardInfoModel = z.object({ cardholderName: z.string().min(1).max(128), cardNumber: z .string() .min(1) .max(20) .regex(/^\d+$/, "Card number must be numeric") .refine((val) => isValidLuhn(val), { message: "Invalid card number", }), expiry: z .string() .regex(/^(0[1-9]|1[0-2])\/\d{2}$/, "Expiry must be in mm/yy format") .refine((val) => isValidExpiry(val), { message: "Invalid expiry date", }), cvv: z .string() .min(3) .max(4) .regex(/^\d{3,4}$/, "CVV must be 3-4 digits"), }); export type CardInfo = z.infer; export const paymentInfoPayloadModel = z.object({ method: z.enum([PaymentMethod.Card]), cardDetails: cardInfoModel, productId: z.number().int(), orderId: z.number().int(), }); export type PaymentInfoPayload = z.infer; export const paymentInfoModel = cardInfoModel.merge( z.object({ id: z.number().int(), productId: z.number().int(), orderId: z.number().int(), createdAt: z.string().datetime(), updatedAt: z.string().datetime(), }), ); export type PaymentInfo = z.infer;