Hexafood - Payments

⛵ Sobre o projeto

Esse projeto faz parte do trabalho "Tech Challenge - Fase 04", ministrado no quarto módulo do curso de Pós Graduação Software Architecture da FIAP em parceria com a Alura.

É um microserviço de pagamento, onde sua comunicaçao é através de mensageria.

🔨 Tecnologias:

🚀 Como rodar esse projeto

Se você estiver usando Windows, vai precisar do WSL para rodar esse projeto de forma prática. Para isso, você pode instalá-lo seguindo o seguinte tutorial. Também será necessário uma distribuição linux para utilizar o WSL. Recomendo o Ubuntu que pode ser baixando na própria Microsoft Store no link. Depois, vai precisar do Docker, o qual a versão de Windows pode ser encontrada aqui. Então, clone o projeto dentro do WSL, vá para pasta dele e execute o comando:

docker compose build --no-cache

Após a construção da imagem, basta executar o comando:

docker compose up

O projeto estará executando no endereço http://localhost:8080/.

Para limpar o volume db do docker, execute o comando:

docker-compose down -v

Filas

A aplicaçao hexafood-payments fica escutando a fila de 'novo_pedido' com o seguinte contrato:

{
  "id": 2,
  "codigo_pedido": "X5UO5W",
  "valor_total": 10,
  "status": "INICIADO",
  "itens": [
    {
      "id": 1,
      "quantidade": 1,
      "valor": 10,
      "produto": {
        "id": 1,
        "nome": "Hamburger"
      }
    }
  ],
  "cliente": {
    "id": 1,
    "nome": "Teste",
    "cpf": "12345678910"
  }
}

Listener:

@Service
class PaymentConsumer(
    private val createPaymentUseCase: ICreatePaymentUseCase,
) {

    private val logger = LoggerFactory.getLogger(javaClass)

    @SqsListener("novo_pedido")
    fun onMessage(order: CreatePaymentRequest) {
        try {
            logger.info("**** Receiving message from queue novo_pedido with order_id=${order.codigoPedido}****")

            createPaymentUseCase.save(order.toPayment()).runCatching {  }

            logger.info("**** message from novo_pedido with order_id=${order.codigoPedido} was saved in database ****")
        } catch (e: Exception) {
            logger.info("**** message wasn't save, because the order_id=${order.codigoPedido} already exist! ****")
        }
    }
}

O Pedido é processado e salvo na tabela PAYMENTS com o status 'IN_PROGRESS', a aplicacao nao aceita 'codigo_pedido' duplicado para processamento.

Em seguida, temos um schedule para pegar os registros com status 'IN_PROGRESS' e fazer a manipulacao:

@Service
@EnableScheduling
class PaymentProducer(
    private val queueMessagingTemplate: QueueMessagingTemplate,
    private val repository: IFindPaymentsByStatusUseCase,
    private val paymentStatus: IPaymentStatusManagerUseCase
) {
    private val logger = LoggerFactory.getLogger(javaClass)
    private val queueFinishedPayment = "pagamento_processado"

    @Scheduled(fixedDelay = 1000)
    fun SendMessage() {
        val paymentesInProcess = repository.findByStatus(PaymentStatus.IN_PROCESS)

        paymentesInProcess?.forEach {
            logger.info("**** Processing Payment ****")
            paymentStatus.updateToApprovedStatus(it.paymentId!!)
            it.status = PaymentStatus.APPROVED

            logger.info("**** Publishing payment APPROVED with order_id= ${it.order} to queue ****")

            sendMessageToSqs(queueFinishedPayment, it.toPaymentResponse())
        }

    }
    fun sendMessageToSqs(queueName: String, message: PaymentResponse) {

        logger.info("**** Published message to pagamento_processado queue ****")

        queueMessagingTemplate.convertAndSend<Any>(queueName, message) //send to queue
    }
}

a mensagem publicada na fila 'pagamento_processado':

{
  "id_pagamento": "4ecddff8-7aee-43ac-ba58-e32d5d9d5030",
  "id_pedido": 1,
  "status": "APROVADO",
  "update_at" : "",
  "metodo_pagamento": "PIX"
}