Files
domain-wall/packages/logic/domains/paymentinfo/data/entities.ts
2025-10-20 17:07:41 +03:00

93 lines
2.3 KiB
TypeScript

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<typeof cardInfoModel>;
export const paymentDetailsPayloadModel = z.object({
method: z.enum([PaymentMethod.Card]),
cardDetails: cardInfoModel,
flightTicketInfoId: z.number().int(),
});
export type PaymentDetailsPayload = z.infer<typeof paymentDetailsPayloadModel>;
export const paymentDetailsModel = cardInfoModel.merge(
z.object({
id: z.number().int(),
flightTicketInfoId: z.number().int(),
createdAt: z.string().datetime(),
updatedAt: z.string().datetime(),
}),
);
export type PaymentDetails = z.infer<typeof paymentDetailsModel>;