/** * Orders Domain - WHMCS Provider Mapper * * Transforms normalized order data to WHMCS API format. */ import type { OrderDetails, OrderItemDetails } from "../../contract"; import { type WhmcsOrderItem, type WhmcsAddOrderParams, type WhmcsAddOrderPayload, } from "./raw.types"; export interface OrderItemMappingResult { whmcsItems: WhmcsOrderItem[]; summary: { totalItems: number; serviceItems: number; activationItems: number; }; } function normalizeBillingCycle(cycle: string | undefined): WhmcsOrderItem["billingCycle"] { if (!cycle) return "monthly"; // Default 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"; } /** * Map a single order item to WHMCS format */ export function mapOrderItemToWhmcs( item: OrderItemDetails, index = 0 ): WhmcsOrderItem { if (!item.product?.whmcsProductId) { throw new Error(`Order item ${index} missing WHMCS product ID`); } const whmcsItem: WhmcsOrderItem = { productId: item.product.whmcsProductId, billingCycle: normalizeBillingCycle(item.billingCycle), quantity: item.quantity, }; return whmcsItem; } /** * Map order details to WHMCS items format * Extracts items from OrderDetails and transforms to WHMCS API format */ export function mapOrderToWhmcsItems( orderDetails: OrderDetails ): OrderItemMappingResult { if (!orderDetails.items || orderDetails.items.length === 0) { throw new Error("No order items provided for WHMCS mapping"); } const whmcsItems: WhmcsOrderItem[] = []; let serviceItems = 0; let activationItems = 0; orderDetails.items.forEach((item, index) => { const mapped = mapOrderItemToWhmcs(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 structured params into 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 serialized data */ function serializeForWhmcs(data: Record): 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("; "); }