import { Injectable, Inject } from "@nestjs/common"; import type { Invoice, InvoiceList } from "@customer-portal/domain/billing"; import type { Subscription, SubscriptionList } from "@customer-portal/domain/subscriptions"; import type { PaymentMethodList, PaymentGatewayList } from "@customer-portal/domain/payments"; import { addressSchema, type Address, type WhmcsClient } from "@customer-portal/domain/customer"; import { prepareWhmcsClientAddressUpdate } from "@customer-portal/domain/customer/providers"; import { WhmcsConnectionOrchestratorService } from "./connection/services/whmcs-connection-orchestrator.service.js"; import { WhmcsInvoiceService } from "./services/whmcs-invoice.service.js"; import type { InvoiceFilters } from "./services/whmcs-invoice.service.js"; import { WhmcsSubscriptionService } from "./services/whmcs-subscription.service.js"; import type { SubscriptionFilters } from "./services/whmcs-subscription.service.js"; import { WhmcsClientService } from "./services/whmcs-client.service.js"; import { WhmcsPaymentService } from "./services/whmcs-payment.service.js"; import { WhmcsSsoService } from "./services/whmcs-sso.service.js"; import { WhmcsOrderService } from "./services/whmcs-order.service.js"; import type { WhmcsAddClientParams, WhmcsClientResponse, } from "@customer-portal/domain/customer/providers"; import type { WhmcsGetClientsProductsParams, WhmcsProductListResponse, } from "@customer-portal/domain/subscriptions/providers"; import type { WhmcsCatalogProductNormalized } from "@customer-portal/domain/services/providers"; import { Logger } from "nestjs-pino"; @Injectable() export class WhmcsService { constructor( private readonly connectionService: WhmcsConnectionOrchestratorService, private readonly invoiceService: WhmcsInvoiceService, private readonly subscriptionService: WhmcsSubscriptionService, private readonly clientService: WhmcsClientService, private readonly paymentService: WhmcsPaymentService, private readonly ssoService: WhmcsSsoService, private readonly orderService: WhmcsOrderService, @Inject(Logger) private readonly logger: Logger ) {} // ========================================== // INVOICE OPERATIONS (delegate to InvoiceService) // ========================================== /** * Get paginated invoices for a client with caching */ async getInvoices( clientId: number, userId: string, filters: InvoiceFilters = {} ): Promise { return this.invoiceService.getInvoices(clientId, userId, filters); } /** * Get invoices with items (for subscription linking) */ async getInvoicesWithItems( clientId: number, userId: string, filters: InvoiceFilters = {} ): Promise { return this.invoiceService.getInvoicesWithItems(clientId, userId, filters); } /** * Get individual invoice by ID with caching */ async getInvoiceById(clientId: number, userId: string, invoiceId: number): Promise { return this.invoiceService.getInvoiceById(clientId, userId, invoiceId); } /** * Invalidate cache for a specific invoice */ async invalidateInvoiceCache(userId: string, invoiceId: number): Promise { return this.invoiceService.invalidateInvoiceCache(userId, invoiceId); } // ========================================== // SUBSCRIPTION OPERATIONS (delegate to SubscriptionService) // ========================================== /** * Get client subscriptions/services with caching */ async getSubscriptions( clientId: number, userId: string, filters: SubscriptionFilters = {} ): Promise { return this.subscriptionService.getSubscriptions(clientId, userId, filters); } /** * Get individual subscription by ID */ async getSubscriptionById( clientId: number, userId: string, subscriptionId: number ): Promise { return this.subscriptionService.getSubscriptionById(clientId, userId, subscriptionId); } /** * Invalidate cache for a specific subscription */ async invalidateSubscriptionCache(userId: string, subscriptionId: number): Promise { return this.subscriptionService.invalidateSubscriptionCache(userId, subscriptionId); } // ========================================== // CLIENT OPERATIONS (delegate to ClientService) // ========================================== /** * Validate client login credentials */ async validateLogin( email: string, password: string ): Promise<{ userId: number; passwordHash: string }> { return this.clientService.validateLogin(email, password); } /** * Get client details by ID * Returns internal WhmcsClient (type inferred) */ async getClientDetails(clientId: number): Promise { return this.clientService.getClientDetails(clientId); } /** * Update client details in WHMCS */ async updateClient( clientId: number, updateData: Partial ): Promise { return this.clientService.updateClient(clientId, updateData); } /** * Convenience helpers for address get/update on WHMCS client */ async getClientAddress(clientId: number): Promise
{ const customer = await this.clientService.getClientDetails(clientId); return addressSchema.parse(customer.address ?? {}); } async updateClientAddress(clientId: number, address: Partial
): Promise { const parsed = addressSchema.partial().parse(address ?? {}); const updateData = prepareWhmcsClientAddressUpdate(parsed); if (Object.keys(updateData).length === 0) return; await this.clientService.updateClient(clientId, updateData); } /** * Add new client */ async addClient(clientData: WhmcsAddClientParams): Promise<{ clientId: number }> { return this.clientService.addClient(clientData); } /** * Invalidate cache for a user */ async invalidateUserCache(userId: string): Promise { return this.clientService.invalidateUserCache(userId); } // ========================================== // PAYMENT OPERATIONS (delegate to PaymentService) // ========================================== /** * Get payment methods for a client */ async getPaymentMethods(clientId: number, userId: string): Promise { return this.paymentService.getPaymentMethods(clientId, userId); } /** * Get available payment gateways */ async getPaymentGateways(): Promise { return this.paymentService.getPaymentGateways(); } /** * Invalidate payment methods cache for a user */ async invalidatePaymentMethodsCache(userId: string): Promise { return this.paymentService.invalidatePaymentMethodsCache(userId); } /** * Create SSO token with payment method for invoice payment */ async createPaymentSsoToken( clientId: number, invoiceId: number, paymentMethodId?: number, gatewayName?: string ): Promise<{ url: string; expiresAt: string }> { return this.paymentService.createPaymentSsoToken( clientId, invoiceId, paymentMethodId, gatewayName ); } /** * Get products catalog */ async getProducts(): Promise { return this.paymentService.getProducts(); } // ========================================== // SSO OPERATIONS (delegate to SsoService) // ========================================== /** * Create SSO token for WHMCS access */ async createSsoToken( clientId: number, destination?: string, ssoRedirectPath?: string ): Promise<{ url: string; expiresAt: string }> { return this.ssoService.createSsoToken(clientId, destination, ssoRedirectPath); } /** * Helper function to create SSO links for invoices */ async whmcsSsoForInvoice( clientId: number, invoiceId: number, target: "view" | "download" | "pay" ): Promise { return this.ssoService.whmcsSsoForInvoice(clientId, invoiceId, target); } // ========================================== // CONNECTION & HEALTH (delegate to ConnectionService) // ========================================== /** * Health check for WHMCS API */ async healthCheck(): Promise { return this.connectionService.healthCheck(); } /** * Check if WHMCS service is available */ async isAvailable(): Promise { return this.connectionService.isAvailable(); } /** * Get WHMCS system information */ async getSystemInfo(): Promise { return this.connectionService.getSystemInfo(); } async getClientsProducts( params: WhmcsGetClientsProductsParams ): Promise { return this.connectionService.getClientsProducts(params); } // ========================================== // ORDER OPERATIONS (delegate to OrderService) // ========================================== /** * Get order service for direct access to order operations * Used by OrderProvisioningService for complex order workflows */ getOrderService(): WhmcsOrderService { return this.orderService; } // ========================================== // INVOICE CREATION AND PAYMENT OPERATIONS (Used by SIM/Order services) // ========================================== /** * Create a new invoice for a client */ async createInvoice(params: { clientId: number; description: string; amount: number; currency?: string; dueDate?: Date; notes?: string; }): Promise<{ id: number; number: string; total: number; status: string }> { return this.invoiceService.createInvoice(params); } /** * Update an existing invoice */ async updateInvoice(params: { invoiceId: number; status?: | "Draft" | "Unpaid" | "Paid" | "Cancelled" | "Refunded" | "Collections" | "Payment Pending"; dueDate?: Date; notes?: string; }): Promise<{ success: boolean; message?: string }> { return this.invoiceService.updateInvoice(params); } /** * Capture payment for an invoice */ async capturePayment(params: { invoiceId: number; amount: number; currency?: string; userId?: string; }): Promise<{ success: boolean; transactionId?: string; error?: string }> { return this.invoiceService.capturePayment(params); } }