- Removed the domain mappings module, consolidating related types and schemas into the id-mappings feature. - Updated import paths across the BFF to reflect the new structure, ensuring compliance with import hygiene rules. - Cleaned up unused files and optimized the codebase for better maintainability and clarity.
167 lines
6.0 KiB
TypeScript
167 lines
6.0 KiB
TypeScript
import { Controller, Get, Post, Param, Query, Request, HttpCode, HttpStatus } from "@nestjs/common";
|
|
import { InvoicesOrchestratorService } from "./services/invoices-orchestrator.service.js";
|
|
import { WhmcsService } from "@bff/integrations/whmcs/whmcs.service.js";
|
|
import { MappingsService } from "@bff/modules/id-mappings/mappings.service.js";
|
|
import { createZodDto, ZodResponse } from "nestjs-zod";
|
|
import type { RequestWithUser } from "@bff/modules/auth/auth.types.js";
|
|
|
|
import type { Invoice, InvoiceList, InvoiceSsoLink } from "@customer-portal/domain/billing";
|
|
import {
|
|
invoiceIdParamSchema,
|
|
invoiceListQuerySchema,
|
|
invoiceListSchema,
|
|
invoiceSchema,
|
|
invoiceSsoLinkSchema,
|
|
invoiceSsoQuerySchema,
|
|
invoicePaymentLinkQuerySchema,
|
|
} from "@customer-portal/domain/billing";
|
|
import type { Subscription } from "@customer-portal/domain/subscriptions";
|
|
import type {
|
|
PaymentMethodList,
|
|
PaymentGatewayList,
|
|
InvoicePaymentLink,
|
|
} from "@customer-portal/domain/payments";
|
|
import {
|
|
paymentMethodListSchema,
|
|
paymentGatewayListSchema,
|
|
invoicePaymentLinkSchema,
|
|
} from "@customer-portal/domain/payments";
|
|
|
|
class InvoiceListQueryDto extends createZodDto(invoiceListQuerySchema) {}
|
|
class InvoiceIdParamDto extends createZodDto(invoiceIdParamSchema) {}
|
|
class InvoiceListDto extends createZodDto(invoiceListSchema) {}
|
|
class InvoiceDto extends createZodDto(invoiceSchema) {}
|
|
class InvoiceSsoLinkDto extends createZodDto(invoiceSsoLinkSchema) {}
|
|
class InvoiceSsoQueryDto extends createZodDto(invoiceSsoQuerySchema) {}
|
|
class InvoicePaymentLinkQueryDto extends createZodDto(invoicePaymentLinkQuerySchema) {}
|
|
class PaymentMethodListDto extends createZodDto(paymentMethodListSchema) {}
|
|
class PaymentGatewayListDto extends createZodDto(paymentGatewayListSchema) {}
|
|
class InvoicePaymentLinkDto extends createZodDto(invoicePaymentLinkSchema) {}
|
|
|
|
/**
|
|
* Billing Controller
|
|
*
|
|
* All request validation is handled by Zod schemas via global ZodValidationPipe.
|
|
* Business logic is delegated to service layer.
|
|
*/
|
|
@Controller("invoices")
|
|
export class BillingController {
|
|
constructor(
|
|
private readonly invoicesService: InvoicesOrchestratorService,
|
|
private readonly whmcsService: WhmcsService,
|
|
private readonly mappingsService: MappingsService
|
|
) {}
|
|
|
|
@Get()
|
|
@ZodResponse({ description: "List invoices", type: InvoiceListDto })
|
|
async getInvoices(
|
|
@Request() req: RequestWithUser,
|
|
@Query() query: InvoiceListQueryDto
|
|
): Promise<InvoiceList> {
|
|
return this.invoicesService.getInvoices(req.user.id, query);
|
|
}
|
|
|
|
@Get("payment-methods")
|
|
@ZodResponse({ description: "List payment methods", type: PaymentMethodListDto })
|
|
async getPaymentMethods(@Request() req: RequestWithUser): Promise<PaymentMethodList> {
|
|
const mapping = await this.mappingsService.findByUserId(req.user.id);
|
|
if (!mapping?.whmcsClientId) {
|
|
throw new Error("WHMCS client mapping not found");
|
|
}
|
|
return this.whmcsService.getPaymentMethods(mapping.whmcsClientId, req.user.id);
|
|
}
|
|
|
|
@Get("payment-gateways")
|
|
@ZodResponse({ description: "List payment gateways", type: PaymentGatewayListDto })
|
|
async getPaymentGateways(): Promise<PaymentGatewayList> {
|
|
return this.whmcsService.getPaymentGateways();
|
|
}
|
|
|
|
@Post("payment-methods/refresh")
|
|
@HttpCode(HttpStatus.OK)
|
|
@ZodResponse({ description: "Refresh payment methods", type: PaymentMethodListDto })
|
|
async refreshPaymentMethods(@Request() req: RequestWithUser): Promise<PaymentMethodList> {
|
|
// Invalidate cache first
|
|
await this.whmcsService.invalidatePaymentMethodsCache(req.user.id);
|
|
|
|
// Return fresh payment methods
|
|
const mapping = await this.mappingsService.findByUserId(req.user.id);
|
|
if (!mapping?.whmcsClientId) {
|
|
throw new Error("WHMCS client mapping not found");
|
|
}
|
|
return this.whmcsService.getPaymentMethods(mapping.whmcsClientId, req.user.id);
|
|
}
|
|
|
|
@Get(":id")
|
|
@ZodResponse({ description: "Get invoice by id", type: InvoiceDto })
|
|
async getInvoiceById(
|
|
@Request() req: RequestWithUser,
|
|
@Param() params: InvoiceIdParamDto
|
|
): Promise<Invoice> {
|
|
return this.invoicesService.getInvoiceById(req.user.id, params.id);
|
|
}
|
|
|
|
@Get(":id/subscriptions")
|
|
getInvoiceSubscriptions(): Subscription[] {
|
|
// This functionality has been moved to WHMCS directly
|
|
// For now, return empty array as subscriptions are managed in WHMCS
|
|
return [];
|
|
}
|
|
|
|
@Post(":id/sso-link")
|
|
@HttpCode(HttpStatus.OK)
|
|
@ZodResponse({ description: "Create invoice SSO link", type: InvoiceSsoLinkDto })
|
|
async createSsoLink(
|
|
@Request() req: RequestWithUser,
|
|
@Param() params: InvoiceIdParamDto,
|
|
@Query() query: InvoiceSsoQueryDto
|
|
): Promise<InvoiceSsoLink> {
|
|
const mapping = await this.mappingsService.findByUserId(req.user.id);
|
|
if (!mapping?.whmcsClientId) {
|
|
throw new Error("WHMCS client mapping not found");
|
|
}
|
|
|
|
const parsedQuery = invoiceSsoQuerySchema.parse(query as unknown);
|
|
|
|
const ssoUrl = await this.whmcsService.whmcsSsoForInvoice(
|
|
mapping.whmcsClientId,
|
|
params.id,
|
|
parsedQuery.target
|
|
);
|
|
|
|
return {
|
|
url: ssoUrl,
|
|
expiresAt: new Date(Date.now() + 60000).toISOString(), // 60 seconds per WHMCS spec
|
|
};
|
|
}
|
|
|
|
@Post(":id/payment-link")
|
|
@HttpCode(HttpStatus.OK)
|
|
@ZodResponse({ description: "Create invoice payment link", type: InvoicePaymentLinkDto })
|
|
async createPaymentLink(
|
|
@Request() req: RequestWithUser,
|
|
@Param() params: InvoiceIdParamDto,
|
|
@Query() query: InvoicePaymentLinkQueryDto
|
|
): Promise<InvoicePaymentLink> {
|
|
const mapping = await this.mappingsService.findByUserId(req.user.id);
|
|
if (!mapping?.whmcsClientId) {
|
|
throw new Error("WHMCS client mapping not found");
|
|
}
|
|
|
|
const parsedQuery = invoicePaymentLinkQuerySchema.parse(query as unknown);
|
|
|
|
const ssoResult = await this.whmcsService.createPaymentSsoToken(
|
|
mapping.whmcsClientId,
|
|
params.id,
|
|
parsedQuery.paymentMethodId,
|
|
parsedQuery.gatewayName
|
|
);
|
|
|
|
return {
|
|
url: ssoResult.url,
|
|
expiresAt: ssoResult.expiresAt,
|
|
gatewayName: parsedQuery.gatewayName,
|
|
};
|
|
}
|
|
}
|