import { Body, Controller, forwardRef, HttpCode, HttpStatus, Inject, Logger, Post, UseGuards, } from "@nestjs/common"; import { ApiOperation, ApiTags } from "@nestjs/swagger"; import { PaymentEventDto, MarkPaidResponseDto, BillQueryRequestDto, BillQueryResponseDto, } from "./internal-payment.dto"; import { PaymentService } from "./payment.service"; import { BillingService } from "../billing/billing.service"; import { ServiceAuthGuard } from "../../common/guards/service-auth.guard"; /** * Consumer side of the payment microservice's outbox relay. Only the payment service may * call this (shared service token — restored per docs/cbe/CBE_IMPLEMENTATION_PLAN.md R8). * Idempotent by design — the relay delivers at-least-once, so duplicates must be harmless. * Becomes a queue consumer via PaymentEventsConsumer when RabbitMQ is available; * this HTTP endpoint remains as a transport-agnostic fallback. */ @ApiTags("Internal Payments") @UseGuards(ServiceAuthGuard) @Controller("internal/payments") export class InternalPaymentController { private readonly logger = new Logger(InternalPaymentController.name); constructor( private readonly paymentService: PaymentService, @Inject(forwardRef(() => BillingService)) private readonly billingService: BillingService, ) { } @Post("mark-paid") @HttpCode(HttpStatus.OK) @ApiOperation({ summary: "Apply a payment.succeeded / payment.failed event from the payment service (idempotent)", }) async markPaid(@Body() event: PaymentEventDto): Promise { this.logger.log(`Marking payment ${event} as PAID`); return this.paymentService.handlePaymentEvent(event); } @Post("bill-query") @HttpCode(HttpStatus.OK) @ApiOperation({ summary: "Live still-payable check + payer name for a CBE bill (called while CBE is on the line)", }) async billQuery( @Body() request: BillQueryRequestDto, ): Promise { return this.billingService.billQuery(request.referenceId); } }