diff --git a/apps/edr-passenger-api/src/modules/tasks/tasks.service.ts b/apps/edr-passenger-api/src/modules/tasks/tasks.service.ts index f3d0692f7..284eb61c0 100644 --- a/apps/edr-passenger-api/src/modules/tasks/tasks.service.ts +++ b/apps/edr-passenger-api/src/modules/tasks/tasks.service.ts @@ -1,5 +1,6 @@ import { Injectable, Logger } from '@nestjs/common'; import { Cron } from '@nestjs/schedule'; +import { ModuleRef } from '@nestjs/core'; import { PrismaService } from '../../common/prisma.service'; import { SmsClientService } from '../notifications/sms-client.service'; import { CurrencyService } from '../currency/currency.service'; @@ -29,7 +30,10 @@ export class TasksService { private readonly prisma: PrismaService, private readonly sms: SmsClientService, private readonly currencyService: CurrencyService, - private readonly paymentsService: PaymentsService, + // ModuleRef (NOT direct injection): PaymentsService is request-scoped (AuditService injects + // REQUEST), and injecting a request-scoped provider here would make TasksService request-scoped + // too — which silently stops all its @Cron methods from firing. Resolve it per-tick instead. + private readonly moduleRef: ModuleRef, ) {} // ───────────────────────────────────────────────────────────────────────── @@ -292,6 +296,14 @@ export class TasksService { let cancelledCount = 0; + // resolve() (not direct injection) because PaymentsService is request-scoped — same pattern + // as PaymentSyncService. strict:false resolves it from the app context. + const paymentsService = await this.moduleRef.resolve( + PaymentsService, + undefined, + { strict: false }, + ); + for (const booking of expiredBookings) { try { // Re-verify exact deadline to avoid racing with a concurrent payment confirmation. @@ -314,7 +326,7 @@ export class TasksService { // event may have been lost (RabbitMQ down) or arrived late, leaving a paid booking stuck // PENDING_PAYMENT. Ask the payment service over HTTP; it confirms the booking synchronously // if paid. Only proceed to cancel when settlement is VERIFIED unpaid. - const settlement = await this.paymentsService.reconcileAndConfirmIfPaid(booking.id); + const settlement = await paymentsService.reconcileAndConfirmIfPaid(booking.id); if (settlement.paid || !settlement.verified) { this.logger.log( `Skip auto-cancel ${booking.bookingRef}: ${settlement.paid ? 'PAID → confirmed' : 'unverifiable → deferred'}`,