diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..1f1bc90
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,103 @@
+
+
+ 4.0.0
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 3.4.4
+
+
+ com.ibk
+ reto
+ 0.0.1-SNAPSHOT
+ reto
+ Demo project for Spring Boot
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 17
+
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.kafka
+ spring-kafka
+
+
+
+ com.fasterxml.jackson.datatype
+ jackson-datatype-jsr310
+ 2.15.0
+
+
+
+ org.postgresql
+ postgresql
+ runtime
+
+
+ org.projectlombok
+ lombok
+ true
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ org.springframework.kafka
+ spring-kafka-test
+ test
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+
+ org.projectlombok
+ lombok
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+ org.projectlombok
+ lombok
+
+
+
+
+
+
+
+
diff --git a/src/main/java/com/ibk/reto/RetoApplication.java b/src/main/java/com/ibk/reto/RetoApplication.java
new file mode 100644
index 0000000..f1b9365
--- /dev/null
+++ b/src/main/java/com/ibk/reto/RetoApplication.java
@@ -0,0 +1,20 @@
+package com.ibk.reto;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+/**
+ * Reto Tecnico IBK.
+ *
+ * @author Alvaro Cubas Huarca
+ * @version 1.0
+ * @since 2025-03-23
+ */
+@SpringBootApplication
+public class RetoApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(RetoApplication.class, args);
+ }
+
+}
diff --git a/src/main/java/com/ibk/reto/configuration/JacksonConfig.java b/src/main/java/com/ibk/reto/configuration/JacksonConfig.java
new file mode 100644
index 0000000..f165428
--- /dev/null
+++ b/src/main/java/com/ibk/reto/configuration/JacksonConfig.java
@@ -0,0 +1,22 @@
+package com.ibk.reto.configuration;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * Reto Tecnico IBK.
+ *
+ * @author Alvaro Cubas Huarca
+ * @version 1.0
+ * @since 2025-03-23
+ */
+@Configuration
+public class JacksonConfig {
+ @Bean
+ public ObjectMapper objectMapper() {
+ return new ObjectMapper().registerModule(new JavaTimeModule());
+ }
+
+}
diff --git a/src/main/java/com/ibk/reto/controller/TransactionController.java b/src/main/java/com/ibk/reto/controller/TransactionController.java
new file mode 100644
index 0000000..7408cef
--- /dev/null
+++ b/src/main/java/com/ibk/reto/controller/TransactionController.java
@@ -0,0 +1,31 @@
+package com.ibk.reto.controller;
+
+import com.ibk.reto.model.Transaction;
+import com.ibk.reto.service.TransactionService;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.UUID;
+
+/**
+ * Reto Tecnico IBK.
+ *
+ * @author Alvaro Cubas Huarca
+ * @version 1.0
+ * @since 2025-03-23
+ */
+@RestController
+@RequestMapping("/transactions")
+public class TransactionController {
+ private final TransactionService service;
+ public TransactionController(TransactionService service) {
+ this.service = service;
+ }
+ @PostMapping
+ public Transaction createTransaction(@RequestBody Transaction transaction) {
+ return service.createTransaction(transaction);
+ }
+ @GetMapping("/{id}")
+ public Transaction getTransaction(@PathVariable UUID id) {
+ return service.getTransactionById(id);
+ }
+}
diff --git a/src/main/java/com/ibk/reto/kafka/KafkaConsumer.java b/src/main/java/com/ibk/reto/kafka/KafkaConsumer.java
new file mode 100644
index 0000000..c691b61
--- /dev/null
+++ b/src/main/java/com/ibk/reto/kafka/KafkaConsumer.java
@@ -0,0 +1,45 @@
+package com.ibk.reto.kafka;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+import com.ibk.reto.model.Transaction;
+import com.ibk.reto.model.TransactionEstatus;
+import com.ibk.reto.service.TransactionService;
+import org.apache.kafka.clients.consumer.ConsumerRecord;
+import org.springframework.kafka.annotation.KafkaListener;
+import org.springframework.stereotype.Service;
+
+/**
+ * Reto Tecnico IBK.
+ *
+ * @author Alvaro Cubas Huarca
+ * @version 1.0
+ * @since 2025-03-23
+ */
+@Service
+public class KafkaConsumer {
+ private final KafkaProducer kafkaProducer;
+ private final ObjectMapper objectMapper;
+
+ public KafkaConsumer(KafkaProducer kafkaProducer) {
+ this.kafkaProducer = kafkaProducer;
+ this.objectMapper = new ObjectMapper();
+ this.objectMapper.registerModule(new JavaTimeModule());
+ }
+
+ @KafkaListener(topics = "transaction_created", groupId = "antifraud-group")
+ public void processTransaction(ConsumerRecord record) {
+ try {
+ Transaction transaction = objectMapper.readValue(record.value(), Transaction.class);
+
+ // Lógica antifraude: rechazar transacciones > 1000
+ TransactionEstatus status = transaction.getValue() > 1000 ? TransactionEstatus.RECHAZADO : TransactionEstatus.APROBADO;
+
+ // Enviar evento de actualización de estado a transaction service
+ kafkaProducer.sendTransactionStatus(transaction.getTransactionExternalId(), status);
+
+ } catch (Exception e) {
+ throw new RuntimeException("Error processing Kafka message in AntiFraud Service", e);
+ }
+ }
+}
diff --git a/src/main/java/com/ibk/reto/kafka/KafkaProducer.java b/src/main/java/com/ibk/reto/kafka/KafkaProducer.java
new file mode 100644
index 0000000..e871b96
--- /dev/null
+++ b/src/main/java/com/ibk/reto/kafka/KafkaProducer.java
@@ -0,0 +1,58 @@
+package com.ibk.reto.kafka;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+import com.ibk.reto.model.Transaction;
+import com.ibk.reto.model.TransactionEstatus;
+import org.springframework.kafka.core.KafkaTemplate;
+import org.springframework.stereotype.Service;
+
+import java.util.UUID;
+
+/**
+ * Reto Tecnico IBK.
+ *
+ * @author Alvaro Cubas Huarca
+ * @version 1.0
+ * @since 2025-03-23
+ */
+@Service
+public class KafkaProducer {
+
+ private final KafkaTemplate kafkaTemplate;
+ private final ObjectMapper objectMapper;
+
+ public KafkaProducer(KafkaTemplate kafkaTemplate) {
+ this.kafkaTemplate = kafkaTemplate;
+ this.objectMapper = new ObjectMapper();
+ this.objectMapper.registerModule(new JavaTimeModule());
+ }
+
+ public void sendTransaction(Transaction transaction) {
+ try {
+ String message = objectMapper.writeValueAsString(transaction);
+ kafkaTemplate.send("transaction_created", message);
+ } catch (Exception e) {
+ throw new RuntimeException("Error sending Kafka message", e);
+ }
+ }
+
+ public void sendTransactionStatus(UUID transactionId, TransactionEstatus status) {
+ try {
+ String message = objectMapper.writeValueAsString(new TransactionStatusUpdate(transactionId, status));
+ kafkaTemplate.send("transaction_status_updated", message);
+ } catch (Exception e) {
+ throw new RuntimeException("Error sending Kafka message", e);
+ }
+ }
+
+ private static class TransactionStatusUpdate {
+ public UUID transactionId;
+ public TransactionEstatus status;
+
+ public TransactionStatusUpdate(UUID transactionId, TransactionEstatus status) {
+ this.transactionId = transactionId;
+ this.status = status;
+ }
+ }
+}
diff --git a/src/main/java/com/ibk/reto/kafka/TransactionKafkaConsumer.java b/src/main/java/com/ibk/reto/kafka/TransactionKafkaConsumer.java
new file mode 100644
index 0000000..e0af108
--- /dev/null
+++ b/src/main/java/com/ibk/reto/kafka/TransactionKafkaConsumer.java
@@ -0,0 +1,46 @@
+package com.ibk.reto.kafka;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+import com.ibk.reto.model.TransactionEstatus;
+import com.ibk.reto.service.TransactionService;
+import org.apache.kafka.clients.consumer.ConsumerRecord;
+import org.springframework.kafka.annotation.KafkaListener;
+import org.springframework.stereotype.Service;
+
+import java.util.UUID;
+
+/**
+ * Reto Tecnico IBK.
+ *
+ * @author Alvaro Cubas Huarca
+ * @version 1.0
+ * @since 2025-03-23
+ */
+@Service
+public class TransactionKafkaConsumer {
+ private final TransactionService transactionService;
+ private final ObjectMapper objectMapper;
+
+ public TransactionKafkaConsumer(TransactionService transactionService) {
+ this.transactionService = transactionService;
+ this.objectMapper = new ObjectMapper();
+ this.objectMapper.registerModule(new JavaTimeModule());
+ }
+
+ @KafkaListener(topics = "transaction_status_updated", groupId = "transaction-group")
+ public void updateTransactionStatus(ConsumerRecord record) {
+ try {
+ JsonNode jsonNode = objectMapper.readTree(record.value());
+ UUID transactionId = UUID.fromString(jsonNode.get("transactionId").asText());
+ TransactionEstatus status = TransactionEstatus.valueOf(jsonNode.get("status").asText());
+
+ // Actualizar estado en la base de datos
+ transactionService.updateTransactionStatus(transactionId, status);
+
+ } catch (Exception e) {
+ throw new RuntimeException("Error processing Kafka message in Transaction Service", e);
+ }
+ }
+}
diff --git a/src/main/java/com/ibk/reto/model/Transaction.java b/src/main/java/com/ibk/reto/model/Transaction.java
new file mode 100644
index 0000000..0473d98
--- /dev/null
+++ b/src/main/java/com/ibk/reto/model/Transaction.java
@@ -0,0 +1,33 @@
+package com.ibk.reto.model;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import jakarta.persistence.*;
+import lombok.Data;
+import java.time.LocalDateTime;
+import java.util.UUID;
+
+/**
+ * Reto Tecnico IBK.
+ *
+ * @author Alvaro Cubas Huarca
+ * @version 1.0
+ * @since 2025-03-23
+ */
+@Entity
+@Data
+public class Transaction {
+ @Id
+ @GeneratedValue(strategy = GenerationType.AUTO)
+ private UUID transactionExternalId;
+
+ private UUID accountExternalIdDebit;
+ private UUID accountExternalIdCredit;
+ private Integer tranferTypeId;
+ private Double value;
+
+ @Enumerated(EnumType.STRING)
+ private TransactionEstatus transactionStatus;
+
+ @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
+ private LocalDateTime createdAt = LocalDateTime.now();
+}
diff --git a/src/main/java/com/ibk/reto/model/TransactionEstatus.java b/src/main/java/com/ibk/reto/model/TransactionEstatus.java
new file mode 100644
index 0000000..2b697b5
--- /dev/null
+++ b/src/main/java/com/ibk/reto/model/TransactionEstatus.java
@@ -0,0 +1,12 @@
+package com.ibk.reto.model;
+
+/**
+ * Reto Tecnico IBK.
+ *
+ * @author Alvaro Cubas Huarca
+ * @version 1.0
+ * @since 2025-03-23
+ */
+public enum TransactionEstatus {
+ PENDIENTE, APROBADO, RECHAZADO
+}
diff --git a/src/main/java/com/ibk/reto/repository/TransactionRepository.java b/src/main/java/com/ibk/reto/repository/TransactionRepository.java
new file mode 100644
index 0000000..32b3bae
--- /dev/null
+++ b/src/main/java/com/ibk/reto/repository/TransactionRepository.java
@@ -0,0 +1,16 @@
+package com.ibk.reto.repository;
+
+import com.ibk.reto.model.Transaction;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import java.util.UUID;
+
+/**
+ * Reto Tecnico IBK.
+ *
+ * @author Alvaro Cubas Huarca
+ * @version 1.0
+ * @since 2025-03-23
+ */
+public interface TransactionRepository extends JpaRepository {
+}
diff --git a/src/main/java/com/ibk/reto/service/TransactionService.java b/src/main/java/com/ibk/reto/service/TransactionService.java
new file mode 100644
index 0000000..4167c2c
--- /dev/null
+++ b/src/main/java/com/ibk/reto/service/TransactionService.java
@@ -0,0 +1,41 @@
+package com.ibk.reto.service;
+
+import com.ibk.reto.kafka.KafkaProducer;
+import com.ibk.reto.model.Transaction;
+import com.ibk.reto.model.TransactionEstatus;
+import com.ibk.reto.repository.TransactionRepository;
+import jakarta.transaction.Transactional;
+import org.springframework.stereotype.Service;
+
+import java.util.UUID;
+
+@Service
+public class TransactionService {
+ private final TransactionRepository repository;
+ private final KafkaProducer kafkaProducer;
+
+ public TransactionService(TransactionRepository repository, KafkaProducer kafkaProducer) {
+ this.repository = repository;
+ this.kafkaProducer = kafkaProducer;
+ }
+
+ @Transactional
+ public Transaction createTransaction(Transaction transaction) {
+ transaction.setTransactionStatus(TransactionEstatus.PENDIENTE);
+ Transaction savedTransaction = repository.save(transaction);
+ kafkaProducer.sendTransaction(savedTransaction);
+ return savedTransaction;
+ }
+
+ public Transaction getTransactionById(UUID id) {
+ return repository.findById(id).orElseThrow(() -> new RuntimeException("Transaction not found"));
+ }
+
+ @Transactional
+ public void updateTransactionStatus(UUID transactionId, TransactionEstatus status) {
+ Transaction transaction = getTransactionById(transactionId);
+ transaction.setTransactionStatus(status);
+ repository.save(transaction);
+ }
+
+}
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
new file mode 100644
index 0000000..425149d
--- /dev/null
+++ b/src/main/resources/application.yml
@@ -0,0 +1,21 @@
+spring:
+ datasource:
+ url: jdbc:postgresql://localhost:5432/postgres
+ username: postgres
+ password: 123456
+ driver-class-name: org.postgresql.Driver
+ jpa:
+ hibernate:
+ ddl-auto: update
+ show-sql: true
+ jackson:
+ serialization:
+ WRITE_DATES_AS_TIMESTAMPS: false
+ kafka:
+ bootstrap-servers: localhost:9092
+ consumer:
+ group-id: transaction-group
+ auto-offset-reset: earliest
+ producer:
+ key-serializer: org.apache.kafka.common.serialization.StringSerializer
+ value-serializer: org.apache.kafka.common.serialization.StringSerializer
\ No newline at end of file
diff --git a/src/test/java/com/ibk/reto/RetoApplicationTests.java b/src/test/java/com/ibk/reto/RetoApplicationTests.java
new file mode 100644
index 0000000..3be625e
--- /dev/null
+++ b/src/test/java/com/ibk/reto/RetoApplicationTests.java
@@ -0,0 +1,13 @@
+package com.ibk.reto;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+
+@SpringBootTest
+class RetoApplicationTests {
+
+ /*@Test
+ void contextLoads() {
+ }*/
+
+}