import { Controller, Get, Post, Patch, Body, Param, Query, UseGuards, Request, } from '@nestjs/common'; import { AuthGuard } from '@nestjs/passport'; import { ApiTags, ApiOperation, ApiBearerAuth } from '@nestjs/swagger'; import { BillingService } from './billing.service'; import { BillingOpsService } from './billing-ops.service'; import { assertStubGatewayAllowed } from './payment-gateway.util'; import { ChargeWalletDto, InitiateInvoicePaymentDto, VerifyInvoiceGatewayDto, UpdateInvoiceStatusDto, } from './dto/billing.dto'; import { RolesGuard } from '../common/guards/roles.guard'; import { Roles } from '../common/decorators/roles.decorator'; import { UserRole, InvoiceStatus, PaymentMethod } from '../common/enums'; @ApiTags('Billing') @ApiBearerAuth() @Controller('billing') @UseGuards(AuthGuard('jwt'), RolesGuard) export class BillingInvoicesController { constructor( private readonly billingService: BillingService, private readonly billingOpsService: BillingOpsService, ) {} // ─── Invoices ───────────────────────────────────────────────────── @Get('invoices') @ApiOperation({ summary: 'List my invoices' }) async listMyInvoices( @Request() req: any, @Query('status') status?: InvoiceStatus, @Query('applicationId') applicationId?: string, @Query('limit') limit?: string, ) { return this.billingService.listInvoices(req.user, { status, applicationId, limit: limit ? parseInt(limit, 10) : undefined, }); } @Get('invoices/:id') @ApiOperation({ summary: 'Get one invoice with line items and transactions' }) async getInvoice(@Request() req: any, @Param('id') id: string) { return this.billingService.getInvoiceForUser(id, req.user); } @Post('invoices/:id/pay/mixed') @ApiOperation({ summary: 'Pay invoice with wallet first, then gateway for the remaining amount' }) async initiateInvoiceMixed( @Request() req: any, @Param('id') id: string, @Body() dto: InitiateInvoicePaymentDto, ) { const result = await this.billingService.initiateInvoiceMixedPayment(id, req.user, dto.callbackUrl); const effect = await this.billingOpsService.completePaidInvoiceEffect(result.invoice); return { ...result, effect }; } @Post('invoices/:id/gateway/verify') @ApiOperation({ summary: 'Verify invoice gateway payment' }) async verifyInvoiceGateway( @Request() req: any, @Param('id') id: string, @Body() dto: VerifyInvoiceGatewayDto, ) { assertStubGatewayAllowed(); const result = await this.billingService.verifyInvoiceGatewayPayment( id, req.user, dto.trackingCode, dto.amount, ); const effect = await this.billingOpsService.completePaidInvoiceEffect(result.invoice); return { ...result, effect }; } // ─── Invoice Admin ──────────────────────────────────────────────── @Get('admin/invoices') @Roles(UserRole.ADMIN) @ApiOperation({ summary: 'List all invoices (Admin)' }) async listAdminInvoices( @Request() req: any, @Query('status') status?: InvoiceStatus, @Query('userId') userId?: string, @Query('applicationId') applicationId?: string, @Query('paymentMethod') paymentMethod?: PaymentMethod, @Query('search') search?: string, @Query('limit') limit?: string, ) { return this.billingService.listInvoices(req.user, { status, userId, applicationId, paymentMethod, search, limit: limit ? parseInt(limit, 10) : undefined, }); } @Get('admin/invoices/:id') @Roles(UserRole.ADMIN) @ApiOperation({ summary: 'Get invoice details (Admin)' }) async getAdminInvoice(@Request() req: any, @Param('id') id: string) { return this.billingService.getInvoiceForUser(id, req.user); } @Patch('admin/invoices/:id/status') @Roles(UserRole.ADMIN) @ApiOperation({ summary: 'Update invoice status with reason (Admin)' }) async updateAdminInvoiceStatus( @Param('id') id: string, @Body() dto: UpdateInvoiceStatusDto, ) { return this.billingService.updateInvoiceStatus(id, dto.status, dto.reason); } // ─── Wallet Admin ───────────────────────────────────────────────── @Get('admin/wallets') @Roles(UserRole.ADMIN) @ApiOperation({ summary: 'List all wallets (Admin)' }) async getAllWallets() { return this.billingService.getAllWallets(); } @Post('admin/wallets/:userId/charge') @Roles(UserRole.ADMIN) @ApiOperation({ summary: 'Charge a user\'s wallet (Admin)' }) async adminChargeWallet( @Param('userId') userId: string, @Body() dto: ChargeWalletDto, ) { return this.billingService.adminChargeWallet(userId, dto.amount, dto.description); } }