From a339eb0bd0cd380faf2f3d5e3faabdd2691da7cf Mon Sep 17 00:00:00 2001
From: ayush-anilan <109529899+ayush-anilan@users.noreply.github.com>
Date: Wed, 5 Feb 2025 20:44:46 +0530
Subject: [PATCH 1/4] Task 1 completed
---
pom.xml | 63 +++++++++++++++++++
.../java/com/jpmc/midascore/TaskOneTests.java | 8 +++
2 files changed, 71 insertions(+)
diff --git a/pom.xml b/pom.xml
index d1dedfe..7ea1e50 100644
--- a/pom.xml
+++ b/pom.xml
@@ -17,6 +17,64 @@
17
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+ 3.2.5
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+ 3.2.5
+
+
+
+
+ org.springframework.kafka
+ spring-kafka
+ 3.1.4
+
+
+
+
+ com.h2database
+ h2
+ 2.2.224
+ test
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ 3.2.5
+ test
+
+
+
+
+ org.springframework.kafka
+ spring-kafka-test
+ 3.1.4
+ test
+
+
+
+
+ org.testcontainers
+ kafka
+ 1.19.1
+ test
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 3.1.2
+
+
@@ -25,6 +83,11 @@
org.springframework.boot
spring-boot-maven-plugin
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 3.1.2
+
diff --git a/src/test/java/com/jpmc/midascore/TaskOneTests.java b/src/test/java/com/jpmc/midascore/TaskOneTests.java
index 4f5a384..0fba3ab 100644
--- a/src/test/java/com/jpmc/midascore/TaskOneTests.java
+++ b/src/test/java/com/jpmc/midascore/TaskOneTests.java
@@ -4,11 +4,19 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Configuration;
@SpringBootTest
class TaskOneTests {
static final Logger logger = LoggerFactory.getLogger(TaskOneTests.class);
+ @Configuration
+ static class Config {
+ Config() {
+ System.out.println("Config");
+ }
+ }
+
@Test
void task_one_verifier() throws InterruptedException {
Thread.sleep(2000);
From 1a1adef7940d52b1ab6ebf254e54bfc32c4637e1 Mon Sep 17 00:00:00 2001
From: ayush-anilan <109529899+ayush-anilan@users.noreply.github.com>
Date: Fri, 7 Feb 2025 12:14:43 +0530
Subject: [PATCH 2/4] Task 2 completed integrated spring application with kafka
---
application.yml | 16 ++++++++++
pom.xml | 17 ++++++-----
.../midascore/kafka/TransactionListener.java | 20 +++++++++++++
.../midascore/kafka/TransactionProducer.java | 29 +++++++++++++++++++
.../com/jpmc/midascore/KafkaProducer.java | 2 ++
5 files changed, 77 insertions(+), 7 deletions(-)
create mode 100644 src/main/java/com/jpmc/midascore/kafka/TransactionListener.java
create mode 100644 src/main/java/com/jpmc/midascore/kafka/TransactionProducer.java
diff --git a/application.yml b/application.yml
index e69de29..55fd77d 100644
--- a/application.yml
+++ b/application.yml
@@ -0,0 +1,16 @@
+spring:
+ kafka:
+ bootstrap-servers: localhost:9092
+ producer:
+ key-serializer: org.apache.kafka.common.serialization.StringSerializer
+ value-serializer: org.springframework.kafka.support.serializer.JsonSerializer
+ consumer:
+ group-id: midas-core-group
+ auto-offset-reset: earliest
+ key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
+ value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer
+ properties:
+ spring.json.trusted.packages: "*"
+
+general:
+ kafka-topic: transactions
diff --git a/pom.xml b/pom.xml
index 7ea1e50..679dc72 100644
--- a/pom.xml
+++ b/pom.xml
@@ -31,13 +31,6 @@
3.2.5
-
-
- org.springframework.kafka
- spring-kafka
- 3.1.4
-
-
com.h2database
@@ -62,6 +55,16 @@
test
+
+
+ org.springframework.kafka
+ spring-kafka
+ 3.1.4
+
+
+
+
+
org.testcontainers
diff --git a/src/main/java/com/jpmc/midascore/kafka/TransactionListener.java b/src/main/java/com/jpmc/midascore/kafka/TransactionListener.java
new file mode 100644
index 0000000..6c1aea3
--- /dev/null
+++ b/src/main/java/com/jpmc/midascore/kafka/TransactionListener.java
@@ -0,0 +1,20 @@
+package com.jpmc.midascore.kafka;
+
+import com.jpmc.midascore.foundation.Transaction;
+import org.apache.kafka.clients.consumer.ConsumerRecord;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.kafka.annotation.KafkaListener;
+import org.springframework.stereotype.Component;
+
+@Component
+public class TransactionListener {
+
+ @Value("${general.kafka-topic}")
+ private String topic;
+
+ @KafkaListener(topics = "${general.kafka-topic}", groupId = "midas-core-group")
+ public void listen(ConsumerRecord record){
+ Transaction transaction = record.value();
+ System.out.println("Received transaction: " +transaction);
+ }
+}
diff --git a/src/main/java/com/jpmc/midascore/kafka/TransactionProducer.java b/src/main/java/com/jpmc/midascore/kafka/TransactionProducer.java
new file mode 100644
index 0000000..cf45b58
--- /dev/null
+++ b/src/main/java/com/jpmc/midascore/kafka/TransactionProducer.java
@@ -0,0 +1,29 @@
+package com.jpmc.midascore.kafka;
+
+import com.jpmc.midascore.foundation.Transaction;
+import org.apache.kafka.clients.producer.KafkaProducer;
+import org.apache.kafka.clients.producer.Producer;
+import org.apache.kafka.clients.producer.ProducerRecord;
+
+import java.util.Properties;
+
+public class TransactionProducer {
+ public static void main(String[] args) {
+ String topic = "transactions";
+
+ Properties props = new Properties();
+ props.put("bootstrap.servers", "localhost:9092");
+ props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
+ props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
+
+ Producer producer = new KafkaProducer<>(props);
+
+ Transaction transaction = new Transaction(101, 202, 500.75f);
+ ProducerRecord record = new ProducerRecord(topic, "txn-key", transaction);
+
+ producer.send(record);
+ producer.close();
+
+ System.out.println("Transaction sent successfully");
+ }
+}
diff --git a/src/test/java/com/jpmc/midascore/KafkaProducer.java b/src/test/java/com/jpmc/midascore/KafkaProducer.java
index fa22e2e..d2571d7 100644
--- a/src/test/java/com/jpmc/midascore/KafkaProducer.java
+++ b/src/test/java/com/jpmc/midascore/KafkaProducer.java
@@ -1,6 +1,7 @@
package com.jpmc.midascore;
import com.jpmc.midascore.foundation.Transaction;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Component;
@@ -8,6 +9,7 @@
@Component
public class KafkaProducer {
private final String topic;
+ @Autowired
private final KafkaTemplate kafkaTemplate;
public KafkaProducer(@Value("${general.kafka-topic}") String topic, KafkaTemplate kafkaTemplate) {
From f9b9d2f0f01fbbb3db1ee8f6f180578e6dd11aa4 Mon Sep 17 00:00:00 2001
From: ayush-anilan <109529899+ayush-anilan@users.noreply.github.com>
Date: Fri, 7 Feb 2025 13:00:30 +0530
Subject: [PATCH 3/4] Task 3 completed integrated spring application with H2
database
---
application.yml | 15 ++++++
.../midascore/kafka/TransactionListener.java | 53 +++++++++++++++----
.../midascore/model/TransactionRecord.java | 36 +++++++++++++
.../repository/TransactionRepository.java | 7 +++
.../midascore/repository/UserRepository.java | 4 +-
5 files changed, 104 insertions(+), 11 deletions(-)
create mode 100644 src/main/java/com/jpmc/midascore/model/TransactionRecord.java
create mode 100644 src/main/java/com/jpmc/midascore/repository/TransactionRepository.java
diff --git a/application.yml b/application.yml
index 55fd77d..3a942fa 100644
--- a/application.yml
+++ b/application.yml
@@ -12,5 +12,20 @@ spring:
properties:
spring.json.trusted.packages: "*"
+ datasource:
+ url: jdbc:h2:mem:midasdb # ✅ In-memory H2 database
+ driver-class-name: org.h2.Driver
+ username: sa
+ password:
+
+ jpa:
+ database-platform: org.hibernate.dialect.H2Dialect
+ hibernate:
+ ddl-auto: update # ✅ Automatically updates schema
+
+ h2:
+ console:
+ enabled: true # ✅ Enables H2 web console
+
general:
kafka-topic: transactions
diff --git a/src/main/java/com/jpmc/midascore/kafka/TransactionListener.java b/src/main/java/com/jpmc/midascore/kafka/TransactionListener.java
index 6c1aea3..dad9c99 100644
--- a/src/main/java/com/jpmc/midascore/kafka/TransactionListener.java
+++ b/src/main/java/com/jpmc/midascore/kafka/TransactionListener.java
@@ -1,20 +1,55 @@
package com.jpmc.midascore.kafka;
+import com.jpmc.midascore.entity.UserRecord;
import com.jpmc.midascore.foundation.Transaction;
-import org.apache.kafka.clients.consumer.ConsumerRecord;
-import org.springframework.beans.factory.annotation.Value;
+import com.jpmc.midascore.model.TransactionRecord;
+import com.jpmc.midascore.repository.TransactionRepository;
+import com.jpmc.midascore.repository.UserRepository;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.annotation.KafkaListener;
-import org.springframework.stereotype.Component;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
-@Component
+import java.util.Optional;
+
+@Service
public class TransactionListener {
- @Value("${general.kafka-topic}")
- private String topic;
+ @Autowired
+ private UserRepository userRepository;
+
+ @Autowired
+ private TransactionRepository transactionRepository;
@KafkaListener(topics = "${general.kafka-topic}", groupId = "midas-core-group")
- public void listen(ConsumerRecord record){
- Transaction transaction = record.value();
- System.out.println("Received transaction: " +transaction);
+ @Transactional
+ public void listen(Transaction transaction) {
+ Optional senderOpt = Optional.ofNullable(userRepository.findById(transaction.getSenderId()));
+ Optional recipientOpt = Optional.ofNullable(userRepository.findById(transaction.getRecipientId()));
+
+ if (senderOpt.isPresent() && recipientOpt.isPresent()) {
+ UserRecord sender = senderOpt.get();
+ UserRecord recipient = recipientOpt.get();
+
+ if (sender.getBalance() >= transaction.getAmount()) {
+ // Deduct from sender and add to recipient
+ sender.setBalance(sender.getBalance() - transaction.getAmount());
+ recipient.setBalance(recipient.getBalance() + transaction.getAmount());
+
+ // Save updated users
+ userRepository.save(sender);
+ userRepository.save(recipient);
+
+ // Save transaction record
+ TransactionRecord record = new TransactionRecord(sender, recipient, transaction.getAmount());
+ transactionRepository.save(record);
+
+ System.out.println("✅ Transaction recorded: " + record.getSender() + " Balance: " + transaction.getAmount());
+ } else {
+ System.out.println("❌ Transaction failed: Insufficient balance for sender ID " + sender.getId());
+ }
+ } else {
+ System.out.println("❌ Transaction failed: Invalid sender or recipient ID.");
+ }
}
}
diff --git a/src/main/java/com/jpmc/midascore/model/TransactionRecord.java b/src/main/java/com/jpmc/midascore/model/TransactionRecord.java
new file mode 100644
index 0000000..2b3f4b3
--- /dev/null
+++ b/src/main/java/com/jpmc/midascore/model/TransactionRecord.java
@@ -0,0 +1,36 @@
+package com.jpmc.midascore.model;
+
+
+import com.jpmc.midascore.entity.UserRecord;
+import jakarta.persistence.*;
+
+@Entity
+public class TransactionRecord {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private long id;
+
+ @ManyToOne
+ @JoinColumn(name = "sender_id", nullable = false)
+ private UserRecord sender;
+
+ @ManyToOne
+ @JoinColumn(name = "recipient_id", nullable = false)
+ private UserRecord recipient;
+
+ private float amount;
+
+ public TransactionRecord() {}
+
+ public TransactionRecord(UserRecord sender, UserRecord recipient, float amount) {
+ this.sender = sender;
+ this.recipient = recipient;
+ this.amount = amount;
+ }
+
+ public long getId() {return id;}
+ public UserRecord getSender() {return sender;}
+ public UserRecord getRecipient() {return recipient;}
+ public float getAmount() {return amount;}
+}
diff --git a/src/main/java/com/jpmc/midascore/repository/TransactionRepository.java b/src/main/java/com/jpmc/midascore/repository/TransactionRepository.java
new file mode 100644
index 0000000..d23b714
--- /dev/null
+++ b/src/main/java/com/jpmc/midascore/repository/TransactionRepository.java
@@ -0,0 +1,7 @@
+package com.jpmc.midascore.repository;
+
+import com.jpmc.midascore.model.TransactionRecord;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface TransactionRepository extends JpaRepository {
+}
diff --git a/src/main/java/com/jpmc/midascore/repository/UserRepository.java b/src/main/java/com/jpmc/midascore/repository/UserRepository.java
index 937275b..6e71158 100644
--- a/src/main/java/com/jpmc/midascore/repository/UserRepository.java
+++ b/src/main/java/com/jpmc/midascore/repository/UserRepository.java
@@ -1,8 +1,8 @@
package com.jpmc.midascore.repository;
import com.jpmc.midascore.entity.UserRecord;
-import org.springframework.data.repository.CrudRepository;
+import org.springframework.data.jpa.repository.JpaRepository;
-public interface UserRepository extends CrudRepository {
+public interface UserRepository extends JpaRepository {
UserRecord findById(long id);
}
From 80f30863dd35150f5275c1ecf6b43ba84e15c967 Mon Sep 17 00:00:00 2001
From: ayush-anilan <109529899+ayush-anilan@users.noreply.github.com>
Date: Fri, 7 Feb 2025 15:21:38 +0530
Subject: [PATCH 4/4] Task 4 and 5 completed integrated spring application with
an external REST API and exposed a REST API in a spring application
---
application.yml | 4 ++
.../jpmc/midascore/MidasCoreApplication.java | 7 +++
.../controller/BalanceController.java | 24 +++++++++
.../midascore/kafka/TransactionListener.java | 2 +-
.../com/jpmc/midascore/model/Balance.java | 17 ++++++
.../com/jpmc/midascore/model/Incentive.java | 13 +++++
.../midascore/model/TransactionRecord.java | 9 +++-
.../midascore/service/IncentiveService.java | 21 ++++++++
.../midascore/service/TransactionService.java | 52 +++++++++++++++++++
9 files changed, 147 insertions(+), 2 deletions(-)
create mode 100644 src/main/java/com/jpmc/midascore/controller/BalanceController.java
create mode 100644 src/main/java/com/jpmc/midascore/model/Balance.java
create mode 100644 src/main/java/com/jpmc/midascore/model/Incentive.java
create mode 100644 src/main/java/com/jpmc/midascore/service/IncentiveService.java
create mode 100644 src/main/java/com/jpmc/midascore/service/TransactionService.java
diff --git a/application.yml b/application.yml
index 3a942fa..c2dca9f 100644
--- a/application.yml
+++ b/application.yml
@@ -27,5 +27,9 @@ spring:
console:
enabled: true # ✅ Enables H2 web console
+server:
+ port: 33400 # ✅ Set application to run on port 33400
+
+
general:
kafka-topic: transactions
diff --git a/src/main/java/com/jpmc/midascore/MidasCoreApplication.java b/src/main/java/com/jpmc/midascore/MidasCoreApplication.java
index 9222581..639f450 100644
--- a/src/main/java/com/jpmc/midascore/MidasCoreApplication.java
+++ b/src/main/java/com/jpmc/midascore/MidasCoreApplication.java
@@ -2,6 +2,8 @@
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.Bean;
+import org.springframework.web.client.RestTemplate;
@SpringBootApplication
public class MidasCoreApplication {
@@ -10,4 +12,9 @@ public static void main(String[] args) {
SpringApplication.run(MidasCoreApplication.class, args);
}
+ @Bean
+ public RestTemplate restTemplate() {
+ return new RestTemplate();
+ }
+
}
diff --git a/src/main/java/com/jpmc/midascore/controller/BalanceController.java b/src/main/java/com/jpmc/midascore/controller/BalanceController.java
new file mode 100644
index 0000000..e9bd477
--- /dev/null
+++ b/src/main/java/com/jpmc/midascore/controller/BalanceController.java
@@ -0,0 +1,24 @@
+package com.jpmc.midascore.controller;
+
+import com.jpmc.midascore.model.Balance;
+import com.jpmc.midascore.entity.UserRecord;
+import com.jpmc.midascore.repository.UserRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Optional;
+
+@RestController
+@RequestMapping
+public class BalanceController {
+
+ @Autowired
+ private UserRepository userRepository;
+
+ @GetMapping("/balance")
+ public Balance getBalance(@RequestParam Long userId) {
+ Optional userOpt = userRepository.findById(userId);
+ float balance = userOpt.map(UserRecord::getBalance).orElse(0.0f);
+ return new Balance(balance);
+ }
+}
diff --git a/src/main/java/com/jpmc/midascore/kafka/TransactionListener.java b/src/main/java/com/jpmc/midascore/kafka/TransactionListener.java
index dad9c99..3e9571e 100644
--- a/src/main/java/com/jpmc/midascore/kafka/TransactionListener.java
+++ b/src/main/java/com/jpmc/midascore/kafka/TransactionListener.java
@@ -41,7 +41,7 @@ public void listen(Transaction transaction) {
userRepository.save(recipient);
// Save transaction record
- TransactionRecord record = new TransactionRecord(sender, recipient, transaction.getAmount());
+ TransactionRecord record = new TransactionRecord(sender, recipient, transaction.getAmount(), 0);
transactionRepository.save(record);
System.out.println("✅ Transaction recorded: " + record.getSender() + " Balance: " + transaction.getAmount());
diff --git a/src/main/java/com/jpmc/midascore/model/Balance.java b/src/main/java/com/jpmc/midascore/model/Balance.java
new file mode 100644
index 0000000..0057305
--- /dev/null
+++ b/src/main/java/com/jpmc/midascore/model/Balance.java
@@ -0,0 +1,17 @@
+package com.jpmc.midascore.model;
+
+public class Balance {
+ private float balance;
+
+ public Balance(float balance) {
+ this.balance = balance;
+ }
+
+ public float getBalance() {
+ return balance;
+ }
+
+ public void setBalance(float balance) {
+ this.balance = balance;
+ }
+}
diff --git a/src/main/java/com/jpmc/midascore/model/Incentive.java b/src/main/java/com/jpmc/midascore/model/Incentive.java
new file mode 100644
index 0000000..e22496d
--- /dev/null
+++ b/src/main/java/com/jpmc/midascore/model/Incentive.java
@@ -0,0 +1,13 @@
+package com.jpmc.midascore.model;
+
+public class Incentive {
+ private float amount;
+
+ public float getAmount() {
+ return amount;
+ }
+
+ public void setAmount(float amount) {
+ this.amount = amount;
+ }
+}
diff --git a/src/main/java/com/jpmc/midascore/model/TransactionRecord.java b/src/main/java/com/jpmc/midascore/model/TransactionRecord.java
index 2b3f4b3..d4a94a3 100644
--- a/src/main/java/com/jpmc/midascore/model/TransactionRecord.java
+++ b/src/main/java/com/jpmc/midascore/model/TransactionRecord.java
@@ -19,16 +19,23 @@ public class TransactionRecord {
@JoinColumn(name = "recipient_id", nullable = false)
private UserRecord recipient;
+ @Column(nullable = false)
private float amount;
+ @Column(nullable = false)
+ private float incentive;
+
public TransactionRecord() {}
- public TransactionRecord(UserRecord sender, UserRecord recipient, float amount) {
+ public TransactionRecord(UserRecord sender, UserRecord recipient, float amount, float incentive) {
this.sender = sender;
this.recipient = recipient;
this.amount = amount;
+ this.incentive = incentive;
}
+ public float getIncentive() {return incentive;}
+ public void setIncentive(float incentive){this.incentive = incentive;}
public long getId() {return id;}
public UserRecord getSender() {return sender;}
public UserRecord getRecipient() {return recipient;}
diff --git a/src/main/java/com/jpmc/midascore/service/IncentiveService.java b/src/main/java/com/jpmc/midascore/service/IncentiveService.java
new file mode 100644
index 0000000..e70822d
--- /dev/null
+++ b/src/main/java/com/jpmc/midascore/service/IncentiveService.java
@@ -0,0 +1,21 @@
+package com.jpmc.midascore.service;
+
+import com.jpmc.midascore.foundation.Transaction;
+import com.jpmc.midascore.model.Incentive;
+import org.springframework.stereotype.Service;
+import org.springframework.web.client.RestTemplate;
+
+@Service
+public class IncentiveService {
+
+ private final RestTemplate restTemplate;
+
+ public IncentiveService(RestTemplate restTemplate) {
+ this.restTemplate = restTemplate;
+ }
+
+ public Incentive getIncentive(Transaction transaction) {
+ String url = "http://localhost:8080/incentive";
+ return restTemplate.postForObject(url, transaction, Incentive.class);
+ }
+}
diff --git a/src/main/java/com/jpmc/midascore/service/TransactionService.java b/src/main/java/com/jpmc/midascore/service/TransactionService.java
new file mode 100644
index 0000000..14a91e8
--- /dev/null
+++ b/src/main/java/com/jpmc/midascore/service/TransactionService.java
@@ -0,0 +1,52 @@
+package com.jpmc.midascore.service;
+
+import com.jpmc.midascore.entity.UserRecord;
+import com.jpmc.midascore.foundation.Transaction;
+import com.jpmc.midascore.model.TransactionRecord;
+import com.jpmc.midascore.repository.TransactionRepository;
+import com.jpmc.midascore.repository.UserRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+@Service
+public class TransactionService {
+
+ private final UserRepository userRepository;
+ private final TransactionRepository transactionRepository;
+ private final IncentiveService incentiveService;
+
+ @Autowired
+ public TransactionService(UserRepository userRepository, TransactionRepository transactionRepository, IncentiveService incentiveService) {
+ this.userRepository = userRepository;
+ this.transactionRepository = transactionRepository;
+ this.incentiveService = incentiveService;
+ }
+
+ @Transactional
+ public void processTransaction(Transaction transaction) {
+ UserRecord sender = userRepository.findById(transaction.getSenderId());
+ UserRecord recipient = userRepository.findById(transaction.getRecipientId());
+
+ if (sender == null || recipient == null || sender.getBalance() < transaction.getAmount()) {
+ return; // Invalid transaction
+ }
+
+ // Deduct amount from sender
+ sender.setBalance(sender.getBalance() - transaction.getAmount());
+
+ // Call Incentive API
+ float incentiveAmount = incentiveService.getIncentive(transaction).getAmount();
+
+ // Add amount + incentive to recipient
+ recipient.setBalance(recipient.getBalance() + transaction.getAmount() + incentiveAmount);
+
+ // Save the transaction with incentive
+ TransactionRecord transactionRecord = new TransactionRecord(sender, recipient, transaction.getAmount(), incentiveAmount);
+ transactionRepository.save(transactionRecord);
+
+ // Update users
+ userRepository.save(sender);
+ userRepository.save(recipient);
+ }
+}
\ No newline at end of file