Refactor ESLint configuration and TypeScript paths to remove deprecated contracts and schemas packages, enhancing type safety and maintainability. Update import paths across various modules to align with the new domain structure, ensuring consistent data handling. Introduce new request and response schemas in the authentication domain, improving validation and organization. Streamline Freebit and WHMCS integration services by utilizing updated provider methods and removing obsolete types.
This commit is contained in:
parent
c206598615
commit
a3367c56d5
@ -20,6 +20,8 @@ import {
|
||||
validateSignupRequestSchema,
|
||||
linkWhmcsRequestSchema,
|
||||
setPasswordRequestSchema,
|
||||
updateProfileRequestSchema,
|
||||
updateAddressRequestSchema,
|
||||
changePasswordRequestSchema,
|
||||
} from "@customer-portal/domain/auth";
|
||||
|
||||
|
||||
@ -35,7 +35,6 @@ import {
|
||||
refreshTokenRequestSchema,
|
||||
type SignupRequest,
|
||||
type PasswordResetRequest,
|
||||
type PasswordReset,
|
||||
type SetPasswordRequest,
|
||||
type LinkWhmcsRequest,
|
||||
type ChangePasswordRequest,
|
||||
@ -252,12 +251,15 @@ export class AuthController {
|
||||
|
||||
@Public()
|
||||
@Post("reset-password")
|
||||
@Throttle({ default: { limit: 5, ttl: 900000 } }) // 5 attempts per 15 minutes (standard for password operations)
|
||||
@HttpCode(200)
|
||||
@UsePipes(new ZodValidationPipe(passwordResetSchema))
|
||||
async resetPassword(@Body() body: PasswordResetInput, @Res({ passthrough: true }) res: Response) {
|
||||
const result = await this.authFacade.resetPassword(body.token, body.password);
|
||||
this.setAuthCookies(res, result.tokens);
|
||||
return result;
|
||||
async resetPassword(@Body() body: PasswordResetRequest, @Res({ passthrough: true }) res: Response) {
|
||||
await this.authFacade.resetPassword(body.token, body.password);
|
||||
|
||||
// Clear auth cookies after password reset to force re-login
|
||||
res.clearCookie("access_token", { httpOnly: true, sameSite: "lax" });
|
||||
res.clearCookie("refresh_token", { httpOnly: true, sameSite: "lax" });
|
||||
return { message: "Password reset successful" };
|
||||
}
|
||||
|
||||
@Post("change-password")
|
||||
|
||||
@ -15,7 +15,7 @@ import {
|
||||
updateAddressRequestSchema,
|
||||
type UpdateProfileRequest,
|
||||
type UpdateAddressRequest,
|
||||
} from "@customer-portal/domain/billing";
|
||||
} from "@customer-portal/domain/auth";
|
||||
import type { RequestWithUser } from "@bff/modules/auth/auth.types";
|
||||
|
||||
@Controller("me")
|
||||
@ -36,7 +36,7 @@ export class UsersController {
|
||||
@Patch()
|
||||
@UsePipes(new ZodValidationPipe(updateProfileRequestSchema))
|
||||
async updateProfile(@Req() req: RequestWithUser, @Body() updateData: UpdateProfileRequest) {
|
||||
return this.usersService.update(req.user.id, updateData);
|
||||
return this.usersService.updateProfile(req.user.id, updateData);
|
||||
}
|
||||
|
||||
@Get("address")
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { getErrorMessage } from "@bff/core/utils/error.util";
|
||||
import { normalizeAndValidateEmail, validateUuidV4OrThrow } from "@bff/core/utils/validation.util";
|
||||
import type { UpdateAddressRequest } from "@customer-portal/domain/auth";
|
||||
import type { UpdateAddressRequest, UpdateProfileRequest } 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";
|
||||
@ -9,6 +9,9 @@ import {
|
||||
Activity,
|
||||
Address,
|
||||
type AuthenticatedUser,
|
||||
updateProfileRequestSchema,
|
||||
updateAddressRequestSchema,
|
||||
type UpdateProfileRequest,
|
||||
} from "@customer-portal/domain/auth";
|
||||
import type { Subscription } from "@customer-portal/domain/subscriptions";
|
||||
import type { Invoice } from "@customer-portal/domain/billing";
|
||||
@ -238,6 +241,34 @@ export class UsersService {
|
||||
}
|
||||
}
|
||||
|
||||
async updateProfile(id: string, profile: UpdateProfileRequest): Promise<UserProfile> {
|
||||
const validId = this.validateUserId(id);
|
||||
const parsed = updateProfileRequestSchema.parse(profile);
|
||||
|
||||
try {
|
||||
const updatedUser = await this.prisma.user.update({
|
||||
where: { id: validId },
|
||||
data: {
|
||||
firstName: parsed.firstName ?? null,
|
||||
lastName: parsed.lastName ?? null,
|
||||
company: parsed.company ?? null,
|
||||
phone: parsed.phone ?? null,
|
||||
avatar: parsed.avatar ?? null,
|
||||
nationality: parsed.nationality ?? null,
|
||||
dateOfBirth: parsed.dateOfBirth ?? null,
|
||||
gender: parsed.gender ?? null,
|
||||
},
|
||||
});
|
||||
|
||||
return mapPrismaUserToUserProfile(updatedUser);
|
||||
} catch (error) {
|
||||
this.logger.error("Failed to update user profile", {
|
||||
error: getErrorMessage(error),
|
||||
});
|
||||
throw new Error("Failed to update profile");
|
||||
}
|
||||
}
|
||||
|
||||
private sanitizeUserData(userData: UserUpdateData): Partial<PrismaUser> {
|
||||
const sanitized: Partial<PrismaUser> = {};
|
||||
if (userData.firstName !== undefined)
|
||||
@ -483,13 +514,14 @@ export class UsersService {
|
||||
* Update address in WHMCS (authoritative for client record address fields)
|
||||
*/
|
||||
async updateAddress(userId: string, address: UpdateAddressRequest): Promise<void> {
|
||||
const parsed = updateAddressRequestSchema.parse(address);
|
||||
try {
|
||||
const mapping = await this.mappingsService.findByUserId(userId);
|
||||
if (!mapping) {
|
||||
throw new NotFoundException("User mapping not found");
|
||||
}
|
||||
|
||||
await this.whmcsService.updateClientAddress(mapping.whmcsClientId, address);
|
||||
await this.whmcsService.updateClientAddress(mapping.whmcsClientId, parsed);
|
||||
this.logger.log({ userId }, "Successfully updated address in WHMCS");
|
||||
} catch (error) {
|
||||
const msg = getErrorMessage(error);
|
||||
|
||||
@ -14,10 +14,6 @@
|
||||
"@bff/integrations/*": ["src/integrations/*"],
|
||||
"@customer-portal/domain": ["../../packages/domain/index.ts"],
|
||||
"@customer-portal/domain/*": ["../../packages/domain/*"],
|
||||
"@customer-portal/contracts": ["../../packages/contracts/src"],
|
||||
"@customer-portal/contracts/*": ["../../packages/contracts/src/*"],
|
||||
"@customer-portal/schemas": ["../../packages/schemas/src"],
|
||||
"@customer-portal/schemas/*": ["../../packages/schemas/src/*"],
|
||||
"@customer-portal/integrations-whmcs": ["../../packages/integrations/whmcs/src"],
|
||||
"@customer-portal/integrations-whmcs/*": ["../../packages/integrations/whmcs/src/*"],
|
||||
"@customer-portal/integrations-freebit": ["../../packages/integrations/freebit/src"],
|
||||
|
||||
@ -18,10 +18,6 @@
|
||||
"@/lib/*": ["./src/lib/*"],
|
||||
"@customer-portal/domain": ["../../packages/domain/index.ts"],
|
||||
"@customer-portal/domain/*": ["../../packages/domain/*"],
|
||||
"@customer-portal/contracts": ["../../packages/contracts/src"],
|
||||
"@customer-portal/contracts/*": ["../../packages/contracts/src/*"],
|
||||
"@customer-portal/schemas": ["../../packages/schemas/src"],
|
||||
"@customer-portal/schemas/*": ["../../packages/schemas/src/*"],
|
||||
"@customer-portal/logging": ["../../packages/logging/src"],
|
||||
"@customer-portal/logging/*": ["../../packages/logging/src/*"],
|
||||
"@customer-portal/validation": ["../../packages/validation/src"],
|
||||
|
||||
@ -145,19 +145,9 @@ export default [
|
||||
{
|
||||
patterns: [
|
||||
{
|
||||
group: ["@customer-portal/domain/src/**"],
|
||||
group: ["@customer-portal/domain/**/src/**"],
|
||||
message:
|
||||
"Don't import from domain package internals. Use @customer-portal/contracts/* or @customer-portal/schemas/* instead.",
|
||||
},
|
||||
{
|
||||
group: ["@customer-portal/contracts/src/**"],
|
||||
message:
|
||||
"Don't import from contracts package internals. Use @customer-portal/contracts/* subpath exports.",
|
||||
},
|
||||
{
|
||||
group: ["@customer-portal/schemas/src/**"],
|
||||
message:
|
||||
"Don't import from schemas package internals. Use @customer-portal/schemas/* subpath exports.",
|
||||
"Don't import from domain package internals. Use @customer-portal/domain/<domain> or its Providers namespace instead.",
|
||||
},
|
||||
{
|
||||
group: ["**/utils/ui-state*"],
|
||||
@ -167,7 +157,7 @@ export default [
|
||||
{
|
||||
group: ["@/types"],
|
||||
message:
|
||||
"Avoid importing from @/types. Import types directly from @customer-portal/contracts/* or define locally.",
|
||||
"Avoid importing from @/types. Import types directly from @customer-portal/domain/<domain> or define locally.",
|
||||
},
|
||||
],
|
||||
},
|
||||
@ -219,38 +209,19 @@ export default [
|
||||
selector:
|
||||
"TSInterfaceDeclaration[id.name=/^(Invoice|InvoiceItem|Subscription|PaymentMethod|SimDetails)$/]",
|
||||
message:
|
||||
"Don't re-declare domain types in application code. Import from @customer-portal/contracts/* instead.",
|
||||
"Don't re-declare domain types in application code. Import from @customer-portal/domain/<domain> instead.",
|
||||
},
|
||||
{
|
||||
selector:
|
||||
"TSTypeAliasDeclaration[id.name=/^(Invoice|InvoiceItem|Subscription|PaymentMethod|SimDetails)$/]",
|
||||
message:
|
||||
"Don't re-declare domain types in application code. Import from @customer-portal/contracts/* instead.",
|
||||
"Don't re-declare domain types in application code. Import from @customer-portal/domain/<domain> instead.",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
// Contracts package: must remain pure (no runtime dependencies)
|
||||
{
|
||||
files: ["packages/contracts/src/**/*.ts"],
|
||||
rules: {
|
||||
"no-restricted-imports": [
|
||||
"error",
|
||||
{
|
||||
patterns: [
|
||||
{
|
||||
group: ["zod", "@customer-portal/schemas"],
|
||||
message:
|
||||
"Contracts package must be pure types only. Don't import runtime dependencies. Use @customer-portal/schemas for runtime validation.",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
// Integration packages: must use contracts and schemas
|
||||
// Integration packages: must use domain providers and avoid legacy packages
|
||||
{
|
||||
files: ["packages/integrations/**/src/**/*.ts"],
|
||||
rules: {
|
||||
@ -259,9 +230,9 @@ export default [
|
||||
{
|
||||
patterns: [
|
||||
{
|
||||
group: ["@customer-portal/domain"],
|
||||
group: ["@customer-portal/contracts", "@customer-portal/contracts/*", "@customer-portal/schemas", "@customer-portal/schemas/*"],
|
||||
message:
|
||||
"Integration packages should import from @customer-portal/contracts/* or @customer-portal/schemas/*, not legacy domain package.",
|
||||
"Legacy contracts/schemas packages have been removed. Import from @customer-portal/domain/<domain>/providers instead.",
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@ -1,54 +0,0 @@
|
||||
{
|
||||
"name": "@customer-portal/contracts",
|
||||
"version": "0.1.0",
|
||||
"description": "Shared domain contracts (types only, no runtime dependencies).",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"private": true,
|
||||
"sideEffects": false,
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"default": "./dist/index.js"
|
||||
},
|
||||
"./billing": {
|
||||
"types": "./dist/billing/index.d.ts",
|
||||
"default": "./dist/billing/index.js"
|
||||
},
|
||||
"./subscriptions": {
|
||||
"types": "./dist/subscriptions/index.d.ts",
|
||||
"default": "./dist/subscriptions/index.js"
|
||||
},
|
||||
"./payments": {
|
||||
"types": "./dist/payments/index.d.ts",
|
||||
"default": "./dist/payments/index.js"
|
||||
},
|
||||
"./sim": {
|
||||
"types": "./dist/sim/index.d.ts",
|
||||
"default": "./dist/sim/index.js"
|
||||
},
|
||||
"./orders": {
|
||||
"types": "./dist/orders/index.d.ts",
|
||||
"default": "./dist/orders/index.js"
|
||||
},
|
||||
"./freebit": {
|
||||
"types": "./dist/freebit/index.d.ts",
|
||||
"default": "./dist/freebit/index.js"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc -b",
|
||||
"dev": "tsc -b -w --preserveWatchOutput",
|
||||
"clean": "rm -rf dist",
|
||||
"type-check": "tsc --project tsconfig.json --noEmit",
|
||||
"test": "echo \"No tests for contracts package\"",
|
||||
"lint": "eslint .",
|
||||
"lint:fix": "eslint . --fix"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5.9.2"
|
||||
}
|
||||
}
|
||||
2
packages/contracts/src/billing/index.d.ts
vendored
2
packages/contracts/src/billing/index.d.ts
vendored
@ -1,2 +0,0 @@
|
||||
export * from "./invoice";
|
||||
//# sourceMappingURL=index.d.ts.map
|
||||
@ -1 +0,0 @@
|
||||
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC"}
|
||||
@ -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("./invoice"), exports);
|
||||
//# sourceMappingURL=index.js.map
|
||||
@ -1 +0,0 @@
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,4CAA0B"}
|
||||
@ -1 +0,0 @@
|
||||
export * from "./invoice";
|
||||
38
packages/contracts/src/billing/invoice.d.ts
vendored
38
packages/contracts/src/billing/invoice.d.ts
vendored
@ -1,38 +0,0 @@
|
||||
export type InvoiceStatus = "Draft" | "Pending" | "Paid" | "Unpaid" | "Overdue" | "Cancelled" | "Refunded" | "Collections";
|
||||
export interface InvoiceItem {
|
||||
id: number;
|
||||
description: string;
|
||||
amount: number;
|
||||
quantity?: number;
|
||||
type: string;
|
||||
serviceId?: number;
|
||||
}
|
||||
export interface Invoice {
|
||||
id: number;
|
||||
number: string;
|
||||
status: InvoiceStatus;
|
||||
currency: string;
|
||||
currencySymbol?: string;
|
||||
total: number;
|
||||
subtotal: number;
|
||||
tax: number;
|
||||
issuedAt?: string;
|
||||
dueDate?: string;
|
||||
paidDate?: string;
|
||||
pdfUrl?: string;
|
||||
paymentUrl?: string;
|
||||
description?: string;
|
||||
items?: InvoiceItem[];
|
||||
daysOverdue?: number;
|
||||
}
|
||||
export interface InvoicePagination {
|
||||
page: number;
|
||||
totalPages: number;
|
||||
totalItems: number;
|
||||
nextCursor?: string;
|
||||
}
|
||||
export interface InvoiceList {
|
||||
invoices: Invoice[];
|
||||
pagination: InvoicePagination;
|
||||
}
|
||||
//# sourceMappingURL=invoice.d.ts.map
|
||||
@ -1 +0,0 @@
|
||||
{"version":3,"file":"invoice.d.ts","sourceRoot":"","sources":["invoice.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,aAAa,GACrB,OAAO,GACP,SAAS,GACT,MAAM,GACN,QAAQ,GACR,SAAS,GACT,WAAW,GACX,UAAU,GACV,aAAa,CAAC;AAElB,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,aAAa,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,WAAW,EAAE,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,UAAU,EAAE,iBAAiB,CAAC;CAC/B"}
|
||||
@ -1,3 +0,0 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
//# sourceMappingURL=invoice.js.map
|
||||
@ -1 +0,0 @@
|
||||
{"version":3,"file":"invoice.js","sourceRoot":"","sources":["invoice.ts"],"names":[],"mappings":""}
|
||||
@ -1,49 +0,0 @@
|
||||
export type InvoiceStatus =
|
||||
| "Draft"
|
||||
| "Pending"
|
||||
| "Paid"
|
||||
| "Unpaid"
|
||||
| "Overdue"
|
||||
| "Cancelled"
|
||||
| "Refunded"
|
||||
| "Collections";
|
||||
|
||||
export interface InvoiceItem {
|
||||
id: number;
|
||||
description: string;
|
||||
amount: number;
|
||||
quantity?: number;
|
||||
type: string;
|
||||
serviceId?: number;
|
||||
}
|
||||
|
||||
export interface Invoice {
|
||||
id: number;
|
||||
number: string;
|
||||
status: InvoiceStatus;
|
||||
currency: string;
|
||||
currencySymbol?: string;
|
||||
total: number;
|
||||
subtotal: number;
|
||||
tax: number;
|
||||
issuedAt?: string;
|
||||
dueDate?: string;
|
||||
paidDate?: string;
|
||||
pdfUrl?: string;
|
||||
paymentUrl?: string;
|
||||
description?: string;
|
||||
items?: InvoiceItem[];
|
||||
daysOverdue?: number;
|
||||
}
|
||||
|
||||
export interface InvoicePagination {
|
||||
page: number;
|
||||
totalPages: number;
|
||||
totalItems: number;
|
||||
nextCursor?: string;
|
||||
}
|
||||
|
||||
export interface InvoiceList {
|
||||
invoices: Invoice[];
|
||||
pagination: InvoicePagination;
|
||||
}
|
||||
@ -1 +0,0 @@
|
||||
export * from "./requests";
|
||||
@ -1,46 +0,0 @@
|
||||
export interface FreebitAccountDetailsRequest {
|
||||
version?: string;
|
||||
requestDatas: Array<{ kind: "MASTER" | "MVNO"; account?: string | number }>;
|
||||
}
|
||||
|
||||
export interface FreebitTrafficInfoRequest {
|
||||
account: string;
|
||||
}
|
||||
|
||||
export interface FreebitTopUpOptions {
|
||||
campaignCode?: string;
|
||||
expiryDate?: string;
|
||||
scheduledAt?: string;
|
||||
}
|
||||
|
||||
export interface FreebitTopUpRequestPayload {
|
||||
account: string;
|
||||
quotaMb: number;
|
||||
options?: FreebitTopUpOptions;
|
||||
}
|
||||
|
||||
export interface FreebitPlanChangeRequestData {
|
||||
account: string;
|
||||
newPlanCode: string;
|
||||
assignGlobalIp?: boolean;
|
||||
scheduledAt?: string;
|
||||
}
|
||||
|
||||
export interface FreebitAddSpecRequestData {
|
||||
account: string;
|
||||
specCode: string;
|
||||
enabled?: boolean;
|
||||
networkType?: "4G" | "5G";
|
||||
}
|
||||
|
||||
export interface FreebitCancelPlanRequestData {
|
||||
account: string;
|
||||
runDate: string;
|
||||
}
|
||||
|
||||
export interface FreebitEsimReissueRequestData {
|
||||
account: string;
|
||||
newEid: string;
|
||||
oldEid?: string;
|
||||
planCode?: string;
|
||||
}
|
||||
@ -1,13 +0,0 @@
|
||||
export * as BillingContracts from "./billing";
|
||||
export * as SubscriptionContracts from "./subscriptions";
|
||||
export * as PaymentContracts from "./payments";
|
||||
export * as SimContracts from "./sim";
|
||||
export * as OrderContracts from "./orders";
|
||||
export * as FreebitContracts from "./freebit";
|
||||
|
||||
export * from "./billing";
|
||||
export * from "./subscriptions";
|
||||
export * from "./payments";
|
||||
export * from "./sim";
|
||||
export * from "./orders";
|
||||
export * from "./freebit";
|
||||
@ -1,20 +0,0 @@
|
||||
export interface FulfillmentOrderProduct {
|
||||
id?: string;
|
||||
sku?: string;
|
||||
itemClass?: string;
|
||||
whmcsProductId?: string;
|
||||
billingCycle?: string;
|
||||
}
|
||||
|
||||
export interface FulfillmentOrderItem {
|
||||
id: string;
|
||||
orderId: string;
|
||||
quantity: number;
|
||||
product: FulfillmentOrderProduct | null;
|
||||
}
|
||||
|
||||
export interface FulfillmentOrderDetails {
|
||||
id: string;
|
||||
orderType?: string;
|
||||
items: FulfillmentOrderItem[];
|
||||
}
|
||||
@ -1 +0,0 @@
|
||||
export * from "./fulfillment";
|
||||
2
packages/contracts/src/payments/index.d.ts
vendored
2
packages/contracts/src/payments/index.d.ts
vendored
@ -1,2 +0,0 @@
|
||||
export * from "./payment";
|
||||
//# sourceMappingURL=index.d.ts.map
|
||||
@ -1 +0,0 @@
|
||||
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC"}
|
||||
@ -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("./payment"), exports);
|
||||
//# sourceMappingURL=index.js.map
|
||||
@ -1 +0,0 @@
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,4CAA0B"}
|
||||
@ -1 +0,0 @@
|
||||
export * from "./payment";
|
||||
35
packages/contracts/src/payments/payment.d.ts
vendored
35
packages/contracts/src/payments/payment.d.ts
vendored
@ -1,35 +0,0 @@
|
||||
export type PaymentMethodType = "CreditCard" | "BankAccount" | "RemoteCreditCard" | "RemoteBankAccount" | "Manual";
|
||||
export interface PaymentMethod {
|
||||
id: number;
|
||||
type: PaymentMethodType;
|
||||
description: string;
|
||||
gatewayName?: string;
|
||||
contactType?: string;
|
||||
contactId?: number;
|
||||
cardLastFour?: string;
|
||||
expiryDate?: string;
|
||||
startDate?: string;
|
||||
issueNumber?: string;
|
||||
cardType?: string;
|
||||
remoteToken?: string;
|
||||
lastUpdated?: string;
|
||||
bankName?: string;
|
||||
isDefault?: boolean;
|
||||
}
|
||||
export interface PaymentMethodList {
|
||||
paymentMethods: PaymentMethod[];
|
||||
totalCount: number;
|
||||
}
|
||||
export type PaymentGatewayType = "merchant" | "thirdparty" | "tokenization" | "manual";
|
||||
export interface PaymentGateway {
|
||||
name: string;
|
||||
displayName: string;
|
||||
type: PaymentGatewayType;
|
||||
isActive: boolean;
|
||||
configuration?: Record<string, unknown>;
|
||||
}
|
||||
export interface PaymentGatewayList {
|
||||
gateways: PaymentGateway[];
|
||||
totalCount: number;
|
||||
}
|
||||
//# sourceMappingURL=payment.d.ts.map
|
||||
@ -1 +0,0 @@
|
||||
{"version":3,"file":"payment.d.ts","sourceRoot":"","sources":["payment.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,iBAAiB,GACzB,YAAY,GACZ,aAAa,GACb,kBAAkB,GAClB,mBAAmB,GACnB,QAAQ,CAAC;AAEb,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,iBAAiB,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,iBAAiB;IAChC,cAAc,EAAE,aAAa,EAAE,CAAC;IAChC,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,kBAAkB,GAAG,UAAU,GAAG,YAAY,GAAG,cAAc,GAAG,QAAQ,CAAC;AAEvF,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,kBAAkB,CAAC;IACzB,QAAQ,EAAE,OAAO,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACzC;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,cAAc,EAAE,CAAC;IAC3B,UAAU,EAAE,MAAM,CAAC;CACpB"}
|
||||
@ -1,3 +0,0 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
//# sourceMappingURL=payment.js.map
|
||||
@ -1 +0,0 @@
|
||||
{"version":3,"file":"payment.js","sourceRoot":"","sources":["payment.ts"],"names":[],"mappings":""}
|
||||
@ -1,44 +0,0 @@
|
||||
export type PaymentMethodType =
|
||||
| "CreditCard"
|
||||
| "BankAccount"
|
||||
| "RemoteCreditCard"
|
||||
| "RemoteBankAccount"
|
||||
| "Manual";
|
||||
|
||||
export interface PaymentMethod {
|
||||
id: number;
|
||||
type: PaymentMethodType;
|
||||
description: string;
|
||||
gatewayName?: string;
|
||||
contactType?: string;
|
||||
contactId?: number;
|
||||
cardLastFour?: string;
|
||||
expiryDate?: string;
|
||||
startDate?: string;
|
||||
issueNumber?: string;
|
||||
cardType?: string;
|
||||
remoteToken?: string;
|
||||
lastUpdated?: string;
|
||||
bankName?: string;
|
||||
isDefault?: boolean;
|
||||
}
|
||||
|
||||
export interface PaymentMethodList {
|
||||
paymentMethods: PaymentMethod[];
|
||||
totalCount: number;
|
||||
}
|
||||
|
||||
export type PaymentGatewayType = "merchant" | "thirdparty" | "tokenization" | "manual";
|
||||
|
||||
export interface PaymentGateway {
|
||||
name: string;
|
||||
displayName: string;
|
||||
type: PaymentGatewayType;
|
||||
isActive: boolean;
|
||||
configuration?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
export interface PaymentGatewayList {
|
||||
gateways: PaymentGateway[];
|
||||
totalCount: number;
|
||||
}
|
||||
2
packages/contracts/src/sim/index.d.ts
vendored
2
packages/contracts/src/sim/index.d.ts
vendored
@ -1,2 +0,0 @@
|
||||
export * from "./types";
|
||||
//# sourceMappingURL=index.d.ts.map
|
||||
@ -1 +0,0 @@
|
||||
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC"}
|
||||
@ -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("./types"), exports);
|
||||
//# sourceMappingURL=index.js.map
|
||||
@ -1 +0,0 @@
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,0CAAwB"}
|
||||
@ -1 +0,0 @@
|
||||
export * from "./types";
|
||||
50
packages/contracts/src/sim/types.d.ts
vendored
50
packages/contracts/src/sim/types.d.ts
vendored
@ -1,50 +0,0 @@
|
||||
export type SimStatus = "active" | "suspended" | "cancelled" | "pending";
|
||||
export type SimType = "standard" | "nano" | "micro" | "esim";
|
||||
export interface SimDetails {
|
||||
account: string;
|
||||
status: SimStatus;
|
||||
planCode: string;
|
||||
planName: string;
|
||||
simType: SimType;
|
||||
iccid: string;
|
||||
eid: string;
|
||||
msisdn: string;
|
||||
imsi: string;
|
||||
remainingQuotaMb: number;
|
||||
remainingQuotaKb: number;
|
||||
voiceMailEnabled: boolean;
|
||||
callWaitingEnabled: boolean;
|
||||
internationalRoamingEnabled: boolean;
|
||||
networkType: string;
|
||||
activatedAt?: string;
|
||||
expiresAt?: string;
|
||||
}
|
||||
export interface RecentDayUsage {
|
||||
date: string;
|
||||
usageKb: number;
|
||||
usageMb: number;
|
||||
}
|
||||
export interface SimUsage {
|
||||
account: string;
|
||||
todayUsageMb: number;
|
||||
todayUsageKb: number;
|
||||
monthlyUsageMb?: number;
|
||||
monthlyUsageKb?: number;
|
||||
recentDaysUsage: RecentDayUsage[];
|
||||
isBlacklisted: boolean;
|
||||
lastUpdated?: string;
|
||||
}
|
||||
export interface SimTopUpHistoryEntry {
|
||||
quotaKb: number;
|
||||
quotaMb: number;
|
||||
addedDate: string;
|
||||
expiryDate: string;
|
||||
campaignCode: string;
|
||||
}
|
||||
export interface SimTopUpHistory {
|
||||
account: string;
|
||||
totalAdditions: number;
|
||||
additionCount: number;
|
||||
history: SimTopUpHistoryEntry[];
|
||||
}
|
||||
//# sourceMappingURL=types.d.ts.map
|
||||
@ -1 +0,0 @@
|
||||
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,WAAW,GAAG,WAAW,GAAG,SAAS,CAAC;AACzE,MAAM,MAAM,OAAO,GAAG,UAAU,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;AAE7D,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,SAAS,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,gBAAgB,EAAE,MAAM,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,kBAAkB,EAAE,OAAO,CAAC;IAC5B,2BAA2B,EAAE,OAAO,CAAC;IACrC,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,cAAc,EAAE,CAAC;IAClC,aAAa,EAAE,OAAO,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,oBAAoB,EAAE,CAAC;CACjC"}
|
||||
@ -1,3 +0,0 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
//# sourceMappingURL=types.js.map
|
||||
@ -1 +0,0 @@
|
||||
{"version":3,"file":"types.js","sourceRoot":"","sources":["types.ts"],"names":[],"mappings":""}
|
||||
@ -1,54 +0,0 @@
|
||||
export type SimStatus = "active" | "suspended" | "cancelled" | "pending";
|
||||
export type SimType = "standard" | "nano" | "micro" | "esim";
|
||||
|
||||
export interface SimDetails {
|
||||
account: string;
|
||||
status: SimStatus;
|
||||
planCode: string;
|
||||
planName: string;
|
||||
simType: SimType;
|
||||
iccid: string;
|
||||
eid: string;
|
||||
msisdn: string;
|
||||
imsi: string;
|
||||
remainingQuotaMb: number;
|
||||
remainingQuotaKb: number;
|
||||
voiceMailEnabled: boolean;
|
||||
callWaitingEnabled: boolean;
|
||||
internationalRoamingEnabled: boolean;
|
||||
networkType: string;
|
||||
activatedAt?: string;
|
||||
expiresAt?: string;
|
||||
}
|
||||
|
||||
export interface RecentDayUsage {
|
||||
date: string;
|
||||
usageKb: number;
|
||||
usageMb: number;
|
||||
}
|
||||
|
||||
export interface SimUsage {
|
||||
account: string;
|
||||
todayUsageMb: number;
|
||||
todayUsageKb: number;
|
||||
monthlyUsageMb?: number;
|
||||
monthlyUsageKb?: number;
|
||||
recentDaysUsage: RecentDayUsage[];
|
||||
isBlacklisted: boolean;
|
||||
lastUpdated?: string;
|
||||
}
|
||||
|
||||
export interface SimTopUpHistoryEntry {
|
||||
quotaKb: number;
|
||||
quotaMb: number;
|
||||
addedDate: string;
|
||||
expiryDate: string;
|
||||
campaignCode: string;
|
||||
}
|
||||
|
||||
export interface SimTopUpHistory {
|
||||
account: string;
|
||||
totalAdditions: number;
|
||||
additionCount: number;
|
||||
history: SimTopUpHistoryEntry[];
|
||||
}
|
||||
@ -1,2 +0,0 @@
|
||||
export * from "./subscription";
|
||||
//# sourceMappingURL=index.d.ts.map
|
||||
@ -1 +0,0 @@
|
||||
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC"}
|
||||
@ -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("./subscription"), exports);
|
||||
//# sourceMappingURL=index.js.map
|
||||
@ -1 +0,0 @@
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,iDAA+B"}
|
||||
@ -1 +0,0 @@
|
||||
export * from "./subscription";
|
||||
@ -1,26 +0,0 @@
|
||||
export type SubscriptionStatus = "Active" | "Inactive" | "Pending" | "Cancelled" | "Suspended" | "Terminated" | "Completed";
|
||||
export type SubscriptionCycle = "Monthly" | "Quarterly" | "Semi-Annually" | "Annually" | "Biennially" | "Triennially" | "One-time" | "Free";
|
||||
export interface Subscription {
|
||||
id: number;
|
||||
serviceId: number;
|
||||
productName: string;
|
||||
domain?: string;
|
||||
cycle: SubscriptionCycle;
|
||||
status: SubscriptionStatus;
|
||||
nextDue?: string;
|
||||
amount: number;
|
||||
currency: string;
|
||||
currencySymbol?: string;
|
||||
registrationDate: string;
|
||||
notes?: string;
|
||||
customFields?: Record<string, string>;
|
||||
orderNumber?: string;
|
||||
groupName?: string;
|
||||
paymentMethod?: string;
|
||||
serverName?: string;
|
||||
}
|
||||
export interface SubscriptionList {
|
||||
subscriptions: Subscription[];
|
||||
totalCount: number;
|
||||
}
|
||||
//# sourceMappingURL=subscription.d.ts.map
|
||||
@ -1 +0,0 @@
|
||||
{"version":3,"file":"subscription.d.ts","sourceRoot":"","sources":["subscription.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,kBAAkB,GAC1B,QAAQ,GACR,UAAU,GACV,SAAS,GACT,WAAW,GACX,WAAW,GACX,YAAY,GACZ,WAAW,CAAC;AAEhB,MAAM,MAAM,iBAAiB,GACzB,SAAS,GACT,WAAW,GACX,eAAe,GACf,UAAU,GACV,YAAY,GACZ,aAAa,GACb,UAAU,GACV,MAAM,CAAC;AAEX,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,iBAAiB,CAAC;IACzB,MAAM,EAAE,kBAAkB,CAAC;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC/B,aAAa,EAAE,YAAY,EAAE,CAAC;IAC9B,UAAU,EAAE,MAAM,CAAC;CACpB"}
|
||||
@ -1,3 +0,0 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
//# sourceMappingURL=subscription.js.map
|
||||
@ -1 +0,0 @@
|
||||
{"version":3,"file":"subscription.js","sourceRoot":"","sources":["subscription.ts"],"names":[],"mappings":""}
|
||||
@ -1,43 +0,0 @@
|
||||
export type SubscriptionStatus =
|
||||
| "Active"
|
||||
| "Inactive"
|
||||
| "Pending"
|
||||
| "Cancelled"
|
||||
| "Suspended"
|
||||
| "Terminated"
|
||||
| "Completed";
|
||||
|
||||
export type SubscriptionCycle =
|
||||
| "Monthly"
|
||||
| "Quarterly"
|
||||
| "Semi-Annually"
|
||||
| "Annually"
|
||||
| "Biennially"
|
||||
| "Triennially"
|
||||
| "One-time"
|
||||
| "Free";
|
||||
|
||||
export interface Subscription {
|
||||
id: number;
|
||||
serviceId: number;
|
||||
productName: string;
|
||||
domain?: string;
|
||||
cycle: SubscriptionCycle;
|
||||
status: SubscriptionStatus;
|
||||
nextDue?: string;
|
||||
amount: number;
|
||||
currency: string;
|
||||
currencySymbol?: string;
|
||||
registrationDate: string;
|
||||
notes?: string;
|
||||
customFields?: Record<string, string>;
|
||||
orderNumber?: string;
|
||||
groupName?: string;
|
||||
paymentMethod?: string;
|
||||
serverName?: string;
|
||||
}
|
||||
|
||||
export interface SubscriptionList {
|
||||
subscriptions: Subscription[];
|
||||
totalCount: number;
|
||||
}
|
||||
@ -1,16 +0,0 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"module": "NodeNext",
|
||||
"moduleResolution": "nodenext",
|
||||
"lib": ["ES2022"],
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"composite": true,
|
||||
"tsBuildInfoFile": "./tsconfig.tsbuildinfo"
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.spec.ts"]
|
||||
}
|
||||
@ -90,6 +90,32 @@ export interface SetPasswordRequest {
|
||||
password: string;
|
||||
}
|
||||
|
||||
export interface ValidateSignupRequest {
|
||||
sfNumber: string;
|
||||
}
|
||||
|
||||
export interface UpdateProfileRequest {
|
||||
firstName?: string;
|
||||
lastName?: string;
|
||||
company?: string;
|
||||
phone?: string;
|
||||
avatar?: string;
|
||||
nationality?: string;
|
||||
dateOfBirth?: string;
|
||||
gender?: "male" | "female" | "other";
|
||||
}
|
||||
|
||||
export interface UpdateAddressRequest {
|
||||
address: Address;
|
||||
}
|
||||
|
||||
export interface Activity {
|
||||
id: string;
|
||||
type: string;
|
||||
description: string;
|
||||
createdAt: IsoDateTimeString;
|
||||
}
|
||||
|
||||
export interface AuthError {
|
||||
code:
|
||||
| "INVALID_CREDENTIALS"
|
||||
|
||||
@ -1,3 +1,18 @@
|
||||
export * from "./contract";
|
||||
export * from "./schema";
|
||||
export {
|
||||
loginRequestSchema,
|
||||
signupRequestSchema,
|
||||
passwordResetRequestSchema,
|
||||
passwordResetSchema,
|
||||
setPasswordRequestSchema,
|
||||
changePasswordRequestSchema,
|
||||
linkWhmcsRequestSchema,
|
||||
validateSignupRequestSchema,
|
||||
accountStatusRequestSchema,
|
||||
ssoLinkRequestSchema,
|
||||
checkPasswordNeededRequestSchema,
|
||||
refreshTokenRequestSchema,
|
||||
updateProfileRequestSchema,
|
||||
updateAddressRequestSchema,
|
||||
} from "./schema";
|
||||
|
||||
|
||||
@ -61,6 +61,21 @@ export const validateSignupRequestSchema = z.object({
|
||||
sfNumber: z.string().min(1, "Customer number is required"),
|
||||
});
|
||||
|
||||
export const updateProfileRequestSchema = z.object({
|
||||
firstName: nameSchema.optional(),
|
||||
lastName: nameSchema.optional(),
|
||||
company: z.string().optional(),
|
||||
phone: phoneSchema.optional(),
|
||||
avatar: z.string().optional(),
|
||||
nationality: z.string().optional(),
|
||||
dateOfBirth: z.string().optional(),
|
||||
gender: genderEnum.optional(),
|
||||
});
|
||||
|
||||
export const updateAddressRequestSchema = z.object({
|
||||
address: addressSchema,
|
||||
});
|
||||
|
||||
export const accountStatusRequestSchema = z.object({
|
||||
email: emailSchema,
|
||||
});
|
||||
@ -91,4 +106,8 @@ export const authResponseSchema = z.object({
|
||||
tokens: authTokensSchema,
|
||||
});
|
||||
|
||||
export type ValidateSignupRequest = z.infer<typeof validateSignupRequestSchema>;
|
||||
export type UpdateProfileRequest = z.infer<typeof updateProfileRequestSchema>;
|
||||
export type UpdateAddressRequest = z.infer<typeof updateAddressRequestSchema>;
|
||||
|
||||
|
||||
|
||||
@ -3,10 +3,14 @@
|
||||
*/
|
||||
|
||||
import * as WhmcsMapper from "./whmcs/mapper";
|
||||
import * as WhmcsRaw from "./whmcs/raw.types";
|
||||
|
||||
export const Whmcs = {
|
||||
...WhmcsMapper,
|
||||
mapper: WhmcsMapper,
|
||||
raw: WhmcsRaw,
|
||||
};
|
||||
|
||||
// Re-export raw types for convenience
|
||||
export { WhmcsMapper, WhmcsRaw };
|
||||
export * from "./whmcs/mapper";
|
||||
export * from "./whmcs/raw.types";
|
||||
|
||||
@ -3,10 +3,14 @@
|
||||
*/
|
||||
|
||||
import * as SalesforceMapper from "./salesforce/mapper";
|
||||
import * as SalesforceRaw from "./salesforce/raw.types";
|
||||
|
||||
export const Salesforce = {
|
||||
...SalesforceMapper,
|
||||
mapper: SalesforceMapper,
|
||||
raw: SalesforceRaw,
|
||||
};
|
||||
|
||||
// Re-export raw types for convenience
|
||||
export { SalesforceMapper, SalesforceRaw };
|
||||
export * from "./salesforce/mapper";
|
||||
export * from "./salesforce/raw.types";
|
||||
|
||||
@ -3,16 +3,24 @@
|
||||
*/
|
||||
|
||||
import * as WhmcsMapper from "./whmcs/mapper";
|
||||
import * as WhmcsRaw from "./whmcs/raw.types";
|
||||
import * as SalesforceMapper from "./salesforce/mapper";
|
||||
import * as SalesforceRaw from "./salesforce/raw.types";
|
||||
|
||||
export const Whmcs = {
|
||||
...WhmcsMapper,
|
||||
mapper: WhmcsMapper,
|
||||
raw: WhmcsRaw,
|
||||
};
|
||||
|
||||
export const Salesforce = {
|
||||
...SalesforceMapper,
|
||||
mapper: SalesforceMapper,
|
||||
raw: SalesforceRaw,
|
||||
};
|
||||
|
||||
// Re-export raw types for convenience
|
||||
export { WhmcsMapper, WhmcsRaw, SalesforceMapper, SalesforceRaw };
|
||||
export * from "./whmcs/mapper";
|
||||
export * from "./whmcs/raw.types";
|
||||
export * from "./salesforce/mapper";
|
||||
export * from "./salesforce/raw.types";
|
||||
|
||||
@ -3,10 +3,14 @@
|
||||
*/
|
||||
|
||||
import * as WhmcsMapper from "./whmcs/mapper";
|
||||
import * as WhmcsRaw from "./whmcs/raw.types";
|
||||
|
||||
export const Whmcs = {
|
||||
...WhmcsMapper,
|
||||
mapper: WhmcsMapper,
|
||||
raw: WhmcsRaw,
|
||||
};
|
||||
|
||||
// Re-export raw types for convenience
|
||||
export { WhmcsMapper, WhmcsRaw };
|
||||
export * from "./whmcs/mapper";
|
||||
export * from "./whmcs/raw.types";
|
||||
|
||||
@ -25,6 +25,7 @@ export const schemas = {
|
||||
|
||||
export const raw = RawTypes;
|
||||
export const mapper = Mapper;
|
||||
export const requests = Requests;
|
||||
|
||||
export type EsimAccountActivationRequest = Requests.FreebitEsimActivationRequest;
|
||||
export type TopUpRequest = Requests.FreebitTopUpRequest;
|
||||
@ -46,7 +47,6 @@ export type CancelPlanResponse = ReturnType<typeof Mapper.transformFreebitCancel
|
||||
export type CancelAccountResponse = ReturnType<typeof Mapper.transformFreebitCancelAccountResponse>;
|
||||
export type EsimReissueResponse = ReturnType<typeof Mapper.transformFreebitEsimReissueResponse>;
|
||||
export type EsimAddAccountResponse = ReturnType<typeof Mapper.transformFreebitEsimAddAccountResponse>;
|
||||
export type EsimActivationResponse = ReturnType<typeof Mapper.transformFreebitEsimActivationResponse>;
|
||||
export type AuthResponse = ReturnType<typeof Mapper.transformFreebitAuthResponse>;
|
||||
|
||||
export * from "./mapper";
|
||||
@ -55,8 +55,10 @@ export * from "./requests";
|
||||
|
||||
export const Freebit = {
|
||||
...Mapper,
|
||||
mapper: Mapper,
|
||||
raw: RawTypes,
|
||||
schemas,
|
||||
requests: Requests,
|
||||
};
|
||||
|
||||
export const normalizeAccount = Mapper.normalizeAccount;
|
||||
|
||||
@ -208,8 +208,6 @@ export function transformFreebitEsimActivationResponse(raw: unknown) {
|
||||
return freebitEsimAddAccountRawSchema.parse(raw);
|
||||
}
|
||||
|
||||
export type FreebitEsimActivationResponse = ReturnType<typeof transformFreebitEsimActivationResponse>;
|
||||
|
||||
export function transformFreebitAuthResponse(raw: unknown): FreebitAuthResponseRaw {
|
||||
return freebitAuthResponseRawSchema.parse(raw);
|
||||
}
|
||||
|
||||
@ -2,4 +2,9 @@
|
||||
* SIM Domain - Providers
|
||||
*/
|
||||
|
||||
export * as Freebit from "./freebit";
|
||||
import * as FreebitModule from "./freebit";
|
||||
|
||||
export const Freebit = FreebitModule;
|
||||
|
||||
export { FreebitModule };
|
||||
export * from "./freebit";
|
||||
|
||||
@ -3,10 +3,14 @@
|
||||
*/
|
||||
|
||||
import * as WhmcsMapper from "./whmcs/mapper";
|
||||
import * as WhmcsRaw from "./whmcs/raw.types";
|
||||
|
||||
export const Whmcs = {
|
||||
...WhmcsMapper,
|
||||
mapper: WhmcsMapper,
|
||||
raw: WhmcsRaw,
|
||||
};
|
||||
|
||||
// Re-export raw types for convenience
|
||||
export { WhmcsMapper, WhmcsRaw };
|
||||
export * from "./whmcs/mapper";
|
||||
export * from "./whmcs/raw.types";
|
||||
|
||||
@ -32,7 +32,6 @@
|
||||
"typescript": "^5.9.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@customer-portal/contracts": "workspace:*",
|
||||
"@customer-portal/schemas": "workspace:*"
|
||||
"@customer-portal/domain": "workspace:*"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,138 +1 @@
|
||||
import type {
|
||||
SimDetails,
|
||||
SimTopUpHistory,
|
||||
SimUsage,
|
||||
} from "@customer-portal/contracts/sim";
|
||||
import {
|
||||
simDetailsSchema,
|
||||
simTopUpHistorySchema,
|
||||
simUsageSchema,
|
||||
} from "@customer-portal/schemas/sim/sim.schema";
|
||||
import {
|
||||
freebitAccountDetailsResponseSchema,
|
||||
} from "@customer-portal/schemas/integrations/freebit/account.schema";
|
||||
import {
|
||||
freebitTrafficInfoSchema,
|
||||
} from "@customer-portal/schemas/integrations/freebit/traffic.schema";
|
||||
import {
|
||||
freebitQuotaHistoryResponseSchema,
|
||||
} from "@customer-portal/schemas/integrations/freebit/quota.schema";
|
||||
|
||||
import { asString, formatIsoDate, parseBooleanFlag, parseNumber } from "../utils/data-utils";
|
||||
|
||||
function mapSimStatus(status: string | undefined): SimDetails["status"] {
|
||||
if (!status) return "pending";
|
||||
const normalized = status.toLowerCase();
|
||||
if (normalized === "active") return "active";
|
||||
if (normalized === "suspended" || normalized === "temporary") return "suspended";
|
||||
if (normalized === "obsolete" || normalized === "cancelled" || normalized === "canceled") {
|
||||
return "cancelled";
|
||||
}
|
||||
return "pending";
|
||||
}
|
||||
|
||||
function deriveSimType(detail: unknown, eid?: string | null): SimDetails["simType"] {
|
||||
const raw = typeof detail === "string" ? detail.toLowerCase() : undefined;
|
||||
if (eid) {
|
||||
return "esim";
|
||||
}
|
||||
switch (raw) {
|
||||
case "nano":
|
||||
return "nano";
|
||||
case "micro":
|
||||
return "micro";
|
||||
case "esim":
|
||||
return "esim";
|
||||
default:
|
||||
return "standard";
|
||||
}
|
||||
}
|
||||
|
||||
export function transformFreebitAccountDetails(raw: unknown): SimDetails {
|
||||
const response = freebitAccountDetailsResponseSchema.parse(raw);
|
||||
const account = response.responseDatas.at(0);
|
||||
if (!account) {
|
||||
throw new Error("Freebit account details missing response data");
|
||||
}
|
||||
|
||||
const sanitizedAccount = asString(account.account);
|
||||
const simSizeValue = account.simSize ?? (account as any).size;
|
||||
const eidValue = account.eid;
|
||||
const simType = deriveSimType(
|
||||
typeof simSizeValue === 'number' ? String(simSizeValue) : simSizeValue,
|
||||
typeof eidValue === 'number' ? String(eidValue) : eidValue
|
||||
);
|
||||
const voiceMailEnabled = parseBooleanFlag(account.voicemail ?? account.voiceMail);
|
||||
const callWaitingEnabled = parseBooleanFlag(account.callwaiting ?? account.callWaiting);
|
||||
const internationalRoamingEnabled = parseBooleanFlag(account.worldwing ?? account.worldWing);
|
||||
|
||||
const simDetails: SimDetails = {
|
||||
account: sanitizedAccount,
|
||||
status: mapSimStatus(asString(account.state ?? account.status)),
|
||||
planCode: asString(account.planCode),
|
||||
planName: asString(account.planName),
|
||||
simType,
|
||||
iccid: asString(account.iccid),
|
||||
eid: asString(account.eid),
|
||||
msisdn: asString(account.msisdn ?? account.account),
|
||||
imsi: asString(account.imsi),
|
||||
remainingQuotaMb: parseNumber(account.remainingQuotaMb),
|
||||
remainingQuotaKb: parseNumber(account.remainingQuotaKb),
|
||||
voiceMailEnabled,
|
||||
callWaitingEnabled,
|
||||
internationalRoamingEnabled,
|
||||
networkType: asString(account.contractLine ?? account.simType ?? "4G"),
|
||||
activatedAt: formatIsoDate(account.startDate),
|
||||
expiresAt: formatIsoDate(account.async?.date),
|
||||
};
|
||||
|
||||
return simDetailsSchema.parse(simDetails);
|
||||
}
|
||||
|
||||
export function transformFreebitTrafficInfo(raw: unknown): SimUsage {
|
||||
const response = freebitTrafficInfoSchema.parse(raw);
|
||||
const todayUsageKb = parseNumber(response.traffic.today);
|
||||
const recentDaysUsage = response.traffic.inRecentDays
|
||||
.split(",")
|
||||
.map((usage, index) => {
|
||||
const usageKb = parseNumber(usage);
|
||||
const date = new Date();
|
||||
date.setDate(date.getDate() - (index + 1));
|
||||
return {
|
||||
date: date.toISOString().split("T")[0],
|
||||
usageKb,
|
||||
usageMb: Math.round((usageKb / 1024) * 100) / 100,
|
||||
};
|
||||
});
|
||||
|
||||
const simUsage: SimUsage = {
|
||||
account: response.account,
|
||||
todayUsageKb,
|
||||
todayUsageMb: Math.round((todayUsageKb / 1024) * 100) / 100,
|
||||
recentDaysUsage,
|
||||
isBlacklisted: response.traffic.blackList === "10",
|
||||
};
|
||||
|
||||
return simUsageSchema.parse(simUsage);
|
||||
}
|
||||
|
||||
export function transformFreebitQuotaHistory(raw: unknown, account: string): SimTopUpHistory {
|
||||
const response = freebitQuotaHistoryResponseSchema.parse(raw);
|
||||
|
||||
const history = response.quotaHistory.map(entry => ({
|
||||
quotaKb: parseNumber(entry.quota),
|
||||
quotaMb: Math.round((parseNumber(entry.quota) / 1024) * 100) / 100,
|
||||
addedDate: entry.date,
|
||||
expiryDate: entry.expire,
|
||||
campaignCode: entry.quotaCode ?? "",
|
||||
}));
|
||||
|
||||
const simHistory: SimTopUpHistory = {
|
||||
account,
|
||||
totalAdditions: parseNumber(response.total),
|
||||
additionCount: parseNumber(response.count),
|
||||
history,
|
||||
};
|
||||
|
||||
return simTopUpHistorySchema.parse(simHistory);
|
||||
}
|
||||
export * from "@customer-portal/domain/sim/providers/freebit";
|
||||
|
||||
@ -1,46 +0,0 @@
|
||||
export function parseNumber(value: unknown, fallback = 0): number {
|
||||
if (typeof value === "number") {
|
||||
return value;
|
||||
}
|
||||
if (typeof value === "string") {
|
||||
const parsed = Number.parseFloat(value);
|
||||
return Number.isNaN(parsed) ? fallback : parsed;
|
||||
}
|
||||
return fallback;
|
||||
}
|
||||
|
||||
export function asString(value: unknown, fallback = ""): string {
|
||||
if (typeof value === "string") return value;
|
||||
if (typeof value === "number") return String(value);
|
||||
return fallback;
|
||||
}
|
||||
|
||||
export function parseBooleanFlag(value: unknown): boolean {
|
||||
if (typeof value === "boolean") return value;
|
||||
if (typeof value === "number") return value === 1 || value === 10 || value === 20;
|
||||
if (typeof value === "string") {
|
||||
const normalized = value.trim();
|
||||
return normalized === "1" || normalized === "10" || normalized === "20" || normalized.toLowerCase() === "true";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function formatIsoDate(input: unknown): string | undefined {
|
||||
if (typeof input !== "string" && typeof input !== "number") {
|
||||
return undefined;
|
||||
}
|
||||
const raw = String(input);
|
||||
if (!raw) return undefined;
|
||||
const isoCandidate = Number.isNaN(Date.parse(raw)) && /^\d{8}$/.test(raw)
|
||||
? `${raw.slice(0, 4)}-${raw.slice(4, 6)}-${raw.slice(6, 8)}`
|
||||
: raw;
|
||||
const date = new Date(isoCandidate);
|
||||
if (Number.isNaN(date.getTime())) {
|
||||
return undefined;
|
||||
}
|
||||
return date.toISOString();
|
||||
}
|
||||
|
||||
export function normalizeAccount(account: string): string {
|
||||
return account.replace(/[-\s()]/g, "");
|
||||
}
|
||||
@ -1 +1 @@
|
||||
export * from "./data-utils";
|
||||
export * from "@customer-portal/domain/sim/providers/freebit";
|
||||
|
||||
@ -11,10 +11,8 @@
|
||||
"composite": true,
|
||||
"tsBuildInfoFile": "./tsconfig.tsbuildinfo",
|
||||
"paths": {
|
||||
"@customer-portal/contracts": ["../../contracts/src"],
|
||||
"@customer-portal/contracts/*": ["../../contracts/src/*"],
|
||||
"@customer-portal/schemas": ["../../schemas/src"],
|
||||
"@customer-portal/schemas/*": ["../../schemas/src/*"],
|
||||
"@customer-portal/domain": ["../domain/index.ts"],
|
||||
"@customer-portal/domain/*": ["../domain/*"],
|
||||
"@customer-portal/integrations-freebit": ["./src"],
|
||||
"@customer-portal/integrations-freebit/*": ["./src/*"]
|
||||
}
|
||||
@ -22,7 +20,6 @@
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.spec.ts"],
|
||||
"references": [
|
||||
{ "path": "../../contracts" },
|
||||
{ "path": "../../schemas" }
|
||||
{ "path": "../../domain" }
|
||||
]
|
||||
}
|
||||
|
||||
@ -32,7 +32,6 @@
|
||||
"typescript": "^5.9.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@customer-portal/contracts": "workspace:*",
|
||||
"@customer-portal/schemas": "workspace:*"
|
||||
"@customer-portal/domain": "workspace:*"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,85 +1 @@
|
||||
import type { Invoice, InvoiceItem } from "@customer-portal/contracts/billing";
|
||||
import { invoiceSchema } from "@customer-portal/schemas/billing/invoice.schema";
|
||||
import {
|
||||
whmcsInvoiceItemsSchema,
|
||||
whmcsInvoiceSchema,
|
||||
} from "@customer-portal/schemas/integrations/whmcs/invoice.schema";
|
||||
|
||||
import { formatDate, parseAmount } from "../utils/data-utils";
|
||||
|
||||
export interface TransformInvoiceOptions {
|
||||
defaultCurrencyCode?: string;
|
||||
defaultCurrencySymbol?: string;
|
||||
}
|
||||
|
||||
const statusMap: Record<string, Invoice["status"]> = {
|
||||
draft: "Draft",
|
||||
pending: "Pending",
|
||||
"payment pending": "Pending",
|
||||
paid: "Paid",
|
||||
unpaid: "Unpaid",
|
||||
cancelled: "Cancelled",
|
||||
canceled: "Cancelled",
|
||||
overdue: "Overdue",
|
||||
refunded: "Refunded",
|
||||
collections: "Collections",
|
||||
};
|
||||
|
||||
function mapStatus(status: string): Invoice["status"] {
|
||||
const normalized = status?.trim().toLowerCase();
|
||||
if (!normalized) {
|
||||
throw new Error("Invoice status missing");
|
||||
}
|
||||
|
||||
const mapped = statusMap[normalized];
|
||||
if (!mapped) {
|
||||
throw new Error(`Unsupported WHMCS invoice status: ${status}`);
|
||||
}
|
||||
return mapped;
|
||||
}
|
||||
|
||||
function mapItems(rawItems: unknown): InvoiceItem[] {
|
||||
if (!rawItems) return [];
|
||||
|
||||
const parsed = whmcsInvoiceItemsSchema.parse(rawItems);
|
||||
const itemArray = Array.isArray(parsed.item) ? parsed.item : [parsed.item];
|
||||
return itemArray.map(item => ({
|
||||
id: item.id,
|
||||
description: item.description,
|
||||
amount: parseAmount(item.amount),
|
||||
quantity: 1,
|
||||
type: item.type,
|
||||
serviceId: typeof item.relid === "number" && item.relid > 0 ? item.relid : undefined,
|
||||
}));
|
||||
}
|
||||
|
||||
export function transformWhmcsInvoice(
|
||||
rawInvoice: unknown,
|
||||
options: TransformInvoiceOptions = {}
|
||||
): Invoice {
|
||||
const whmcsInvoice = whmcsInvoiceSchema.parse(rawInvoice);
|
||||
|
||||
const currency = whmcsInvoice.currencycode || options.defaultCurrencyCode || "JPY";
|
||||
const currencySymbol =
|
||||
whmcsInvoice.currencyprefix ||
|
||||
whmcsInvoice.currencysuffix ||
|
||||
options.defaultCurrencySymbol;
|
||||
|
||||
const invoice: Invoice = {
|
||||
id: whmcsInvoice.invoiceid ?? whmcsInvoice.id ?? 0,
|
||||
number: whmcsInvoice.invoicenum || `INV-${whmcsInvoice.invoiceid}`,
|
||||
status: mapStatus(whmcsInvoice.status),
|
||||
currency,
|
||||
currencySymbol,
|
||||
total: parseAmount(whmcsInvoice.total),
|
||||
subtotal: parseAmount(whmcsInvoice.subtotal),
|
||||
tax: parseAmount(whmcsInvoice.tax) + parseAmount(whmcsInvoice.tax2),
|
||||
issuedAt: formatDate(whmcsInvoice.date || whmcsInvoice.datecreated),
|
||||
dueDate: formatDate(whmcsInvoice.duedate),
|
||||
paidDate: formatDate(whmcsInvoice.datepaid),
|
||||
description: whmcsInvoice.notes || undefined,
|
||||
items: mapItems(whmcsInvoice.items),
|
||||
};
|
||||
|
||||
return invoiceSchema.parse(invoice);
|
||||
}
|
||||
export * from "@customer-portal/domain/billing/providers/whmcs";
|
||||
|
||||
@ -1,189 +1 @@
|
||||
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/domain/orders/providers/whmcs";
|
||||
import { z } from "zod";
|
||||
|
||||
const fulfillmentOrderItemSchema = z.object({
|
||||
id: z.string(),
|
||||
orderId: z.string(),
|
||||
quantity: z.number().int().min(1),
|
||||
product: z
|
||||
.object({
|
||||
id: z.string().optional(),
|
||||
sku: z.string().optional(),
|
||||
itemClass: z.string().optional(),
|
||||
whmcsProductId: z.string().min(1),
|
||||
billingCycle: z.string().min(1),
|
||||
})
|
||||
.nullable(),
|
||||
});
|
||||
|
||||
export interface OrderItemMappingResult {
|
||||
whmcsItems: WhmcsOrderItem[];
|
||||
summary: {
|
||||
totalItems: number;
|
||||
serviceItems: number;
|
||||
activationItems: number;
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeBillingCycle(cycle: string): WhmcsOrderItem["billingCycle"] {
|
||||
const normalized = cycle.trim().toLowerCase();
|
||||
if (normalized.includes("monthly")) return "monthly";
|
||||
if (normalized.includes("one")) return "onetime";
|
||||
if (normalized.includes("annual")) return "annually";
|
||||
if (normalized.includes("quarter")) return "quarterly";
|
||||
// Default to monthly if unrecognized
|
||||
return "monthly";
|
||||
}
|
||||
|
||||
export function mapFulfillmentOrderItem(
|
||||
item: FulfillmentOrderItem,
|
||||
index = 0
|
||||
): WhmcsOrderItem {
|
||||
const parsed = fulfillmentOrderItemSchema.parse(item);
|
||||
|
||||
if (!parsed.product) {
|
||||
throw new Error(`Order item ${index} missing product information`);
|
||||
}
|
||||
|
||||
const whmcsItem: WhmcsOrderItem = {
|
||||
productId: parsed.product.whmcsProductId,
|
||||
billingCycle: normalizeBillingCycle(parsed.product.billingCycle),
|
||||
quantity: parsed.quantity,
|
||||
};
|
||||
|
||||
return whmcsItem;
|
||||
}
|
||||
|
||||
export function mapFulfillmentOrderItems(
|
||||
items: FulfillmentOrderItem[]
|
||||
): OrderItemMappingResult {
|
||||
if (!Array.isArray(items) || items.length === 0) {
|
||||
throw new Error("No order items provided for WHMCS mapping");
|
||||
}
|
||||
|
||||
const whmcsItems: WhmcsOrderItem[] = [];
|
||||
let serviceItems = 0;
|
||||
let activationItems = 0;
|
||||
|
||||
items.forEach((item, index) => {
|
||||
const mapped = mapFulfillmentOrderItem(item, index);
|
||||
whmcsItems.push(mapped);
|
||||
if (mapped.billingCycle === "monthly") {
|
||||
serviceItems++;
|
||||
} else if (mapped.billingCycle === "onetime") {
|
||||
activationItems++;
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
whmcsItems,
|
||||
summary: {
|
||||
totalItems: whmcsItems.length,
|
||||
serviceItems,
|
||||
activationItems,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Build WHMCS AddOrder API payload from parameters
|
||||
* Converts our structured params into the WHMCS API array format
|
||||
*/
|
||||
export function buildWhmcsAddOrderPayload(params: WhmcsAddOrderParams): WhmcsAddOrderPayload {
|
||||
const pids: string[] = [];
|
||||
const billingCycles: string[] = [];
|
||||
const quantities: number[] = [];
|
||||
const configOptions: string[] = [];
|
||||
const customFields: string[] = [];
|
||||
|
||||
params.items.forEach(item => {
|
||||
pids.push(item.productId);
|
||||
billingCycles.push(item.billingCycle);
|
||||
quantities.push(item.quantity);
|
||||
|
||||
// Handle config options - WHMCS expects base64 encoded serialized arrays
|
||||
if (item.configOptions && Object.keys(item.configOptions).length > 0) {
|
||||
const serialized = serializeForWhmcs(item.configOptions);
|
||||
configOptions.push(serialized);
|
||||
} else {
|
||||
configOptions.push(""); // Empty string for items without config options
|
||||
}
|
||||
|
||||
// Handle custom fields - WHMCS expects base64 encoded serialized arrays
|
||||
if (item.customFields && Object.keys(item.customFields).length > 0) {
|
||||
const serialized = serializeForWhmcs(item.customFields);
|
||||
customFields.push(serialized);
|
||||
} else {
|
||||
customFields.push(""); // Empty string for items without custom fields
|
||||
}
|
||||
});
|
||||
|
||||
const payload: WhmcsAddOrderPayload = {
|
||||
clientid: params.clientId,
|
||||
paymentmethod: params.paymentMethod,
|
||||
pid: pids,
|
||||
billingcycle: billingCycles,
|
||||
qty: quantities,
|
||||
};
|
||||
|
||||
// Add optional fields
|
||||
if (params.promoCode) {
|
||||
payload.promocode = params.promoCode;
|
||||
}
|
||||
if (params.noinvoice !== undefined) {
|
||||
payload.noinvoice = params.noinvoice;
|
||||
}
|
||||
if (params.noinvoiceemail !== undefined) {
|
||||
payload.noinvoiceemail = params.noinvoiceemail;
|
||||
}
|
||||
if (params.noemail !== undefined) {
|
||||
payload.noemail = params.noemail;
|
||||
}
|
||||
if (configOptions.some(opt => opt !== "")) {
|
||||
payload.configoptions = configOptions;
|
||||
}
|
||||
if (customFields.some(field => field !== "")) {
|
||||
payload.customfields = customFields;
|
||||
}
|
||||
|
||||
return payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize object for WHMCS API
|
||||
* WHMCS expects base64-encoded PHP serialized data
|
||||
*/
|
||||
function serializeForWhmcs(data: Record<string, string>): string {
|
||||
const jsonStr = JSON.stringify(data);
|
||||
return Buffer.from(jsonStr).toString("base64");
|
||||
}
|
||||
|
||||
/**
|
||||
* Create order notes with Salesforce tracking information
|
||||
*/
|
||||
export function createOrderNotes(sfOrderId: string, additionalNotes?: string): string {
|
||||
const notes: string[] = [];
|
||||
|
||||
// Always include Salesforce Order ID for tracking
|
||||
notes.push(`sfOrderId=${sfOrderId}`);
|
||||
|
||||
// Add provisioning timestamp
|
||||
notes.push(`provisionedAt=${new Date().toISOString()}`);
|
||||
|
||||
// Add additional notes if provided
|
||||
if (additionalNotes) {
|
||||
notes.push(additionalNotes);
|
||||
}
|
||||
|
||||
return notes.join("; ");
|
||||
}
|
||||
export * from "@customer-portal/domain/orders/providers/whmcs";
|
||||
|
||||
@ -1,95 +1 @@
|
||||
import type { PaymentGateway, PaymentMethod } from "@customer-portal/domain/payments";
|
||||
import {
|
||||
paymentGatewaySchema,
|
||||
paymentMethodSchema,
|
||||
Providers,
|
||||
} from "@customer-portal/domain/payments";
|
||||
import {
|
||||
whmcsPaymentGatewayRawSchema,
|
||||
whmcsPaymentMethodRawSchema,
|
||||
} from "@customer-portal/domain/payments/providers/whmcs";
|
||||
|
||||
const paymentMethodTypeMap: Record<string, PaymentMethod["type"]> = {
|
||||
creditcard: "CreditCard",
|
||||
bankaccount: "BankAccount",
|
||||
remotecard: "RemoteCreditCard",
|
||||
remotebankaccount: "RemoteBankAccount",
|
||||
manual: "Manual",
|
||||
remoteccreditcard: "RemoteCreditCard",
|
||||
};
|
||||
|
||||
function mapPaymentMethodType(type: string): PaymentMethod["type"] {
|
||||
const normalized = type.trim().toLowerCase();
|
||||
return paymentMethodTypeMap[normalized] ?? "Manual";
|
||||
}
|
||||
|
||||
const paymentGatewayTypeMap: Record<string, PaymentGateway["type"]> = {
|
||||
merchant: "merchant",
|
||||
thirdparty: "thirdparty",
|
||||
"third-party": "thirdparty",
|
||||
tokenization: "tokenization",
|
||||
tokenised: "tokenization",
|
||||
manual: "manual",
|
||||
};
|
||||
|
||||
function mapPaymentGatewayType(type?: string | null): PaymentGateway["type"] {
|
||||
if (!type) {
|
||||
return "manual";
|
||||
}
|
||||
const normalized = type.trim().toLowerCase();
|
||||
return paymentGatewayTypeMap[normalized] ?? "manual";
|
||||
}
|
||||
|
||||
export function transformWhmcsPaymentMethod(raw: unknown): PaymentMethod {
|
||||
const method = whmcsPaymentMethodRawSchema.parse(raw);
|
||||
|
||||
const paymentMethod: PaymentMethod = {
|
||||
id: method.id,
|
||||
type: mapPaymentMethodType(method.type),
|
||||
description: method.description || method.type || "Manual payment method",
|
||||
gatewayName: method.gateway_name || undefined,
|
||||
contactType: method.contact_type || undefined,
|
||||
contactId: method.contact_id ?? undefined,
|
||||
cardLastFour: method.card_last_four || undefined,
|
||||
expiryDate: method.expiry_date || undefined,
|
||||
startDate: method.start_date || undefined,
|
||||
issueNumber: method.issue_number || undefined,
|
||||
cardType: method.card_type || undefined,
|
||||
remoteToken: method.remote_token || undefined,
|
||||
lastUpdated: method.last_updated || undefined,
|
||||
bankName: method.bank_name || undefined,
|
||||
isDefault:
|
||||
typeof method.is_default === "boolean"
|
||||
? method.is_default
|
||||
: method.is_default === 1
|
||||
? true
|
||||
: undefined,
|
||||
};
|
||||
|
||||
return paymentMethodSchema.parse(paymentMethod);
|
||||
}
|
||||
|
||||
export function transformWhmcsPaymentGateway(raw: unknown): PaymentGateway {
|
||||
const gateway = whmcsPaymentGatewayRawSchema.parse(raw);
|
||||
|
||||
const paymentGateway: PaymentGateway = {
|
||||
name: gateway.name,
|
||||
displayName: gateway.display_name || gateway.name,
|
||||
type: mapPaymentGatewayType(gateway.type),
|
||||
isActive:
|
||||
typeof gateway.active === "boolean"
|
||||
? gateway.active
|
||||
: gateway.active === 1
|
||||
? true
|
||||
: gateway.active === "1"
|
||||
? true
|
||||
: gateway.active === 0
|
||||
? false
|
||||
: gateway.active === "0"
|
||||
? false
|
||||
: true,
|
||||
configuration: undefined,
|
||||
};
|
||||
|
||||
return paymentGatewaySchema.parse(paymentGateway);
|
||||
}
|
||||
export * from "@customer-portal/domain/payments/providers/whmcs";
|
||||
|
||||
@ -1,129 +1 @@
|
||||
import type { z } from "zod";
|
||||
|
||||
import type { Subscription } from "@customer-portal/contracts/subscriptions";
|
||||
import {
|
||||
subscriptionSchema,
|
||||
} from "@customer-portal/schemas/subscriptions/subscription.schema";
|
||||
import {
|
||||
whmcsCustomFieldsContainerSchema,
|
||||
whmcsProductSchema,
|
||||
} from "@customer-portal/schemas/integrations/whmcs/product.schema";
|
||||
|
||||
import { formatDate, parseAmount } from "../utils/data-utils";
|
||||
|
||||
export interface TransformSubscriptionOptions {
|
||||
defaultCurrencyCode?: string;
|
||||
defaultCurrencySymbol?: string;
|
||||
}
|
||||
|
||||
const statusMap: Record<string, Subscription["status"]> = {
|
||||
active: "Active",
|
||||
inactive: "Inactive",
|
||||
pending: "Pending",
|
||||
cancelled: "Cancelled",
|
||||
canceled: "Cancelled",
|
||||
terminated: "Terminated",
|
||||
completed: "Completed",
|
||||
suspended: "Suspended",
|
||||
fraud: "Cancelled",
|
||||
};
|
||||
|
||||
const cycleMap: Record<string, Subscription["cycle"]> = {
|
||||
monthly: "Monthly",
|
||||
annually: "Annually",
|
||||
annual: "Annually",
|
||||
yearly: "Annually",
|
||||
quarterly: "Quarterly",
|
||||
"semi annually": "Semi-Annually",
|
||||
semiannually: "Semi-Annually",
|
||||
"semi-annually": "Semi-Annually",
|
||||
biennially: "Biennially",
|
||||
triennially: "Triennially",
|
||||
"one time": "One-time",
|
||||
onetime: "One-time",
|
||||
"one-time": "One-time",
|
||||
"one time fee": "One-time",
|
||||
free: "Free",
|
||||
};
|
||||
|
||||
function mapStatus(status?: string | null): Subscription["status"] {
|
||||
if (!status) {
|
||||
return "Cancelled";
|
||||
}
|
||||
const mapped = statusMap[status.trim().toLowerCase()];
|
||||
return mapped ?? "Cancelled";
|
||||
}
|
||||
|
||||
function mapCycle(cycle?: string | null): Subscription["cycle"] {
|
||||
if (!cycle) {
|
||||
return "One-time";
|
||||
}
|
||||
const normalized = cycle.trim().toLowerCase().replace(/[_\s-]+/g, " ");
|
||||
return cycleMap[normalized] ?? "One-time";
|
||||
}
|
||||
|
||||
function extractCustomFields(raw: unknown): Record<string, string> | undefined {
|
||||
if (!raw) return undefined;
|
||||
|
||||
const container = whmcsCustomFieldsContainerSchema.safeParse(raw);
|
||||
if (!container.success) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const customfield = container.data.customfield;
|
||||
const fieldsArray = Array.isArray(customfield) ? customfield : [customfield];
|
||||
|
||||
const entries = fieldsArray.reduce<Record<string, string>>((acc, field) => {
|
||||
if (field?.name && field.value) {
|
||||
acc[field.name] = field.value;
|
||||
}
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
return Object.keys(entries).length > 0 ? entries : undefined;
|
||||
}
|
||||
|
||||
function resolveAmount(product: z.infer<typeof whmcsProductSchema>): number {
|
||||
const recurring = parseAmount(product.recurringamount);
|
||||
if (recurring > 0) {
|
||||
return recurring;
|
||||
}
|
||||
return parseAmount(product.firstpaymentamount);
|
||||
}
|
||||
|
||||
export function transformWhmcsSubscription(
|
||||
rawProduct: unknown,
|
||||
options: TransformSubscriptionOptions = {}
|
||||
): Subscription {
|
||||
const product = whmcsProductSchema.parse(rawProduct);
|
||||
|
||||
const id = Number(product.id);
|
||||
if (!Number.isFinite(id)) {
|
||||
throw new Error("WHMCS product id is not numeric");
|
||||
}
|
||||
|
||||
const currency = options.defaultCurrencyCode ?? "JPY";
|
||||
const currencySymbol = options.defaultCurrencySymbol;
|
||||
|
||||
const subscription: Subscription = {
|
||||
id,
|
||||
serviceId: id,
|
||||
productName: product.name || product.translated_name || String(product.id),
|
||||
domain: product.domain || undefined,
|
||||
status: mapStatus(product.status),
|
||||
cycle: mapCycle(product.billingcycle),
|
||||
amount: resolveAmount(product),
|
||||
currency,
|
||||
currencySymbol,
|
||||
nextDue: formatDate(product.nextduedate),
|
||||
registrationDate: formatDate(product.regdate) || new Date().toISOString(),
|
||||
customFields: extractCustomFields(product.customfields),
|
||||
notes: product.notes || undefined,
|
||||
orderNumber: product.ordernumber || undefined,
|
||||
groupName: product.groupname || product.translated_groupname || undefined,
|
||||
paymentMethod: product.paymentmethodname || product.paymentmethod || undefined,
|
||||
serverName: product.servername || product.serverhostname || undefined,
|
||||
};
|
||||
|
||||
return subscriptionSchema.parse(subscription);
|
||||
}
|
||||
export * from "@customer-portal/domain/subscriptions/providers/whmcs";
|
||||
|
||||
@ -11,10 +11,8 @@
|
||||
"composite": true,
|
||||
"tsBuildInfoFile": "./tsconfig.tsbuildinfo",
|
||||
"paths": {
|
||||
"@customer-portal/contracts": ["../../contracts/src"],
|
||||
"@customer-portal/contracts/*": ["../../contracts/src/*"],
|
||||
"@customer-portal/schemas": ["../../schemas/src"],
|
||||
"@customer-portal/schemas/*": ["../../schemas/src/*"],
|
||||
"@customer-portal/domain": ["../domain/index.ts"],
|
||||
"@customer-portal/domain/*": ["../domain/*"],
|
||||
"@customer-portal/integrations-whmcs": ["./src"],
|
||||
"@customer-portal/integrations-whmcs/*": ["./src/*"]
|
||||
}
|
||||
@ -22,7 +20,6 @@
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.spec.ts"],
|
||||
"references": [
|
||||
{ "path": "../../contracts" },
|
||||
{ "path": "../../schemas" }
|
||||
{ "path": "../../domain" }
|
||||
]
|
||||
}
|
||||
|
||||
@ -1,53 +0,0 @@
|
||||
{
|
||||
"name": "@customer-portal/schemas",
|
||||
"version": "0.1.0",
|
||||
"description": "Runtime validation schemas for customer portal domain and integrations.",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"private": true,
|
||||
"sideEffects": false,
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"default": "./dist/index.js"
|
||||
},
|
||||
"./billing": {
|
||||
"types": "./dist/billing/index.d.ts",
|
||||
"default": "./dist/billing/index.js"
|
||||
},
|
||||
"./subscriptions": {
|
||||
"types": "./dist/subscriptions/index.d.ts",
|
||||
"default": "./dist/subscriptions/index.js"
|
||||
},
|
||||
"./payments": {
|
||||
"types": "./dist/payments/index.d.ts",
|
||||
"default": "./dist/payments/index.js"
|
||||
},
|
||||
"./sim": {
|
||||
"types": "./dist/sim/index.d.ts",
|
||||
"default": "./dist/sim/index.js"
|
||||
},
|
||||
"./integrations": {
|
||||
"types": "./dist/integrations/index.d.ts",
|
||||
"default": "./dist/integrations/index.js"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc -b",
|
||||
"dev": "tsc -b -w --preserveWatchOutput",
|
||||
"clean": "rm -rf dist",
|
||||
"type-check": "tsc --project tsconfig.json --noEmit",
|
||||
"test": "echo \"No tests for schemas package\"",
|
||||
"lint": "eslint .",
|
||||
"lint:fix": "eslint . --fix"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5.9.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"zod": "^4.1.9"
|
||||
}
|
||||
}
|
||||
@ -1 +0,0 @@
|
||||
export * from "./invoice.schema";
|
||||
@ -1,60 +0,0 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import type { Invoice, InvoiceItem, InvoiceList, InvoiceStatus } from "@customer-portal/contracts/billing";
|
||||
|
||||
const invoiceStatusValues: readonly InvoiceStatus[] = [
|
||||
"Draft",
|
||||
"Pending",
|
||||
"Paid",
|
||||
"Unpaid",
|
||||
"Overdue",
|
||||
"Cancelled",
|
||||
"Refunded",
|
||||
"Collections",
|
||||
] as const;
|
||||
|
||||
export const invoiceStatusSchema = z.enum(invoiceStatusValues);
|
||||
|
||||
export const invoiceItemSchema = z.object({
|
||||
id: z.number().int().positive("Invoice item id must be positive"),
|
||||
description: z.string().min(1, "Description is required"),
|
||||
amount: z.number(),
|
||||
quantity: z.number().int().positive("Quantity must be positive").optional(),
|
||||
type: z.string().min(1, "Item type is required"),
|
||||
serviceId: z.number().int().positive().optional(),
|
||||
}) satisfies z.ZodType<InvoiceItem>;
|
||||
|
||||
export const invoiceSchema = z.object({
|
||||
id: z.number().int().positive("Invoice id must be positive"),
|
||||
number: z.string().min(1, "Invoice number is required"),
|
||||
status: invoiceStatusSchema,
|
||||
currency: z.string().min(1, "Currency is required"),
|
||||
currencySymbol: z.string().min(1, "Currency symbol is required").optional(),
|
||||
total: z.number(),
|
||||
subtotal: z.number(),
|
||||
tax: z.number(),
|
||||
issuedAt: z.string().optional(),
|
||||
dueDate: z.string().optional(),
|
||||
paidDate: z.string().optional(),
|
||||
pdfUrl: z.string().optional(),
|
||||
paymentUrl: z.string().optional(),
|
||||
description: z.string().optional(),
|
||||
items: z.array(invoiceItemSchema).optional(),
|
||||
daysOverdue: z.number().int().nonnegative().optional(),
|
||||
}) satisfies z.ZodType<Invoice>;
|
||||
|
||||
export const invoicePaginationSchema = z.object({
|
||||
page: z.number().int().nonnegative(),
|
||||
totalPages: z.number().int().nonnegative(),
|
||||
totalItems: z.number().int().nonnegative(),
|
||||
nextCursor: z.string().optional(),
|
||||
});
|
||||
|
||||
export const invoiceListSchema = z.object({
|
||||
invoices: z.array(invoiceSchema),
|
||||
pagination: invoicePaginationSchema,
|
||||
}) satisfies z.ZodType<InvoiceList>;
|
||||
|
||||
export type InvoiceSchema = typeof invoiceSchema;
|
||||
export type InvoiceItemSchema = typeof invoiceItemSchema;
|
||||
export type InvoiceListSchema = typeof invoiceListSchema;
|
||||
@ -1,11 +0,0 @@
|
||||
export * as BillingSchemas from "./billing";
|
||||
export * as SubscriptionSchemas from "./subscriptions";
|
||||
export * as PaymentSchemas from "./payments";
|
||||
export * as SimSchemas from "./sim";
|
||||
export * as IntegrationSchemas from "./integrations";
|
||||
|
||||
export * from "./billing";
|
||||
export * from "./subscriptions";
|
||||
export * from "./payments";
|
||||
export * from "./sim";
|
||||
export * from "./integrations";
|
||||
@ -1,47 +0,0 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export const freebitStatusSchema = z.object({
|
||||
message: z.string().optional(),
|
||||
statusCode: z.union([z.string(), z.number()]).optional(),
|
||||
});
|
||||
|
||||
export const freebitAccountDetailSchema = z.object({
|
||||
kind: z.string().optional(),
|
||||
account: z.union([z.string(), z.number()]).nullable().optional(),
|
||||
state: z.string().optional(),
|
||||
status: z.string().optional(),
|
||||
planCode: z.union([z.string(), z.number()]).nullable().optional(),
|
||||
planName: z.union([z.string(), z.number()]).nullable().optional(),
|
||||
simSize: z.string().nullable().optional(),
|
||||
eid: z.union([z.string(), z.number()]).nullable().optional(),
|
||||
iccid: z.union([z.string(), z.number()]).nullable().optional(),
|
||||
imsi: z.union([z.string(), z.number()]).nullable().optional(),
|
||||
msisdn: z.union([z.string(), z.number()]).nullable().optional(),
|
||||
remainingQuotaMb: z.union([z.string(), z.number()]).nullable().optional(),
|
||||
remainingQuotaKb: z.union([z.string(), z.number()]).nullable().optional(),
|
||||
voicemail: z.union([z.string(), z.number()]).nullable().optional(),
|
||||
voiceMail: z.union([z.string(), z.number()]).nullable().optional(),
|
||||
callwaiting: z.union([z.string(), z.number()]).nullable().optional(),
|
||||
callWaiting: z.union([z.string(), z.number()]).nullable().optional(),
|
||||
worldwing: z.union([z.string(), z.number()]).nullable().optional(),
|
||||
worldWing: z.union([z.string(), z.number()]).nullable().optional(),
|
||||
async: z
|
||||
.object({
|
||||
func: z.string().optional(),
|
||||
date: z.union([z.string(), z.number()]).optional(),
|
||||
})
|
||||
.nullable()
|
||||
.optional(),
|
||||
simType: z.string().nullable().optional(),
|
||||
contractLine: z.string().nullable().optional(),
|
||||
startDate: z.union([z.string(), z.number()]).nullable().optional(),
|
||||
});
|
||||
|
||||
export const freebitAccountDetailsResponseSchema = z.object({
|
||||
resultCode: z.string(),
|
||||
status: freebitStatusSchema,
|
||||
masterAccount: z.union([z.string(), z.number()]).nullable().optional(),
|
||||
responseDatas: z.array(freebitAccountDetailSchema),
|
||||
});
|
||||
|
||||
export type FreebitAccountDetailsResponseSchema = typeof freebitAccountDetailsResponseSchema;
|
||||
@ -1,4 +0,0 @@
|
||||
export * from "./account.schema";
|
||||
export * from "./traffic.schema";
|
||||
export * from "./quota.schema";
|
||||
export * as FreebitRequestSchemas from "./requests";
|
||||
@ -1,17 +0,0 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export const freebitAccountDetailsRequestSchema = z.object({
|
||||
version: z.string().optional(),
|
||||
requestDatas: z
|
||||
.array(
|
||||
z.object({
|
||||
kind: z.enum(["MASTER", "MVNO"]),
|
||||
account: z.union([z.string(), z.number()]).optional(),
|
||||
})
|
||||
)
|
||||
.min(1, "At least one request data entry is required"),
|
||||
});
|
||||
|
||||
export const freebitTrafficInfoRequestSchema = z.object({
|
||||
account: z.string().min(1, "Account is required"),
|
||||
});
|
||||
@ -1,94 +0,0 @@
|
||||
import { z } from "zod";
|
||||
|
||||
/**
|
||||
* Freebit eSIM MNP (Mobile Number Portability) Schema
|
||||
*/
|
||||
export const freebitEsimMnpSchema = z.object({
|
||||
reserveNumber: z.string().min(1, "Reserve number is required"),
|
||||
reserveExpireDate: z.string().regex(/^\d{8}$/, "Reserve expire date must be in YYYYMMDD format"),
|
||||
});
|
||||
|
||||
/**
|
||||
* Freebit eSIM Identity Schema
|
||||
* Customer identity information required for activation
|
||||
*/
|
||||
export const freebitEsimIdentitySchema = z.object({
|
||||
firstnameKanji: z.string().optional(),
|
||||
lastnameKanji: z.string().optional(),
|
||||
firstnameZenKana: z.string().optional(),
|
||||
lastnameZenKana: z.string().optional(),
|
||||
gender: z.enum(["M", "F"]).optional(),
|
||||
birthday: z.string().regex(/^\d{8}$/, "Birthday must be in YYYYMMDD format").optional(),
|
||||
});
|
||||
|
||||
/**
|
||||
* Freebit eSIM Account Activation Request Schema
|
||||
* PA05-41 (addAcct) API endpoint
|
||||
*
|
||||
* Based on Freebit API documentation for eSIM account activation
|
||||
*/
|
||||
export const freebitEsimActivationRequestSchema = z.object({
|
||||
authKey: z.string().min(1, "Auth key is required"),
|
||||
aladinOperated: z.enum(["10", "20"]).default("10"), // 10: issue profile, 20: no-issue
|
||||
createType: z.enum(["new", "reissue", "exchange"]).default("new"),
|
||||
account: z.string().min(1, "Account (MSISDN) is required"),
|
||||
eid: z.string().min(1, "EID is required for eSIM"),
|
||||
simkind: z.enum(["esim", "psim"]).default("esim"),
|
||||
planCode: z.string().optional(),
|
||||
contractLine: z.enum(["4G", "5G"]).optional(),
|
||||
shipDate: z.string().regex(/^\d{8}$/, "Ship date must be in YYYYMMDD format").optional(),
|
||||
mnp: freebitEsimMnpSchema.optional(),
|
||||
// Identity fields (flattened for API)
|
||||
firstnameKanji: z.string().optional(),
|
||||
lastnameKanji: z.string().optional(),
|
||||
firstnameZenKana: z.string().optional(),
|
||||
lastnameZenKana: z.string().optional(),
|
||||
gender: z.enum(["M", "F"]).optional(),
|
||||
birthday: z.string().regex(/^\d{8}$/, "Birthday must be in YYYYMMDD format").optional(),
|
||||
// Additional fields for reissue/exchange
|
||||
masterAccount: z.string().optional(),
|
||||
masterPassword: z.string().optional(),
|
||||
repAccount: z.string().optional(),
|
||||
size: z.string().optional(),
|
||||
addKind: z.string().optional(), // 'R' for reissue
|
||||
oldEid: z.string().optional(),
|
||||
oldProductNumber: z.string().optional(),
|
||||
deliveryCode: z.string().optional(),
|
||||
globalIp: z.enum(["10", "20"]).optional(), // 10: none, 20: with global IP
|
||||
});
|
||||
|
||||
export type FreebitEsimActivationRequest = z.infer<typeof freebitEsimActivationRequestSchema>;
|
||||
|
||||
/**
|
||||
* Freebit eSIM Account Activation Response Schema
|
||||
*/
|
||||
export const freebitEsimActivationResponseSchema = z.object({
|
||||
resultCode: z.string(),
|
||||
resultMessage: z.string().optional(),
|
||||
data: z.any().optional(),
|
||||
status: z.object({
|
||||
statusCode: z.union([z.string(), z.number()]),
|
||||
message: z.string(),
|
||||
}).optional(),
|
||||
message: z.string().optional(),
|
||||
});
|
||||
|
||||
export type FreebitEsimActivationResponse = z.infer<typeof freebitEsimActivationResponseSchema>;
|
||||
|
||||
/**
|
||||
* Higher-level eSIM activation parameters schema
|
||||
* Used for business logic layer before mapping to API request
|
||||
*/
|
||||
export const freebitEsimActivationParamsSchema = z.object({
|
||||
account: z.string().min(1, "Account is required"),
|
||||
eid: z.string().min(1, "EID is required"),
|
||||
planCode: z.string().optional(),
|
||||
contractLine: z.enum(["4G", "5G"]).optional(),
|
||||
aladinOperated: z.enum(["10", "20"]).default("10"),
|
||||
shipDate: z.string().regex(/^\d{8}$/, "Ship date must be in YYYYMMDD format").optional(),
|
||||
mnp: freebitEsimMnpSchema.optional(),
|
||||
identity: freebitEsimIdentitySchema.optional(),
|
||||
});
|
||||
|
||||
export type FreebitEsimActivationParams = z.infer<typeof freebitEsimActivationParamsSchema>;
|
||||
|
||||
@ -1,36 +0,0 @@
|
||||
import { z } from "zod";
|
||||
|
||||
/**
|
||||
* Freebit SIM Feature Update Request Schema
|
||||
* For enabling/disabling features like voice mail, call waiting, etc.
|
||||
*/
|
||||
export const freebitSimFeaturesRequestSchema = z.object({
|
||||
account: z.string().min(1, "Account is required"),
|
||||
voiceMailEnabled: z.boolean().optional(),
|
||||
callWaitingEnabled: z.boolean().optional(),
|
||||
callForwardingEnabled: z.boolean().optional(),
|
||||
callerIdEnabled: z.boolean().optional(),
|
||||
});
|
||||
|
||||
export type FreebitSimFeaturesRequest = z.infer<typeof freebitSimFeaturesRequestSchema>;
|
||||
|
||||
/**
|
||||
* Freebit Remove Spec Request Schema
|
||||
*/
|
||||
export const freebitRemoveSpecRequestSchema = z.object({
|
||||
account: z.string().min(1, "Account is required"),
|
||||
specCode: z.string().min(1, "Spec code is required"),
|
||||
});
|
||||
|
||||
export type FreebitRemoveSpecRequest = z.infer<typeof freebitRemoveSpecRequestSchema>;
|
||||
|
||||
/**
|
||||
* Freebit Global IP Assignment Request Schema
|
||||
*/
|
||||
export const freebitGlobalIpRequestSchema = z.object({
|
||||
account: z.string().min(1, "Account is required"),
|
||||
assign: z.boolean(), // true to assign, false to remove
|
||||
});
|
||||
|
||||
export type FreebitGlobalIpRequest = z.infer<typeof freebitGlobalIpRequestSchema>;
|
||||
|
||||
@ -1,5 +0,0 @@
|
||||
export * from "./topup.schema";
|
||||
export * from "./account.schema";
|
||||
export * from "./plan-change.schema";
|
||||
export * from "./esim-activation.schema";
|
||||
export * from "./features.schema";
|
||||
@ -1,28 +0,0 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export const freebitPlanChangeRequestSchema = z.object({
|
||||
account: z.string().min(1, "Account is required"),
|
||||
newPlanCode: z.string().min(1, "New plan code is required"),
|
||||
assignGlobalIp: z.boolean().optional(),
|
||||
scheduledAt: 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"),
|
||||
enabled: z.boolean().optional(),
|
||||
networkType: z.enum(["4G", "5G"]).optional(),
|
||||
});
|
||||
|
||||
export const freebitCancelPlanRequestSchema = z.object({
|
||||
account: z.string().min(1, "Account is required"),
|
||||
runDate: 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"),
|
||||
oldEid: z.string().optional(),
|
||||
planCode: z.string().optional(),
|
||||
oldProductNumber: z.string().optional(),
|
||||
});
|
||||
@ -1,13 +0,0 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export const freebitTopUpOptionsSchema = z.object({
|
||||
campaignCode: z.string().optional(),
|
||||
expiryDate: z.string().optional(),
|
||||
scheduledAt: z.string().optional(),
|
||||
});
|
||||
|
||||
export const freebitTopUpRequestPayloadSchema = z.object({
|
||||
account: z.string().min(1, "Account is required"),
|
||||
quotaMb: z.number().positive("Quota must be positive"),
|
||||
options: freebitTopUpOptionsSchema.optional(),
|
||||
});
|
||||
@ -1,17 +0,0 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export const freebitTrafficInfoSchema = z.object({
|
||||
resultCode: z.string(),
|
||||
status: z.object({
|
||||
message: z.string().optional(),
|
||||
statusCode: z.union([z.string(), z.number()]).optional(),
|
||||
}),
|
||||
account: z.string(),
|
||||
traffic: z.object({
|
||||
today: z.string(),
|
||||
inRecentDays: z.string(),
|
||||
blackList: z.string(),
|
||||
}),
|
||||
});
|
||||
|
||||
export type FreebitTrafficInfoSchema = typeof freebitTrafficInfoSchema;
|
||||
@ -1,4 +0,0 @@
|
||||
export * as WhmcsSchemas from "./whmcs";
|
||||
export * as FreebitSchemas from "./freebit";
|
||||
export * from "./whmcs";
|
||||
export * from "./freebit";
|
||||
@ -1,4 +0,0 @@
|
||||
export * from "./invoice.schema";
|
||||
export * from "./product.schema";
|
||||
export * from "./payment.schema";
|
||||
export * from "./order.schema";
|
||||
@ -1,47 +0,0 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export const whmcsInvoiceItemSchema = z.object({
|
||||
id: z.number(),
|
||||
type: z.string(),
|
||||
relid: z.number(),
|
||||
description: z.string(),
|
||||
amount: z.union([z.string(), z.number()]),
|
||||
taxed: z.number().optional(),
|
||||
});
|
||||
|
||||
export const whmcsInvoiceItemsSchema = z.object({
|
||||
item: z.union([whmcsInvoiceItemSchema, z.array(whmcsInvoiceItemSchema)]),
|
||||
});
|
||||
|
||||
export const whmcsInvoiceSchema = z.object({
|
||||
invoiceid: z.number(),
|
||||
invoicenum: z.string(),
|
||||
userid: z.number(),
|
||||
date: z.string(),
|
||||
duedate: z.string(),
|
||||
subtotal: z.string(),
|
||||
credit: z.string(),
|
||||
tax: z.string(),
|
||||
tax2: z.string(),
|
||||
total: z.string(),
|
||||
balance: z.string().optional(),
|
||||
status: z.string(),
|
||||
paymentmethod: z.string(),
|
||||
notes: z.string().optional(),
|
||||
ccgateway: z.boolean().optional(),
|
||||
items: whmcsInvoiceItemsSchema.optional(),
|
||||
transactions: z.unknown().optional(),
|
||||
id: z.number().optional(),
|
||||
clientid: z.number().optional(),
|
||||
datecreated: z.string().optional(),
|
||||
paymentmethodname: z.string().optional(),
|
||||
currencycode: z.string().optional(),
|
||||
currencyprefix: z.string().optional(),
|
||||
currencysuffix: z.string().optional(),
|
||||
lastcaptureattempt: z.string().optional(),
|
||||
taxrate: z.string().optional(),
|
||||
taxrate2: z.string().optional(),
|
||||
datepaid: z.string().optional(),
|
||||
});
|
||||
|
||||
export type WhmcsInvoiceSchema = typeof whmcsInvoiceSchema;
|
||||
@ -1,70 +0,0 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export const whmcsCustomFieldSchema = z.object({
|
||||
id: z.number(),
|
||||
value: z.string().optional(),
|
||||
name: z.string().optional(),
|
||||
type: z.string().optional(),
|
||||
});
|
||||
|
||||
export const whmcsCustomFieldsContainerSchema = z.object({
|
||||
customfield: z.union([whmcsCustomFieldSchema, z.array(whmcsCustomFieldSchema)]),
|
||||
});
|
||||
|
||||
export const whmcsConfigOptionSchema = z.object({
|
||||
id: z.union([z.number(), z.string()]).optional(),
|
||||
option: z.string().optional(),
|
||||
type: z.string().optional(),
|
||||
value: z.string().optional(),
|
||||
});
|
||||
|
||||
export const whmcsConfigOptionsSchema = z.object({
|
||||
configoption: z.union([whmcsConfigOptionSchema, z.array(whmcsConfigOptionSchema)]).optional(),
|
||||
});
|
||||
|
||||
export const whmcsProductSchema = z.object({
|
||||
id: z.union([z.number(), z.string()]),
|
||||
qty: z.string().optional(),
|
||||
clientid: z.union([z.number(), z.string()]).optional(),
|
||||
orderid: z.union([z.number(), z.string()]).optional(),
|
||||
ordernumber: z.string().optional(),
|
||||
pid: z.union([z.number(), z.string()]).optional(),
|
||||
regdate: z.string().optional(),
|
||||
name: z.string().optional(),
|
||||
translated_name: z.string().optional(),
|
||||
groupname: z.string().optional(),
|
||||
translated_groupname: z.string().optional(),
|
||||
domain: z.string().optional(),
|
||||
dedicatedip: z.string().optional(),
|
||||
serverid: z.union([z.number(), z.string()]).optional(),
|
||||
servername: z.string().optional(),
|
||||
serverip: z.string().optional(),
|
||||
serverhostname: z.string().optional(),
|
||||
suspensionreason: z.string().optional(),
|
||||
firstpaymentamount: z.string().optional(),
|
||||
recurringamount: z.string().optional(),
|
||||
paymentmethod: z.string().optional(),
|
||||
paymentmethodname: z.string().optional(),
|
||||
billingcycle: z.string().optional(),
|
||||
nextduedate: z.string().optional(),
|
||||
status: z.string().optional(),
|
||||
username: z.string().optional(),
|
||||
password: z.string().optional(),
|
||||
subscriptionid: z.string().optional(),
|
||||
promoid: z.union([z.number(), z.string()]).optional(),
|
||||
overideautosuspend: z.string().optional(),
|
||||
overidesuspenduntil: z.string().optional(),
|
||||
ns1: z.string().optional(),
|
||||
ns2: z.string().optional(),
|
||||
assignedips: z.string().optional(),
|
||||
notes: z.string().optional(),
|
||||
diskusage: z.string().optional(),
|
||||
disklimit: z.string().optional(),
|
||||
bwusage: z.string().optional(),
|
||||
bwlimit: z.string().optional(),
|
||||
lastupdate: z.string().optional(),
|
||||
customfields: whmcsCustomFieldsContainerSchema.optional(),
|
||||
configoptions: whmcsConfigOptionsSchema.optional(),
|
||||
});
|
||||
|
||||
export type WhmcsProductSchema = typeof whmcsProductSchema;
|
||||
@ -1 +0,0 @@
|
||||
export * from "./payment.schema";
|
||||
@ -1,67 +0,0 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import type {
|
||||
PaymentGateway,
|
||||
PaymentGatewayList,
|
||||
PaymentGatewayType,
|
||||
PaymentMethod,
|
||||
PaymentMethodList,
|
||||
PaymentMethodType,
|
||||
} from "@customer-portal/contracts/payments";
|
||||
|
||||
const paymentMethodTypeValues: readonly PaymentMethodType[] = [
|
||||
"CreditCard",
|
||||
"BankAccount",
|
||||
"RemoteCreditCard",
|
||||
"RemoteBankAccount",
|
||||
"Manual",
|
||||
] as const;
|
||||
|
||||
const paymentGatewayTypeValues: readonly PaymentGatewayType[] = [
|
||||
"merchant",
|
||||
"thirdparty",
|
||||
"tokenization",
|
||||
"manual",
|
||||
] as const;
|
||||
|
||||
export const paymentMethodTypeSchema = z.enum(paymentMethodTypeValues);
|
||||
export const paymentGatewayTypeSchema = z.enum(paymentGatewayTypeValues);
|
||||
|
||||
export const paymentMethodSchema = z.object({
|
||||
id: z.number().int().nonnegative(),
|
||||
type: paymentMethodTypeSchema,
|
||||
description: z.string().min(1, "Description is required"),
|
||||
gatewayName: z.string().optional(),
|
||||
contactType: z.string().optional(),
|
||||
contactId: z.number().int().nonnegative().optional(),
|
||||
cardLastFour: z.string().optional(),
|
||||
expiryDate: z.string().optional(),
|
||||
startDate: z.string().optional(),
|
||||
issueNumber: z.string().optional(),
|
||||
cardType: z.string().optional(),
|
||||
remoteToken: z.string().optional(),
|
||||
lastUpdated: z.string().optional(),
|
||||
bankName: z.string().optional(),
|
||||
isDefault: z.boolean().optional(),
|
||||
}) satisfies z.ZodType<PaymentMethod>;
|
||||
|
||||
export const paymentMethodListSchema = z.object({
|
||||
paymentMethods: z.array(paymentMethodSchema),
|
||||
totalCount: z.number().int().nonnegative(),
|
||||
}) satisfies z.ZodType<PaymentMethodList>;
|
||||
|
||||
export const paymentGatewaySchema = z.object({
|
||||
name: z.string().min(1, "Gateway name is required"),
|
||||
displayName: z.string().min(1, "Display name is required"),
|
||||
type: paymentGatewayTypeSchema,
|
||||
isActive: z.boolean(),
|
||||
configuration: z.record(z.string(), z.unknown()).optional(),
|
||||
}) satisfies z.ZodType<PaymentGateway>;
|
||||
|
||||
export const paymentGatewayListSchema = z.object({
|
||||
gateways: z.array(paymentGatewaySchema),
|
||||
totalCount: z.number().int().nonnegative(),
|
||||
}) satisfies z.ZodType<PaymentGatewayList>;
|
||||
|
||||
export type PaymentMethodSchema = typeof paymentMethodSchema;
|
||||
export type PaymentGatewaySchema = typeof paymentGatewaySchema;
|
||||
@ -1 +0,0 @@
|
||||
export * from "./sim.schema";
|
||||
@ -1,70 +0,0 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import type {
|
||||
SimDetails,
|
||||
SimTopUpHistory,
|
||||
SimTopUpHistoryEntry,
|
||||
SimUsage,
|
||||
} from "@customer-portal/contracts/sim";
|
||||
|
||||
const simStatusValues = ["active", "suspended", "cancelled", "pending"] as const;
|
||||
const simTypeValues = ["standard", "nano", "micro", "esim"] as const;
|
||||
|
||||
export const simStatusSchema = z.enum(simStatusValues);
|
||||
export const simTypeSchema = z.enum(simTypeValues);
|
||||
|
||||
export const simDetailsSchema = z.object({
|
||||
account: z.string().min(1, "Account is required"),
|
||||
status: simStatusSchema,
|
||||
planCode: z.string().min(1, "Plan code is required"),
|
||||
planName: z.string().min(1, "Plan name is required"),
|
||||
simType: simTypeSchema,
|
||||
iccid: z.string().min(1, "ICCID is required"),
|
||||
eid: z.string().optional().default(""),
|
||||
msisdn: z.string().min(1, "MSISDN is required"),
|
||||
imsi: z.string().min(1, "IMSI is required"),
|
||||
remainingQuotaMb: z.number().nonnegative(),
|
||||
remainingQuotaKb: z.number().nonnegative(),
|
||||
voiceMailEnabled: z.boolean(),
|
||||
callWaitingEnabled: z.boolean(),
|
||||
internationalRoamingEnabled: z.boolean(),
|
||||
networkType: z.string().min(1, "Network type is required"),
|
||||
activatedAt: z.string().optional(),
|
||||
expiresAt: z.string().optional(),
|
||||
}) satisfies z.ZodType<SimDetails>;
|
||||
|
||||
export const recentDayUsageSchema = z.object({
|
||||
date: z.string().min(1, "Usage date required"),
|
||||
usageKb: z.number().nonnegative(),
|
||||
usageMb: z.number().nonnegative(),
|
||||
});
|
||||
|
||||
export const simUsageSchema = z.object({
|
||||
account: z.string().min(1, "Account is required"),
|
||||
todayUsageMb: z.number().nonnegative(),
|
||||
todayUsageKb: z.number().nonnegative(),
|
||||
monthlyUsageMb: z.number().nonnegative().optional(),
|
||||
monthlyUsageKb: z.number().nonnegative().optional(),
|
||||
recentDaysUsage: z.array(recentDayUsageSchema),
|
||||
isBlacklisted: z.boolean(),
|
||||
lastUpdated: z.string().optional(),
|
||||
}) satisfies z.ZodType<SimUsage>;
|
||||
|
||||
export const simTopUpHistoryEntrySchema = z.object({
|
||||
quotaKb: z.number().nonnegative(),
|
||||
quotaMb: z.number().nonnegative(),
|
||||
addedDate: z.string().min(1, "Added date is required"),
|
||||
expiryDate: z.string().min(1, "Expiry date is required"),
|
||||
campaignCode: z.string().min(1, "Campaign code is required"),
|
||||
}) satisfies z.ZodType<SimTopUpHistoryEntry>;
|
||||
|
||||
export const simTopUpHistorySchema = z.object({
|
||||
account: z.string().min(1, "Account is required"),
|
||||
totalAdditions: z.number().nonnegative(),
|
||||
additionCount: z.number().nonnegative(),
|
||||
history: z.array(simTopUpHistoryEntrySchema),
|
||||
}) satisfies z.ZodType<SimTopUpHistory>;
|
||||
|
||||
export type SimDetailsSchema = typeof simDetailsSchema;
|
||||
export type SimUsageSchema = typeof simUsageSchema;
|
||||
export type SimTopUpHistorySchema = typeof simTopUpHistorySchema;
|
||||
@ -1 +0,0 @@
|
||||
export * from "./subscription.schema";
|
||||
@ -1,60 +0,0 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import type {
|
||||
Subscription,
|
||||
SubscriptionCycle,
|
||||
SubscriptionList,
|
||||
SubscriptionStatus,
|
||||
} from "@customer-portal/contracts/subscriptions";
|
||||
|
||||
const subscriptionStatusValues: readonly SubscriptionStatus[] = [
|
||||
"Active",
|
||||
"Inactive",
|
||||
"Pending",
|
||||
"Cancelled",
|
||||
"Suspended",
|
||||
"Terminated",
|
||||
"Completed",
|
||||
] as const;
|
||||
|
||||
const subscriptionCycleValues: readonly SubscriptionCycle[] = [
|
||||
"Monthly",
|
||||
"Quarterly",
|
||||
"Semi-Annually",
|
||||
"Annually",
|
||||
"Biennially",
|
||||
"Triennially",
|
||||
"One-time",
|
||||
"Free",
|
||||
] as const;
|
||||
|
||||
export const subscriptionStatusSchema = z.enum(subscriptionStatusValues);
|
||||
export const subscriptionCycleSchema = z.enum(subscriptionCycleValues);
|
||||
|
||||
export const subscriptionSchema = z.object({
|
||||
id: z.number().int().positive("Subscription id must be positive"),
|
||||
serviceId: z.number().int().positive("Service id must be positive"),
|
||||
productName: z.string().min(1, "Product name is required"),
|
||||
domain: z.string().optional(),
|
||||
cycle: subscriptionCycleSchema,
|
||||
status: subscriptionStatusSchema,
|
||||
nextDue: z.string().optional(),
|
||||
amount: z.number(),
|
||||
currency: z.string().min(1, "Currency is required"),
|
||||
currencySymbol: z.string().optional(),
|
||||
registrationDate: z.string().min(1, "Registration date is required"),
|
||||
notes: z.string().optional(),
|
||||
customFields: z.record(z.string(), z.string()).optional(),
|
||||
orderNumber: z.string().optional(),
|
||||
groupName: z.string().optional(),
|
||||
paymentMethod: z.string().optional(),
|
||||
serverName: z.string().optional(),
|
||||
}) satisfies z.ZodType<Subscription>;
|
||||
|
||||
export const subscriptionListSchema = z.object({
|
||||
subscriptions: z.array(subscriptionSchema),
|
||||
totalCount: z.number().int().nonnegative(),
|
||||
}) satisfies z.ZodType<SubscriptionList>;
|
||||
|
||||
export type SubscriptionSchema = typeof subscriptionSchema;
|
||||
export type SubscriptionListSchema = typeof subscriptionListSchema;
|
||||
@ -1,22 +0,0 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"module": "NodeNext",
|
||||
"moduleResolution": "nodenext",
|
||||
"lib": ["ES2022"],
|
||||
"outDir": "./dist",
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"composite": true,
|
||||
"tsBuildInfoFile": "./tsconfig.tsbuildinfo",
|
||||
"skipLibCheck": true,
|
||||
"paths": {
|
||||
"@customer-portal/contracts/*": ["../contracts/dist/*"]
|
||||
}
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.spec.ts"],
|
||||
"references": [
|
||||
{ "path": "../contracts" }
|
||||
]
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user