Add PaymentsService, ProcessPaymentService, and RetryPaymentService for payment processing logic

This commit is contained in:
Jose Eduardo 2025-08-09 22:36:37 -04:00
parent 04b874a3a2
commit 8d6a8f75ea
3 changed files with 191 additions and 0 deletions

View File

@ -0,0 +1,34 @@
import { Injectable, Logger } from '@nestjs/common';
import { CreatePaymentDto } from '../dto/create-payment.dto';
import { InjectRepository } from '@nestjs/typeorm';
import { Payment } from '../entities/payment.entity';
import { Repository } from 'typeorm';
import { QueueService } from '../../queue/queue.service';
import { PaymentStatusEnum } from '../enumns/payment-status.enum';
@Injectable()
export class PaymentsService {
private readonly logger = new Logger(PaymentsService.name);
constructor(
@InjectRepository(Payment) private readonly repository: Repository<Payment>,
private queueService: QueueService,
) {}
async store(createPaymentDto: CreatePaymentDto) {
const payment = await this.repository.save({
...createPaymentDto,
status: PaymentStatusEnum.PENDING,
});
await this.queueService.addPaymentJob({
paymentId: payment.id,
paymentData: createPaymentDto,
});
return {
success: true,
message: 'Payment created successfully and added to processing queue',
};
}
}

View File

@ -0,0 +1,73 @@
import { Injectable, Logger } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Payment } from '../entities/payment.entity';
import { Repository } from 'typeorm';
import { HealthService } from '../../health/services/health.service';
import { ProcessorTypeEnum } from '../enumns/processor-type.enum';
import { PaymentStatusEnum } from '../enumns/payment-status.enum';
import { PaymentDefaultProcessor } from '../processor/payment-default.processor';
import { PaymentFallbackProcessor } from '../processor/payment-fallback.processor';
import { RetryPaymentService } from './retry-payment.service';
@Injectable()
export class ProcessPaymentService {
private readonly logger = new Logger(ProcessPaymentService.name);
constructor(
@InjectRepository(Payment) private readonly repository: Repository<Payment>,
private healthService: HealthService,
private paymentDefaultProcessor: PaymentDefaultProcessor,
private paymentFallbackProcessor: PaymentFallbackProcessor,
private retryPaymentService: RetryPaymentService,
) {}
async execute(paymentId: string): Promise<void> {
const payment = await this.repository.findOne({
where: { id: paymentId },
});
if (!payment) {
throw new Error(`Payment ${paymentId} not found`);
}
await this.repository.update(paymentId, {
status: PaymentStatusEnum.PROCESSING,
});
const preferredProcessor = this.healthService.getPreferredProcessor();
if (!preferredProcessor) {
throw new Error('No payment processor available');
}
let result;
let processorUsed: ProcessorTypeEnum =
preferredProcessor === ProcessorTypeEnum.DEFAULT
? ProcessorTypeEnum.DEFAULT
: ProcessorTypeEnum.FALLBACK;
try {
if (preferredProcessor === ProcessorTypeEnum.DEFAULT) {
result = await this.paymentDefaultProcessor.execute(payment);
processorUsed = ProcessorTypeEnum.DEFAULT;
} else {
result = await this.paymentFallbackProcessor.execute(payment);
processorUsed = ProcessorTypeEnum.FALLBACK;
}
if (!result) {
await this.retryPaymentService.execute(payment.id, processorUsed);
}
} catch (error) {
this.logger.error(
`Error processing payment ${paymentId}:`,
error.message,
);
await this.repository.update(payment.id, {
status: PaymentStatusEnum.RETRY,
});
await this.retryPaymentService.execute(payment.id, processorUsed);
}
}
}

View File

@ -0,0 +1,84 @@
import { Injectable, Logger } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Payment } from '../entities/payment.entity';
import { Repository } from 'typeorm';
import { ProcessorTypeEnum } from '../enumns/processor-type.enum';
import { HealthService } from '../../health/services/health.service';
import { PaymentStatusEnum } from '../enumns/payment-status.enum';
import { PaymentDefaultProcessor } from '../processor/payment-default.processor';
import { PaymentFallbackProcessor } from '../processor/payment-fallback.processor';
import { QueueService } from '../../queue/queue.service';
@Injectable()
export class RetryPaymentService {
private readonly logger = new Logger(RetryPaymentService.name);
constructor(
@InjectRepository(Payment) private readonly repository: Repository<Payment>,
private healthService: HealthService,
private paymentDefaultProcessor: PaymentDefaultProcessor,
private paymentFallbackProcessor: PaymentFallbackProcessor,
private queueService: QueueService,
) {}
async execute(paymentId: string, failedProcessor: ProcessorTypeEnum) {
const alternativeProcessor =
failedProcessor === ProcessorTypeEnum.DEFAULT
? ProcessorTypeEnum.FALLBACK
: ProcessorTypeEnum.DEFAULT;
const payment = await this.repository.findOne({
where: { id: paymentId },
});
if (!payment) {
throw new Error(`Payment ${paymentId} not found`);
}
const isAlternativeAvailable =
alternativeProcessor === ProcessorTypeEnum.DEFAULT
? this.healthService.shouldUseDefaultProcessor()
: this.healthService.shouldUseFallbackProcessor();
if (!isAlternativeAvailable) {
await this.repository.update(payment.id, {
status: PaymentStatusEnum.RETRY,
});
}
try {
let result;
if (failedProcessor === ProcessorTypeEnum.DEFAULT) {
result = await this.paymentDefaultProcessor.execute(payment);
} else {
result = await this.paymentFallbackProcessor.execute(payment);
}
if (result) {
this.logger.log(
`Nova Tentativa do Payment ${payment?.id} processed successfully via ${alternativeProcessor}`,
);
} else {
await this.queueService.addRetryPaymentJob({
paymentId: payment.id,
paymentData: {
amount: payment.amount,
correlationId: payment.correlationId,
},
});
await this.repository.update(payment.id, {
status: PaymentStatusEnum.RETRY,
});
}
} catch (error) {
this.logger.error('Error processing payment:', error.message);
await this.repository.update(payment.id, {
status: PaymentStatusEnum.FAILED,
errorMessage: error.message,
});
}
}
}