Refactor import paths in user mapping and authentication modules to align with the new domain structure, enhancing type safety and maintainability. Remove deprecated Freebit types and streamline integration services by utilizing updated provider methods for improved organization and consistency in data handling.

This commit is contained in:
barsa 2025-10-03 16:37:52 +09:00
parent 71df9add8a
commit 69aa47ad59
65 changed files with 708 additions and 3170 deletions

View File

@ -1,4 +1,4 @@
import type { AuthenticatedUser, User } from "@customer-portal/domain";
import type { AuthenticatedUser, User } from "@customer-portal/domain/auth";
import type { User as PrismaUser } from "@prisma/client";
export function mapPrismaUserToSharedUser(user: PrismaUser): User {

View File

@ -1,330 +0,0 @@
// Freebit API Type Definitions (cleaned)
export interface FreebitAuthRequest {
oemId: string; // 4-char alphanumeric ISP identifier
oemKey: string; // 32-char auth key
}
export interface FreebitAuthResponse {
resultCode: string;
status: {
message: string;
statusCode: string | number;
};
authKey: string; // Token for subsequent API calls
}
export interface FreebitAccountDetailsRequest {
authKey: string;
version?: string | number; // Docs recommend "2"
requestDatas: Array<{
kind: "MASTER" | "MVNO";
account?: string | number;
}>;
}
export interface FreebitAccountDetail {
kind: "MASTER" | "MVNO";
account: string | number;
state: "active" | "suspended" | "temporary" | "waiting" | "obsolete";
status?: "active" | "suspended" | "temporary" | "waiting" | "obsolete";
startDate?: string | number;
relationCode?: string;
resultCode?: string | number;
planCode?: string;
planName?: string;
iccid?: string | number;
imsi?: string | number;
eid?: string;
contractLine?: string;
size?: "standard" | "nano" | "micro" | "esim";
simSize?: "standard" | "nano" | "micro" | "esim";
msisdn?: string | number;
sms?: number; // 10=active, 20=inactive
talk?: number; // 10=active, 20=inactive
ipv4?: string;
ipv6?: string;
quota?: number; // Remaining quota
remainingQuotaMb?: string | number | null;
remainingQuotaKb?: string | number | null;
voicemail?: "10" | "20" | number | null;
voiceMail?: "10" | "20" | number | null;
callwaiting?: "10" | "20" | number | null;
callWaiting?: "10" | "20" | number | null;
worldwing?: "10" | "20" | number | null;
worldWing?: "10" | "20" | number | null;
networkType?: string;
async?: { func: string; date: string | number };
}
export interface FreebitAccountDetailsResponse {
resultCode: string;
status: {
message: string;
statusCode: string | number;
};
masterAccount?: string;
responseDatas: FreebitAccountDetail[];
}
export interface FreebitTrafficInfoRequest {
authKey: string;
account: string;
}
export interface FreebitTrafficInfoResponse {
resultCode: string;
status: {
message: string;
statusCode: string | number;
};
account: string;
traffic: {
today: string; // Today's usage in KB
inRecentDays: string; // Comma-separated recent days usage
blackList: string; // 10=blacklisted, 20=not blacklisted
};
}
export interface FreebitTopUpRequest {
authKey: string;
account: string;
quota: number; // KB units (e.g., 102400 for 100MB)
quotaCode?: string; // Campaign code
expire?: string; // YYYYMMDD format
runTime?: string; // Scheduled execution time (YYYYMMDDHHmm)
}
export interface FreebitTopUpResponse {
resultCode: string;
status: { message: string; statusCode: string | number };
}
// AddSpec request for updating SIM options/features immediately
export interface FreebitAddSpecRequest {
authKey: string;
account: string;
kind?: string; // e.g. 'MVNO'
// Feature flags: 10 = enabled, 20 = disabled
voiceMail?: "10" | "20";
voicemail?: "10" | "20";
callWaiting?: "10" | "20";
callwaiting?: "10" | "20";
worldWing?: "10" | "20";
worldwing?: "10" | "20";
contractLine?: string; // '4G' or '5G'
}
export interface FreebitAddSpecResponse {
resultCode: string;
status: { message: string; statusCode: string | number };
}
export interface FreebitQuotaHistoryRequest {
authKey: string;
account: string;
fromDate: string;
toDate: string;
}
export interface FreebitQuotaHistoryItem {
quota: string; // KB as string
date: string;
expire: string;
quotaCode: string;
}
export interface FreebitQuotaHistoryResponse {
resultCode: string;
status: { message: string; statusCode: string | number };
total: string | number;
count: string | number;
quotaHistory: FreebitQuotaHistoryItem[];
}
export interface FreebitPlanChangeRequest {
authKey: string;
account: string;
plancode: string;
globalip?: "0" | "1"; // 0=no IP, 1=assign global IP
runTime?: string; // YYYYMMDD - optional
}
export interface FreebitPlanChangeResponse {
resultCode: string;
status: { message: string; statusCode: string | number };
ipv4?: string;
ipv6?: string;
}
export interface FreebitPlanChangePayload {
requestDatas: Array<{
kind: "MVNO";
account: string;
newPlanCode: string;
assignGlobalIp: boolean;
scheduledAt?: string;
}>;
}
export interface FreebitAddSpecPayload {
requestDatas: Array<{
kind: "MVNO";
account: string;
specCode: string;
enabled?: boolean;
networkType?: "4G" | "5G";
}>;
}
export interface FreebitCancelPlanPayload {
requestDatas: Array<{
kind: "MVNO";
account: string;
runDate: string;
}>;
}
export interface FreebitEsimReissuePayload {
requestDatas: Array<{
kind: "MVNO";
account: string;
newEid: string;
oldEid?: string;
planCode?: string;
}>;
}
export interface FreebitContractLineChangeRequest {
authKey: string;
account: string;
contractLine: "4G" | "5G";
productNumber?: string;
eid?: string;
}
export interface FreebitContractLineChangeResponse {
resultCode: string | number;
status?: { message?: string; statusCode?: string | number };
statusCode?: string | number;
message?: string;
}
export interface FreebitCancelPlanRequest {
authKey: string;
account: string;
runTime?: string; // YYYYMMDD - optional
}
export interface FreebitCancelPlanResponse {
resultCode: string;
status: { message: string; statusCode: string | number };
}
// PA02-04: Account Cancellation (master/cnclAcnt)
export interface FreebitCancelAccountRequest {
authKey: string;
kind: string; // e.g., 'MVNO'
account: string;
runDate?: string; // YYYYMMDD
}
export interface FreebitCancelAccountResponse {
resultCode: string;
status: { message: string; statusCode: string | number };
}
export interface FreebitEsimReissueRequest {
authKey: string;
requestDatas: Array<{
kind: "MVNO";
account: string;
newEid?: string;
oldEid?: string;
planCode?: string;
}>;
}
export interface FreebitEsimReissueResponse {
resultCode: string;
status: { message: string; statusCode: string | number };
}
export interface FreebitEsimAddAccountRequest {
authKey: string;
aladinOperated: string; // '10' for issue, '20' for no-issue
account: string;
eid: string;
addKind: "N" | "R"; // N = new, R = reissue
shipDate?: string;
planCode?: string;
contractLine?: string;
mnp?: {
reserveNumber: string;
reserveExpireDate: string;
};
}
export interface FreebitEsimAddAccountResponse {
resultCode: string;
status: { message: string; statusCode: string | number };
}
// PA05-41 eSIM Account Activation (addAcct)
export interface FreebitEsimAccountActivationRequest {
authKey: string;
aladinOperated: string; // '10' issue, '20' no-issue
masterAccount?: string;
masterPassword?: string;
createType: string;
eid?: string; // required for reissue/exchange per business rules
account: string; // MSISDN
simkind: string;
repAccount?: string;
size?: string;
addKind?: string; // e.g., 'R' for reissue
oldEid?: string;
oldProductNumber?: string;
mnp?: { reserveNumber: string; reserveExpireDate: string };
firstnameKanji?: string;
lastnameKanji?: string;
firstnameZenKana?: string;
lastnameZenKana?: string;
gender?: string; // 'M' | 'F' | etc
birthday?: string; // YYYYMMDD
shipDate?: string; // YYYYMMDD
planCode?: string;
deliveryCode?: string;
globalIp?: string; // '10' none, '20' with global IP (env-specific mapping)
contractLine?: string; // '4G' | '5G'
}
export interface FreebitEsimAccountActivationResponse {
resultCode: string;
status?: {
message?: string;
statusCode?: string | number;
};
statusCode?: string | number;
message?: string;
}
// Portal-specific types for SIM management
export type { SimDetails, SimUsage, SimTopUpHistory } from "@customer-portal/domain/sim";
// Error handling
export interface FreebitError extends Error {
resultCode: string;
statusCode: string | number;
freebititMessage: string;
}
// Configuration
export interface FreebitConfig {
baseUrl: string;
oemId: string;
oemKey: string;
timeout: number;
retryAttempts: number;
detailsEndpoint?: string;
}

View File

@ -3,7 +3,7 @@ import type {
FreebitAccountDetailsResponse,
FreebitTrafficInfoResponse,
FreebitQuotaHistoryResponse,
} from "../interfaces/freebit.types";
} from "@customer-portal/domain/sim/providers/freebit";
import type { SimDetails, SimTopUpHistory, SimUsage } from "@customer-portal/domain/sim";
import { Providers } from "@customer-portal/domain/sim";

View File

