barsa 5c329bbe96 refactor: update WHMCS mappers and schemas to use numberLike for type safety
- Refactor various mappers in billing, payments, services, and subscriptions to ensure IDs are consistently converted to numbers.
- Update raw types schemas to utilize whmcsNumberLike and whmcsString for improved validation and type safety.
- Enhance the whmcs-utils to include schema exports for better modularity.
2026-02-24 13:56:02 +09:00

125 lines
3.4 KiB
TypeScript

import { parseAmount } from "../../../common/providers/whmcs-utils/index.js";
import {
whmcsCatalogProductListResponseSchema,
type WhmcsCatalogProductListResponse,
} from "./raw.types.js";
export interface WhmcsCatalogPricing {
currency: string;
prefix?: string | undefined;
suffix?: string | undefined;
monthly?: number | undefined;
quarterly?: number | undefined;
semiannually?: number | undefined;
annually?: number | undefined;
biennially?: number | undefined;
triennially?: number | undefined;
setupFees: {
monthly?: number | undefined;
quarterly?: number | undefined;
semiannually?: number | undefined;
annually?: number | undefined;
biennially?: number | undefined;
triennially?: number | undefined;
};
}
export interface WhmcsCatalogProductNormalized {
id: string;
groupId: number;
name: string;
description: string;
module: string;
payType: string;
pricing: Record<string, WhmcsCatalogPricing>;
}
const cycles = [
"monthly",
"quarterly",
"semiannually",
"annually",
"biennially",
"triennially",
] as const;
const setupFeeKeys = [
"msetupfee",
"qsetupfee",
"ssetupfee",
"asetupfee",
"bsetupfee",
"tsetupfee",
] as const;
const normalizePrice = (value: string | undefined): number | undefined =>
value === undefined ? undefined : parseAmount(value);
export function parseWhmcsCatalogProductListResponse(
raw: unknown
): WhmcsCatalogProductListResponse {
return whmcsCatalogProductListResponseSchema.parse(raw);
}
export function transformWhmcsCatalogProductsResponse(
raw: unknown
): WhmcsCatalogProductNormalized[] {
const parsed = parseWhmcsCatalogProductListResponse(raw);
const products = parsed.products.product;
return products.map(product => {
const pricingEntries = Object.entries(product.pricing ?? {});
const pricing = pricingEntries.reduce<Record<string, WhmcsCatalogPricing>>(
(acc, [currency, cyclePricing]) => {
const normalizedCycles: Partial<WhmcsCatalogPricing> = {
currency,
prefix: cyclePricing.prefix,
suffix: cyclePricing.suffix,
};
for (const cycle of cycles) {
const value = cyclePricing[cycle];
if (value !== undefined) {
normalizedCycles[cycle] = normalizePrice(value);
}
}
const setupFees: WhmcsCatalogPricing["setupFees"] = {};
for (const [index, feeKey] of setupFeeKeys.entries()) {
const value = cyclePricing[feeKey];
const cycleKey = cycles[index];
if (value !== undefined && cycleKey !== undefined) {
setupFees[cycleKey] = normalizePrice(value);
}
}
acc[currency] = {
currency,
prefix: cyclePricing.prefix,
suffix: cyclePricing.suffix,
monthly: normalizedCycles.monthly,
quarterly: normalizedCycles.quarterly,
semiannually: normalizedCycles.semiannually,
annually: normalizedCycles.annually,
biennially: normalizedCycles.biennially,
triennially: normalizedCycles.triennially,
setupFees,
};
return acc;
},
{}
);
return {
id: String(product.pid),
groupId: Number(product.gid),
name: product.name,
description: product.description,
module: product.module,
payType: product.paytype,
pricing,
} satisfies WhmcsCatalogProductNormalized;
});
}