- Standardized import statements and formatting in various files for better code clarity. - Enhanced error messages and logging for improved debugging and user experience. - Adjusted whitespace and line breaks in multiple components to follow best practices. - Updated environment variable handling and configuration for consistency across services.
372 lines
10 KiB
TypeScript
372 lines
10 KiB
TypeScript
import { getErrorMessage } from "../../common/utils/error.util";
|
|
import { Injectable, Inject } from "@nestjs/common";
|
|
import type {
|
|
Invoice,
|
|
InvoiceList,
|
|
Subscription,
|
|
SubscriptionList,
|
|
PaymentMethodList,
|
|
PaymentGatewayList,
|
|
} from "@customer-portal/shared";
|
|
import { WhmcsConnectionService } from "./services/whmcs-connection.service";
|
|
import { WhmcsInvoiceService, InvoiceFilters } from "./services/whmcs-invoice.service";
|
|
import {
|
|
WhmcsSubscriptionService,
|
|
SubscriptionFilters,
|
|
} from "./services/whmcs-subscription.service";
|
|
import { WhmcsClientService } from "./services/whmcs-client.service";
|
|
import { WhmcsPaymentService } from "./services/whmcs-payment.service";
|
|
import { WhmcsSsoService } from "./services/whmcs-sso.service";
|
|
import { WhmcsOrderService } from "./services/whmcs-order.service";
|
|
import {
|
|
WhmcsAddClientParams,
|
|
WhmcsClientResponse,
|
|
WhmcsCatalogProductsResponse,
|
|
} from "./types/whmcs-api.types";
|
|
import { Logger } from "nestjs-pino";
|
|
|
|
// Re-export interfaces for backward compatibility
|
|
export type { InvoiceFilters, SubscriptionFilters };
|
|
|
|
@Injectable()
|
|
export class WhmcsService {
|
|
constructor(
|
|
private readonly connectionService: WhmcsConnectionService,
|
|
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<InvoiceList> {
|
|
return this.invoiceService.getInvoices(clientId, userId, filters);
|
|
}
|
|
|
|
/**
|
|
* Get invoices with items (for subscription linking)
|
|
*/
|
|
async getInvoicesWithItems(
|
|
clientId: number,
|
|
userId: string,
|
|
filters: InvoiceFilters = {}
|
|
): Promise<InvoiceList> {
|
|
return this.invoiceService.getInvoicesWithItems(clientId, userId, filters);
|
|
}
|
|
|
|
/**
|
|
* Get individual invoice by ID with caching
|
|
*/
|
|
async getInvoiceById(clientId: number, userId: string, invoiceId: number): Promise<Invoice> {
|
|
return this.invoiceService.getInvoiceById(clientId, userId, invoiceId);
|
|
}
|
|
|
|
/**
|
|
* Invalidate cache for a specific invoice
|
|
*/
|
|
async invalidateInvoiceCache(userId: string, invoiceId: number): Promise<void> {
|
|
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<SubscriptionList> {
|
|
return this.subscriptionService.getSubscriptions(clientId, userId, filters);
|
|
}
|
|
|
|
/**
|
|
* Get individual subscription by ID
|
|
*/
|
|
async getSubscriptionById(
|
|
clientId: number,
|
|
userId: string,
|
|
subscriptionId: number
|
|
): Promise<Subscription> {
|
|
return this.subscriptionService.getSubscriptionById(clientId, userId, subscriptionId);
|
|
}
|
|
|
|
/**
|
|
* Invalidate cache for a specific subscription
|
|
*/
|
|
async invalidateSubscriptionCache(userId: string, subscriptionId: number): Promise<void> {
|
|
return this.subscriptionService.invalidateSubscriptionCache(userId, subscriptionId);
|
|
}
|
|
|
|
/**
|
|
* Get subscription statistics for a client
|
|
*/
|
|
async getSubscriptionStats(
|
|
clientId: number,
|
|
userId: string
|
|
): Promise<{
|
|
total: number;
|
|
active: number;
|
|
suspended: number;
|
|
cancelled: number;
|
|
pending: number;
|
|
}> {
|
|
try {
|
|
const subscriptionList = await this.subscriptionService.getSubscriptions(clientId, userId);
|
|
const subscriptions: Subscription[] = subscriptionList.subscriptions;
|
|
|
|
const stats = {
|
|
total: subscriptions.length,
|
|
active: subscriptions.filter((s: Subscription) => s.status === "Active").length,
|
|
suspended: subscriptions.filter((s: Subscription) => s.status === "Suspended").length,
|
|
cancelled: subscriptions.filter((s: Subscription) => s.status === "Cancelled").length,
|
|
pending: subscriptions.filter((s: Subscription) => s.status === "Pending").length,
|
|
};
|
|
|
|
this.logger.debug(`Generated subscription stats for client ${clientId}:`, stats);
|
|
return stats;
|
|
} catch (error) {
|
|
this.logger.error(`Failed to get subscription stats for client ${clientId}`, {
|
|
error: getErrorMessage(error),
|
|
userId,
|
|
});
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
// ==========================================
|
|
// 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
|
|
*/
|
|
async getClientDetails(clientId: number): Promise<WhmcsClientResponse["client"]> {
|
|
return this.clientService.getClientDetails(clientId);
|
|
}
|
|
|
|
/**
|
|
* Get client details by email
|
|
*/
|
|
async getClientDetailsByEmail(email: string): Promise<WhmcsClientResponse["client"]> {
|
|
return this.clientService.getClientDetailsByEmail(email);
|
|
}
|
|
|
|
/**
|
|
* Update client details in WHMCS
|
|
*/
|
|
async updateClient(
|
|
clientId: number,
|
|
updateData: Partial<WhmcsClientResponse["client"]>
|
|
): Promise<void> {
|
|
return 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<void> {
|
|
return this.clientService.invalidateUserCache(userId);
|
|
}
|
|
|
|
// ==========================================
|
|
// PAYMENT OPERATIONS (delegate to PaymentService)
|
|
// ==========================================
|
|
|
|
/**
|
|
* Get payment methods for a client
|
|
*/
|
|
async getPaymentMethods(clientId: number, userId: string): Promise<PaymentMethodList> {
|
|
return this.paymentService.getPaymentMethods(clientId, userId);
|
|
}
|
|
|
|
/**
|
|
* Get available payment gateways
|
|
*/
|
|
async getPaymentGateways(): Promise<PaymentGatewayList> {
|
|
return this.paymentService.getPaymentGateways();
|
|
}
|
|
|
|
/**
|
|
* Invalidate payment methods cache for a user
|
|
*/
|
|
async invalidatePaymentMethodsCache(userId: string): Promise<void> {
|
|
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<WhmcsCatalogProductsResponse> {
|
|
return this.paymentService.getProducts() as Promise<WhmcsCatalogProductsResponse>;
|
|
}
|
|
|
|
/**
|
|
* Transform product data (delegate to transformer)
|
|
*/
|
|
transformProduct(whmcsProduct: WhmcsCatalogProductsResponse["products"]["product"][0]): unknown {
|
|
return this.paymentService.transformProduct(whmcsProduct);
|
|
}
|
|
|
|
// ==========================================
|
|
// 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<string> {
|
|
return this.ssoService.whmcsSsoForInvoice(clientId, invoiceId, target);
|
|
}
|
|
|
|
// ==========================================
|
|
// CONNECTION & HEALTH (delegate to ConnectionService)
|
|
// ==========================================
|
|
|
|
/**
|
|
* Health check for WHMCS API
|
|
*/
|
|
async healthCheck(): Promise<boolean> {
|
|
return this.connectionService.healthCheck();
|
|
}
|
|
|
|
/**
|
|
* Check if WHMCS service is available
|
|
*/
|
|
async isAvailable(): Promise<boolean> {
|
|
return this.connectionService.isAvailable();
|
|
}
|
|
|
|
/**
|
|
* Get WHMCS system information
|
|
*/
|
|
async getSystemInfo(): Promise<unknown> {
|
|
return this.connectionService.getSystemInfo();
|
|
}
|
|
|
|
// ==========================================
|
|
// INVOICE CREATION AND PAYMENT OPERATIONS
|
|
// ==========================================
|
|
|
|
/**
|
|
* 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;
|
|
}): Promise<{ success: boolean; transactionId?: string; error?: string }> {
|
|
return this.invoiceService.capturePayment(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;
|
|
}
|
|
}
|