@ -15,17 +15,22 @@ import type {
FreebitEsimReissueResponse,
FreebitEsimAddAccountResponse,
FreebitEsimAccountActivationResponse,
} from "../interfaces/freebit.types";
} from "@customer-portal/domain/sim/providers/freebit";
import type {
FreebitTopUpRequest,
FreebitPlanChangeRequest,
FreebitCancelPlanRequest,
FreebitEsimReissueRequest,
FreebitEsimActivationRequest,
FreebitEsimActivationParams,
FreebitAccountDetailsRequest,
FreebitTrafficInfoRequest,
FreebitQuotaHistoryRequest,
FreebitQuotaHistoryResponse,
FreebitEsimAddAccountRequest,
} from "@customer-portal/domain/sim/providers/freebit";
import type { SimDetails, SimTopUpHistory, SimUsage } from "@customer-portal/domain/sim";
import {
freebitAccountDetailsRequestSchema,
freebitAddSpecRequestSchema,
freebitCancelPlanRequestSchema,
freebitEsimReissueRequestSchema,
freebitPlanChangeRequestSchema,
freebitTopUpRequestPayloadSchema,
freebitTrafficInfoRequestSchema,
} from "@customer-portal/domain/sim";
import { Providers } from "@customer-portal/domain/sim";
@Injectable()
export class FreebitOperationsService {
@ -41,7 +46,7 @@ export class FreebitOperationsService {
*/
async getSimDetails(account: string): Promise<SimDetails> {
try {
const request = freebitAccountDetailsRequestSchema.parse({
const request: FreebitAccountDetailsRequest = Providers.Freebit.schemas.accountDetails.parse({
version: "2",
requestDatas: [{ kind: "MVNO", account }],
});
@ -113,7 +118,7 @@ export class FreebitOperationsService {
*/
async getSimUsage(account: string): Promise<SimUsage> {
try {
const request = freebitTrafficInfoRequestSchema.parse({ account });
const request: FreebitTrafficInfoRequest = Providers.Freebit.schemas.trafficInfo.parse({ account });
const response = await this.client.makeAuthenticatedRequest<
FreebitTrafficInfoResponse,
@ -140,7 +145,7 @@ export class FreebitOperationsService {
options: { campaignCode?: string; expiryDate?: string; scheduledAt?: string } = {}
): Promise<void> {
try {
const payload = freebitTopUpRequestPayloadSchema.parse({ account, quotaMb, options });
const payload: FreebitTopUpRequest = Providers.Freebit.schemas.topUp.parse({ account, quotaMb, options });
const quotaKb = Math.round(payload.quotaMb * 1024);
const baseRequest = {
account: payload.account,
@ -187,11 +192,11 @@ export class FreebitOperationsService {
toDate: string
): Promise<SimTopUpHistory> {
try {
const request: Omit<FreebitQuotaHistoryRequest, "authKey"> = {
const request: FreebitQuotaHistoryRequest = Providers.Freebit.schemas.quotaHistory.parse({
account,
fromDate,
toDate,
};
});
const response = await this.client.makeAuthenticatedRequest<
FreebitQuotaHistoryResponse,
@ -220,18 +225,14 @@ export class FreebitOperationsService {
options: { assignGlobalIp?: boolean; scheduledAt?: string } = {}
): Promise<{ ipv4?: string; ipv6?: string }> {
// Import and validate with the schema
const { freebitPlanChangeRequestSchema } = await import(
"@customer-portal/domain/sim/providers/freebit/requests"
);
const parsed: FreebitPlanChangeRequest = Providers.Freebit.schemas.planChange.parse({
account,
newPlanCode,
assignGlobalIp: options.assignGlobalIp,
scheduledAt: options.scheduledAt,
});
try {
const parsed = freebitPlanChangeRequestSchema.parse({
account,
newPlanCode,
assignGlobalIp: options.assignGlobalIp,
scheduledAt: options.scheduledAt,
});
const request = {
account: parsed.account,
plancode: parsed.newPlanCode,
@ -280,11 +281,7 @@ export class FreebitOperationsService {
): Promise<void> {
try {
// Import and validate with the new schema
const { freebitSimFeaturesRequestSchema } = await import(
"@customer-portal/domain/sim/providers/freebit/requests"
);
const validatedFeatures = freebitSimFeaturesRequestSchema.parse({
const parsed = Providers.Freebit.schemas.simFeatures.parse({
account,
voiceMailEnabled: features.voiceMailEnabled,
callWaitingEnabled: features.callWaitingEnabled,
@ -293,17 +290,17 @@ export class FreebitOperationsService {
});
const payload: Record<string, unknown> = {
account: validatedFeatures.account,
account: parsed.account,
};
if (typeof validatedFeatures.voiceMailEnabled === "boolean") {
const flag = validatedFeatures.voiceMailEnabled ? "10" : "20";
if (typeof parsed.voiceMailEnabled === "boolean") {
const flag = parsed.voiceMailEnabled ? "10" : "20";
payload.voiceMail = flag;
payload.voicemail = flag;
}
if (typeof validatedFeatures.callWaitingEnabled === "boolean") {
const flag = validatedFeatures.callWaitingEnabled ? "10" : "20";
if (typeof parsed.callWaitingEnabled === "boolean") {
const flag = parsed.callWaitingEnabled ? "10" : "20";
payload.callWaiting = flag;
payload.callwaiting = flag;
}
@ -346,7 +343,7 @@ export class FreebitOperationsService {
*/
async cancelSim(account: string, scheduledAt?: string): Promise<void> {
try {
const parsed = freebitCancelPlanRequestSchema.parse({
const parsed: FreebitCancelPlanRequest = Providers.Freebit.schemas.cancelPlan.parse({
account,
runDate: scheduledAt,
});
@ -410,7 +407,7 @@ export class FreebitOperationsService {
options: { oldProductNumber?: string; oldEid?: string; planCode?: string } = {}
): Promise<void> {
try {
const parsed = freebitEsimReissueRequestSchema.parse({
const parsed: FreebitEsimReissueRequest = Providers.Freebit.schemas.esimReissue.parse({
account,
newEid,
oldEid: options.oldEid,
@ -418,15 +415,20 @@ export class FreebitOperationsService {
oldProductNumber: options.oldProductNumber,
});
const request: Omit<FreebitEsimAddAccountRequest, "authKey"> = {
const requestPayload = Providers.Freebit.schemas.esimAddAccount.parse({
aladinOperated: "20",
account: parsed.account,
eid: parsed.newEid,
addKind: "R",
planCode: parsed.planCode,
});
const request: FreebitEsimAddAccountRequest = {
...requestPayload,
authKey: await this.auth.getAuthKey(),
};
await this.client.makeAuthenticatedRequest<FreebitEsimAddAccountResponse, typeof request>(
await this.client.makeAuthenticatedRequest<FreebitEsimAddAccountResponse, FreebitEsimAddAccountRequest>(
"/mvno/esim/addAcnt/",
request
);
@ -480,12 +482,7 @@ export class FreebitOperationsService {
} = params;
// Import schemas dynamically to avoid circular dependencies
const { freebitEsimActivationParamsSchema, freebitEsimActivationRequestSchema } = await import(
"@customer-portal/domain/sim/providers/freebit/requests"
);
// Validate input parameters
const validatedParams = freebitEsimActivationParamsSchema.parse({
const validatedParams: FreebitEsimActivationParams = Providers.Freebit.schemas.esimActivationParams.parse({
account,
eid,
planCode,
@ -501,12 +498,12 @@ export class FreebitOperationsService {
}
try {
const payload: FreebitEsimAccountActivationRequest = {
const payload: FreebitEsimActivationRequest = {
authKey: await this.auth.getAuthKey(),
aladinOperated: validatedParams.aladinOperated,
createType: "new",
eid: validatedParams.eid,
account: validatedParams.account,
eid: validatedParams.eid,
simkind: "esim",
planCode: validatedParams.planCode,
contractLine: validatedParams.contractLine,
@ -516,12 +513,12 @@ export class FreebitOperationsService {
};
// Validate the full API request payload
freebitEsimActivationRequestSchema.parse(payload);
Providers.Freebit.schemas.esimActivationRequest.parse(payload);
// Use JSON request for PA05-41
await this.client.makeAuthenticatedJsonRequest<
FreebitEsimAccountActivationResponse,
FreebitEsimAccountActivationRequest
typeof payload
>("/mvno/esim/addAcct/", payload);
this.logger.log("Successfully activated new eSIM account via PA05-41", {

View File

@ -1,7 +1,7 @@
import { Injectable } from "@nestjs/common";
import { FreebitOperationsService } from "./freebit-operations.service";
import { FreebitMapperService } from "./freebit-mapper.service";
import type { SimDetails, SimUsage, SimTopUpHistory } from "../interfaces/freebit.types";
import type { SimDetails, SimUsage, SimTopUpHistory } from "@customer-portal/domain/sim";
@Injectable()
export class FreebitOrchestratorService {

View File

@ -11,11 +11,17 @@ import { getErrorMessage } from "@bff/core/utils/error.util";
import { Logger } from "nestjs-pino";
import { sanitizeWhmcsRedirectPath } from "@bff/core/utils/sso.util";
import {
type SignupRequestInput,
type ValidateSignupRequestInput,
type LinkWhmcsRequestInput,
type SetPasswordRequestInput,
} from "@customer-portal/domain";
type SignupRequest,
type ValidateSignupRequest,
type LinkWhmcsRequest,
type SetPasswordRequest,
type ChangePasswordRequest,
signupRequestSchema,
validateSignupRequestSchema,
linkWhmcsRequestSchema,
setPasswordRequestSchema,
changePasswordRequestSchema,
} from "@customer-portal/domain/auth";
import type { User as PrismaUser } from "@prisma/client";
import type { Request } from "express";
@ -96,11 +102,11 @@ export class AuthFacade {
};
}
async validateSignup(validateData: ValidateSignupRequestInput, request?: Request) {
async validateSignup(validateData: ValidateSignupRequest, request?: Request) {
return this.signupWorkflow.validateSignup(validateData, request);
}
async signup(signupData: SignupRequestInput, request?: Request) {
async signup(signupData: SignupRequest, request?: Request) {
return this.signupWorkflow.signup(signupData, request);
}
@ -161,7 +167,7 @@ export class AuthFacade {
};
}
async linkWhmcsUser(linkData: LinkWhmcsRequestInput) {
async linkWhmcsUser(linkData: LinkWhmcsRequest) {
return this.whmcsLinkWorkflow.linkWhmcsUser(linkData.email, linkData.password);
}
@ -169,7 +175,7 @@ export class AuthFacade {
return this.passwordWorkflow.checkPasswordNeeded(email);
}
async setPassword(setPasswordData: SetPasswordRequestInput) {
async setPassword(setPasswordData: SetPasswordRequest) {
return this.passwordWorkflow.setPassword(setPasswordData.email, setPasswordData.password);
}
@ -436,13 +442,8 @@ export class AuthFacade {
};
}
async changePassword(
userId: string,
currentPassword: string,
newPassword: string,
request?: Request
) {
return this.passwordWorkflow.changePassword(userId, currentPassword, newPassword, request);
async changePassword(userId: string, data: ChangePasswordRequest, request?: Request) {
return this.passwordWorkflow.changePassword(userId, data, request);
}
async refreshTokens(
@ -464,7 +465,7 @@ export class AuthFacade {
* Preflight validation for signup. No side effects.
* Returns a clear nextAction for the UI and detailed flags.
*/
async signupPreflight(signupData: SignupRequestInput) {
async signupPreflight(signupData: SignupRequest) {
return this.signupWorkflow.signupPreflight(signupData);
}
}

View File

@ -1,4 +1,4 @@
import type { AuthenticatedUser } from "@customer-portal/domain";
import type { AuthenticatedUser } from "@customer-portal/domain/auth";
import type { Request } from "express";
export type RequestWithUser = Request & { user: AuthenticatedUser };

View File

@ -9,7 +9,7 @@ import { ConfigService } from "@nestjs/config";
import { Redis } from "ioredis";
import { Logger } from "nestjs-pino";
import { randomBytes, createHash } from "crypto";
import type { AuthTokens, AuthenticatedUser } from "@customer-portal/domain";
import type { AuthTokens, AuthenticatedUser } from "@customer-portal/domain/auth";
import { UsersService } from "@bff/modules/users/users.service";
import { mapPrismaUserToUserProfile } from "@bff/infra/utils/user-mapper.util";

View File

@ -10,7 +10,12 @@ import { EmailService } from "@bff/infra/email/email.service";
import { getErrorMessage } from "@bff/core/utils/error.util";
import { AuthTokenService } from "../../token/token.service";
import { AuthRateLimitService } from "../../rate-limiting/auth-rate-limit.service";
import { type AuthTokens, type UserProfile } from "@customer-portal/domain";
import {
type AuthTokens,
type UserProfile,
type ChangePasswordRequest,
changePasswordRequestSchema,
} from "@customer-portal/domain/auth";
import { mapPrismaUserToUserProfile } from "@bff/infra/utils/user-mapper.util";
export interface PasswordChangeResult {
@ -152,8 +157,7 @@ export class PasswordWorkflowService {
async changePassword(
userId: string,
currentPassword: string,
newPassword: string,
data: ChangePasswordRequest,
request?: Request
): Promise<PasswordChangeResult> {
const user = await this.usersService.findByIdInternal(userId);
@ -166,6 +170,9 @@ export class PasswordWorkflowService {
throw new BadRequestException("No password set. Please set a password first.");
}
const parsed = changePasswordRequestSchema.parse(data);
const { currentPassword, newPassword } = parsed;
const isCurrentValid = await bcrypt.compare(currentPassword, user.passwordHash);
if (!isCurrentValid) {
await this.auditService.logAuthEvent(

View File

@ -20,11 +20,11 @@ import { AuthRateLimitService } from "../../rate-limiting/auth-rate-limit.servic
import { getErrorMessage } from "@bff/core/utils/error.util";
import {
signupRequestSchema,
type SignupRequestInput,
type ValidateSignupRequestInput,
type SignupRequest,
type ValidateSignupRequest,
type AuthTokens,
type UserProfile,
} from "@customer-portal/domain";
} from "@customer-portal/domain/auth";
import { mapPrismaUserToUserProfile } from "@bff/infra/utils/user-mapper.util";
import type { User as PrismaUser } from "@prisma/client";
@ -53,7 +53,7 @@ export class SignupWorkflowService {
@Inject(Logger) private readonly logger: Logger
) {}
async validateSignup(validateData: ValidateSignupRequestInput, request?: Request) {
async validateSignup(validateData: ValidateSignupRequest, request?: Request) {
const { sfNumber } = validateData;
try {
@ -137,7 +137,7 @@ export class SignupWorkflowService {
}
}
async signup(signupData: SignupRequestInput, request?: Request): Promise<SignupResult> {
async signup(signupData: SignupRequest, request?: Request): Promise<SignupResult> {
if (request) {
await this.authRateLimitService.consumeSignupAttempt(request);
}
@ -379,7 +379,7 @@ export class SignupWorkflowService {
}
}
async signupPreflight(signupData: SignupRequestInput) {
async signupPreflight(signupData: SignupRequest) {
const { email, sfNumber } = signupData;
const normalizedEmail = email.toLowerCase().trim();
@ -477,7 +477,7 @@ export class SignupWorkflowService {
return result;
}
private validateSignupData(signupData: SignupRequestInput) {
private validateSignupData(signupData: SignupRequest) {
const validation = signupRequestSchema.safeParse(signupData);
if (!validation.success) {
const message =

View File

@ -12,7 +12,7 @@ import { WhmcsService } from "@bff/integrations/whmcs/whmcs.service";
import { SalesforceService } from "@bff/integrations/salesforce/salesforce.service";
import { getErrorMessage } from "@bff/core/utils/error.util";
import { mapPrismaUserToUserProfile } from "@bff/infra/utils/user-mapper.util";
import type { UserProfile } from "@customer-portal/domain";
import type { UserProfile } from "@customer-portal/domain/auth";
import type { WhmcsClientResponse } from "@bff/integrations/whmcs/types/whmcs-api.types";
@Injectable()

View File

@ -32,20 +32,20 @@ import {
accountStatusRequestSchema,
ssoLinkRequestSchema,
checkPasswordNeededRequestSchema,
type SignupRequestInput,
type PasswordResetRequestInput,
type PasswordResetInput,
type SetPasswordRequestInput,
type LinkWhmcsRequestInput,
type ChangePasswordRequestInput,
type ValidateSignupRequestInput,
type AccountStatusRequestInput,
type SsoLinkRequestInput,
type CheckPasswordNeededRequestInput,
refreshTokenRequestSchema,
type RefreshTokenRequestInput,
} from "@customer-portal/domain";
import type { AuthTokens } from "@customer-portal/domain";
type SignupRequest,
type PasswordResetRequest,
type PasswordReset,
type SetPasswordRequest,
type LinkWhmcsRequest,
type ChangePasswordRequest,
type ValidateSignupRequest,
type AccountStatusRequest,
type SsoLinkRequest,
type CheckPasswordNeededRequest,
type RefreshTokenRequest,
type AuthTokens,
} from "@customer-portal/domain/auth";
type RequestWithCookies = Request & {
cookies: Record<string, any>;
@ -123,7 +123,7 @@ export class AuthController {
@UseGuards(AuthThrottleGuard)
@Throttle({ default: { limit: 20, ttl: 600000 } }) // 20 validations per 10 minutes per IP
@UsePipes(new ZodValidationPipe(validateSignupRequestSchema))
async validateSignup(@Body() validateData: ValidateSignupRequestInput, @Req() req: Request) {
async validateSignup(@Body() validateData: ValidateSignupRequest, @Req() req: Request) {
return this.authFacade.validateSignup(validateData, req);
}
@ -139,14 +139,14 @@ export class AuthController {
@Throttle({ default: { limit: 20, ttl: 600000 } }) // 20 validations per 10 minutes per IP
@UsePipes(new ZodValidationPipe(signupRequestSchema))
@HttpCode(200)
async signupPreflight(@Body() signupData: SignupRequestInput) {
async signupPreflight(@Body() signupData: SignupRequest) {
return this.authFacade.signupPreflight(signupData);
}
@Public()
@Post("account-status")
@UsePipes(new ZodValidationPipe(accountStatusRequestSchema))
async accountStatus(@Body() body: AccountStatusRequestInput) {
async accountStatus(@Body() body: AccountStatusRequest) {
return this.authFacade.getAccountStatus(body.email);
}
@ -156,7 +156,7 @@ export class AuthController {
@Throttle({ default: { limit: 5, ttl: 900000 } }) // 5 signups per 15 minutes per IP (reasonable for account creation)
@UsePipes(new ZodValidationPipe(signupRequestSchema))
async signup(
@Body() signupData: SignupRequestInput,
@Body() signupData: SignupRequest,
@Req() req: Request,
@Res({ passthrough: true }) res: Response
) {
@ -194,7 +194,7 @@ export class AuthController {
@Throttle({ default: { limit: 10, ttl: 300000 } }) // 10 attempts per 5 minutes per IP
@UsePipes(new ZodValidationPipe(refreshTokenRequestSchema))
async refreshToken(
@Body() body: RefreshTokenRequestInput,
@Body() body: RefreshTokenRequest,
@Req() req: RequestWithCookies,
@Res({ passthrough: true }) res: Response
) {
@ -214,7 +214,7 @@ export class AuthController {
@UseGuards(AuthThrottleGuard)
@Throttle({ default: { limit: 5, ttl: 600000 } }) // 5 attempts per 10 minutes per IP (industry standard)
@UsePipes(new ZodValidationPipe(linkWhmcsRequestSchema))
async linkWhmcs(@Body() linkData: LinkWhmcsRequestInput, @Req() _req: Request) {
async linkWhmcs(@Body() linkData: LinkWhmcsRequest, @Req() _req: Request) {
return this.authFacade.linkWhmcsUser(linkData);
}
@ -224,7 +224,7 @@ export class AuthController {
@Throttle({ default: { limit: 5, ttl: 600000 } }) // 5 attempts per 10 minutes per IP+UA (industry standard)
@UsePipes(new ZodValidationPipe(setPasswordRequestSchema))
async setPassword(
@Body() setPasswordData: SetPasswordRequestInput,
@Body() setPasswordData: SetPasswordRequest,
@Req() _req: Request,
@Res({ passthrough: true }) res: Response
) {
@ -237,7 +237,7 @@ export class AuthController {
@Post("check-password-needed")
@UsePipes(new ZodValidationPipe(checkPasswordNeededRequestSchema))
@HttpCode(200)
async checkPasswordNeeded(@Body() data: CheckPasswordNeededRequestInput) {
async checkPasswordNeeded(@Body() data: CheckPasswordNeededRequest) {
return this.authFacade.checkPasswordNeeded(data.email);
}
@ -245,7 +245,7 @@ export class AuthController {
@Post("request-password-reset")
@Throttle({ default: { limit: 5, ttl: 900000 } }) // 5 attempts per 15 minutes (standard for password operations)
@UsePipes(new ZodValidationPipe(passwordResetRequestSchema))
async requestPasswordReset(@Body() body: PasswordResetRequestInput, @Req() req: Request) {
async requestPasswordReset(@Body() body: PasswordResetRequest, @Req() req: Request) {
await this.authFacade.requestPasswordReset(body.email, req);
return { message: "If an account exists, a reset email has been sent" };
}
@ -265,7 +265,7 @@ export class AuthController {
@UsePipes(new ZodValidationPipe(changePasswordRequestSchema))
async changePassword(
@Req() req: Request & { user: { id: string } },
@Body() body: ChangePasswordRequestInput,
@Body() body: ChangePasswordRequest,
@Res({ passthrough: true }) res: Response
) {
const result = await this.authFacade.changePassword(
@ -295,7 +295,7 @@ export class AuthController {
@UsePipes(new ZodValidationPipe(ssoLinkRequestSchema))
async createSsoLink(
@Req() req: Request & { user: { id: string } },
@Body() body: SsoLinkRequestInput
@Body() body: SsoLinkRequest
) {
const destination = body?.destination;
return this.authFacade.createSsoLink(req.user.id, destination);

View File

@ -2,7 +2,7 @@ import { Injectable, UnauthorizedException } from "@nestjs/common";
import { PassportStrategy } from "@nestjs/passport";
import { ExtractJwt, Strategy } from "passport-jwt";
import { ConfigService } from "@nestjs/config";
import type { AuthenticatedUser } from "@customer-portal/domain";
import type { AuthenticatedUser } from "@customer-portal/domain/auth";
import { UsersService } from "@bff/modules/users/users.service";
import { mapPrismaUserToUserProfile } from "@bff/infra/utils/user-mapper.util";
import type { Request } from "express";

View File

@ -16,18 +16,24 @@ import { SimManagementService } from "./sim-management.service";
import {
Subscription,
SubscriptionList,
subscriptionQuerySchema,
type SubscriptionQuery,
} from "@customer-portal/domain/subscriptions";
import {
InvoiceList,
invoiceListQuerySchema,
type InvoiceListQuery,
} from "@customer-portal/domain/billing";
import {
simTopupRequestSchema,
simChangePlanRequestSchema,
simCancelRequestSchema,
simFeaturesRequestSchema,
subscriptionQuerySchema,
invoiceListQuerySchema,
type SimTopupRequest,
type SimChangePlanRequest,
type SimCancelRequest,
type SimFeaturesRequest,
type SubscriptionQuery,
} from "@customer-portal/domain/sim";
import { ZodValidationPipe } from "@bff/core/validation";
import type { RequestWithUser } from "@bff/modules/auth/auth.types";

View File

@ -1,11 +1,17 @@
import { getErrorMessage } from "@bff/core/utils/error.util";
import { normalizeAndValidateEmail, validateUuidV4OrThrow } from "@bff/core/utils/validation.util";
import type { UpdateAddressRequest } from "@customer-portal/domain";
import type { UpdateAddressRequest } from "@customer-portal/domain/auth";
import { Injectable, Inject, NotFoundException, BadRequestException } from "@nestjs/common";
import { Logger } from "nestjs-pino";
import { PrismaService } from "@bff/infra/database/prisma.service";
import { User, Activity, Address, type AuthenticatedUser } from "@customer-portal/domain";
import type { Subscription, Invoice } from "@customer-portal/domain";
import {
User,
Activity,
Address,
type AuthenticatedUser,
} from "@customer-portal/domain/auth";
import type { Subscription } from "@customer-portal/domain/subscriptions";
import type { Invoice } from "@customer-portal/domain/billing";
import type { User as PrismaUser } from "@prisma/client";
import { WhmcsService } from "@bff/integrations/whmcs/whmcs.service";
import { SalesforceService } from "@bff/integrations/salesforce/salesforce.service";

View File

@ -4,11 +4,8 @@ import { useCallback } from "react";
import { Button, Input, ErrorMessage } from "@/components/atoms";
import { FormField } from "@/components/molecules/FormField/FormField";
import { useWhmcsLink } from "@/features/auth/hooks";
import {
linkWhmcsRequestSchema,
type LinkWhmcsFormData,
type LinkWhmcsRequestInput,
} from "@customer-portal/domain";
import { linkWhmcsRequestSchema, type LinkWhmcsFormData } from "@customer-portal/domain/validation";
import type { LinkWhmcsRequest } from "@customer-portal/domain/auth";
import { useZodForm } from "@customer-portal/validation";
interface LinkWhmcsFormProps {
@ -22,7 +19,7 @@ export function LinkWhmcsForm({ onTransferred, className = "" }: LinkWhmcsFormPr
const handleLink = useCallback(
async (formData: LinkWhmcsFormData) => {
clearError();
const payload: LinkWhmcsRequestInput = {
const payload: LinkWhmcsRequest = {
email: formData.email,
password: formData.password,
};

View File

@ -12,8 +12,8 @@ import { useSignup } from "../../hooks/use-auth";
import {
signupFormSchema,
signupFormToRequest,
type SignupRequestInput,
} from "@customer-portal/domain";
} from "@customer-portal/domain/validation";
import type { SignupRequest } from "@customer-portal/domain/auth";
import { useZodForm } from "@customer-portal/validation";
import { z } from "zod";
@ -62,7 +62,7 @@ export function SignupForm({
async ({ confirmPassword: _confirm, ...formData }: SignupFormValues) => {
clearError();
try {
const request: SignupRequestInput = signupFormToRequest(formData);
const request: SignupRequest = signupFormToRequest(formData);
await signup(request);
onSuccess?.();
} catch (err) {

View File

@ -9,7 +9,7 @@ import { useCallback, useEffect } from "react";
import { useRouter, useSearchParams } from "next/navigation";
import { useAuthStore } from "../services/auth.store";
import { getPostLoginRedirect } from "@/features/auth/utils/route-protection";
import type { SignupRequestInput, LoginRequestInput } from "@customer-portal/domain";
import type { SignupRequest, LoginRequest } from "@customer-portal/domain/auth";
/**
* Main authentication hook
@ -40,7 +40,7 @@ export function useAuth() {
// Enhanced login with redirect handling
const login = useCallback(
async (credentials: LoginRequestInput) => {
async (credentials: LoginRequest) => {
await loginAction(credentials);
// Keep loading state active during redirect
const redirectTo = getPostLoginRedirect(searchParams);
@ -52,7 +52,7 @@ export function useAuth() {
// Enhanced signup with redirect handling
const signup = useCallback(
async (data: SignupRequestInput) => {
async (data: SignupRequest) => {
await signupAction(data);
const redirectTo = getPostLoginRedirect(searchParams);
router.push(redirectTo);

View File

@ -8,13 +8,13 @@ import { apiClient, getNullableData } from "@/lib/api";
import { getErrorInfo, handleAuthError } from "@/lib/utils/error-handling";
import logger from "@customer-portal/logging";
import type {
AuthTokensSchema,
AuthTokens,
AuthenticatedUser,
LinkWhmcsRequestInput,
LoginRequestInput,
SignupRequestInput,
} from "@customer-portal/domain";
import { authResponseSchema } from "@customer-portal/domain/validation";
LinkWhmcsRequest,
LoginRequest,
SignupRequest,
} from "@customer-portal/domain/auth";
import { authResponseSchema } from "@customer-portal/domain/auth";
interface SessionState {
accessExpiresAt?: string;
@ -29,16 +29,14 @@ export interface AuthState {
error: string | null;
hasCheckedAuth: boolean;
login: (credentials: LoginRequestInput) => Promise<void>;
signup: (data: SignupRequestInput) => Promise<void>;
login: (credentials: LoginRequest) => Promise<void>;
signup: (data: SignupRequest) => Promise<void>;
logout: () => Promise<void>;
requestPasswordReset: (email: string) => Promise<void>;
resetPassword: (token: string, password: string) => Promise<void>;
changePassword: (currentPassword: string, newPassword: string) => Promise<void>;
checkPasswordNeeded: (email: string) => Promise<{ needsPasswordSet: boolean }>;
linkWhmcs: (
request: LinkWhmcsRequestInput
) => Promise<{ needsPasswordSet: boolean; email: string }>;
linkWhmcs: (request: LinkWhmcsRequest) => Promise<{ needsPasswordSet: boolean; email: string }>;
setPassword: (email: string, password: string) => Promise<void>;
refreshUser: () => Promise<void>;
refreshSession: () => Promise<void>;
@ -50,7 +48,7 @@ export interface AuthState {
type AuthResponseData = {
user: AuthenticatedUser;
tokens: AuthTokensSchema;
tokens: AuthTokens;
};
export const useAuthStore = create<AuthState>()((set, get) => {
@ -159,7 +157,7 @@ export const useAuthStore = create<AuthState>()((set, get) => {
}
},
changePassword: async (currentPassword: string, newPassword: string) => {
changePassword: async (currentPassword: string, newPassword: string) => {
set({ loading: true, error: null });
try {
const response = await apiClient.POST("/api/auth/change-password", {
@ -201,7 +199,7 @@ export const useAuthStore = create<AuthState>()((set, get) => {
}
},
linkWhmcs: async ({ email, password }: LinkWhmcsRequestInput) => {
linkWhmcs: async ({ email, password }: LinkWhmcsRequest) => {
set({ loading: true, error: null });
try {
const response = await apiClient.POST("/api/auth/link-whmcs", {

View File

@ -0,0 +1,107 @@
/**
* Auth Domain - Contract
*
* Canonical authentication types shared across applications.
*/
import type { IsoDateTimeString, Address } from "../common/types";
export type UserRole = "USER" | "ADMIN";
export interface UserProfile {
id: string;
email: string;
firstName?: string;
lastName?: string;
company?: string;
phone?: string;
address?: Address;
avatar?: string;
preferences?: Record<string, unknown>;
emailVerified: boolean;
mfaEnabled: boolean;
lastLoginAt?: IsoDateTimeString;
createdAt?: IsoDateTimeString;
updatedAt?: IsoDateTimeString;
}
export interface AuthenticatedUser extends UserProfile {
role: UserRole;
}
export interface AuthTokens {
accessToken: string;
refreshToken: string;
expiresAt: IsoDateTimeString;
refreshExpiresAt: IsoDateTimeString;
tokenType: "Bearer";
}
export interface AuthResponse {
user: AuthenticatedUser;
tokens: AuthTokens;
}
export interface LoginRequest {
email: string;
password: string;
mfaCode?: string;
rememberMe?: boolean;
}
export interface SignupRequest {
email: string;
password: string;
firstName: string;
lastName: string;
phone?: string;
company?: string;
sfNumber: string;
address?: Address;
nationality?: string;
dateOfBirth?: string;
gender?: "male" | "female" | "other";
acceptTerms: boolean;
marketingConsent?: boolean;
}
export interface ForgotPasswordRequest {
email: string;
}
export interface ResetPasswordRequest {
token: string;
password: string;
confirmPassword?: string;
}
export interface ChangePasswordRequest {
currentPassword: string;
newPassword: string;
}
export interface LinkWhmcsRequest {
email: string;
password: string;
}
export interface SetPasswordRequest {
email: string;
password: string;
}
export interface AuthError {
code:
| "INVALID_CREDENTIALS"
| "USER_NOT_FOUND"
| "EMAIL_ALREADY_EXISTS"
| "EMAIL_NOT_VERIFIED"
| "INVALID_TOKEN"
| "TOKEN_EXPIRED"
| "ACCOUNT_LOCKED"
| "RATE_LIMITED"
| "NETWORK_ERROR";
message: string;
details?: Record<string, unknown>;
}

View File

@ -0,0 +1,3 @@
export * from "./contract";
export * from "./schema";

View File

@ -0,0 +1,102 @@
/**
* Auth Domain - Schemas
*/
import { z } from "zod";
import {
addressSchema,
emailSchema,
nameSchema,
passwordSchema,
phoneSchema,
} from "../common/schema";
const genderEnum = z.enum(["male", "female", "other"]);
export const loginRequestSchema = z.object({
email: emailSchema,
password: z.string().min(1, "Password is required"),
});
export const signupRequestSchema = z.object({
email: emailSchema,
password: passwordSchema,
firstName: nameSchema,
lastName: nameSchema,
company: z.string().optional(),
phone: phoneSchema,
sfNumber: z.string().min(6, "Customer number must be at least 6 characters"),
address: addressSchema.optional(),
nationality: z.string().optional(),
dateOfBirth: z.string().optional(),
gender: genderEnum.optional(),
acceptTerms: z.boolean(),
marketingConsent: z.boolean().optional(),
});
export const passwordResetRequestSchema = z.object({ email: emailSchema });
export const passwordResetSchema = z.object({
token: z.string().min(1, "Reset token is required"),
password: passwordSchema,
});
export const setPasswordRequestSchema = z.object({
email: emailSchema,
password: passwordSchema,
});
export const changePasswordRequestSchema = z.object({
currentPassword: z.string().min(1, "Current password is required"),
newPassword: passwordSchema,
});
export const linkWhmcsRequestSchema = z.object({
email: emailSchema,
password: z.string().min(1, "Password is required"),
});
export const validateSignupRequestSchema = z.object({
sfNumber: z.string().min(1, "Customer number is required"),
});
export const accountStatusRequestSchema = z.object({
email: emailSchema,
});
export const ssoLinkRequestSchema = z.object({
destination: z.string().optional(),
});
export const checkPasswordNeededRequestSchema = z.object({
email: emailSchema,
});
export const refreshTokenRequestSchema = z.object({
refreshToken: z.string().min(1, "Refresh token is required").optional(),
deviceId: z.string().optional(),
});
export const authTokensSchema = z.object({
accessToken: z.string().min(1, "Access token is required"),
refreshToken: z.string().min(1, "Refresh token is required"),
expiresAt: z.string().min(1, "Access token expiry required"),
refreshExpiresAt: z.string().min(1, "Refresh token expiry required"),
tokenType: z.literal("Bearer"),
});
export const authResponseSchema = z.object({
user: z.unknown(),
tokens: authTokensSchema,
});
export const validateSignupRequestSchema = signupRequestSchema.pick({ sfNumber: true });
export const accountStatusRequestSchema = z.object({ email: emailSchema });
export const ssoLinkRequestSchema = z.object({ destination: z.string().optional() });
export const checkPasswordNeededRequestSchema = z.object({ email: emailSchema });
export const refreshTokenRequestSchema = z.object({
refreshToken: z.string().min(1, "Refresh token is required").optional(),
deviceId: z.string().optional(),
});

View File

@ -5,4 +5,5 @@
*/
export * from "./types";
export * from "./schema";

View File

@ -0,0 +1,76 @@
/**
* Common Domain - Schemas
*
* Shared validation primitives used across all domains.
*/
import { z } from "zod";
export const emailSchema = z
.string()
.email("Please enter a valid email address")
.toLowerCase()
.trim();
export const passwordSchema = z
.string()
.min(8, "Password must be at least 8 characters")
.regex(/[A-Z]/, "Password must contain at least one uppercase letter")
.regex(/[a-z]/, "Password must contain at least one lowercase letter")
.regex(/[0-9]/, "Password must contain at least one number")
.regex(/[^A-Za-z0-9]/, "Password must contain at least one special character");
export const nameSchema = z.string().min(1, "Name is required").max(100, "Name must be less than 100 characters").trim();
export const phoneSchema = z
.string()
.regex(/^[+]?[0-9\s\-()]{7,20}$/u, "Please enter a valid phone number")
.trim();
export const addressSchema = z.object({
street: z.string().max(200, "Street address is too long").nullable(),
streetLine2: z.string().max(200, "Street address line 2 is too long").nullable(),
city: z.string().max(100, "City name is too long").nullable(),
state: z.string().max(100, "State/Prefecture name is too long").nullable(),
postalCode: z.string().max(20, "Postal code is too long").nullable(),
country: z.string().max(100, "Country name is too long").nullable(),
});
export const requiredAddressSchema = z.object({
street: z.string().min(1, "Street address is required").max(200, "Street address is too long").trim(),
streetLine2: z.string().max(200, "Street address line 2 is too long").optional(),
city: z.string().min(1, "City is required").max(100, "City name is too long").trim(),
state: z
.string()
.min(1, "State/Prefecture is required")
.max(100, "State/Prefecture name is too long")
.trim(),
postalCode: z.string().min(1, "Postal code is required").max(20, "Postal code is too long").trim(),
country: z.string().min(1, "Country is required").max(100, "Country name is too long").trim(),
});
export const countryCodeSchema = z.string().length(2, "Country code must be 2 characters");
export const currencyCodeSchema = z.string().length(3, "Currency code must be 3 characters");
export const timestampSchema = z.string().datetime("Invalid timestamp format");
export const dateSchema = z.string().date("Invalid date format");
export const moneyAmountSchema = z.number().int().nonnegative("Amount must be non-negative");
export const percentageSchema = z.number().min(0).max(100, "Percentage must be between 0 and 100");
export const genderEnum = z.enum(["male", "female", "other"]);
export const statusEnum = z.enum(["active", "inactive", "pending", "suspended"]);
export const priorityEnum = z.enum(["low", "medium", "high", "urgent"]);
export const categoryEnum = z.enum(["technical", "billing", "account", "general"]);
export const billingCycleEnum = z.enum(["Monthly", "Quarterly", "Annually", "Onetime", "Free"]);
export const subscriptionBillingCycleEnum = z.enum([
"Monthly",
"Quarterly",
"Semi-Annually",
"Annually",
"Biennially",
"Triennially",
"One-time",
"Free",
]);

View File

@ -21,17 +21,20 @@ export type Currency = "JPY" | "USD" | "EUR";
// ============================================================================
export type UserId = string & { readonly __brand: "UserId" };
export type AccountId = string & { readonly __brand: "AccountId" };
export type OrderId = string & { readonly __brand: "OrderId" };
export type InvoiceId = string & { readonly __brand: "InvoiceId" };
export type ProductId = string & { readonly __brand: "ProductId" };
export type SimId = string & { readonly __brand: "SimId" };
export type WhmcsClientId = number & { readonly __brand: "WhmcsClientId" };
export type WhmcsInvoiceId = number & { readonly __brand: "WhmcsInvoiceId" };
export type WhmcsProductId = number & { readonly __brand: "WhmcsProductId" };
export type SalesforceContactId = string & { readonly __brand: "SalesforceContactId" };
export type SalesforceAccountId = string & { readonly __brand: "SalesforceAccountId" };
export type SalesforceCaseId = string & { readonly __brand: "SalesforceCaseId" };
// ============================================================================
// Address
// ============================================================================

View File

@ -12,4 +12,5 @@ export * as Orders from "./orders";
export * as Catalog from "./catalog";
export * as Common from "./common";
export * as Toolkit from "./toolkit";
export * as Auth from "./auth";

View File

@ -20,6 +20,8 @@
"./catalog/*": "./dist/catalog/*",
"./common": "./dist/common/index.js",
"./common/*": "./dist/common/*",
"./auth": "./dist/auth/index.js",
"./auth/*": "./dist/auth/*",
"./toolkit": "./dist/toolkit/index.js",
"./toolkit/*": "./dist/toolkit/*"
},

View File

@ -7,7 +7,3 @@ export * from "./schema";
// Provider adapters
export * as Providers from "./providers";
// Re-export provider types and schemas for convenience
export * from "./providers/freebit/raw.types";
export * from "./providers/freebit/requests";

View File

@ -1,2 +1,53 @@
import * as Mapper from "./mapper";
import * as RawTypes from "./raw.types";
import * as Requests from "./requests";
export const schemas = {
accountDetails: Requests.freebitAccountDetailsRequestSchema,
trafficInfo: Requests.freebitTrafficInfoRequestSchema,
topUp: Requests.freebitTopUpRequestPayloadSchema,
topUpApi: Requests.freebitTopUpApiRequestSchema,
planChange: Requests.freebitPlanChangeRequestSchema,
planChangeApi: Requests.freebitPlanChangeApiRequestSchema,
addSpec: Requests.freebitAddSpecRequestSchema,
cancelPlan: Requests.freebitCancelPlanRequestSchema,
cancelPlanApi: Requests.freebitCancelPlanApiRequestSchema,
quotaHistory: Requests.freebitQuotaHistoryRequestSchema,
esimReissue: Requests.freebitEsimReissueRequestSchema,
simFeatures: Requests.freebitSimFeaturesRequestSchema,
globalIp: Requests.freebitGlobalIpRequestSchema,
esimActivationParams: Requests.freebitEsimActivationParamsSchema,
esimActivationRequest: Requests.freebitEsimActivationRequestSchema,
esimAddAccount: Requests.freebitEsimAddAccountRequestSchema,
auth: Requests.freebitAuthRequestSchema,
cancelAccount: Requests.freebitCancelAccountRequestSchema,
};
export const raw = RawTypes;
export const mapper = Mapper;
export type EsimAccountActivationRequest = Requests.FreebitEsimActivationRequest;
export type TopUpRequest = Requests.FreebitTopUpRequest;
export type TopUpApiRequest = Requests.FreebitTopUpApiRequest;
export type PlanChangeRequest = Requests.FreebitPlanChangeRequest;
export type PlanChangeApiRequest = Requests.FreebitPlanChangeApiRequest;
export type SimFeaturesRequest = Requests.FreebitSimFeaturesRequest;
export type QuotaHistoryRequest = Requests.FreebitQuotaHistoryRequest;
export type EsimAddAccountRequest = Requests.FreebitEsimAddAccountRequest;
export type TrafficInfoRequest = Requests.FreebitTrafficInfoRequest;
export type CancelPlanRequest = Requests.FreebitCancelPlanRequest;
export type CancelPlanApiRequest = Requests.FreebitCancelPlanApiRequest;
export type CancelAccountRequest = Requests.FreebitCancelAccountRequest;
export type AuthRequest = Requests.FreebitAuthRequest;
export * from "./mapper";
export * from "./raw.types";
export * from "./requests";
export const Freebit = {
...Mapper,
raw: RawTypes,
schemas,
};
export const normalizeAccount = Mapper.normalizeAccount;

View File

@ -8,9 +8,23 @@ import {
type FreebitAccountDetailsRaw,
type FreebitTrafficInfoRaw,
type FreebitQuotaHistoryRaw,
type FreebitTopUpRaw,
type FreebitAddSpecRaw,
type FreebitPlanChangeRaw,
type FreebitCancelPlanRaw,
type FreebitCancelAccountRaw,
type FreebitEsimReissueRaw,
type FreebitEsimAddAccountRaw,
freebitAccountDetailsRawSchema,
freebitTrafficInfoRawSchema,
freebitQuotaHistoryRawSchema,
freebitTopUpRawSchema,
freebitAddSpecRawSchema,
freebitPlanChangeRawSchema,
freebitCancelPlanRawSchema,
freebitCancelAccountRawSchema,
freebitEsimReissueRawSchema,
freebitEsimAddAccountRawSchema,
} from "./raw.types";
function asString(value: unknown): string {
@ -150,3 +164,31 @@ export function normalizeAccount(account: string): string {
return account.replace(/\D/g, "");
}
export function transformFreebitTopUpResponse(raw: unknown) {
return freebitTopUpRawSchema.parse(raw);
}
export function transformFreebitAddSpecResponse(raw: unknown) {
return freebitAddSpecRawSchema.parse(raw);
}
export function transformFreebitPlanChangeResponse(raw: unknown) {
return freebitPlanChangeRawSchema.parse(raw);
}
export function transformFreebitCancelPlanResponse(raw: unknown) {
return freebitCancelPlanRawSchema.parse(raw);
}
export function transformFreebitCancelAccountResponse(raw: unknown) {
return freebitCancelAccountRawSchema.parse(raw);
}
export function transformFreebitEsimReissueResponse(raw: unknown) {
return freebitEsimReissueRawSchema.parse(raw);
}
export function transformFreebitEsimAddAccountResponse(raw: unknown) {
return freebitEsimAddAccountRawSchema.parse(raw);
}

View File

@ -41,19 +41,104 @@ export type FreebitAccountDetailsRaw = z.infer<typeof freebitAccountDetailsRawSc
// Freebit Traffic Info Response
export const freebitTrafficInfoRawSchema = z.object({
resultCode: z.string().optional(),
resultMessage: z.string().optional(),
account: z.union([z.string(), z.number()]).optional(),
blacklistFlg: z.union([z.string(), z.number(), z.boolean()]).optional(),
traffic: z.union([z.string(), z.number()]).optional(),
todayData: z.union([z.string(), z.number()]).optional(),
thisMonthData: z.union([z.string(), z.number()]).optional(),
daily: z.array(
z.object({
usageDate: z.string().optional(),
trafficKb: z.union([z.string(), z.number()]).optional(),
status: z
.object({
message: z.string().optional(),
statusCode: z.union([z.string(), z.number()]).optional(),
})
).optional(),
.optional(),
account: z.union([z.string(), z.number()]).optional(),
traffic: z.object({
today: z.union([z.string(), z.number()]).optional(),
inRecentDays: z.union([z.string(), z.number()]).optional(),
blackList: z.union([z.string(), z.number()]).optional(),
}).optional(),
});
export const freebitTopUpRawSchema = z.object({
resultCode: z.string().optional(),
status: z
.object({
message: z.string().optional(),
statusCode: z.union([z.string(), z.number()]).optional(),
})
.optional(),
});
export type FreebitTopUpRaw = z.infer<typeof freebitTopUpRawSchema>;
export const freebitAddSpecRawSchema = z.object({
resultCode: z.string().optional(),
status: z
.object({
message: z.string().optional(),
statusCode: z.union([z.string(), z.number()]).optional(),
})
.optional(),
});
export type FreebitAddSpecRaw = z.infer<typeof freebitAddSpecRawSchema>;
export const freebitPlanChangeRawSchema = z.object({
resultCode: z.string().optional(),
status: z
.object({
message: z.string().optional(),
statusCode: z.union([z.string(), z.number()]).optional(),
})
.optional(),
ipv4: z.string().optional(),
ipv6: z.string().optional(),
});
export type FreebitPlanChangeRaw = z.infer<typeof freebitPlanChangeRawSchema>;
export const freebitCancelPlanRawSchema = z.object({
resultCode: z.string().optional(),
status: z
.object({
message: z.string().optional(),
statusCode: z.union([z.string(), z.number()]).optional(),
})
.optional(),
});
export type FreebitCancelPlanRaw = z.infer<typeof freebitCancelPlanRawSchema>;
export const freebitCancelAccountRawSchema = z.object({
resultCode: z.string().optional(),
status: z
.object({
message: z.string().optional(),
statusCode: z.union([z.string(), z.number()]).optional(),
})
.optional(),
});
export type FreebitCancelAccountRaw = z.infer<typeof freebitCancelAccountRawSchema>;
export const freebitEsimReissueRawSchema = z.object({
resultCode: z.string().optional(),
status: z
.object({
message: z.string().optional(),
statusCode: z.union([z.string(), z.number()]).optional(),
})
.optional(),
});
export type FreebitEsimReissueRaw = z.infer<typeof freebitEsimReissueRawSchema>;
export const freebitEsimAddAccountRawSchema = z.object({
resultCode: z.string().optional(),
status: z
.object({
message: z.string().optional(),
statusCode: z.union([z.string(), z.number()]).optional(),
})
.optional(),
});
export type FreebitEsimAddAccountRaw = z.infer<typeof freebitEsimAddAccountRawSchema>;
export type FreebitTrafficInfoRaw = z.infer<typeof freebitTrafficInfoRawSchema>;

View File

@ -42,6 +42,14 @@ export const freebitTopUpRequestPayloadSchema = z.object({
options: freebitTopUpOptionsSchema.optional(),
});
export const freebitTopUpApiRequestSchema = z.object({
account: z.string().min(1, "Account is required"),
quota: z.number().positive("Quota must be positive"),
quotaCode: z.string().optional(),
expire: z.string().optional(),
runTime: z.string().optional(),
});
// ============================================================================
// Plan Change & Cancellation
// ============================================================================
@ -53,6 +61,13 @@ export const freebitPlanChangeRequestSchema = z.object({
scheduledAt: z.string().optional(),
});
export const freebitPlanChangeApiRequestSchema = z.object({
account: z.string().min(1, "Account is required"),
plancode: z.string().min(1, "Plan code is required"),
globalip: z.enum(["0", "1"]).optional(),
runTime: z.string().optional(),
});
export const freebitAddSpecRequestSchema = z.object({
account: z.string().min(1, "Account is required"),
specCode: z.string().min(1, "Spec code is required"),
@ -70,6 +85,11 @@ export const freebitCancelPlanRequestSchema = z.object({
runDate: z.string().optional(),
});
export const freebitCancelPlanApiRequestSchema = z.object({
account: z.string().min(1, "Account is required"),
runTime: z.string().optional(),
});
export const freebitEsimReissueRequestSchema = z.object({
account: z.string().min(1, "Account is required"),
newEid: z.string().min(1, "New EID is required"),
@ -104,6 +124,16 @@ export const freebitEsimMnpSchema = z.object({
reserveExpireDate: z.string().regex(/^\d{8}$/, "Reserve expire date must be in YYYYMMDD format"),
});
export const freebitAuthRequestSchema = z.object({
oemId: z.string().min(1),
oemKey: z.string().min(1),
});
export const freebitCancelAccountRequestSchema = z.object({
account: z.string().min(1),
runDate: z.string().optional(),
});
export const freebitEsimIdentitySchema = z.object({
firstnameKanji: z.string().optional(),
lastnameKanji: z.string().optional(),
@ -180,14 +210,22 @@ export const freebitEsimActivationParamsSchema = z.object({
export type FreebitAccountDetailsRequest = z.infer<typeof freebitAccountDetailsRequestSchema>;
export type FreebitTrafficInfoRequest = z.infer<typeof freebitTrafficInfoRequestSchema>;
export type FreebitTopUpRequest = z.infer<typeof freebitTopUpRequestPayloadSchema>;
export type FreebitTopUpApiRequest = z.infer<typeof freebitTopUpApiRequestSchema>;
export type FreebitPlanChangeRequest = z.infer<typeof freebitPlanChangeRequestSchema>;
export type FreebitPlanChangeApiRequest = z.infer<typeof freebitPlanChangeApiRequestSchema>;
export type FreebitAddSpecRequest = z.infer<typeof freebitAddSpecRequestSchema>;
export type FreebitRemoveSpecRequest = z.infer<typeof freebitRemoveSpecRequestSchema>;
export type FreebitCancelPlanRequest = z.infer<typeof freebitCancelPlanRequestSchema>;
export type FreebitCancelPlanApiRequest = z.infer<typeof freebitCancelPlanApiRequestSchema>;
export type FreebitSimFeaturesRequest = z.infer<typeof freebitSimFeaturesRequestSchema>;
export type FreebitGlobalIpRequest = z.infer<typeof freebitGlobalIpRequestSchema>;
export type FreebitEsimActivationRequest = z.infer<typeof freebitEsimActivationRequestSchema>;
export type FreebitEsimActivationResponse = z.infer<typeof freebitEsimActivationResponseSchema>;
export type FreebitEsimActivationParams = z.infer<typeof freebitEsimActivationParamsSchema>;
export type FreebitEsimReissueRequest = z.infer<typeof freebitEsimReissueRequestSchema>;
export type FreebitQuotaHistoryRequest = z.infer<typeof freebitQuotaHistoryRequestSchema>;
export type FreebitQuotaHistoryResponse = z.infer<typeof freebitQuotaHistoryResponseSchema>;
export type FreebitEsimAddAccountRequest = z.infer<typeof freebitEsimAddAccountRequestSchema>;
export type FreebitAuthRequest = z.infer<typeof freebitAuthRequestSchema>;
export type FreebitCancelAccountRequest = z.infer<typeof freebitCancelAccountRequestSchema>;

View File

@ -2,12 +2,4 @@
* SIM Domain - Providers
*/
import * as FreebitMapper from "./freebit/mapper";
export const Freebit = {
...FreebitMapper,
};
// Re-export raw types and request schemas for convenience
export * from "./freebit/raw.types";
export * from "./freebit/requests";
export * as Freebit from "./freebit";

View File

@ -1,4 +1,3 @@
export * from "./user";
export * from "./invoice";
export * from "./subscription";
export * from "./payment";

View File

@ -14,7 +14,6 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
__exportStar(require("./user"), exports);
__exportStar(require("./invoice"), exports);
__exportStar(require("./subscription"), exports);
__exportStar(require("./payment"), exports);

View File

@ -1 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AACA,yCAAuB;AACvB,4CAA0B;AAC1B,iDAA+B;AAC/B,4CAA0B;AAC1B,yCAAuB;AACvB,8CAA4B;AAC5B,4CAA0B;AAC1B,yCAAuB"}
{"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AACA,4CAA0B;AAC1B,iDAA+B;AAC/B,4CAA0B;AAC1B,yCAAuB;AACvB,8CAA4B;AAC5B,4CAA0B;AAC1B,yCAAuB"}

View File

@ -1,5 +1,4 @@
// Export all entities - resolving conflicts explicitly
export * from "./user";
export * from "./invoice";
export * from "./subscription";
export * from "./payment";

View File

@ -1,104 +0,0 @@
import type { BaseEntity, Address, IsoDateTimeString } from "../common";
import type { InvoiceList } from "./invoice";
export interface User extends BaseEntity {
email: string;
firstName?: string;
lastName?: string;
company?: string;
phone?: string;
address?: Address;
mfaEnabled: boolean;
emailVerified: boolean;
}
export interface UserSummary {
user: User;
stats: {
activeSubscriptions: number;
unpaidInvoices: number;
openCases: number;
totalSpent: number;
currency: string;
};
nextInvoice?: InvoiceList["invoices"][number];
recentActivity: Activity[];
}
export interface Activity {
id: string;
type: "invoice_created" | "invoice_paid" | "service_activated" | "case_created" | "case_closed";
title: string;
description?: string;
date: string;
relatedId?: number;
metadata?: Record<string, unknown>;
}
export interface AuthTokens {
accessToken: string;
refreshToken: string;
expiresAt: IsoDateTimeString;
refreshExpiresAt: IsoDateTimeString;
tokenType: "Bearer";
}
export interface LoginRequest {
email: string;
password: string;
mfaCode?: string;
rememberMe?: boolean;
}
export interface SignupRequest {
email: string;
password: string;
confirmPassword?: string;
firstName: string;
lastName: string;
company?: string;
phone?: string;
sfNumber: string;
address?: Address;
nationality?: string;
dateOfBirth?: string;
gender?: "male" | "female" | "other";
acceptTerms: boolean;
marketingConsent?: boolean;
}
export interface ForgotPasswordRequest {
email: string;
}
export interface ResetPasswordRequest {
token: string;
password: string;
confirmPassword?: string;
}
export interface ChangePasswordRequest {
currentPassword: string;
newPassword: string;
confirmPassword?: string;
}
export interface AuthError {
code: string;
message: string;
details?: Record<string, unknown>;
}
export type AuthErrorCode = "INVALID_CREDENTIALS" | "USER_NOT_FOUND" | "EMAIL_ALREADY_EXISTS" | "EMAIL_NOT_VERIFIED" | "INVALID_TOKEN" | "TOKEN_EXPIRED" | "ACCOUNT_LOCKED" | "RATE_LIMITED" | "NETWORK_ERROR";
export interface UserProfile extends User {
avatar?: string;
preferences?: Record<string, unknown>;
lastLoginAt?: string;
}
export type UserRole = "USER" | "ADMIN";
export interface AuthenticatedUser extends UserProfile {
role: UserRole;
}
export interface MnpDetails {
currentProvider: string;
phoneNumber: string;
accountNumber?: string;
pin?: string;
}
export interface LinkWhmcsRequest {
email: string;
whmcsToken?: string;
}
export interface SetPasswordRequest {
password: string;
confirmPassword: string;
}

View File

@ -1,134 +0,0 @@
// User and authentication types
import type { BaseEntity, Address, IsoDateTimeString } from "../common";
import type { InvoiceList } from "./invoice";
export interface User extends BaseEntity {
email: string;
firstName?: string;
lastName?: string;
company?: string;
phone?: string;
address?: Address;
mfaEnabled: boolean;
emailVerified: boolean;
}
export interface UserSummary {
user: User;
stats: {
activeSubscriptions: number;
unpaidInvoices: number;
openCases: number;
totalSpent: number;
currency: string;
};
nextInvoice?: InvoiceList["invoices"][number];
recentActivity: Activity[];
}
export interface Activity {
id: string;
type: "invoice_created" | "invoice_paid" | "service_activated" | "case_created" | "case_closed";
title: string;
description?: string;
date: string; // ISO
relatedId?: number; // ID of related invoice, subscription, etc.
metadata?: Record<string, unknown>;
}
// Complete auth interfaces (business logic)
export interface AuthTokens {
accessToken: string;
refreshToken: string;
expiresAt: IsoDateTimeString;
refreshExpiresAt: IsoDateTimeString;
tokenType: "Bearer";
}
export interface LoginRequest {
email: string;
password: string;
mfaCode?: string;
rememberMe?: boolean;
}
export interface SignupRequest {
email: string;
password: string;
confirmPassword?: string;
firstName: string;
lastName: string;
company?: string;
phone?: string;
sfNumber: string; // Customer Number
address?: Address;
nationality?: string;
dateOfBirth?: string; // ISO or locale string per frontend validation
gender?: "male" | "female" | "other";
acceptTerms: boolean;
marketingConsent?: boolean;
}
export interface ForgotPasswordRequest {
email: string;
}
export interface ResetPasswordRequest {
token: string;
password: string;
confirmPassword?: string;
}
export interface ChangePasswordRequest {
currentPassword: string;
newPassword: string;
confirmPassword?: string;
}
export interface AuthError {
code: string;
message: string;
details?: Record<string, unknown>;
}
export type AuthErrorCode =
| "INVALID_CREDENTIALS"
| "USER_NOT_FOUND"
| "EMAIL_ALREADY_EXISTS"
| "EMAIL_NOT_VERIFIED"
| "INVALID_TOKEN"
| "TOKEN_EXPIRED"
| "ACCOUNT_LOCKED"
| "RATE_LIMITED"
| "NETWORK_ERROR";
// Enhanced user profile exposed to clients
export interface UserProfile extends User {
avatar?: string;
preferences?: Record<string, unknown>;
lastLoginAt?: string;
}
export type UserRole = "USER" | "ADMIN";
export interface AuthenticatedUser extends UserProfile {
role: UserRole;
}
// MNP (Mobile Number Portability) business entity
export interface MnpDetails {
currentProvider: string;
phoneNumber: string;
accountNumber?: string;
pin?: string;
}
export interface LinkWhmcsRequest {
email: string;
whmcsToken?: string; // From OIDC or ValidateLogin
}
export interface SetPasswordRequest {
password: string;
confirmPassword: string;
}

View File

@ -1,391 +0,0 @@
import { z } from "zod";
export declare const paginationQuerySchema: z.ZodObject<{
page: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
limit: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
}, z.core.$strip>;
export type PaginationQuery = z.infer<typeof paginationQuerySchema>;
export declare const auditLogQuerySchema: z.ZodObject<{
page: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
limit: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
action: z.ZodOptional<z.ZodString>;
userId: z.ZodOptional<z.ZodString>;
}, z.core.$strip>;
export type AuditLogQuery = z.infer<typeof auditLogQuerySchema>;
export declare const dryRunQuerySchema: z.ZodObject<{
dryRun: z.ZodOptional<z.ZodCoercedBoolean<unknown>>;
}, z.core.$strip>;
export type DryRunQuery = z.infer<typeof dryRunQuerySchema>;
export declare const invoiceListQuerySchema: z.ZodObject<{
page: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
limit: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
status: z.ZodOptional<z.ZodEnum<{
Paid: "Paid";
Unpaid: "Unpaid";
Overdue: "Overdue";
Cancelled: "Cancelled";
Collections: "Collections";
}>>;
}, z.core.$strip>;
export type InvoiceListQuery = z.infer<typeof invoiceListQuerySchema>;
export declare const subscriptionQuerySchema: z.ZodObject<{
status: z.ZodOptional<z.ZodEnum<{
Pending: "Pending";
Cancelled: "Cancelled";
Active: "Active";
Suspended: "Suspended";
Terminated: "Terminated";
}>>;
}, z.core.$strip>;
export type SubscriptionQuery = z.infer<typeof subscriptionQuerySchema>;
export declare const loginRequestSchema: z.ZodObject<{
email: z.ZodString;
password: z.ZodString;
}, z.core.$strip>;
export declare const signupRequestSchema: z.ZodObject<{
email: z.ZodString;
password: z.ZodString;
firstName: z.ZodString;
lastName: z.ZodString;
company: z.ZodOptional<z.ZodString>;
phone: z.ZodString;
sfNumber: z.ZodString;
address: z.ZodObject<{
street: z.ZodString;
streetLine2: z.ZodOptional<z.ZodString>;
city: z.ZodString;
state: z.ZodString;
postalCode: z.ZodString;
country: z.ZodString;
}, z.core.$strip>;
nationality: z.ZodOptional<z.ZodString>;
dateOfBirth: z.ZodOptional<z.ZodString>;
gender: z.ZodOptional<z.ZodEnum<{
male: "male";
female: "female";
other: "other";
}>>;
}, z.core.$strip>;
export declare const passwordResetRequestSchema: z.ZodObject<{
email: z.ZodString;
}, z.core.$strip>;
export declare const passwordResetSchema: z.ZodObject<{
token: z.ZodString;
password: z.ZodString;
}, z.core.$strip>;
export declare const setPasswordRequestSchema: z.ZodObject<{
email: z.ZodString;
password: z.ZodString;
}, z.core.$strip>;
export declare const changePasswordRequestSchema: z.ZodObject<{
currentPassword: z.ZodString;
newPassword: z.ZodString;
}, z.core.$strip>;
export declare const linkWhmcsRequestSchema: z.ZodObject<{
email: z.ZodString;
password: z.ZodString;
}, z.core.$strip>;
export declare const validateSignupRequestSchema: z.ZodObject<{
sfNumber: z.ZodString;
}, z.core.$strip>;
export declare const accountStatusRequestSchema: z.ZodObject<{
email: z.ZodString;
}, z.core.$strip>;
export declare const ssoLinkRequestSchema: z.ZodObject<{
destination: z.ZodOptional<z.ZodString>;
}, z.core.$strip>;
export declare const checkPasswordNeededRequestSchema: z.ZodObject<{
email: z.ZodString;
}, z.core.$strip>;
export declare const refreshTokenRequestSchema: z.ZodObject<{
refreshToken: z.ZodOptional<z.ZodString>;
deviceId: z.ZodOptional<z.ZodString>;
}, z.core.$strip>;
export type LoginRequestInput = z.infer<typeof loginRequestSchema>;
export type SignupRequestInput = z.infer<typeof signupRequestSchema>;
export type PasswordResetRequestInput = z.infer<typeof passwordResetRequestSchema>;
export type PasswordResetInput = z.infer<typeof passwordResetSchema>;
export type SetPasswordRequestInput = z.infer<typeof setPasswordRequestSchema>;
export type ChangePasswordRequestInput = z.infer<typeof changePasswordRequestSchema>;
export type LinkWhmcsRequestInput = z.infer<typeof linkWhmcsRequestSchema>;
export type ValidateSignupRequestInput = z.infer<typeof validateSignupRequestSchema>;
export type AccountStatusRequestInput = z.infer<typeof accountStatusRequestSchema>;
export type SsoLinkRequestInput = z.infer<typeof ssoLinkRequestSchema>;
export type CheckPasswordNeededRequestInput = z.infer<typeof checkPasswordNeededRequestSchema>;
export type RefreshTokenRequestInput = z.infer<typeof refreshTokenRequestSchema>;
export declare const updateProfileRequestSchema: z.ZodObject<{
firstName: z.ZodOptional<z.ZodString>;
lastName: z.ZodOptional<z.ZodString>;
phone: z.ZodOptional<z.ZodString>;
company: z.ZodOptional<z.ZodString>;
}, z.core.$strip>;
export declare const updateAddressRequestSchema: z.ZodObject<{
street: z.ZodNullable<z.ZodString>;
streetLine2: z.ZodNullable<z.ZodString>;
city: z.ZodNullable<z.ZodString>;
state: z.ZodNullable<z.ZodString>;
postalCode: z.ZodNullable<z.ZodString>;
country: z.ZodNullable<z.ZodString>;
}, z.core.$strip>;
export declare const orderConfigurationsSchema: z.ZodObject<{
activationType: z.ZodOptional<z.ZodEnum<{
Immediate: "Immediate";
Scheduled: "Scheduled";
}>>;
scheduledAt: z.ZodOptional<z.ZodString>;
accessMode: z.ZodOptional<z.ZodEnum<{
"IPoE-HGW": "IPoE-HGW";
"IPoE-BYOR": "IPoE-BYOR";
PPPoE: "PPPoE";
}>>;
simType: z.ZodOptional<z.ZodEnum<{
eSIM: "eSIM";
"Physical SIM": "Physical SIM";
}>>;
eid: z.ZodOptional<z.ZodString>;
isMnp: z.ZodOptional<z.ZodString>;
mnpNumber: z.ZodOptional<z.ZodString>;
mnpExpiry: z.ZodOptional<z.ZodString>;
mnpPhone: z.ZodOptional<z.ZodString>;
mvnoAccountNumber: z.ZodOptional<z.ZodString>;
portingLastName: z.ZodOptional<z.ZodString>;
portingFirstName: z.ZodOptional<z.ZodString>;
portingLastNameKatakana: z.ZodOptional<z.ZodString>;
portingFirstNameKatakana: z.ZodOptional<z.ZodString>;
portingGender: z.ZodOptional<z.ZodEnum<{
Male: "Male";
Female: "Female";
"Corporate/Other": "Corporate/Other";
}>>;
portingDateOfBirth: z.ZodOptional<z.ZodString>;
address: z.ZodOptional<z.ZodObject<{
street: z.ZodNullable<z.ZodString>;
streetLine2: z.ZodNullable<z.ZodString>;
city: z.ZodNullable<z.ZodString>;
state: z.ZodNullable<z.ZodString>;
postalCode: z.ZodNullable<z.ZodString>;
country: z.ZodNullable<z.ZodString>;
}, z.core.$strip>>;
}, z.core.$strip>;
export declare const createOrderRequestSchema: z.ZodObject<{
orderType: z.ZodEnum<{
SIM: "SIM";
Internet: "Internet";
VPN: "VPN";
Other: "Other";
}>;
skus: z.ZodArray<z.ZodString>;
configurations: z.ZodOptional<z.ZodObject<{
activationType: z.ZodOptional<z.ZodEnum<{
Immediate: "Immediate";
Scheduled: "Scheduled";
}>>;
scheduledAt: z.ZodOptional<z.ZodString>;
accessMode: z.ZodOptional<z.ZodEnum<{
"IPoE-HGW": "IPoE-HGW";
"IPoE-BYOR": "IPoE-BYOR";
PPPoE: "PPPoE";
}>>;
simType: z.ZodOptional<z.ZodEnum<{
eSIM: "eSIM";
"Physical SIM": "Physical SIM";
}>>;
eid: z.ZodOptional<z.ZodString>;
isMnp: z.ZodOptional<z.ZodString>;
mnpNumber: z.ZodOptional<z.ZodString>;
mnpExpiry: z.ZodOptional<z.ZodString>;
mnpPhone: z.ZodOptional<z.ZodString>;
mvnoAccountNumber: z.ZodOptional<z.ZodString>;
portingLastName: z.ZodOptional<z.ZodString>;
portingFirstName: z.ZodOptional<z.ZodString>;
portingLastNameKatakana: z.ZodOptional<z.ZodString>;
portingFirstNameKatakana: z.ZodOptional<z.ZodString>;
portingGender: z.ZodOptional<z.ZodEnum<{
Male: "Male";
Female: "Female";
"Corporate/Other": "Corporate/Other";
}>>;
portingDateOfBirth: z.ZodOptional<z.ZodString>;
address: z.ZodOptional<z.ZodObject<{
street: z.ZodNullable<z.ZodString>;
streetLine2: z.ZodNullable<z.ZodString>;
city: z.ZodNullable<z.ZodString>;
state: z.ZodNullable<z.ZodString>;
postalCode: z.ZodNullable<z.ZodString>;
country: z.ZodNullable<z.ZodString>;
}, z.core.$strip>>;
}, z.core.$strip>>;
}, z.core.$strip>;
export declare const orderIdParamSchema: z.ZodObject<{
id: z.ZodCoercedNumber<unknown>;
}, z.core.$strip>;
export declare const simTopupRequestSchema: z.ZodObject<{
amount: z.ZodNumber;
currency: z.ZodDefault<z.ZodString>;
quotaMb: z.ZodNumber;
}, z.core.$strip>;
export declare const simCancelRequestSchema: z.ZodObject<{
reason: z.ZodOptional<z.ZodString>;
scheduledAt: z.ZodOptional<z.ZodString>;
}, z.core.$strip>;
export declare const simChangePlanRequestSchema: z.ZodObject<{
newPlanSku: z.ZodString;
newPlanCode: z.ZodString;
effectiveDate: z.ZodOptional<z.ZodString>;
}, z.core.$strip>;
export declare const simFeaturesRequestSchema: z.ZodObject<{
voiceMailEnabled: z.ZodOptional<z.ZodBoolean>;
callWaitingEnabled: z.ZodOptional<z.ZodBoolean>;
internationalRoamingEnabled: z.ZodOptional<z.ZodBoolean>;
networkType: z.ZodOptional<z.ZodEnum<{
"4G": "4G";
"5G": "5G";
}>>;
}, z.core.$strip>;
export declare const contactRequestSchema: z.ZodObject<{
subject: z.ZodString;
message: z.ZodString;
category: z.ZodEnum<{
billing: "billing";
technical: "technical";
account: "account";
general: "general";
}>;
priority: z.ZodDefault<z.ZodEnum<{
low: "low";
medium: "medium";
high: "high";
urgent: "urgent";
}>>;
}, z.core.$strip>;
export type UpdateProfileRequest = z.infer<typeof updateProfileRequestSchema>;
export type UpdateAddressRequest = z.infer<typeof updateAddressRequestSchema>;
export type OrderConfigurations = z.infer<typeof orderConfigurationsSchema>;
export type CreateOrderRequest = z.infer<typeof createOrderRequestSchema>;
export type SimTopupRequest = z.infer<typeof simTopupRequestSchema>;
export type SimCancelRequest = z.infer<typeof simCancelRequestSchema>;
export type SimChangePlanRequest = z.infer<typeof simChangePlanRequestSchema>;
export type SimFeaturesRequest = z.infer<typeof simFeaturesRequestSchema>;
export type ContactRequest = z.infer<typeof contactRequestSchema>;
export declare const invoiceItemSchema: z.ZodObject<{
id: z.ZodNumber;
description: z.ZodString;
amount: z.ZodNumber;
quantity: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
type: z.ZodString;
serviceId: z.ZodOptional<z.ZodNumber>;
}, z.core.$strip>;
export declare const invoiceSchema: z.ZodObject<{
id: z.ZodNumber;
number: z.ZodString;
status: z.ZodEnum<{
Draft: "Draft";
Pending: "Pending";
Paid: "Paid";
Unpaid: "Unpaid";
Overdue: "Overdue";
Cancelled: "Cancelled";
Refunded: "Refunded";
Collections: "Collections";
}>;
currency: z.ZodString;
currencySymbol: z.ZodOptional<z.ZodString>;
total: z.ZodNumber;
subtotal: z.ZodNumber;
tax: z.ZodNumber;
issuedAt: z.ZodOptional<z.ZodString>;
dueDate: z.ZodOptional<z.ZodString>;
paidDate: z.ZodOptional<z.ZodString>;
pdfUrl: z.ZodOptional<z.ZodString>;
paymentUrl: z.ZodOptional<z.ZodString>;
description: z.ZodOptional<z.ZodString>;
items: z.ZodOptional<z.ZodArray<z.ZodObject<{
id: z.ZodNumber;
description: z.ZodString;
amount: z.ZodNumber;
quantity: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
type: z.ZodString;
serviceId: z.ZodOptional<z.ZodNumber>;
}, z.core.$strip>>>;
}, z.core.$strip>;
export declare const paginationSchema: z.ZodObject<{
page: z.ZodNumber;
totalPages: z.ZodNumber;
totalItems: z.ZodNumber;
nextCursor: z.ZodOptional<z.ZodString>;
}, z.core.$strip>;
export declare const invoiceListSchema: z.ZodObject<{
invoices: z.ZodArray<z.ZodObject<{
id: z.ZodNumber;
number: z.ZodString;
status: z.ZodEnum<{
Draft: "Draft";
Pending: "Pending";
Paid: "Paid";
Unpaid: "Unpaid";
Overdue: "Overdue";
Cancelled: "Cancelled";
Refunded: "Refunded";
Collections: "Collections";
}>;
currency: z.ZodString;
currencySymbol: z.ZodOptional<z.ZodString>;
total: z.ZodNumber;
subtotal: z.ZodNumber;
tax: z.ZodNumber;
issuedAt: z.ZodOptional<z.ZodString>;
dueDate: z.ZodOptional<z.ZodString>;
paidDate: z.ZodOptional<z.ZodString>;
pdfUrl: z.ZodOptional<z.ZodString>;
paymentUrl: z.ZodOptional<z.ZodString>;
description: z.ZodOptional<z.ZodString>;
items: z.ZodOptional<z.ZodArray<z.ZodObject<{
id: z.ZodNumber;
description: z.ZodString;
amount: z.ZodNumber;
quantity: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
type: z.ZodString;
serviceId: z.ZodOptional<z.ZodNumber>;
}, z.core.$strip>>>;
}, z.core.$strip>>;
pagination: z.ZodObject<{
page: z.ZodNumber;
totalPages: z.ZodNumber;
totalItems: z.ZodNumber;
nextCursor: z.ZodOptional<z.ZodString>;
}, z.core.$strip>;
}, z.core.$strip>;
export type InvoiceItem = z.infer<typeof invoiceItemSchema>;
export type Invoice = z.infer<typeof invoiceSchema>;
export type Pagination = z.infer<typeof paginationSchema>;
export type InvoiceList = z.infer<typeof invoiceListSchema>;
export declare const invoicePaymentLinkSchema: z.ZodObject<{
paymentMethodId: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
gatewayName: z.ZodOptional<z.ZodString>;
}, z.core.$strip>;
export type InvoicePaymentLinkInput = z.infer<typeof invoicePaymentLinkSchema>;
export declare const sfOrderIdParamSchema: z.ZodObject<{
sfOrderId: z.ZodString;
}, z.core.$strip>;
export type SfOrderIdParam = z.infer<typeof sfOrderIdParamSchema>;
export declare const createMappingRequestSchema: z.ZodObject<{
userId: z.ZodString;
whmcsClientId: z.ZodNumber;
sfAccountId: z.ZodOptional<z.ZodString>;
}, z.core.$strip>;
export declare const updateMappingRequestSchema: z.ZodObject<{
whmcsClientId: z.ZodOptional<z.ZodNumber>;
sfAccountId: z.ZodOptional<z.ZodString>;
}, z.core.$strip>;
export declare const userIdMappingSchema: z.ZodObject<{
userId: z.ZodString;
whmcsClientId: z.ZodNumber;
sfAccountId: z.ZodOptional<z.ZodString>;
createdAt: z.ZodOptional<z.ZodString>;
updatedAt: z.ZodOptional<z.ZodString>;
}, z.core.$strip>;
export type CreateMappingRequest = z.infer<typeof createMappingRequestSchema>;
export type UpdateMappingRequest = z.infer<typeof updateMappingRequestSchema>;
export type UserIdMapping = z.infer<typeof userIdMappingSchema>;
export type UpdateProfileRequestInput = z.infer<typeof updateProfileRequestSchema>;
export type UpdateAddressRequestInput = z.infer<typeof updateAddressRequestSchema>;
export type ContactRequestInput = z.infer<typeof contactRequestSchema>;

View File

@ -1,222 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.userIdMappingSchema = exports.updateMappingRequestSchema = exports.createMappingRequestSchema = exports.sfOrderIdParamSchema = exports.invoicePaymentLinkSchema = exports.invoiceListSchema = exports.paginationSchema = exports.invoiceSchema = exports.invoiceItemSchema = exports.contactRequestSchema = exports.simFeaturesRequestSchema = exports.simChangePlanRequestSchema = exports.simCancelRequestSchema = exports.simTopupRequestSchema = exports.orderIdParamSchema = exports.createOrderRequestSchema = exports.orderConfigurationsSchema = exports.updateAddressRequestSchema = exports.updateProfileRequestSchema = exports.refreshTokenRequestSchema = exports.checkPasswordNeededRequestSchema = exports.ssoLinkRequestSchema = exports.accountStatusRequestSchema = exports.validateSignupRequestSchema = exports.linkWhmcsRequestSchema = exports.changePasswordRequestSchema = exports.setPasswordRequestSchema = exports.passwordResetSchema = exports.passwordResetRequestSchema = exports.signupRequestSchema = exports.loginRequestSchema = exports.subscriptionQuerySchema = exports.invoiceListQuerySchema = exports.dryRunQuerySchema = exports.auditLogQuerySchema = exports.paginationQuerySchema = void 0;
const zod_1 = require("zod");
const primitives_1 = require("../shared/primitives");
const entities_1 = require("../shared/entities");
const invoiceStatusEnum = zod_1.z.enum(["Paid", "Unpaid", "Overdue", "Cancelled", "Collections"]);
const subscriptionStatusEnum = zod_1.z.enum([
"Active",
"Suspended",
"Terminated",
"Cancelled",
"Pending",
]);
exports.paginationQuerySchema = zod_1.z.object({
page: zod_1.z.coerce.number().int().min(1).default(1),
limit: zod_1.z.coerce.number().int().min(1).max(100).default(10),
});
exports.auditLogQuerySchema = exports.paginationQuerySchema.extend({
action: zod_1.z.string().optional(),
userId: zod_1.z.string().uuid().optional(),
});
exports.dryRunQuerySchema = zod_1.z.object({
dryRun: zod_1.z.coerce.boolean().optional(),
});
exports.invoiceListQuerySchema = exports.paginationQuerySchema.extend({
status: invoiceStatusEnum.optional(),
});
exports.subscriptionQuerySchema = zod_1.z.object({
status: subscriptionStatusEnum.optional(),
});
exports.loginRequestSchema = zod_1.z.object({
email: primitives_1.emailSchema,
password: zod_1.z.string().min(1, "Password is required"),
});
exports.signupRequestSchema = zod_1.z.object({
email: primitives_1.emailSchema,
password: primitives_1.passwordSchema,
firstName: primitives_1.nameSchema,
lastName: primitives_1.nameSchema,
company: zod_1.z.string().optional(),
phone: primitives_1.phoneSchema,
sfNumber: zod_1.z.string().min(6, "Customer number must be at least 6 characters"),
address: primitives_1.requiredAddressSchema,
nationality: zod_1.z.string().optional(),
dateOfBirth: zod_1.z.string().optional(),
gender: primitives_1.genderEnum.optional(),
});
exports.passwordResetRequestSchema = zod_1.z.object({
email: primitives_1.emailSchema,
});
exports.passwordResetSchema = zod_1.z.object({
token: zod_1.z.string().min(1, "Reset token is required"),
password: primitives_1.passwordSchema,
});
exports.setPasswordRequestSchema = zod_1.z.object({
email: primitives_1.emailSchema,
password: primitives_1.passwordSchema,
});
exports.changePasswordRequestSchema = zod_1.z.object({
currentPassword: zod_1.z.string().min(1, "Current password is required"),
newPassword: primitives_1.passwordSchema,
});
exports.linkWhmcsRequestSchema = zod_1.z.object({
email: primitives_1.emailSchema,
password: zod_1.z.string().min(1, "Password is required"),
});
exports.validateSignupRequestSchema = zod_1.z.object({
sfNumber: zod_1.z.string().min(1, "Customer number is required"),
});
exports.accountStatusRequestSchema = zod_1.z.object({
email: primitives_1.emailSchema,
});
exports.ssoLinkRequestSchema = zod_1.z.object({
destination: zod_1.z.string().optional(),
});
exports.checkPasswordNeededRequestSchema = zod_1.z.object({
email: primitives_1.emailSchema,
});
exports.refreshTokenRequestSchema = zod_1.z.object({
refreshToken: zod_1.z.string().min(1, "Refresh token is required").optional(),
deviceId: zod_1.z.string().optional(),
});
exports.updateProfileRequestSchema = zod_1.z.object({
firstName: primitives_1.nameSchema.optional(),
lastName: primitives_1.nameSchema.optional(),
phone: primitives_1.phoneSchema.optional(),
company: zod_1.z.string().max(200).optional(),
});
exports.updateAddressRequestSchema = primitives_1.addressSchema;
exports.orderConfigurationsSchema = zod_1.z.object({
activationType: zod_1.z.enum(["Immediate", "Scheduled"]).optional(),
scheduledAt: zod_1.z.string().datetime().optional(),
accessMode: zod_1.z.enum(["IPoE-BYOR", "IPoE-HGW", "PPPoE"]).optional(),
simType: zod_1.z.enum(["eSIM", "Physical SIM"]).optional(),
eid: zod_1.z.string().optional(),
isMnp: zod_1.z.string().optional(),
mnpNumber: zod_1.z.string().optional(),
mnpExpiry: zod_1.z.string().optional(),
mnpPhone: zod_1.z.string().optional(),
mvnoAccountNumber: zod_1.z.string().optional(),
portingLastName: zod_1.z.string().optional(),
portingFirstName: zod_1.z.string().optional(),
portingLastNameKatakana: zod_1.z.string().optional(),
portingFirstNameKatakana: zod_1.z.string().optional(),
portingGender: zod_1.z.enum(["Male", "Female", "Corporate/Other"]).optional(),
portingDateOfBirth: zod_1.z.string().date().optional(),
address: primitives_1.addressSchema.optional(),
});
exports.createOrderRequestSchema = zod_1.z.object({
orderType: zod_1.z.enum(["Internet", "SIM", "VPN", "Other"]),
skus: zod_1.z.array(zod_1.z.string().min(1, "SKU cannot be empty")),
configurations: exports.orderConfigurationsSchema.optional(),
});
exports.orderIdParamSchema = zod_1.z.object({
id: zod_1.z.coerce.number().int().positive("Order ID must be positive"),
});
exports.simTopupRequestSchema = zod_1.z.object({
amount: zod_1.z.number().positive("Amount must be positive"),
currency: zod_1.z.string().length(3, "Currency must be 3 characters").default("JPY"),
quotaMb: zod_1.z.number().positive("Quota in MB must be positive"),
});
exports.simCancelRequestSchema = zod_1.z.object({
reason: zod_1.z.string().min(1, "Cancellation reason is required").optional(),
scheduledAt: zod_1.z
.string()
.regex(/^\d{8}$/, "Scheduled date must be in YYYYMMDD format")
.optional(),
});
exports.simChangePlanRequestSchema = zod_1.z.object({
newPlanSku: zod_1.z.string().min(1, "New plan SKU is required"),
newPlanCode: zod_1.z.string().min(1, "New plan code is required"),
effectiveDate: zod_1.z.string().date().optional(),
});
exports.simFeaturesRequestSchema = zod_1.z.object({
voiceMailEnabled: zod_1.z.boolean().optional(),
callWaitingEnabled: zod_1.z.boolean().optional(),
internationalRoamingEnabled: zod_1.z.boolean().optional(),
networkType: zod_1.z.enum(["4G", "5G"]).optional(),
});
exports.contactRequestSchema = zod_1.z.object({
subject: zod_1.z.string().min(1, "Subject is required").max(200, "Subject is too long"),
message: zod_1.z.string().min(1, "Message is required").max(2000, "Message is too long"),
category: zod_1.z.enum(["technical", "billing", "account", "general"]),
priority: zod_1.z.enum(["low", "medium", "high", "urgent"]).default("medium"),
});
exports.invoiceItemSchema = zod_1.z.object({
id: zod_1.z.number().int().positive(),
description: zod_1.z.string().min(1, "Description is required"),
amount: zod_1.z.number().nonnegative("Amount must be non-negative"),
quantity: zod_1.z.number().int().positive().optional().default(1),
type: zod_1.z.string().min(1, "Type is required"),
serviceId: zod_1.z.number().int().positive().optional(),
});
exports.invoiceSchema = zod_1.z.object({
id: zod_1.z.number().int().positive(),
number: zod_1.z.string().min(1, "Invoice number is required"),
status: entities_1.invoiceStatusSchema,
currency: zod_1.z.string().length(3, "Currency must be 3 characters"),
currencySymbol: zod_1.z.string().optional(),
total: zod_1.z.number().nonnegative("Total must be non-negative"),
subtotal: zod_1.z.number().nonnegative("Subtotal must be non-negative"),
tax: zod_1.z.number().nonnegative("Tax must be non-negative"),
issuedAt: zod_1.z.string().datetime().optional(),
dueDate: zod_1.z.string().datetime().optional(),
paidDate: zod_1.z.string().datetime().optional(),
pdfUrl: zod_1.z.string().url().optional(),
paymentUrl: zod_1.z.string().url().optional(),
description: zod_1.z.string().optional(),
items: zod_1.z.array(exports.invoiceItemSchema).optional(),
});
exports.paginationSchema = zod_1.z.object({
page: zod_1.z.number().int().min(1),
totalPages: zod_1.z.number().int().min(0),
totalItems: zod_1.z.number().int().min(0),
nextCursor: zod_1.z.string().optional(),
});
exports.invoiceListSchema = zod_1.z.object({
invoices: zod_1.z.array(exports.invoiceSchema),
pagination: exports.paginationSchema,
});
exports.invoicePaymentLinkSchema = zod_1.z.object({
paymentMethodId: zod_1.z.coerce.number().int().positive().optional(),
gatewayName: zod_1.z.string().min(1).optional(),
});
exports.sfOrderIdParamSchema = zod_1.z.object({
sfOrderId: zod_1.z.string().min(1, "Salesforce order ID is required"),
});
exports.createMappingRequestSchema = zod_1.z.object({
userId: zod_1.z.string().uuid("User ID must be a valid UUID"),
whmcsClientId: zod_1.z.number().int().positive("WHMCS client ID must be a positive integer"),
sfAccountId: zod_1.z
.string()
.regex(/^[a-zA-Z0-9]{15}$|^[a-zA-Z0-9]{18}$/, "Salesforce account ID must be a valid 15 or 18 character ID")
.optional(),
});
exports.updateMappingRequestSchema = zod_1.z
.object({
whmcsClientId: zod_1.z
.number()
.int()
.positive("WHMCS client ID must be a positive integer")
.optional(),
sfAccountId: zod_1.z
.string()
.regex(/^[a-zA-Z0-9]{15}$|^[a-zA-Z0-9]{18}$/, "Salesforce account ID must be a valid 15 or 18 character ID")
.optional(),
})
.refine(data => data.whmcsClientId !== undefined || data.sfAccountId !== undefined, {
message: "At least one field must be provided for update",
});
exports.userIdMappingSchema = zod_1.z.object({
userId: zod_1.z.string().uuid("User ID must be a valid UUID"),
whmcsClientId: zod_1.z.number().int().positive("WHMCS client ID must be a positive integer"),
sfAccountId: zod_1.z
.string()
.regex(/^[a-zA-Z0-9]{15}$|^[a-zA-Z0-9]{18}$/, "Salesforce account ID must be a valid 15 or 18 character ID")
.optional(),
createdAt: zod_1.z.string().datetime().optional(),
updatedAt: zod_1.z.string().datetime().optional(),
});
//# sourceMappingURL=requests.js.map

View File

@ -1,379 +0,0 @@
/**
* API Request Schemas
* Schemas for data that the backend receives and validates
* These are the "source of truth" for business logic validation
*/
import { z } from "zod";
import {
emailSchema,
passwordSchema,
nameSchema,
phoneSchema,
addressSchema,
requiredAddressSchema,
genderEnum,
} from "../shared/primitives";
import { invoiceStatusSchema } from "../shared/entities";
const invoiceStatusEnum = z.enum(["Paid", "Unpaid", "Overdue", "Cancelled", "Collections"]);
const subscriptionStatusEnum = z.enum([
"Active",
"Suspended",
"Terminated",
"Cancelled",
"Pending",
]);
export const paginationQuerySchema = z.object({
page: z.coerce.number().int().min(1).default(1),
limit: z.coerce.number().int().min(1).max(100).default(10),
});
export type PaginationQuery = z.infer<typeof paginationQuerySchema>;
export const auditLogQuerySchema = paginationQuerySchema.extend({
action: z.string().optional(),
userId: z.string().uuid().optional(),
});
export type AuditLogQuery = z.infer<typeof auditLogQuerySchema>;
export const dryRunQuerySchema = z.object({
dryRun: z.coerce.boolean().optional(),
});
export type DryRunQuery = z.infer<typeof dryRunQuerySchema>;
export const invoiceListQuerySchema = paginationQuerySchema.extend({
status: invoiceStatusEnum.optional(),
});
export type InvoiceListQuery = z.infer<typeof invoiceListQuerySchema>;
export const subscriptionQuerySchema = z.object({
status: subscriptionStatusEnum.optional(),
});
export type SubscriptionQuery = z.infer<typeof subscriptionQuerySchema>;
// =====================================================
// AUTH REQUEST SCHEMAS
// =====================================================
export const loginRequestSchema = z.object({
email: emailSchema,
password: z.string().min(1, "Password is required"),
});
export const signupRequestSchema = z.object({
email: emailSchema,
password: passwordSchema,
firstName: nameSchema,
lastName: nameSchema,
company: z.string().optional(),
phone: phoneSchema,
sfNumber: z.string().min(6, "Customer number must be at least 6 characters"),
address: requiredAddressSchema,
nationality: z.string().optional(),
dateOfBirth: z.string().optional(),
gender: genderEnum.optional(),
});
export const passwordResetRequestSchema = z.object({
email: emailSchema,
});
export const passwordResetSchema = z.object({
token: z.string().min(1, "Reset token is required"),
password: passwordSchema,
});
export const setPasswordRequestSchema = z.object({
email: emailSchema,
password: passwordSchema,
});
export const changePasswordRequestSchema = z.object({
currentPassword: z.string().min(1, "Current password is required"),
newPassword: passwordSchema,
});
export const linkWhmcsRequestSchema = z.object({
email: emailSchema,
password: z.string().min(1, "Password is required"),
});
export const validateSignupRequestSchema = z.object({
sfNumber: z.string().min(1, "Customer number is required"),
});
export const accountStatusRequestSchema = z.object({
email: emailSchema,
});
export const ssoLinkRequestSchema = z.object({
destination: z.string().optional(),
});
export const checkPasswordNeededRequestSchema = z.object({
email: emailSchema,
});
export const refreshTokenRequestSchema = z.object({
refreshToken: z.string().min(1, "Refresh token is required").optional(),
deviceId: z.string().optional(),
});
// =====================================================
// TYPE EXPORTS
// =====================================================
export type LoginRequestInput = z.infer<typeof loginRequestSchema>;
export type SignupRequestInput = z.infer<typeof signupRequestSchema>;
export type PasswordResetRequestInput = z.infer<typeof passwordResetRequestSchema>;
export type PasswordResetInput = z.infer<typeof passwordResetSchema>;
export type SetPasswordRequestInput = z.infer<typeof setPasswordRequestSchema>;
export type ChangePasswordRequestInput = z.infer<typeof changePasswordRequestSchema>;
export type LinkWhmcsRequestInput = z.infer<typeof linkWhmcsRequestSchema>;
export type ValidateSignupRequestInput = z.infer<typeof validateSignupRequestSchema>;
export type AccountStatusRequestInput = z.infer<typeof accountStatusRequestSchema>;
export type SsoLinkRequestInput = z.infer<typeof ssoLinkRequestSchema>;
export type CheckPasswordNeededRequestInput = z.infer<typeof checkPasswordNeededRequestSchema>;
export type RefreshTokenRequestInput = z.infer<typeof refreshTokenRequestSchema>;
// =====================================================
// USER MANAGEMENT REQUEST SCHEMAS
// =====================================================
export const updateProfileRequestSchema = z.object({
firstName: nameSchema.optional(),
lastName: nameSchema.optional(),
phone: phoneSchema.optional(),
company: z.string().max(200).optional(),
});
export const updateAddressRequestSchema = addressSchema;
// =====================================================
// ORDER REQUEST SCHEMAS
// =====================================================
export const orderConfigurationsSchema = z.object({
// Activation (All order types)
activationType: z.enum(["Immediate", "Scheduled"]).optional(),
scheduledAt: z.string().datetime().optional(),
// Internet specific
accessMode: z.enum(["IPoE-BYOR", "IPoE-HGW", "PPPoE"]).optional(),
// SIM specific
simType: z.enum(["eSIM", "Physical SIM"]).optional(),
eid: z.string().optional(), // Required for eSIM
// MNP/Porting
isMnp: z.string().optional(), // "true" | "false"
mnpNumber: z.string().optional(),
mnpExpiry: z.string().optional(),
mnpPhone: z.string().optional(),
mvnoAccountNumber: z.string().optional(),
portingLastName: z.string().optional(),
portingFirstName: z.string().optional(),
portingLastNameKatakana: z.string().optional(),
portingFirstNameKatakana: z.string().optional(),
portingGender: z.enum(["Male", "Female", "Corporate/Other"]).optional(),
portingDateOfBirth: z.string().date().optional(),
// Optional address override captured at checkout
address: addressSchema.optional(),
});
export const createOrderRequestSchema = z.object({
orderType: z.enum(["Internet", "SIM", "VPN", "Other"]),
skus: z.array(z.string().min(1, "SKU cannot be empty")),
configurations: orderConfigurationsSchema.optional(),
});
export const orderIdParamSchema = z.object({
id: z.coerce.number().int().positive("Order ID must be positive"),
});
// =====================================================
// SUBSCRIPTION MANAGEMENT REQUEST SCHEMAS
// =====================================================
export const simTopupRequestSchema = z.object({
amount: z.number().positive("Amount must be positive"),
currency: z.string().length(3, "Currency must be 3 characters").default("JPY"),
quotaMb: z.number().positive("Quota in MB must be positive"),
});
export const simCancelRequestSchema = z.object({
reason: z.string().min(1, "Cancellation reason is required").optional(),
scheduledAt: z
.string()
.regex(/^\d{8}$/, "Scheduled date must be in YYYYMMDD format")
.optional(),
});
export const simChangePlanRequestSchema = z.object({
newPlanSku: z.string().min(1, "New plan SKU is required"),
newPlanCode: z.string().min(1, "New plan code is required"),
effectiveDate: z.string().date().optional(),
});
export const simFeaturesRequestSchema = z.object({
voiceMailEnabled: z.boolean().optional(),
callWaitingEnabled: z.boolean().optional(),
internationalRoamingEnabled: z.boolean().optional(),
networkType: z.enum(["4G", "5G"]).optional(),
});
// =====================================================
// CONTACT REQUEST SCHEMAS
// =====================================================
export const contactRequestSchema = z.object({
subject: z.string().min(1, "Subject is required").max(200, "Subject is too long"),
message: z.string().min(1, "Message is required").max(2000, "Message is too long"),
category: z.enum(["technical", "billing", "account", "general"]),
priority: z.enum(["low", "medium", "high", "urgent"]).default("medium"),
});
// =====================================================
// TYPE EXPORTS
// =====================================================
export type UpdateProfileRequest = z.infer<typeof updateProfileRequestSchema>;
export type UpdateAddressRequest = z.infer<typeof updateAddressRequestSchema>;
export type OrderConfigurations = z.infer<typeof orderConfigurationsSchema>;
export type CreateOrderRequest = z.infer<typeof createOrderRequestSchema>;
export type SimTopupRequest = z.infer<typeof simTopupRequestSchema>;
export type SimCancelRequest = z.infer<typeof simCancelRequestSchema>;
export type SimChangePlanRequest = z.infer<typeof simChangePlanRequestSchema>;
export type SimFeaturesRequest = z.infer<typeof simFeaturesRequestSchema>;
export type ContactRequest = z.infer<typeof contactRequestSchema>;
// =====================================================
// INVOICE SCHEMAS
// =====================================================
export const invoiceItemSchema = z.object({
id: z.number().int().positive(),
description: z.string().min(1, "Description is required"),
amount: z.number().nonnegative("Amount must be non-negative"),
quantity: z.number().int().positive().optional().default(1),
type: z.string().min(1, "Type is required"),
serviceId: z.number().int().positive().optional(),
});
export const invoiceSchema = z.object({
id: z.number().int().positive(),
number: z.string().min(1, "Invoice number is required"),
status: invoiceStatusSchema,
currency: z.string().length(3, "Currency must be 3 characters"),
currencySymbol: z.string().optional(),
total: z.number().nonnegative("Total must be non-negative"),
subtotal: z.number().nonnegative("Subtotal must be non-negative"),
tax: z.number().nonnegative("Tax must be non-negative"),
issuedAt: z.string().datetime().optional(),
dueDate: z.string().datetime().optional(),
paidDate: z.string().datetime().optional(),
pdfUrl: z.string().url().optional(),
paymentUrl: z.string().url().optional(),
description: z.string().optional(),
items: z.array(invoiceItemSchema).optional(),
});
export const paginationSchema = z.object({
page: z.number().int().min(1),
totalPages: z.number().int().min(0),
totalItems: z.number().int().min(0),
nextCursor: z.string().optional(),
});
export const invoiceListSchema = z.object({
invoices: z.array(invoiceSchema),
pagination: paginationSchema,
});
// =====================================================
// INVOICE TYPE EXPORTS
// =====================================================
export type InvoiceItem = z.infer<typeof invoiceItemSchema>;
export type Invoice = z.infer<typeof invoiceSchema>;
export type Pagination = z.infer<typeof paginationSchema>;
export type InvoiceList = z.infer<typeof invoiceListSchema>;
export const invoicePaymentLinkSchema = z.object({
paymentMethodId: z.coerce.number().int().positive().optional(),
gatewayName: z.string().min(1).optional(),
});
export type InvoicePaymentLinkInput = z.infer<typeof invoicePaymentLinkSchema>;
export const sfOrderIdParamSchema = z.object({
sfOrderId: z.string().min(1, "Salesforce order ID is required"),
});
export type SfOrderIdParam = z.infer<typeof sfOrderIdParamSchema>;
// =====================================================
// ID MAPPING SCHEMAS
// =====================================================
export const createMappingRequestSchema = z.object({
userId: z.string().uuid("User ID must be a valid UUID"),
whmcsClientId: z.number().int().positive("WHMCS client ID must be a positive integer"),
sfAccountId: z
.string()
.regex(
/^[a-zA-Z0-9]{15}$|^[a-zA-Z0-9]{18}$/,
"Salesforce account ID must be a valid 15 or 18 character ID"
)
.optional(),
});
export const updateMappingRequestSchema = z
.object({
whmcsClientId: z
.number()
.int()
.positive("WHMCS client ID must be a positive integer")
.optional(),
sfAccountId: z
.string()
.regex(
/^[a-zA-Z0-9]{15}$|^[a-zA-Z0-9]{18}$/,
"Salesforce account ID must be a valid 15 or 18 character ID"
)
.optional(),
})
.refine(data => data.whmcsClientId !== undefined || data.sfAccountId !== undefined, {
message: "At least one field must be provided for update",
});
export const userIdMappingSchema = z.object({
userId: z.string().uuid("User ID must be a valid UUID"),
whmcsClientId: z.number().int().positive("WHMCS client ID must be a positive integer"),
sfAccountId: z
.string()
.regex(
/^[a-zA-Z0-9]{15}$|^[a-zA-Z0-9]{18}$/,
"Salesforce account ID must be a valid 15 or 18 character ID"
)
.optional(),
createdAt: z.string().datetime().optional(),
updatedAt: z.string().datetime().optional(),
});
// =====================================================
// ID MAPPING TYPE EXPORTS
// =====================================================
export type CreateMappingRequest = z.infer<typeof createMappingRequestSchema>;
export type UpdateMappingRequest = z.infer<typeof updateMappingRequestSchema>;
export type UserIdMapping = z.infer<typeof userIdMappingSchema>;
export type UpdateProfileRequestInput = z.infer<typeof updateProfileRequestSchema>;
export type UpdateAddressRequestInput = z.infer<typeof updateAddressRequestSchema>;
export type ContactRequestInput = z.infer<typeof contactRequestSchema>;

View File

@ -1,39 +0,0 @@
import { z } from "zod";
export declare const authResponseSchema: z.ZodObject<{
user: z.ZodObject<{
id: z.ZodString;
createdAt: z.ZodString;
updatedAt: z.ZodString;
email: z.ZodString;
firstName: z.ZodOptional<z.ZodString>;
lastName: z.ZodOptional<z.ZodString>;
company: z.ZodOptional<z.ZodString>;
phone: z.ZodOptional<z.ZodString>;
address: z.ZodOptional<z.ZodObject<{
street: z.ZodNullable<z.ZodString>;
streetLine2: z.ZodNullable<z.ZodString>;
city: z.ZodNullable<z.ZodString>;
state: z.ZodNullable<z.ZodString>;
postalCode: z.ZodNullable<z.ZodString>;
country: z.ZodNullable<z.ZodString>;
}, z.core.$strip>>;
mfaEnabled: z.ZodBoolean;
emailVerified: z.ZodBoolean;
avatar: z.ZodOptional<z.ZodString>;
preferences: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
lastLoginAt: z.ZodOptional<z.ZodString>;
role: z.ZodEnum<{
USER: "USER";
ADMIN: "ADMIN";
}>;
}, z.core.$strip>;
tokens: z.ZodObject<{
accessToken: z.ZodString;
refreshToken: z.ZodString;
expiresAt: z.ZodString;
refreshExpiresAt: z.ZodString;
tokenType: z.ZodLiteral<"Bearer">;
}, z.core.$strip>;
}, z.core.$strip>;
export type AuthResponse = z.infer<typeof authResponseSchema>;
export type AuthTokensSchema = AuthResponse["tokens"];

View File

@ -1,16 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.authResponseSchema = void 0;
const zod_1 = require("zod");
const entities_1 = require("../shared/entities");
exports.authResponseSchema = zod_1.z.object({
user: entities_1.userProfileSchema,
tokens: zod_1.z.object({
accessToken: zod_1.z.string().min(1, "Access token is required"),
refreshToken: zod_1.z.string().min(1, "Refresh token is required"),
expiresAt: zod_1.z.string().min(1, "Access token expiry required"),
refreshExpiresAt: zod_1.z.string().min(1, "Refresh token expiry required"),
tokenType: zod_1.z.literal("Bearer"),
}),
});
//# sourceMappingURL=responses.js.map

View File

@ -1,17 +0,0 @@
import { z } from "zod";
import { userProfileSchema } from "../shared/entities";
export const authResponseSchema = z.object({
user: userProfileSchema,
tokens: z.object({
accessToken: z.string().min(1, "Access token is required"),
refreshToken: z.string().min(1, "Refresh token is required"),
expiresAt: z.string().min(1, "Access token expiry required"),
refreshExpiresAt: z.string().min(1, "Refresh token expiry required"),
tokenType: z.literal("Bearer"),
}),
});
export type AuthResponse = z.infer<typeof authResponseSchema>;
export type AuthTokensSchema = AuthResponse["tokens"];

View File

@ -1,18 +0,0 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
__exportStar(require("./orders"), exports);
//# sourceMappingURL=index.js.map

View File

@ -1,6 +0,0 @@
/**
* Business Validation Rules
* Centralized business logic validation
*/
export * from "./orders";

View File

@ -1,8 +1,8 @@
import { z } from "zod";
export declare const orderBusinessValidationSchema: z.ZodObject<{
orderType: z.ZodEnum<{
SIM: "SIM";
Internet: "Internet";
SIM: "SIM";
VPN: "VPN";
Other: "Other";
}>;
@ -14,8 +14,8 @@ export declare const orderBusinessValidationSchema: z.ZodObject<{
}>>;
scheduledAt: z.ZodOptional<z.ZodString>;
accessMode: z.ZodOptional<z.ZodEnum<{
"IPoE-HGW": "IPoE-HGW";
"IPoE-BYOR": "IPoE-BYOR";
"IPoE-HGW": "IPoE-HGW";
PPPoE: "PPPoE";
}>>;
simType: z.ZodOptional<z.ZodEnum<{
@ -54,8 +54,8 @@ export declare const skuValidationSchema: z.ZodObject<{
sku: z.ZodString;
isActive: z.ZodBoolean;
productType: z.ZodEnum<{
SIM: "SIM";
Internet: "Internet";
SIM: "SIM";
VPN: "VPN";
Addon: "Addon";
Fee: "Fee";

View File

@ -1,78 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.paymentMethodValidationSchema = exports.userMappingValidationSchema = exports.skuValidationSchema = exports.orderBusinessValidationSchema = void 0;
const zod_1 = require("zod");
const requests_1 = require("../api/requests");
const identifiers_1 = require("../shared/identifiers");
exports.orderBusinessValidationSchema = requests_1.createOrderRequestSchema
.extend({
userId: identifiers_1.userIdSchema,
opportunityId: zod_1.z.string().optional(),
})
.refine(data => {
if (data.orderType === "Internet") {
const mainServiceSkus = data.skus.filter(sku => {
const upperSku = sku.toUpperCase();
return (!upperSku.includes("INSTALL") &&
!upperSku.includes("ADDON") &&
!upperSku.includes("ACTIVATION") &&
!upperSku.includes("FEE"));
});
return mainServiceSkus.length >= 1;
}
return true;
}, {
message: "Internet orders must have at least one main service SKU (non-installation, non-addon)",
path: ["skus"],
})
.refine(data => {
if (data.orderType === "SIM" && data.configurations) {
return data.configurations.simType !== undefined;
}
return true;
}, {
message: "SIM orders must specify SIM type",
path: ["configurations", "simType"],
})
.refine(data => {
if (data.configurations?.simType === "eSIM") {
return data.configurations.eid !== undefined && data.configurations.eid.length > 0;
}
return true;
}, {
message: "eSIM orders must provide EID",
path: ["configurations", "eid"],
})
.refine(data => {
if (data.configurations?.isMnp === "true") {
const required = ["mnpNumber", "portingLastName", "portingFirstName"];
return required.every(field => data.configurations?.[field] !== undefined);
}
return true;
}, {
message: "MNP orders must provide porting information",
path: ["configurations"],
});
exports.skuValidationSchema = zod_1.z.object({
sku: zod_1.z.string().min(1, "SKU is required"),
isActive: zod_1.z.boolean(),
productType: zod_1.z.enum(["Internet", "SIM", "VPN", "Addon", "Fee"]),
price: zod_1.z.number().nonnegative(),
currency: zod_1.z.string().length(3),
});
exports.userMappingValidationSchema = zod_1.z.object({
userId: identifiers_1.userIdSchema,
sfAccountId: zod_1.z.string().min(15, "Salesforce Account ID must be at least 15 characters"),
whmcsClientId: zod_1.z.number().int().positive("WHMCS Client ID must be positive"),
});
exports.paymentMethodValidationSchema = zod_1.z.object({
userId: identifiers_1.userIdSchema,
whmcsClientId: zod_1.z.number().int().positive(),
hasValidPaymentMethod: zod_1.z.boolean(),
paymentMethods: zod_1.z.array(zod_1.z.object({
id: zod_1.z.string(),
type: zod_1.z.string(),
isDefault: zod_1.z.boolean(),
})),
});
//# sourceMappingURL=orders.js.map

View File

@ -1,129 +0,0 @@
/**
* Order Business Validation
* Business logic validation rules for orders
*/
import { z } from "zod";
import { createOrderRequestSchema } from "../api/requests";
import { userIdSchema } from "../shared/identifiers";
// =====================================================
// BUSINESS VALIDATION SCHEMAS
// =====================================================
export const orderBusinessValidationSchema = createOrderRequestSchema
.extend({
userId: userIdSchema,
opportunityId: z.string().optional(),
})
.refine(
data => {
// Business rule: Internet orders can have one main service + installations + addons
if (data.orderType === "Internet") {
// Allow multiple SKUs but ensure at least one main service
// Main service SKUs don't contain "install", "addon", "activation", or "fee"
const mainServiceSkus = data.skus.filter(sku => {
const upperSku = sku.toUpperCase();
return (
!upperSku.includes("INSTALL") &&
!upperSku.includes("ADDON") &&
!upperSku.includes("ACTIVATION") &&
!upperSku.includes("FEE")
);
});
return mainServiceSkus.length >= 1; // At least one main service required
}
return true;
},
{
message:
"Internet orders must have at least one main service SKU (non-installation, non-addon)",
path: ["skus"],
}
)
.refine(
data => {
// Business rule: SIM orders require SIM-specific configuration
if (data.orderType === "SIM" && data.configurations) {
return data.configurations.simType !== undefined;
}
return true;
},
{
message: "SIM orders must specify SIM type",
path: ["configurations", "simType"],
}
)
.refine(
data => {
// Business rule: eSIM orders require EID
if (data.configurations?.simType === "eSIM") {
return data.configurations.eid !== undefined && data.configurations.eid.length > 0;
}
return true;
},
{
message: "eSIM orders must provide EID",
path: ["configurations", "eid"],
}
)
.refine(
data => {
// Business rule: MNP orders require additional fields
if (data.configurations?.isMnp === "true") {
const required = ["mnpNumber", "portingLastName", "portingFirstName"];
return required.every(
field => data.configurations?.[field as keyof typeof data.configurations] !== undefined
);
}
return true;
},
{
message: "MNP orders must provide porting information",
path: ["configurations"],
}
);
// SKU validation schema
export const skuValidationSchema = z.object({
sku: z.string().min(1, "SKU is required"),
isActive: z.boolean(),
productType: z.enum(["Internet", "SIM", "VPN", "Addon", "Fee"]),
price: z.number().nonnegative(),
currency: z.string().length(3),
});
// User mapping validation schema
export const userMappingValidationSchema = z.object({
userId: userIdSchema,
sfAccountId: z.string().min(15, "Salesforce Account ID must be at least 15 characters"),
whmcsClientId: z.number().int().positive("WHMCS Client ID must be positive"),
});
// Payment method validation schema
export const paymentMethodValidationSchema = z.object({
userId: userIdSchema,
whmcsClientId: z.number().int().positive(),
hasValidPaymentMethod: z.boolean(),
paymentMethods: z.array(
z.object({
id: z.string(),
type: z.string(),
isDefault: z.boolean(),
})
),
});
// =====================================================
// DIRECT ZOD USAGE - NO WRAPPER FUNCTIONS NEEDED
// =====================================================
// Use schema.safeParse(data) directly instead of wrapper functions
// =====================================================
// TYPE EXPORTS
// =====================================================
export type OrderBusinessValidation = z.infer<typeof orderBusinessValidationSchema>;
export type SkuValidation = z.infer<typeof skuValidationSchema>;
export type UserMappingValidation = z.infer<typeof userMappingValidationSchema>;
export type PaymentMethodValidation = z.infer<typeof paymentMethodValidationSchema>;

View File

@ -1,68 +0,0 @@
export declare const loginFormSchema: import("zod").ZodObject<{
email: import("zod").ZodString;
password: import("zod").ZodString;
}, import("zod/v4/core").$strip>;
export declare const signupFormSchema: import("zod").ZodObject<{
email: import("zod").ZodString;
password: import("zod").ZodString;
firstName: import("zod").ZodString;
lastName: import("zod").ZodString;
company: import("zod").ZodOptional<import("zod").ZodString>;
phone: import("zod").ZodString;
sfNumber: import("zod").ZodString;
address: import("zod").ZodObject<{
street: import("zod").ZodString;
streetLine2: import("zod").ZodOptional<import("zod").ZodString>;
city: import("zod").ZodString;
state: import("zod").ZodString;
postalCode: import("zod").ZodString;
country: import("zod").ZodString;
}, import("zod/v4/core").$strip>;
nationality: import("zod").ZodOptional<import("zod").ZodString>;
dateOfBirth: import("zod").ZodOptional<import("zod").ZodString>;
gender: import("zod").ZodOptional<import("zod").ZodEnum<{
male: "male";
female: "female";
other: "other";
}>>;
}, import("zod/v4/core").$strip>;
export declare const passwordResetRequestFormSchema: import("zod").ZodObject<{
email: import("zod").ZodString;
}, import("zod/v4/core").$strip>;
export declare const passwordResetFormSchema: import("zod").ZodObject<{
token: import("zod").ZodString;
password: import("zod").ZodString;
}, import("zod/v4/core").$strip>;
export declare const setPasswordFormSchema: import("zod").ZodObject<{
email: import("zod").ZodString;
password: import("zod").ZodString;
}, import("zod/v4/core").$strip>;
export declare const changePasswordFormSchema: import("zod").ZodObject<{
currentPassword: import("zod").ZodString;
newPassword: import("zod").ZodString;
}, import("zod/v4/core").$strip>;
export declare const linkWhmcsFormSchema: import("zod").ZodObject<{
email: import("zod").ZodString;
password: import("zod").ZodString;
}, import("zod/v4/core").$strip>;
export declare const loginFormToRequest: (formData: LoginFormData) => LoginRequestData;
export declare const signupFormToRequest: (formData: SignupFormData) => SignupRequestData;
export declare const passwordResetFormToRequest: (formData: PasswordResetFormData) => PasswordResetData;
export declare const setPasswordFormToRequest: (formData: SetPasswordFormData) => SetPasswordRequestData;
export declare const changePasswordFormToRequest: (formData: ChangePasswordFormData) => ChangePasswordRequestData;
import type { LoginRequestInput, SignupRequestInput, PasswordResetRequestInput, PasswordResetInput, SetPasswordRequestInput, ChangePasswordRequestInput, LinkWhmcsRequestInput } from "../api/requests";
type LoginRequestData = LoginRequestInput;
type SignupRequestData = SignupRequestInput;
type PasswordResetRequestData = PasswordResetRequestInput;
type PasswordResetData = PasswordResetInput;
type SetPasswordRequestData = SetPasswordRequestInput;
type ChangePasswordRequestData = ChangePasswordRequestInput;
type LinkWhmcsRequestData = LinkWhmcsRequestInput;
export type LoginFormData = LoginRequestInput;
export type SignupFormData = SignupRequestInput;
export type PasswordResetRequestFormData = PasswordResetRequestInput;
export type PasswordResetFormData = PasswordResetInput;
export type SetPasswordFormData = SetPasswordRequestInput;
export type ChangePasswordFormData = ChangePasswordRequestInput;
export type LinkWhmcsFormData = LinkWhmcsRequestInput;
export type { LoginRequestData, SignupRequestData, PasswordResetRequestData, PasswordResetData, SetPasswordRequestData, ChangePasswordRequestData, LinkWhmcsRequestData, };

View File

@ -1,95 +0,0 @@
/**
* Authentication Form Schemas
* Frontend form schemas that extend API request schemas with UI-specific fields
*/
import {
loginRequestSchema,
signupRequestSchema,
passwordResetRequestSchema,
passwordResetSchema,
setPasswordRequestSchema,
changePasswordRequestSchema,
linkWhmcsRequestSchema,
} from "../api/requests";
// =====================================================
// FORM SCHEMAS (Extend API schemas with UI fields)
// =====================================================
export const loginFormSchema = loginRequestSchema;
export const signupFormSchema = signupRequestSchema;
export const passwordResetRequestFormSchema = passwordResetRequestSchema;
export const passwordResetFormSchema = passwordResetSchema;
export const setPasswordFormSchema = setPasswordRequestSchema;
export const changePasswordFormSchema = changePasswordRequestSchema;
export const linkWhmcsFormSchema = linkWhmcsRequestSchema;
// =====================================================
// FORM TO API TRANSFORMATIONS
// =====================================================
export const loginFormToRequest = (formData: LoginFormData): LoginRequestData =>
loginRequestSchema.parse(formData);
export const signupFormToRequest = (formData: SignupFormData): SignupRequestData =>
signupRequestSchema.parse(formData);
export const passwordResetFormToRequest = (formData: PasswordResetFormData): PasswordResetData =>
passwordResetSchema.parse(formData);
export const setPasswordFormToRequest = (formData: SetPasswordFormData): SetPasswordRequestData =>
setPasswordRequestSchema.parse(formData);
export const changePasswordFormToRequest = (
formData: ChangePasswordFormData
): ChangePasswordRequestData => changePasswordRequestSchema.parse(formData);
// =====================================================
// TYPE EXPORTS
// =====================================================
// Import API types
import type {
LoginRequestInput,
SignupRequestInput,
PasswordResetRequestInput,
PasswordResetInput,
SetPasswordRequestInput,
ChangePasswordRequestInput,
LinkWhmcsRequestInput,
} from "../api/requests";
type LoginRequestData = LoginRequestInput;
type SignupRequestData = SignupRequestInput;
type PasswordResetRequestData = PasswordResetRequestInput;
type PasswordResetData = PasswordResetInput;
type SetPasswordRequestData = SetPasswordRequestInput;
type ChangePasswordRequestData = ChangePasswordRequestInput;
type LinkWhmcsRequestData = LinkWhmcsRequestInput;
// Export form types (aliases of API request types)
export type LoginFormData = LoginRequestInput;
export type SignupFormData = SignupRequestInput;
export type PasswordResetRequestFormData = PasswordResetRequestInput;
export type PasswordResetFormData = PasswordResetInput;
export type SetPasswordFormData = SetPasswordRequestInput;
export type ChangePasswordFormData = ChangePasswordRequestInput;
export type LinkWhmcsFormData = LinkWhmcsRequestInput;
// Re-export API types for convenience
export type {
LoginRequestData,
SignupRequestData,
PasswordResetRequestData,
PasswordResetData,
SetPasswordRequestData,
ChangePasswordRequestData,
LinkWhmcsRequestData,
};

View File

@ -1,17 +1,6 @@
import { z } from "zod";
export declare const profileEditFormSchema: z.ZodObject<{
phone: z.ZodOptional<z.ZodString>;
company: z.ZodOptional<z.ZodString>;
firstName: z.ZodString;
lastName: z.ZodString;
}, z.core.$strip>;
export declare const profileDisplaySchema: z.ZodObject<{
phone: z.ZodOptional<z.ZodString>;
company: z.ZodOptional<z.ZodString>;
firstName: z.ZodString;
lastName: z.ZodString;
email: z.ZodString;
}, z.core.$strip>;
export declare const profileEditFormSchema: any;
export declare const profileDisplaySchema: any;
export declare const addressFormSchema: z.ZodObject<{
street: z.ZodString;
streetLine2: z.ZodOptional<z.ZodString>;
@ -20,22 +9,7 @@ export declare const addressFormSchema: z.ZodObject<{
postalCode: z.ZodString;
country: z.ZodString;
}, z.core.$strip>;
export declare const contactFormSchema: z.ZodObject<{
subject: z.ZodString;
message: z.ZodString;
category: z.ZodEnum<{
billing: "billing";
technical: "technical";
account: "account";
general: "general";
}>;
priority: z.ZodDefault<z.ZodOptional<z.ZodEnum<{
low: "low";
medium: "medium";
high: "high";
urgent: "urgent";
}>>>;
}, z.core.$strip>;
export declare const contactFormSchema: any;
export declare const profileFormToRequest: (formData: ProfileEditFormData) => UpdateProfileRequestData;
export declare const addressFormToRequest: (formData: AddressFormData) => UpdateAddressRequestData;
export declare const contactFormToRequest: (formData: ContactFormData) => ContactRequestData;

View File

@ -1,14 +0,0 @@
export * from "./shared/primitives";
export * from "./shared/identifiers";
export * from "./shared/common";
export * from "./shared/utilities";
export * from "./shared/entities";
export * from "./shared/order";
export type { SubscriptionBillingCycleSchema as SubscriptionBillingCycle } from "./shared/primitives";
export { loginRequestSchema, signupRequestSchema, passwordResetRequestSchema, passwordResetSchema, setPasswordRequestSchema, changePasswordRequestSchema, linkWhmcsRequestSchema, validateSignupRequestSchema, accountStatusRequestSchema, ssoLinkRequestSchema, checkPasswordNeededRequestSchema, refreshTokenRequestSchema, updateProfileRequestSchema, updateAddressRequestSchema, createOrderRequestSchema, orderConfigurationsSchema, simTopupRequestSchema, simCancelRequestSchema, simChangePlanRequestSchema, simFeaturesRequestSchema, contactRequestSchema, invoiceItemSchema, invoiceSchema, invoiceListSchema, invoiceListQuerySchema, paginationQuerySchema, subscriptionQuerySchema, invoicePaymentLinkSchema, sfOrderIdParamSchema, type LoginRequestInput, type SignupRequestInput, type PasswordResetRequestInput, type PasswordResetInput, type SetPasswordRequestInput, type ChangePasswordRequestInput, type LinkWhmcsRequestInput, type ValidateSignupRequestInput, type AccountStatusRequestInput, type SsoLinkRequestInput, type CheckPasswordNeededRequestInput, type RefreshTokenRequestInput, type UpdateProfileRequest, type UpdateAddressRequest, type CreateOrderRequest, type OrderConfigurations, type SimTopupRequest, type SimCancelRequest, type SimChangePlanRequest, type SimFeaturesRequest, type ContactRequest, type InvoiceListQuery, type PaginationQuery, type SubscriptionQuery, type InvoicePaymentLinkInput, type SfOrderIdParam, } from "./api/requests";
export { authResponseSchema, type AuthResponse, type AuthTokensSchema } from "./api/responses";
export { loginFormSchema, signupFormSchema, passwordResetRequestFormSchema, passwordResetFormSchema, setPasswordFormSchema, linkWhmcsFormSchema, type LoginFormData, type SignupFormData, type PasswordResetRequestFormData, type PasswordResetFormData, type SetPasswordFormData, type LinkWhmcsFormData, loginFormToRequest, signupFormToRequest, passwordResetFormToRequest, setPasswordFormToRequest, type LinkWhmcsRequestData, } from "./forms/auth";
export { profileEditFormSchema, profileDisplaySchema, addressFormSchema, contactFormSchema, type ProfileEditFormData, type ProfileDisplayData, type AddressFormData, type ContactFormData, profileFormToRequest, addressFormToRequest, contactFormToRequest, } from "./forms/profile";
export { simTypeEnum, activationTypeEnum, mnpGenderEnum, mnpDataSchema, simConfigureFormSchema, simConfigureFormToRequest, type SimType, type ActivationType, type MnpGender, type MnpData, type SimConfigureFormData, } from "./forms/sim-configure";
export { orderBusinessValidationSchema, skuValidationSchema, userMappingValidationSchema, paymentMethodValidationSchema, type OrderBusinessValidation, type SkuValidation, type UserMappingValidation, type PaymentMethodValidation, } from "./business";
export { z, parseOrThrow, safeParse } from "./shared/utilities";

View File

@ -1,176 +0,0 @@
/**
* Validation Module - Unified Architecture
*
* Clean, organized validation system with clear separation of concerns:
*
* 1. shared/* - Modular validation primitives and patterns
* 2. api/requests.ts - Backend API request schemas
* 3. forms/* - Frontend form schemas (extend API schemas with UI fields)
* 4. business/* - Business logic validation rules
*/
// =====================================================
// CORE VALIDATION SYSTEM
// =====================================================
// Shared validation modules (modular architecture) - MUST BE FIRST
export * from "./shared/primitives";
export * from "./shared/identifiers";
export * from "./shared/common";
export * from "./shared/utilities";
export * from "./shared/entities";
export * from "./shared/order";
// Export specific billing cycle types for convenience
export type { SubscriptionBillingCycleSchema as SubscriptionBillingCycle } from "./shared/primitives";
// API request schemas (backend) - explicit exports for better tree shaking
export {
// Auth API schemas
loginRequestSchema,
signupRequestSchema,
passwordResetRequestSchema,
passwordResetSchema,
setPasswordRequestSchema,
changePasswordRequestSchema,
linkWhmcsRequestSchema,
validateSignupRequestSchema,
accountStatusRequestSchema,
ssoLinkRequestSchema,
checkPasswordNeededRequestSchema,
refreshTokenRequestSchema,
// User management API schemas
updateProfileRequestSchema,
updateAddressRequestSchema,
// Order API schemas
createOrderRequestSchema,
orderConfigurationsSchema,
// Subscription API schemas
simTopupRequestSchema,
simCancelRequestSchema,
simChangePlanRequestSchema,
simFeaturesRequestSchema,
// Contact & billing schemas
contactRequestSchema,
invoiceItemSchema,
invoiceSchema,
invoiceListSchema,
invoiceListQuerySchema,
paginationQuerySchema,
subscriptionQuerySchema,
invoicePaymentLinkSchema,
sfOrderIdParamSchema,
// API types
type LoginRequestInput,
type SignupRequestInput,
type PasswordResetRequestInput,
type PasswordResetInput,
type SetPasswordRequestInput,
type ChangePasswordRequestInput,
type LinkWhmcsRequestInput,
type ValidateSignupRequestInput,
type AccountStatusRequestInput,
type SsoLinkRequestInput,
type CheckPasswordNeededRequestInput,
type RefreshTokenRequestInput,
type UpdateProfileRequest,
type UpdateAddressRequest,
type CreateOrderRequest,
type OrderConfigurations,
type SimTopupRequest,
type SimCancelRequest,
type SimChangePlanRequest,
type SimFeaturesRequest,
type ContactRequest,
type InvoiceListQuery,
type PaginationQuery,
type SubscriptionQuery,
type InvoicePaymentLinkInput,
type SfOrderIdParam,
} from "./api/requests";
// Form schemas (frontend) - explicit exports for better tree shaking
export { authResponseSchema, type AuthResponse, type AuthTokensSchema } from "./api/responses";
export {
// Auth form schemas
loginFormSchema,
signupFormSchema,
passwordResetRequestFormSchema,
passwordResetFormSchema,
setPasswordFormSchema,
linkWhmcsFormSchema,
// Auth form types
type LoginFormData,
type SignupFormData,
type PasswordResetRequestFormData,
type PasswordResetFormData,
type SetPasswordFormData,
type LinkWhmcsFormData,
// Auth transformations
loginFormToRequest,
signupFormToRequest,
passwordResetFormToRequest,
setPasswordFormToRequest,
// Auth API type aliases
type LinkWhmcsRequestData,
} from "./forms/auth";
export {
// Profile form schemas
profileEditFormSchema,
profileDisplaySchema,
addressFormSchema,
contactFormSchema,
// Profile form types
type ProfileEditFormData,
type ProfileDisplayData,
type AddressFormData,
type ContactFormData,
// Profile transformations
profileFormToRequest,
addressFormToRequest,
contactFormToRequest,
} from "./forms/profile";
// SIM configuration forms
export {
simTypeEnum,
activationTypeEnum,
mnpGenderEnum,
mnpDataSchema,
simConfigureFormSchema,
simConfigureFormToRequest,
type SimType,
type ActivationType,
type MnpGender,
type MnpData,
type SimConfigureFormData,
} from "./forms/sim-configure";
// Business validation schemas - use schema.safeParse() directly
export {
orderBusinessValidationSchema,
skuValidationSchema,
userMappingValidationSchema,
paymentMethodValidationSchema,
type OrderBusinessValidation,
type SkuValidation,
type UserMappingValidation,
type PaymentMethodValidation,
} from "./business";
// Order validation schemas and types are already exported above via "./shared/order"
// Simple validation utilities (direct Zod usage)
export { z, parseOrThrow, safeParse } from "./shared/utilities";

View File

@ -1,250 +1 @@
import { z } from "zod";
export declare const paymentMethodTypeSchema: any;
export declare const orderStatusSchema: z.ZodEnum<{
Pending: "Pending";
Cancelled: "Cancelled";
Active: "Active";
Fraud: "Fraud";
}>;
export declare const invoiceStatusSchema: z.ZodEnum<{
Draft: "Draft";
Pending: "Pending";
Paid: "Paid";
Unpaid: "Unpaid";
Overdue: "Overdue";
Cancelled: "Cancelled";
Refunded: "Refunded";
Collections: "Collections";
}>;
export declare const subscriptionStatusSchema: any;
export declare const caseStatusSchema: z.ZodEnum<{
New: "New";
Working: "Working";
Escalated: "Escalated";
Closed: "Closed";
}>;
export declare const casePrioritySchema: z.ZodEnum<{
Low: "Low";
Medium: "Medium";
High: "High";
Critical: "Critical";
}>;
export declare const paymentStatusSchema: z.ZodEnum<{
pending: "pending";
completed: "completed";
failed: "failed";
processing: "processing";
cancelled: "cancelled";
refunded: "refunded";
}>;
export declare const caseTypeSchema: z.ZodEnum<{
Question: "Question";
Problem: "Problem";
"Feature Request": "Feature Request";
}>;
export declare const subscriptionCycleSchema: any;
export declare const userSchema: z.ZodObject<{
id: z.ZodString;
createdAt: z.ZodString;
updatedAt: z.ZodString;
email: z.ZodString;
firstName: z.ZodOptional<z.ZodString>;
lastName: z.ZodOptional<z.ZodString>;
company: z.ZodOptional<z.ZodString>;
phone: z.ZodOptional<z.ZodString>;
address: z.ZodOptional<z.ZodObject<{
street: z.ZodNullable<z.ZodString>;
streetLine2: z.ZodNullable<z.ZodString>;
city: z.ZodNullable<z.ZodString>;
state: z.ZodNullable<z.ZodString>;
postalCode: z.ZodNullable<z.ZodString>;
country: z.ZodNullable<z.ZodString>;
}, z.core.$strip>>;
mfaEnabled: z.ZodBoolean;
emailVerified: z.ZodBoolean;
}, z.core.$strip>;
export declare const userProfileSchema: z.ZodObject<{
id: z.ZodString;
createdAt: z.ZodString;
updatedAt: z.ZodString;
email: z.ZodString;
firstName: z.ZodOptional<z.ZodString>;
lastName: z.ZodOptional<z.ZodString>;
company: z.ZodOptional<z.ZodString>;
phone: z.ZodOptional<z.ZodString>;
address: z.ZodOptional<z.ZodObject<{
street: z.ZodNullable<z.ZodString>;
streetLine2: z.ZodNullable<z.ZodString>;
city: z.ZodNullable<z.ZodString>;
state: z.ZodNullable<z.ZodString>;
postalCode: z.ZodNullable<z.ZodString>;
country: z.ZodNullable<z.ZodString>;
}, z.core.$strip>>;
mfaEnabled: z.ZodBoolean;
emailVerified: z.ZodBoolean;
avatar: z.ZodOptional<z.ZodString>;
preferences: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
lastLoginAt: z.ZodOptional<z.ZodString>;
role: z.ZodEnum<{
USER: "USER";
ADMIN: "ADMIN";
}>;
}, z.core.$strip>;
export declare const prismaUserProfileSchema: z.ZodObject<{
id: z.ZodString;
email: z.ZodString;
firstName: z.ZodNullable<z.ZodString>;
lastName: z.ZodNullable<z.ZodString>;
company: z.ZodNullable<z.ZodString>;
phone: z.ZodNullable<z.ZodString>;
mfaSecret: z.ZodNullable<z.ZodString>;
emailVerified: z.ZodBoolean;
createdAt: z.ZodDate;
updatedAt: z.ZodDate;
lastLoginAt: z.ZodNullable<z.ZodDate>;
}, z.core.$strip>;
export declare const mnpDetailsSchema: z.ZodObject<{
currentProvider: z.ZodString;
phoneNumber: z.ZodString;
accountNumber: z.ZodOptional<z.ZodString>;
pin: z.ZodOptional<z.ZodString>;
}, z.core.$strip>;
export declare const orderTotalsSchema: z.ZodObject<{
monthlyTotal: z.ZodNumber;
oneTimeTotal: z.ZodNumber;
}, z.core.$strip>;
export declare const whmcsOrderItemSchema: z.ZodObject<{
productId: z.ZodNumber;
productName: z.ZodString;
domain: z.ZodOptional<z.ZodString>;
cycle: z.ZodString;
quantity: z.ZodNumber;
price: z.ZodNumber;
setup: z.ZodOptional<z.ZodNumber>;
configOptions: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
}, z.core.$strip>;
export declare const whmcsOrderSchema: z.ZodObject<{
id: z.ZodNumber;
orderNumber: z.ZodString;
status: z.ZodEnum<{
Pending: "Pending";
Cancelled: "Cancelled";
Active: "Active";
Fraud: "Fraud";
}>;
date: z.ZodString;
amount: z.ZodNumber;
currency: z.ZodString;
paymentMethod: z.ZodOptional<z.ZodString>;
items: z.ZodArray<z.ZodObject<{
productId: z.ZodNumber;
productName: z.ZodString;
domain: z.ZodOptional<z.ZodString>;
cycle: z.ZodString;
quantity: z.ZodNumber;
price: z.ZodNumber;
setup: z.ZodOptional<z.ZodNumber>;
configOptions: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
}, z.core.$strip>>;
invoiceId: z.ZodOptional<z.ZodNumber>;
}, z.core.$strip>;
export declare const invoiceItemSchema: any;
export declare const invoiceSchema: any;
export declare const invoiceListSchema: any;
export declare const subscriptionSchema: any;
export declare const subscriptionListSchema: any;
export declare const paymentMethodSchema: any;
export declare const paymentGatewaySchema: any;
export declare const paymentSchema: z.ZodObject<{
id: z.core.$ZodBranded<z.ZodString, "PaymentId">;
userId: z.core.$ZodBranded<z.ZodString, "UserId">;
invoiceId: z.ZodOptional<z.core.$ZodBranded<z.ZodString, "InvoiceId">>;
subscriptionId: z.ZodOptional<z.core.$ZodBranded<z.ZodString, "SubscriptionId">>;
amount: z.ZodNumber;
currency: z.ZodOptional<z.ZodString>;
status: z.ZodEnum<{
pending: "pending";
completed: "completed";
failed: "failed";
processing: "processing";
cancelled: "cancelled";
refunded: "refunded";
}>;
transactionId: z.ZodOptional<z.ZodString>;
failureReason: z.ZodOptional<z.ZodString>;
processedAt: z.ZodOptional<z.ZodString>;
createdAt: z.ZodString;
updatedAt: z.ZodString;
}, z.core.$strip>;
export declare const caseCommentSchema: z.ZodObject<{
id: z.ZodString;
body: z.ZodString;
isPublic: z.ZodBoolean;
createdDate: z.ZodString;
createdBy: z.ZodObject<{
id: z.ZodString;
name: z.ZodString;
type: z.ZodEnum<{
user: "user";
customer: "customer";
}>;
}, z.core.$strip>;
}, z.core.$strip>;
export declare const supportCaseSchema: z.ZodObject<{
id: z.ZodString;
createdDate: z.ZodString;
lastModifiedDate: z.ZodString;
number: z.ZodString;
subject: z.ZodString;
description: z.ZodOptional<z.ZodString>;
status: z.ZodEnum<{
New: "New";
Working: "Working";
Escalated: "Escalated";
Closed: "Closed";
}>;
priority: z.ZodEnum<{
Low: "Low";
Medium: "Medium";
High: "High";
Critical: "Critical";
}>;
type: z.ZodEnum<{
Question: "Question";
Problem: "Problem";
"Feature Request": "Feature Request";
}>;
closedDate: z.ZodOptional<z.ZodString>;
contactId: z.ZodOptional<z.ZodString>;
accountId: z.ZodOptional<z.ZodString>;
ownerId: z.ZodOptional<z.ZodString>;
ownerName: z.ZodOptional<z.ZodString>;
comments: z.ZodOptional<z.ZodArray<z.ZodObject<{
id: z.ZodString;
body: z.ZodString;
isPublic: z.ZodBoolean;
createdDate: z.ZodString;
createdBy: z.ZodObject<{
id: z.ZodString;
name: z.ZodString;
type: z.ZodEnum<{
user: "user";
customer: "customer";
}>;
}, z.core.$strip>;
}, z.core.$strip>>>;
}, z.core.$strip>;
export type UserSchema = z.infer<typeof userSchema>;
export type UserProfileSchema = z.infer<typeof userProfileSchema>;
export type MnpDetailsSchema = z.infer<typeof mnpDetailsSchema>;
export type OrderTotalsSchema = z.infer<typeof orderTotalsSchema>;
export type WhmcsOrderItemSchema = z.infer<typeof whmcsOrderItemSchema>;
export type WhmcsOrderSchema = z.infer<typeof whmcsOrderSchema>;
export type InvoiceItemSchema = z.infer<typeof invoiceItemSchema>;
export type InvoiceSchema = z.infer<typeof invoiceSchema>;
export type InvoiceListSchema = z.infer<typeof invoiceListSchema>;
export type SubscriptionSchema = z.infer<typeof subscriptionSchema>;
export type PaymentMethodSchema = z.infer<typeof paymentMethodSchema>;
export type PaymentSchema = z.infer<typeof paymentSchema>;
export type CaseCommentSchema = z.infer<typeof caseCommentSchema>;
export type SupportCaseSchema = z.infer<typeof supportCaseSchema>;
export {};

View File

@ -1,141 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.supportCaseSchema = exports.caseCommentSchema = exports.paymentSchema = exports.paymentGatewaySchema = exports.paymentMethodSchema = exports.subscriptionListSchema = exports.subscriptionSchema = exports.invoiceListSchema = exports.invoiceSchema = exports.invoiceItemSchema = exports.whmcsOrderSchema = exports.whmcsOrderItemSchema = exports.orderTotalsSchema = exports.mnpDetailsSchema = exports.prismaUserProfileSchema = exports.userProfileSchema = exports.userSchema = exports.subscriptionCycleSchema = exports.caseTypeSchema = exports.paymentStatusSchema = exports.casePrioritySchema = exports.caseStatusSchema = exports.subscriptionStatusSchema = exports.invoiceStatusSchema = exports.orderStatusSchema = exports.paymentMethodTypeSchema = void 0;
const zod_1 = require("zod");
const invoice_schema_1 = require("@customer-portal/schemas/billing/invoice.schema");
const subscription_schema_1 = require("@customer-portal/schemas/subscriptions/subscription.schema");
const payment_schema_1 = require("@customer-portal/schemas/payments/payment.schema");
const primitives_1 = require("./primitives");
const identifiers_1 = require("./identifiers");
const common_1 = require("./common");
const status_1 = require("../../enums/status");
const tupleFromEnum = (enumObject) => {
const values = Object.values(enumObject);
if (values.length === 0) {
throw new Error("Enum must have at least one value");
}
return [...new Set(values)];
};
const addressRecordSchema = zod_1.z.object({
street: zod_1.z.string().nullable(),
streetLine2: zod_1.z.string().nullable(),
city: zod_1.z.string().nullable(),
state: zod_1.z.string().nullable(),
postalCode: zod_1.z.string().nullable(),
country: zod_1.z.string().nullable(),
});
exports.paymentMethodTypeSchema = payment_schema_1.paymentMethodTypeSchema;
exports.orderStatusSchema = zod_1.z.enum(["Pending", "Active", "Cancelled", "Fraud"]);
exports.invoiceStatusSchema = zod_1.z.enum(tupleFromEnum(status_1.INVOICE_STATUS));
exports.subscriptionStatusSchema = subscription_schema_1.subscriptionStatusSchema;
exports.caseStatusSchema = zod_1.z.enum(tupleFromEnum(status_1.CASE_STATUS));
exports.casePrioritySchema = zod_1.z.enum(tupleFromEnum(status_1.CASE_PRIORITY));
exports.paymentStatusSchema = zod_1.z.enum(tupleFromEnum(status_1.PAYMENT_STATUS));
exports.caseTypeSchema = zod_1.z.enum(["Question", "Problem", "Feature Request"]);
exports.subscriptionCycleSchema = subscription_schema_1.subscriptionCycleSchema;
exports.userSchema = common_1.baseEntitySchema.extend({
email: primitives_1.emailSchema,
firstName: primitives_1.nameSchema.optional(),
lastName: primitives_1.nameSchema.optional(),
company: zod_1.z.string().optional(),
phone: primitives_1.phoneSchema.optional(),
address: addressRecordSchema.optional(),
mfaEnabled: zod_1.z.boolean(),
emailVerified: zod_1.z.boolean(),
});
exports.userProfileSchema = exports.userSchema.extend({
avatar: zod_1.z.string().optional(),
preferences: zod_1.z.record(zod_1.z.string(), zod_1.z.unknown()).optional(),
lastLoginAt: primitives_1.timestampSchema.optional(),
role: zod_1.z.enum(["USER", "ADMIN"]),
});
exports.prismaUserProfileSchema = zod_1.z.object({
id: zod_1.z.string().uuid(),
email: primitives_1.emailSchema,
firstName: zod_1.z.string().nullable(),
lastName: zod_1.z.string().nullable(),
company: zod_1.z.string().nullable(),
phone: zod_1.z.string().nullable(),
mfaSecret: zod_1.z.string().nullable(),
emailVerified: zod_1.z.boolean(),
createdAt: zod_1.z.date(),
updatedAt: zod_1.z.date(),
lastLoginAt: zod_1.z.date().nullable(),
});
exports.mnpDetailsSchema = zod_1.z.object({
currentProvider: zod_1.z.string().min(1, "Current provider is required"),
phoneNumber: primitives_1.phoneSchema,
accountNumber: zod_1.z.string().min(1, "Account number is required").optional(),
pin: zod_1.z.string().min(1, "PIN is required").optional(),
});
exports.orderTotalsSchema = zod_1.z.object({
monthlyTotal: zod_1.z.number().nonnegative("Monthly total must be non-negative"),
oneTimeTotal: zod_1.z.number().nonnegative("One-time total must be non-negative"),
});
exports.whmcsOrderItemSchema = zod_1.z.object({
productId: zod_1.z.number().int().positive("Product id must be positive"),
productName: zod_1.z.string().min(1, "Product name is required"),
domain: zod_1.z.string().optional(),
cycle: zod_1.z.string().min(1, "Billing cycle is required"),
quantity: zod_1.z.number().int().positive("Quantity must be positive"),
price: zod_1.z.number(),
setup: zod_1.z.number().optional(),
configOptions: zod_1.z.record(zod_1.z.string(), zod_1.z.string()).optional(),
});
exports.whmcsOrderSchema = common_1.whmcsEntitySchema.extend({
orderNumber: zod_1.z.string().min(1, "Order number is required"),
status: exports.orderStatusSchema,
date: zod_1.z.string().min(1, "Order date is required"),
amount: zod_1.z.number(),
currency: zod_1.z.string().min(1, "Currency is required"),
paymentMethod: zod_1.z.string().optional(),
items: zod_1.z.array(exports.whmcsOrderItemSchema),
invoiceId: zod_1.z.number().int().positive().optional(),
});
exports.invoiceItemSchema = invoice_schema_1.invoiceItemSchema;
exports.invoiceSchema = invoice_schema_1.invoiceSchema;
exports.invoiceListSchema = invoice_schema_1.invoiceListSchema;
exports.subscriptionSchema = subscription_schema_1.subscriptionSchema;
exports.subscriptionListSchema = subscription_schema_1.subscriptionListSchema;
exports.paymentMethodSchema = payment_schema_1.paymentMethodSchema;
exports.paymentGatewaySchema = payment_schema_1.paymentGatewaySchema;
exports.paymentSchema = zod_1.z.object({
id: identifiers_1.paymentIdSchema,
userId: identifiers_1.userIdSchema,
invoiceId: identifiers_1.invoiceIdSchema.optional(),
subscriptionId: identifiers_1.subscriptionIdSchema.optional(),
amount: primitives_1.moneyAmountSchema,
currency: zod_1.z.string().length(3, "Currency must be a 3-letter ISO code").optional(),
status: exports.paymentStatusSchema,
transactionId: zod_1.z.string().optional(),
failureReason: zod_1.z.string().optional(),
processedAt: primitives_1.timestampSchema.optional(),
createdAt: primitives_1.timestampSchema,
updatedAt: primitives_1.timestampSchema,
});
exports.caseCommentSchema = zod_1.z.object({
id: zod_1.z.string().min(1, "Comment id is required"),
body: zod_1.z.string().min(1, "Comment body is required"),
isPublic: zod_1.z.boolean(),
createdDate: primitives_1.timestampSchema,
createdBy: zod_1.z.object({
id: zod_1.z.string().min(1, "Created by id is required"),
name: zod_1.z.string().min(1, "Created by name is required"),
type: zod_1.z.enum(["user", "customer"]),
}),
});
exports.supportCaseSchema = common_1.salesforceEntitySchema.extend({
number: zod_1.z.string().min(1, "Case number is required"),
subject: zod_1.z.string().min(1, "Subject is required"),
description: zod_1.z.string().optional(),
status: exports.caseStatusSchema,
priority: exports.casePrioritySchema,
type: exports.caseTypeSchema,
closedDate: primitives_1.timestampSchema.optional(),
contactId: zod_1.z.string().optional(),
accountId: zod_1.z.string().optional(),
ownerId: zod_1.z.string().optional(),
ownerName: zod_1.z.string().optional(),
comments: zod_1.z.array(exports.caseCommentSchema).optional(),
});
//# sourceMappingURL=entities.js.map

File diff suppressed because one or more lines are too long

View File

@ -1,240 +0,0 @@
/**
* Entity Schemas
* Business entity validation schemas aligned with documented domain entities.
*/
import { z } from "zod";
import {
invoiceItemSchema as billingInvoiceItemSchema,
invoiceListSchema as billingInvoiceListSchema,
invoiceSchema as billingInvoiceSchema,
} from "@customer-portal/schemas/billing/invoice.schema";
import {
subscriptionCycleSchema as sharedSubscriptionCycleSchema,
subscriptionListSchema as sharedSubscriptionListSchema,
subscriptionSchema as sharedSubscriptionSchema,
subscriptionStatusSchema as sharedSubscriptionStatusSchema,
} from "@customer-portal/schemas/subscriptions/subscription.schema";
import {
paymentGatewaySchema as sharedPaymentGatewaySchema,
paymentMethodSchema as sharedPaymentMethodSchema,
paymentMethodTypeSchema as sharedPaymentMethodTypeSchema,
} from "@customer-portal/schemas/payments/payment.schema";
import {
emailSchema,
nameSchema,
phoneSchema,
moneyAmountSchema,
timestampSchema,
} from "./primitives";
import {
userIdSchema,
invoiceIdSchema,
subscriptionIdSchema,
paymentIdSchema,
} from "./identifiers";
import { baseEntitySchema, whmcsEntitySchema, salesforceEntitySchema } from "./common";
import {
INVOICE_STATUS,
SUBSCRIPTION_STATUS,
CASE_STATUS,
CASE_PRIORITY,
PAYMENT_STATUS,
} from "../../enums/status";
// =====================================================
// HELPERS
// =====================================================
const tupleFromEnum = <T extends Record<string, string>>(enumObject: T) => {
const values = Object.values(enumObject);
if (values.length === 0) {
throw new Error("Enum must have at least one value");
}
return [...new Set(values)] as [T[keyof T], ...Array<T[keyof T]>];
};
const addressRecordSchema = z.object({
street: z.string().nullable(),
streetLine2: z.string().nullable(),
city: z.string().nullable(),
state: z.string().nullable(),
postalCode: z.string().nullable(),
country: z.string().nullable(),
});
export const paymentMethodTypeSchema = sharedPaymentMethodTypeSchema;
export const orderStatusSchema = z.enum(["Pending", "Active", "Cancelled", "Fraud"]);
export const invoiceStatusSchema = z.enum(tupleFromEnum(INVOICE_STATUS));
export const subscriptionStatusSchema = sharedSubscriptionStatusSchema;
export const caseStatusSchema = z.enum(tupleFromEnum(CASE_STATUS));
export const casePrioritySchema = z.enum(tupleFromEnum(CASE_PRIORITY));
export const paymentStatusSchema = z.enum(tupleFromEnum(PAYMENT_STATUS));
export const caseTypeSchema = z.enum(["Question", "Problem", "Feature Request"]);
export const subscriptionCycleSchema = sharedSubscriptionCycleSchema;
// =====================================================
// USER ENTITIES
// =====================================================
export const userSchema = baseEntitySchema.extend({
email: emailSchema,
firstName: nameSchema.optional(),
lastName: nameSchema.optional(),
company: z.string().optional(),
phone: phoneSchema.optional(),
address: addressRecordSchema.optional(),
mfaEnabled: z.boolean(),
emailVerified: z.boolean(),
});
export const userProfileSchema = userSchema.extend({
avatar: z.string().optional(),
preferences: z.record(z.string(), z.unknown()).optional(),
lastLoginAt: timestampSchema.optional(),
role: z.enum(["USER", "ADMIN"]),
});
export const prismaUserProfileSchema = z.object({
id: z.string().uuid(),
email: emailSchema,
firstName: z.string().nullable(),
lastName: z.string().nullable(),
company: z.string().nullable(),
phone: z.string().nullable(),
mfaSecret: z.string().nullable(),
emailVerified: z.boolean(),
createdAt: z.date(),
updatedAt: z.date(),
lastLoginAt: z.date().nullable(),
});
export const mnpDetailsSchema = z.object({
currentProvider: z.string().min(1, "Current provider is required"),
phoneNumber: phoneSchema,
accountNumber: z.string().min(1, "Account number is required").optional(),
pin: z.string().min(1, "PIN is required").optional(),
});
// =====================================================
// ORDER ENTITIES (WHMCS)
// =====================================================
export const orderTotalsSchema = z.object({
monthlyTotal: z.number().nonnegative("Monthly total must be non-negative"),
oneTimeTotal: z.number().nonnegative("One-time total must be non-negative"),
});
export const whmcsOrderItemSchema = z.object({
productId: z.number().int().positive("Product id must be positive"),
productName: z.string().min(1, "Product name is required"),
domain: z.string().optional(),
cycle: z.string().min(1, "Billing cycle is required"),
quantity: z.number().int().positive("Quantity must be positive"),
price: z.number(),
setup: z.number().optional(),
configOptions: z.record(z.string(), z.string()).optional(),
});
export const whmcsOrderSchema = whmcsEntitySchema.extend({
orderNumber: z.string().min(1, "Order number is required"),
status: orderStatusSchema,
date: z.string().min(1, "Order date is required"),
amount: z.number(),
currency: z.string().min(1, "Currency is required"),
paymentMethod: z.string().optional(),
items: z.array(whmcsOrderItemSchema),
invoiceId: z.number().int().positive().optional(),
});
// =====================================================
// INVOICE ENTITIES (WHMCS)
// =====================================================
export const invoiceItemSchema = billingInvoiceItemSchema;
export const invoiceSchema = billingInvoiceSchema;
export const invoiceListSchema = billingInvoiceListSchema;
// =====================================================
// SUBSCRIPTION ENTITIES (WHMCS)
// =====================================================
export const subscriptionSchema = sharedSubscriptionSchema;
export const subscriptionListSchema = sharedSubscriptionListSchema;
// =====================================================
// PAYMENT ENTITIES (WHMCS & PORTAL)
// =====================================================
export const paymentMethodSchema = sharedPaymentMethodSchema;
export const paymentGatewaySchema = sharedPaymentGatewaySchema;
export const paymentSchema = z.object({
id: paymentIdSchema,
userId: userIdSchema,
invoiceId: invoiceIdSchema.optional(),
subscriptionId: subscriptionIdSchema.optional(),
amount: moneyAmountSchema,
currency: z.string().length(3, "Currency must be a 3-letter ISO code").optional(),
status: paymentStatusSchema,
transactionId: z.string().optional(),
failureReason: z.string().optional(),
processedAt: timestampSchema.optional(),
createdAt: timestampSchema,
updatedAt: timestampSchema,
});
// =====================================================
// SUPPORT CASE ENTITIES (SALESFORCE)
// =====================================================
export const caseCommentSchema = z.object({
id: z.string().min(1, "Comment id is required"),
body: z.string().min(1, "Comment body is required"),
isPublic: z.boolean(),
createdDate: timestampSchema,
createdBy: z.object({
id: z.string().min(1, "Created by id is required"),
name: z.string().min(1, "Created by name is required"),
type: z.enum(["user", "customer"]),
}),
});
export const supportCaseSchema = salesforceEntitySchema.extend({
number: z.string().min(1, "Case number is required"),
subject: z.string().min(1, "Subject is required"),
description: z.string().optional(),
status: caseStatusSchema,
priority: casePrioritySchema,
type: caseTypeSchema,
closedDate: timestampSchema.optional(),
contactId: z.string().optional(),
accountId: z.string().optional(),
ownerId: z.string().optional(),
ownerName: z.string().optional(),
comments: z.array(caseCommentSchema).optional(),
});
// =====================================================
// TYPE EXPORTS (Schema-backed)
// =====================================================
export type UserSchema = z.infer<typeof userSchema>;
export type UserProfileSchema = z.infer<typeof userProfileSchema>;
export type MnpDetailsSchema = z.infer<typeof mnpDetailsSchema>;
export type OrderTotalsSchema = z.infer<typeof orderTotalsSchema>;
export type WhmcsOrderItemSchema = z.infer<typeof whmcsOrderItemSchema>;
export type WhmcsOrderSchema = z.infer<typeof whmcsOrderSchema>;
export type InvoiceItemSchema = z.infer<typeof invoiceItemSchema>;
export type InvoiceSchema = z.infer<typeof invoiceSchema>;
export type InvoiceListSchema = z.infer<typeof invoiceListSchema>;
export type SubscriptionSchema = z.infer<typeof subscriptionSchema>;
export type PaymentMethodSchema = z.infer<typeof paymentMethodSchema>;
export type PaymentSchema = z.infer<typeof paymentSchema>;
export type CaseCommentSchema = z.infer<typeof caseCommentSchema>;
export type SupportCaseSchema = z.infer<typeof supportCaseSchema>;

View File

@ -44,26 +44,26 @@ export declare const priorityEnum: z.ZodEnum<{
}>;
export declare const categoryEnum: z.ZodEnum<{
billing: "billing";
technical: "technical";
account: "account";
technical: "technical";
general: "general";
}>;
export declare const billingCycleEnum: z.ZodEnum<{
Monthly: "Monthly";
Quarterly: "Quarterly";
Annually: "Annually";
Onetime: "Onetime";
Free: "Free";
Onetime: "Onetime";
}>;
export declare const subscriptionBillingCycleEnum: z.ZodEnum<{
"One-time": "One-time";
Monthly: "Monthly";
Quarterly: "Quarterly";
Annually: "Annually";
Free: "Free";
"Semi-Annually": "Semi-Annually";
Annually: "Annually";
Biennially: "Biennially";
Triennially: "Triennially";
"One-time": "One-time";
Free: "Free";
}>;
export type EmailSchema = z.infer<typeof emailSchema>;
export type PasswordSchema = z.infer<typeof passwordSchema>;

View File

@ -23,6 +23,7 @@
"orders/**/*",
"catalog/**/*",
"common/**/*",
"auth/**/*",
"toolkit/**/*",
"index.ts"
],

View File

@ -1,10 +1,14 @@
import type { FulfillmentOrderItem } from "@customer-portal/contracts/orders";
import type { FulfillmentOrderItem } from "@customer-portal/domain/orders";
import {
Providers,
type OrderItemSummary,
} from "@customer-portal/domain/orders";
import {
type WhmcsOrderItem,
type WhmcsAddOrderParams,
type WhmcsAddOrderPayload,
whmcsOrderItemSchema,
} from "@customer-portal/schemas/integrations/whmcs/order.schema";
} from "@customer-portal/domain/orders/providers/whmcs";
import { z } from "zod";
const fulfillmentOrderItemSchema = z.object({

View File

@ -1,12 +1,13 @@
import type { PaymentGateway, PaymentMethod } from "@customer-portal/contracts/payments";
import type { PaymentGateway, PaymentMethod } from "@customer-portal/domain/payments";
import {
paymentGatewaySchema,
paymentMethodSchema,
} from "@customer-portal/schemas/payments/payment.schema";
Providers,
} from "@customer-portal/domain/payments";
import {
whmcsPaymentGatewaySchema,
whmcsPaymentMethodSchema,
} from "@customer-portal/schemas/integrations/whmcs/payment.schema";
whmcsPaymentGatewayRawSchema,
whmcsPaymentMethodRawSchema,
} from "@customer-portal/domain/payments/providers/whmcs";
const paymentMethodTypeMap: Record<string, PaymentMethod["type"]> = {
creditcard: "CreditCard",
@ -40,7 +41,7 @@ function mapPaymentGatewayType(type?: string | null): PaymentGateway["type"] {
}
export function transformWhmcsPaymentMethod(raw: unknown): PaymentMethod {
const method = whmcsPaymentMethodSchema.parse(raw);
const method = whmcsPaymentMethodRawSchema.parse(raw);
const paymentMethod: PaymentMethod = {
id: method.id,
@ -69,7 +70,7 @@ export function transformWhmcsPaymentMethod(raw: unknown): PaymentMethod {
}
export function transformWhmcsPaymentGateway(raw: unknown): PaymentGateway {
const gateway = whmcsPaymentGatewaySchema.parse(raw);
const gateway = whmcsPaymentGatewayRawSchema.parse(raw);
const paymentGateway: PaymentGateway = {
name: gateway.name,

View File

@ -1,78 +0,0 @@
import { z } from "zod";
/**
* WHMCS Order Item Schema
* Represents a single product line item in a WHMCS order
*/
export const whmcsOrderItemSchema = z.object({
productId: z.string().min(1, "Product ID is required"), // WHMCS Product ID
billingCycle: z.enum(["monthly", "quarterly", "semiannually", "annually", "biennially", "triennially", "onetime", "free"]),
quantity: z.number().int().positive("Quantity must be positive").default(1),
configOptions: z.record(z.string(), z.string()).optional(),
customFields: z.record(z.string(), z.string()).optional(),
});
export type WhmcsOrderItem = z.infer<typeof whmcsOrderItemSchema>;
/**
* WHMCS AddOrder API Parameters Schema
* Based on official WHMCS API documentation
* https://developers.whmcs.com/api-reference/addorder/
*/
export const whmcsAddOrderParamsSchema = z.object({
clientId: z.number().int().positive("Client ID must be positive"),
items: z.array(whmcsOrderItemSchema).min(1, "At least one item is required"),
paymentMethod: z.string().min(1, "Payment method is required"), // Required by WHMCS API
promoCode: z.string().optional(),
notes: z.string().optional(),
sfOrderId: z.string().optional(), // For tracking back to Salesforce
noinvoice: z.boolean().optional(), // Don't create invoice during provisioning
noinvoiceemail: z.boolean().optional(), // Suppress invoice email (if invoice is created)
noemail: z.boolean().optional(), // Don't send any emails
});
export type WhmcsAddOrderParams = z.infer<typeof whmcsAddOrderParamsSchema>;
/**
* WHMCS AddOrder API Payload Schema
* The actual payload sent to WHMCS API with array format for items
*/
export const whmcsAddOrderPayloadSchema = z.object({
clientid: z.number().int().positive(),
paymentmethod: z.string().min(1),
promocode: z.string().optional(),
noinvoice: z.boolean().optional(),
noinvoiceemail: z.boolean().optional(),
noemail: z.boolean().optional(),
pid: z.array(z.string()).min(1),
billingcycle: z.array(z.string()).min(1),
qty: z.array(z.number().int().positive()).min(1),
configoptions: z.array(z.string()).optional(), // base64 encoded serialized arrays
customfields: z.array(z.string()).optional(), // base64 encoded serialized arrays
});
export type WhmcsAddOrderPayload = z.infer<typeof whmcsAddOrderPayloadSchema>;
/**
* WHMCS Order Result Schema
*/
export const whmcsOrderResultSchema = z.object({
orderId: z.number().int().positive(),
invoiceId: z.number().int().positive().optional(),
serviceIds: z.array(z.number().int().positive()).default([]),
});
export type WhmcsOrderResult = z.infer<typeof whmcsOrderResultSchema>;
/**
* WHMCS AcceptOrder API Response Schema
*/
export const whmcsAcceptOrderResponseSchema = z.object({
result: z.string(),
orderid: z.number().int().positive(),
invoiceid: z.number().int().positive().optional(),
productids: z.string().optional(), // Comma-separated service IDs
});
export type WhmcsAcceptOrderResponse = z.infer<typeof whmcsAcceptOrderResponseSchema>;

View File

@ -1,29 +0,0 @@
import { z } from "zod";
export const whmcsPaymentMethodSchema = z.object({
id: z.number(),
type: z.string(),
description: z.string().optional(),
gateway_name: z.string().optional(),
contact_type: z.string().optional(),
contact_id: z.number().optional(),
card_last_four: z.string().optional(),
expiry_date: z.string().optional(),
start_date: z.string().optional(),
issue_number: z.string().optional(),
card_type: z.string().optional(),
remote_token: z.string().optional(),
last_updated: z.string().optional(),
bank_name: z.string().optional(),
is_default: z.boolean().optional(),
});
export const whmcsPaymentGatewaySchema = z.object({
name: z.string(),
display_name: z.string().optional(),
type: z.string().optional(),
active: z.boolean().optional(),
});
export type WhmcsPaymentMethodSchema = typeof whmcsPaymentMethodSchema;
export type WhmcsPaymentGatewaySchema = typeof whmcsPaymentGatewaySchema;