diff --git a/pom.xml b/pom.xml
index 24353a4..5df47dc 100644
--- a/pom.xml
+++ b/pom.xml
@@ -83,6 +83,26 @@
commons-collections4
4.4
+
+ jakarta.persistence
+ jakarta.persistence-api
+ 3.1.0
+
+
+ com.googlecode.libphonenumber
+ libphonenumber
+ 8.12.10
+
+
+ com.google.code.gson
+ gson
+ 2.10.1
+
+
+ org.json
+ json
+ 20231013
+
diff --git a/src/main/java/zw/qantra/tm/domain/controllers/ConfirmationController.java b/src/main/java/zw/qantra/tm/domain/controllers/ConfirmationController.java
new file mode 100644
index 0000000..21e0ae0
--- /dev/null
+++ b/src/main/java/zw/qantra/tm/domain/controllers/ConfirmationController.java
@@ -0,0 +1,18 @@
+package zw.qantra.tm.domain.controllers;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping("/confirm/transaction")
+@RequiredArgsConstructor
+public class ConfirmationController {
+ @PostMapping()
+ public ResponseEntity post(@RequestBody Object transactionRequest) {
+ return ResponseEntity.ok("Transaction endpoint is working");
+ }
+}
diff --git a/src/main/java/zw/qantra/tm/domain/controllers/IntegrationController.java b/src/main/java/zw/qantra/tm/domain/controllers/IntegrationController.java
new file mode 100644
index 0000000..ce37cd9
--- /dev/null
+++ b/src/main/java/zw/qantra/tm/domain/controllers/IntegrationController.java
@@ -0,0 +1,18 @@
+package zw.qantra.tm.domain.controllers;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping("/integration/transaction")
+@RequiredArgsConstructor
+public class IntegrationController {
+ @PostMapping()
+ public ResponseEntity post(@RequestBody Object transactionRequest) {
+ return ResponseEntity.ok("Transaction endpoint is working");
+ }
+}
diff --git a/src/main/java/zw/qantra/tm/domain/controllers/TransactonController.java b/src/main/java/zw/qantra/tm/domain/controllers/TransactonController.java
new file mode 100644
index 0000000..0885d41
--- /dev/null
+++ b/src/main/java/zw/qantra/tm/domain/controllers/TransactonController.java
@@ -0,0 +1,18 @@
+package zw.qantra.tm.domain.controllers;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping("/transaction")
+@RequiredArgsConstructor
+public class TransactonController {
+ @PostMapping()
+ public ResponseEntity post(@RequestBody Object transactionRequest) {
+ return ResponseEntity.ok("Transaction endpoint is working");
+ }
+}
diff --git a/src/main/java/zw/qantra/tm/domain/enums/RequestType.java b/src/main/java/zw/qantra/tm/domain/enums/RequestType.java
new file mode 100644
index 0000000..b4386e0
--- /dev/null
+++ b/src/main/java/zw/qantra/tm/domain/enums/RequestType.java
@@ -0,0 +1,6 @@
+package zw.qantra.tm.domain.enums;
+
+public enum RequestType {
+ REQUEST,
+ CONFIRM
+}
diff --git a/src/main/java/zw/qantra/tm/domain/models/ConfirmTransaction.java b/src/main/java/zw/qantra/tm/domain/models/ConfirmTransaction.java
new file mode 100644
index 0000000..188fd5e
--- /dev/null
+++ b/src/main/java/zw/qantra/tm/domain/models/ConfirmTransaction.java
@@ -0,0 +1,52 @@
+package zw.qantra.tm.domain.models;
+
+import jakarta.persistence.Entity;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.GenerationType;
+import jakarta.persistence.Id;
+import lombok.*;
+import zw.qantra.tm.domain.enums.RequestType;
+
+import java.math.BigDecimal;
+import java.util.UUID;
+
+@Entity
+@Getter
+@Setter
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class ConfirmTransaction {
+ @Id
+ @GeneratedValue(strategy = GenerationType.UUID)
+ private UUID id;
+
+ private String trace;
+ private BigDecimal amount;
+ private String reference;
+ private String providerUid;
+ private String productUid;
+ private String paymentProcessorLabel;
+ private String integrationProcessorLabel;
+ private String rrn;
+ private String channelName;
+ private String channel;
+ private String debitPhone;
+ private String debitAccount;
+ private String debitCurrency;
+ private String debitName;
+ private String debitCard;
+ private String debitRef;
+ private String creditPhone;
+ private String creditAccount;
+ private String creditCurrency;
+ private String creditName;
+ private String creditCard;
+ private String creditRef;
+ private String billClientId;
+ private String clientSecret;
+ private String aggregatorId;
+ private String billName;
+ private String billProductName;
+ private RequestType type;
+}
\ No newline at end of file
diff --git a/src/main/java/zw/qantra/tm/domain/models/Currency.java b/src/main/java/zw/qantra/tm/domain/models/Currency.java
new file mode 100644
index 0000000..6c350b0
--- /dev/null
+++ b/src/main/java/zw/qantra/tm/domain/models/Currency.java
@@ -0,0 +1,25 @@
+package zw.qantra.tm.domain.models;
+
+import java.util.UUID;
+
+import jakarta.persistence.Entity;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.GenerationType;
+import jakarta.persistence.Id;
+import lombok.*;
+
+@Entity
+@Getter
+@Setter
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class Currency {
+ @Id
+ @GeneratedValue(strategy = GenerationType.UUID)
+ private UUID id;
+
+ private String isoCode;
+ private String name;
+ private String displayName;
+}
diff --git a/src/main/java/zw/qantra/tm/domain/models/IntegrationProcessor.java b/src/main/java/zw/qantra/tm/domain/models/IntegrationProcessor.java
new file mode 100644
index 0000000..527132b
--- /dev/null
+++ b/src/main/java/zw/qantra/tm/domain/models/IntegrationProcessor.java
@@ -0,0 +1,25 @@
+package zw.qantra.tm.domain.models;
+
+import java.util.UUID;
+
+import jakarta.persistence.Entity;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.GenerationType;
+import jakarta.persistence.Id;
+import lombok.*;
+
+@Entity
+@Getter
+@Setter
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class IntegrationProcessor {
+ @Id
+ @GeneratedValue(strategy = GenerationType.UUID)
+ private UUID id;
+
+ private String name;
+ private String description;
+ private String type;
+}
diff --git a/src/main/java/zw/qantra/tm/domain/models/PaymentProcessor.java b/src/main/java/zw/qantra/tm/domain/models/PaymentProcessor.java
new file mode 100644
index 0000000..49ef204
--- /dev/null
+++ b/src/main/java/zw/qantra/tm/domain/models/PaymentProcessor.java
@@ -0,0 +1,26 @@
+package zw.qantra.tm.domain.models;
+
+import jakarta.persistence.Entity;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.GenerationType;
+import jakarta.persistence.Id;
+import lombok.*;
+
+import java.util.UUID;
+
+@Entity
+@Getter
+@Setter
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class PaymentProcessor {
+ @Id
+ @GeneratedValue(strategy = GenerationType.UUID)
+ private UUID id;
+
+ private String name;
+ private String description;
+ private String label;
+ private String type;
+}
diff --git a/src/main/java/zw/qantra/tm/domain/models/Provider.java b/src/main/java/zw/qantra/tm/domain/models/Provider.java
new file mode 100644
index 0000000..3df2252
--- /dev/null
+++ b/src/main/java/zw/qantra/tm/domain/models/Provider.java
@@ -0,0 +1,25 @@
+package zw.qantra.tm.domain.models;
+
+import jakarta.persistence.Entity;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.GenerationType;
+import jakarta.persistence.Id;
+import lombok.*;
+
+import java.util.UUID;
+
+@Entity
+@Getter
+@Setter
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class Provider {
+ @Id
+ @GeneratedValue(strategy = GenerationType.UUID)
+ private UUID id;
+
+ private String clientId;
+ private String name;
+ private String description;
+}
diff --git a/src/main/java/zw/qantra/tm/domain/models/Setting.java b/src/main/java/zw/qantra/tm/domain/models/Setting.java
new file mode 100644
index 0000000..95ae24c
--- /dev/null
+++ b/src/main/java/zw/qantra/tm/domain/models/Setting.java
@@ -0,0 +1,28 @@
+package zw.qantra.tm.domain.models;
+
+import java.util.UUID;
+
+import jakarta.persistence.Entity;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.GenerationType;
+import jakarta.persistence.Id;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Entity
+@Getter
+@Setter
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class Setting {
+ @Id
+ @GeneratedValue(strategy = GenerationType.UUID)
+ private UUID id;
+
+ private String name;
+ private String value;
+}
\ No newline at end of file
diff --git a/src/main/java/zw/qantra/tm/domain/models/Transaction.java b/src/main/java/zw/qantra/tm/domain/models/Transaction.java
new file mode 100644
index 0000000..d0d95c2
--- /dev/null
+++ b/src/main/java/zw/qantra/tm/domain/models/Transaction.java
@@ -0,0 +1,49 @@
+package zw.qantra.tm.domain.models;
+
+import jakarta.persistence.*;
+import lombok.*;
+import zw.qantra.tm.domain.enums.RequestType;
+
+import java.math.BigDecimal;
+import java.util.UUID;
+
+@Entity
+@Getter
+@Setter
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class Transaction {
+ @Id
+ @GeneratedValue(strategy = GenerationType.UUID)
+ private UUID id;
+
+ private String trace;
+ private BigDecimal amount;
+ private String reference;
+ private String providerUid;
+ private String productUid;
+ private String paymentProcessorLabel;
+ private String integrationProcessorLabel;
+ private String rrn;
+ private String channelName;
+ private String channel;
+ private String debitPhone;
+ private String debitAccount;
+ private String debitCurrency;
+ private String debitName;
+ private String debitCard;
+ private String debitRef;
+ private String creditPhone;
+ private String creditAccount;
+ private String creditCurrency;
+ private String creditName;
+ private String creditCard;
+ private String creditRef;
+ private String billClientId;
+ private String clientSecret;
+ private String aggregatorId;
+ private String billName;
+ private String billProductName;
+ private RequestType type;
+}
\ No newline at end of file
diff --git a/src/main/java/zw/qantra/tm/domain/repositories/ConfirmTransactionRepository.java b/src/main/java/zw/qantra/tm/domain/repositories/ConfirmTransactionRepository.java
new file mode 100644
index 0000000..75edb6a
--- /dev/null
+++ b/src/main/java/zw/qantra/tm/domain/repositories/ConfirmTransactionRepository.java
@@ -0,0 +1,11 @@
+package zw.qantra.tm.domain.repositories;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+import zw.qantra.tm.domain.models.ConfirmTransaction;
+
+import java.util.UUID;
+
+@Repository
+public interface ConfirmTransactionRepository extends JpaRepository {
+}
\ No newline at end of file
diff --git a/src/main/java/zw/qantra/tm/domain/repositories/CurrencyRepository.java b/src/main/java/zw/qantra/tm/domain/repositories/CurrencyRepository.java
new file mode 100644
index 0000000..f71ac3c
--- /dev/null
+++ b/src/main/java/zw/qantra/tm/domain/repositories/CurrencyRepository.java
@@ -0,0 +1,11 @@
+package zw.qantra.tm.domain.repositories;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+import zw.qantra.tm.domain.models.Currency;
+
+import java.util.UUID;
+
+@Repository
+public interface CurrencyRepository extends JpaRepository {
+}
\ No newline at end of file
diff --git a/src/main/java/zw/qantra/tm/domain/repositories/IntegrationProcessorRepository.java b/src/main/java/zw/qantra/tm/domain/repositories/IntegrationProcessorRepository.java
new file mode 100644
index 0000000..bef1e51
--- /dev/null
+++ b/src/main/java/zw/qantra/tm/domain/repositories/IntegrationProcessorRepository.java
@@ -0,0 +1,11 @@
+package zw.qantra.tm.domain.repositories;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+import zw.qantra.tm.domain.models.IntegrationProcessor;
+
+import java.util.UUID;
+
+@Repository
+public interface IntegrationProcessorRepository extends JpaRepository {
+}
\ No newline at end of file
diff --git a/src/main/java/zw/qantra/tm/domain/repositories/PaymentProcessorRepository.java b/src/main/java/zw/qantra/tm/domain/repositories/PaymentProcessorRepository.java
new file mode 100644
index 0000000..a5e9cc0
--- /dev/null
+++ b/src/main/java/zw/qantra/tm/domain/repositories/PaymentProcessorRepository.java
@@ -0,0 +1,11 @@
+package zw.qantra.tm.domain.repositories;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+import zw.qantra.tm.domain.models.PaymentProcessor;
+
+import java.util.UUID;
+
+@Repository
+public interface PaymentProcessorRepository extends JpaRepository {
+}
\ No newline at end of file
diff --git a/src/main/java/zw/qantra/tm/domain/repositories/ProviderRepository.java b/src/main/java/zw/qantra/tm/domain/repositories/ProviderRepository.java
new file mode 100644
index 0000000..ff6b980
--- /dev/null
+++ b/src/main/java/zw/qantra/tm/domain/repositories/ProviderRepository.java
@@ -0,0 +1,11 @@
+package zw.qantra.tm.domain.repositories;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+import zw.qantra.tm.domain.models.Provider;
+
+import java.util.UUID;
+
+@Repository
+public interface ProviderRepository extends JpaRepository {
+}
\ No newline at end of file
diff --git a/src/main/java/zw/qantra/tm/domain/repositories/SettingRepository.java b/src/main/java/zw/qantra/tm/domain/repositories/SettingRepository.java
new file mode 100644
index 0000000..7345976
--- /dev/null
+++ b/src/main/java/zw/qantra/tm/domain/repositories/SettingRepository.java
@@ -0,0 +1,11 @@
+package zw.qantra.tm.domain.repositories;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+import zw.qantra.tm.domain.models.Setting;
+
+import java.util.UUID;
+
+@Repository
+public interface SettingRepository extends JpaRepository {
+}
\ No newline at end of file
diff --git a/src/main/java/zw/qantra/tm/domain/repositories/TransactionRepository.java b/src/main/java/zw/qantra/tm/domain/repositories/TransactionRepository.java
new file mode 100644
index 0000000..2759c5c
--- /dev/null
+++ b/src/main/java/zw/qantra/tm/domain/repositories/TransactionRepository.java
@@ -0,0 +1,11 @@
+package zw.qantra.tm.domain.repositories;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+import zw.qantra.tm.domain.models.Transaction;
+
+import java.util.UUID;
+
+@Repository
+public interface TransactionRepository extends JpaRepository {
+}
\ No newline at end of file
diff --git a/src/main/java/zw/qantra/tm/domain/services/ConfirmTransactionService.java b/src/main/java/zw/qantra/tm/domain/services/ConfirmTransactionService.java
new file mode 100644
index 0000000..cef298e
--- /dev/null
+++ b/src/main/java/zw/qantra/tm/domain/services/ConfirmTransactionService.java
@@ -0,0 +1,11 @@
+package zw.qantra.tm.domain.services;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import zw.qantra.tm.domain.repositories.ConfirmTransactionRepository;
+
+@Service
+@RequiredArgsConstructor
+public class ConfirmTransactionService {
+ private final ConfirmTransactionRepository confirmTransactionRepository;
+}
\ No newline at end of file
diff --git a/src/main/java/zw/qantra/tm/domain/services/CurrencyService.java b/src/main/java/zw/qantra/tm/domain/services/CurrencyService.java
new file mode 100644
index 0000000..b94af52
--- /dev/null
+++ b/src/main/java/zw/qantra/tm/domain/services/CurrencyService.java
@@ -0,0 +1,11 @@
+package zw.qantra.tm.domain.services;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import zw.qantra.tm.domain.repositories.CurrencyRepository;
+
+@Service
+@RequiredArgsConstructor
+public class CurrencyService {
+ private final CurrencyRepository currencyRepository;
+}
\ No newline at end of file
diff --git a/src/main/java/zw/qantra/tm/domain/services/IntegrationProcessorService.java b/src/main/java/zw/qantra/tm/domain/services/IntegrationProcessorService.java
new file mode 100644
index 0000000..77e82c7
--- /dev/null
+++ b/src/main/java/zw/qantra/tm/domain/services/IntegrationProcessorService.java
@@ -0,0 +1,11 @@
+package zw.qantra.tm.domain.services;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import zw.qantra.tm.domain.repositories.IntegrationProcessorRepository;
+
+@Service
+@RequiredArgsConstructor
+public class IntegrationProcessorService {
+ private final IntegrationProcessorRepository integrationProcessorRepository;
+}
\ No newline at end of file
diff --git a/src/main/java/zw/qantra/tm/domain/services/PaymentProcessorService.java b/src/main/java/zw/qantra/tm/domain/services/PaymentProcessorService.java
new file mode 100644
index 0000000..4b2bee4
--- /dev/null
+++ b/src/main/java/zw/qantra/tm/domain/services/PaymentProcessorService.java
@@ -0,0 +1,11 @@
+package zw.qantra.tm.domain.services;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import zw.qantra.tm.domain.repositories.PaymentProcessorRepository;
+
+@Service
+@RequiredArgsConstructor
+public class PaymentProcessorService {
+ private final PaymentProcessorRepository paymentProcessorRepository;
+}
\ No newline at end of file
diff --git a/src/main/java/zw/qantra/tm/domain/services/ProviderService.java b/src/main/java/zw/qantra/tm/domain/services/ProviderService.java
new file mode 100644
index 0000000..17afbf8
--- /dev/null
+++ b/src/main/java/zw/qantra/tm/domain/services/ProviderService.java
@@ -0,0 +1,11 @@
+package zw.qantra.tm.domain.services;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import zw.qantra.tm.domain.repositories.ProviderRepository;
+
+@Service
+@RequiredArgsConstructor
+public class ProviderService {
+ private final ProviderRepository providerRepository;
+}
\ No newline at end of file
diff --git a/src/main/java/zw/qantra/tm/domain/services/SettingService.java b/src/main/java/zw/qantra/tm/domain/services/SettingService.java
new file mode 100644
index 0000000..4b36c93
--- /dev/null
+++ b/src/main/java/zw/qantra/tm/domain/services/SettingService.java
@@ -0,0 +1,11 @@
+package zw.qantra.tm.domain.services;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import zw.qantra.tm.domain.repositories.SettingRepository;
+
+@Service
+@RequiredArgsConstructor
+public class SettingService {
+ private final SettingRepository settingRepository;
+}
\ No newline at end of file
diff --git a/src/main/java/zw/qantra/tm/domain/services/TransactionService.java b/src/main/java/zw/qantra/tm/domain/services/TransactionService.java
new file mode 100644
index 0000000..f15b228
--- /dev/null
+++ b/src/main/java/zw/qantra/tm/domain/services/TransactionService.java
@@ -0,0 +1,21 @@
+package zw.qantra.tm.domain.services;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import zw.qantra.tm.domain.models.PaymentProcessor;
+import zw.qantra.tm.domain.models.Transaction;
+import zw.qantra.tm.domain.repositories.TransactionRepository;
+import zw.qantra.tm.domain.services.factories.PaymentProcessorFactory;
+
+@Service
+@RequiredArgsConstructor
+public class TransactionService {
+ private final TransactionRepository transactionRepository;
+ private final PaymentProcessorFactory paymentProcessorFactory;
+
+ private Transaction bootstrap(Transaction transaction) {
+ String label = transaction.getType() + "_" + transaction.getPaymentProcessorLabel();
+// PaymentProcessor paymentProcessor = paymentProcessorFactory.getPaymentProcessor(label);
+ return transaction;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/zw/qantra/tm/domain/services/factories/PaymentProcessorFactory.java b/src/main/java/zw/qantra/tm/domain/services/factories/PaymentProcessorFactory.java
new file mode 100644
index 0000000..9a0c9b9
--- /dev/null
+++ b/src/main/java/zw/qantra/tm/domain/services/factories/PaymentProcessorFactory.java
@@ -0,0 +1,40 @@
+package zw.qantra.tm.domain.services.factories;
+
+
+import lombok.RequiredArgsConstructor;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.NoSuchBeanDefinitionException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.stereotype.Component;
+
+@Component
+@RequiredArgsConstructor
+public class PaymentProcessorFactory implements ApplicationContextAware {
+ private final Logger logger = LoggerFactory.getLogger(this.getClass().getName());
+
+ private ApplicationContext applicationContext;
+
+ @Override
+ public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+ this.applicationContext = applicationContext;
+ }
+
+ public Object getPaymentProcessor(String paymentProcessorName) {
+ Object paymentProcessor;
+
+ try {
+ paymentProcessor = applicationContext.getBean(paymentProcessorName);
+ } catch (NoSuchBeanDefinitionException exception) {
+ logger.error("Couldn't find payment processor: {}", exception.getMessage());
+ return null;
+ } catch (BeansException e) {
+ logger.error("Error retrieving payment processor bean: {}", e.getMessage());
+ return null;
+ }
+
+ return paymentProcessor;
+ }
+}
diff --git a/src/main/java/zw/qantra/tm/domain/services/processors/TransactionProcessorInterface.java b/src/main/java/zw/qantra/tm/domain/services/processors/TransactionProcessorInterface.java
new file mode 100644
index 0000000..cb452de
--- /dev/null
+++ b/src/main/java/zw/qantra/tm/domain/services/processors/TransactionProcessorInterface.java
@@ -0,0 +1,8 @@
+package zw.qantra.tm.domain.services.processors;
+
+import zw.qantra.tm.domain.models.Transaction;
+
+public interface TransactionProcessorInterface {
+ Object process(Transaction transaction);
+ Object reverse(Transaction transaction);
+}
diff --git a/src/main/java/zw/qantra/tm/domain/services/processors/confirmations/ZesaConfirmationProcessor.java b/src/main/java/zw/qantra/tm/domain/services/processors/confirmations/ZesaConfirmationProcessor.java
new file mode 100644
index 0000000..300db30
--- /dev/null
+++ b/src/main/java/zw/qantra/tm/domain/services/processors/confirmations/ZesaConfirmationProcessor.java
@@ -0,0 +1,25 @@
+package zw.qantra.tm.domain.services.processors.confirmations;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import zw.qantra.tm.domain.models.Transaction;
+import zw.qantra.tm.domain.services.processors.TransactionProcessorInterface;
+import zw.qantra.tm.rest.RestService;
+
+@Service("CONFIRM_POWERTEL_ZESA")
+@RequiredArgsConstructor
+public class ZesaConfirmationProcessor implements TransactionProcessorInterface {
+ private final RestService restService;
+
+
+ @Override
+ public Object process(Transaction transaction) {
+
+ return transaction;
+ }
+
+ @Override
+ public Object reverse(Transaction transaction) {
+ return transaction;
+ }
+}
diff --git a/src/main/java/zw/qantra/tm/rest/RestService.java b/src/main/java/zw/qantra/tm/rest/RestService.java
new file mode 100755
index 0000000..c66a2b0
--- /dev/null
+++ b/src/main/java/zw/qantra/tm/rest/RestService.java
@@ -0,0 +1,195 @@
+package zw.qantra.tm.rest;
+
+import com.google.gson.Gson;
+import lombok.RequiredArgsConstructor;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.*;
+import org.springframework.http.client.BufferingClientHttpRequestFactory;
+import org.springframework.http.client.ClientHttpRequestInterceptor;
+import org.springframework.http.client.SimpleClientHttpRequestFactory;
+import org.springframework.stereotype.Service;
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.client.RestTemplate;
+import zw.qantra.tm.exceptions.RestTemplateResponseErrorHandler;
+
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+/*
+* Rest service that facilitates communication between services. Authentication
+* is either based on client credentials, user credentials or is open. User credentials
+* are only available for registered users
+* */
+
+@Service
+@RequiredArgsConstructor
+public class RestService {
+ Logger logger = LoggerFactory.getLogger(this.getClass().toString());
+
+ @Autowired
+ private Gson gson;
+
+ public final RestTemplate restTemplate;
+
+ public RestService() {
+ this.restTemplate = new RestTemplate(
+ new BufferingClientHttpRequestFactory(
+ new SimpleClientHttpRequestFactory()
+ )
+ );
+
+ this.restTemplate.setErrorHandler(new RestTemplateResponseErrorHandler());
+
+ // bootstrap rest template
+ List interceptors
+ = this.restTemplate.getInterceptors();
+ if (CollectionUtils.isEmpty(interceptors)) {
+ interceptors = new ArrayList<>();
+ }
+ interceptors.add(new RestTemplateInterceptor(gson));
+ this.restTemplate.setInterceptors(interceptors);
+ }
+
+ public String getUserToken(){
+ String token = "Utils.getToken()";
+ logger.info(token);
+ return token;
+ }
+
+ // client token is retrieved from open ID identity provider and
+ // cached for 20 days for easy reuse.
+
+ // todo: definitely not the best way to do this, tried implementing caching but
+ // that needs a bit of time to do correctly
+ public String getClientToken(){
+ logger.info("Getting client token");
+ MultiValueMap payload = new LinkedMultiValueMap<>();
+ payload.add("grant_type", "client_credentials");
+ Map tokenResponse = getToken(payload);
+ assert tokenResponse != null;
+
+ String clientToken = (String) tokenResponse.get("access_token");
+ logger.info("client access token", "log", clientToken);
+ return clientToken;
+ }
+
+ public Map getToken(MultiValueMap payload){
+ HttpHeaders headers = new HttpHeaders();
+ headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
+ // build the request
+ HttpEntity entity = new HttpEntity<>(payload, headers);
+ return this.restTemplate.postForObject("tokenUrl", entity, Map.class);
+ }
+
+ public T getAsClient(String path, Class clazz){
+ HttpHeaders headers = new HttpHeaders();
+ headers.setBearerAuth(getClientToken());
+ HttpEntity entity = new HttpEntity<>(headers);
+ return this.restTemplate.getForObject(path, clazz, entity);
+ }
+
+ public ResponseEntity getExchangeAsClient(String path, Class clazz){
+ HttpHeaders headers = new HttpHeaders();
+ headers.setBearerAuth(getClientToken());
+ HttpEntity entity = new HttpEntity<>(headers);
+ return this.restTemplate.exchange(path, HttpMethod.GET, entity, clazz);
+ }
+
+ public ResponseEntity postAsClient(String path, Object payload, Class clazz){
+ HttpHeaders headers = new HttpHeaders();
+ headers.setContentType(MediaType.APPLICATION_JSON);
+ headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
+ headers.setBearerAuth(getClientToken());
+ // build the request
+ HttpEntity entity = new HttpEntity<>(payload, headers);
+ ResponseEntity responseEntity = this.restTemplate.postForEntity(path, entity, clazz);
+ return responseEntity;
+ }
+
+ public T postAs(String path, Object payload, HttpHeaders headers, Class clazz){
+ // build the request
+ HttpEntity entity = new HttpEntity<>(payload, headers);
+ return this.restTemplate.postForObject(path, entity, clazz);
+ }
+
+ public T post(String path, Object payload, Class clazz){
+ HttpHeaders headers = new HttpHeaders();
+ headers.setContentType(MediaType.APPLICATION_JSON);
+ headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
+ headers.setBearerAuth(getUserToken());
+ // build the request
+ HttpEntity entity = new HttpEntity<>(payload, headers);
+ T t = this.restTemplate.postForObject(path, entity, clazz);
+ return t;
+ }
+
+ public T postWithToken(String token , String path, Object payload, Class clazz){
+ HttpHeaders headers = new HttpHeaders();
+ headers.setContentType(MediaType.APPLICATION_JSON);
+ headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
+ headers.setBearerAuth(token);
+ // build the request
+ HttpEntity entity = new HttpEntity<>(payload, headers);
+ T t = this.restTemplate.postForObject(path, entity, clazz);
+ return t;
+ }
+
+ public T postFormData(String path, MultiValueMap payload, Class clazz)
+ {
+ HttpHeaders headers = new HttpHeaders();
+ headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
+ headers.setContentType(MediaType.MULTIPART_FORM_DATA);
+
+ HttpEntity> request = new HttpEntity<>(payload, headers);
+
+ return this.restTemplate.postForEntity(path, request, clazz).getBody();
+ }
+
+ public T get(String path, Type clazz){
+ HttpHeaders headers = new HttpHeaders();
+ headers.setBearerAuth(getUserToken());
+ HttpEntity entity = new HttpEntity<>(headers);
+
+ ResponseEntity responseEntity = this.restTemplate.exchange(path, HttpMethod.GET, entity, String.class);
+ T response = gson.fromJson(responseEntity.getBody(), clazz);
+ return response;
+ }
+
+ // make post request to endpoint without bearer auth
+ public T postNoAuth(String path, Object payload, Class clazz){
+ HttpHeaders headers = new HttpHeaders();
+ headers.setContentType(MediaType.APPLICATION_JSON);
+ headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
+ // build the request
+ HttpEntity entity = new HttpEntity<>(payload, headers);
+ T t = this.restTemplate.postForObject(path, entity, clazz);
+ return t;
+ }
+
+ // make post request to endpoint without bearer auth
+ public T postWithExternalAuth(String path, Object payload, HttpHeaders headers, Class clazz){
+ headers.setContentType(MediaType.APPLICATION_JSON);
+ headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
+ // build the request
+ HttpEntity entity = new HttpEntity<>(payload, headers);
+ T t = this.restTemplate.postForObject(path, entity, clazz);
+ return t;
+ }
+
+ // make get request to endpoint without bearer auth
+ public T getNoAuth(String path, Class clazz){
+ return this.restTemplate.getForObject(path, clazz);
+ }
+
+ public ResponseEntity> exchangeGetNoAuth(String path, HttpEntity> httpEntity, Class> clazz, String pathVariable) {
+ return this.restTemplate.exchange(path, HttpMethod.GET, httpEntity, clazz, pathVariable);
+ }
+}
diff --git a/src/main/java/zw/qantra/tm/rest/RestTemplateInterceptor.java b/src/main/java/zw/qantra/tm/rest/RestTemplateInterceptor.java
new file mode 100755
index 0000000..ec01b34
--- /dev/null
+++ b/src/main/java/zw/qantra/tm/rest/RestTemplateInterceptor.java
@@ -0,0 +1,72 @@
+package zw.qantra.tm.rest;
+
+import com.google.gson.Gson;
+import lombok.RequiredArgsConstructor;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpRequest;
+import org.springframework.http.client.ClientHttpRequestExecution;
+import org.springframework.http.client.ClientHttpRequestInterceptor;
+import org.springframework.http.client.ClientHttpResponse;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.stream.Collectors;
+
+@RequiredArgsConstructor
+public class RestTemplateInterceptor
+ implements ClientHttpRequestInterceptor {
+ private final Logger logger = LoggerFactory.getLogger(getClass().getName());
+ private final Gson gson;
+
+ @Override
+ public ClientHttpResponse intercept(
+ HttpRequest request,
+ byte[] reqBody,
+ ClientHttpRequestExecution execution) throws IOException {
+ String requestBody = new String(reqBody, StandardCharsets.UTF_8);
+ if(!isJSONValid(requestBody)) requestBody = toBasicJson(requestBody);
+
+ // we don't log file data
+ if(request.getHeaders().getContentLength() < 10000){
+ logger.info("Request: "
+ + request.getMethod() + ": "
+ + request.getURI(), "http", gson.fromJson(requestBody, HashMap.class));
+ }
+ ClientHttpResponse response = execution.execute(request, reqBody);
+ InputStreamReader isr = new InputStreamReader(response.getBody());
+ String body = new BufferedReader(isr).lines()
+ .collect(Collectors.joining("\n"));
+ try{
+ logger.info("Response body: {}", "http", gson.fromJson(body, HashMap.class));
+ }catch (Exception exception){
+ logger.info("Problem serializing resp: {}", "http", exception.getMessage());
+ }
+ return response;
+ }
+
+ public String toBasicJson(String string){
+ HashMap map = new HashMap();
+ map.put("log", string);
+ return gson.toJson(map);
+ }
+
+ public boolean isJSONValid(String test) {
+ try {
+ new JSONObject(test);
+ } catch (JSONException ex) {
+ try {
+ new JSONArray(test);
+ } catch (JSONException ex1) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/src/main/resources/application-lab.properties b/src/main/resources/application-lab.properties
index 0567c1f..706aaeb 100644
--- a/src/main/resources/application-lab.properties
+++ b/src/main/resources/application-lab.properties
@@ -5,7 +5,7 @@ logging.level.org.springframework.boot.autoconfigure=INFO
server.port=6950
-spring.jpa.hibernate.ddl-auto=validate
+spring.jpa.hibernate.ddl-auto=update
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index 0567c1f..706aaeb 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -5,7 +5,7 @@ logging.level.org.springframework.boot.autoconfigure=INFO
server.port=6950
-spring.jpa.hibernate.ddl-auto=validate
+spring.jpa.hibernate.ddl-auto=update
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect
diff --git a/src/main/resources/liquibase-lab.properties b/src/main/resources/liquibase-lab.properties
index 1b82a8e..98053c0 100644
--- a/src/main/resources/liquibase-lab.properties
+++ b/src/main/resources/liquibase-lab.properties
@@ -4,6 +4,6 @@ username=root
password=password
driver=com.mysql.cj.jdbc.Driver
-referenceUrl=hibernate:spring:zw.qantra.tm.domain.models?dialect=org.hibernate.dialect.MySQL5Dialect&hibernate.implicit_naming_strategy=org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy&hibernate.physical_naming_strategy=org.hibernate.boot.model.naming.CamelCaseToUnderscoresNamingStrategy
+referenceUrl=hibernate:spring:zw.qantra.tm.domain.models?dialect=org.hibernate.dialect.MySQLDialect&hibernate.implicit_naming_strategy=org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy&hibernate.physical_naming_strategy=org.hibernate.boot.model.naming.CamelCaseToUnderscoresNamingStrategy
diffChangeLogFile=src/main/resources/liquibase-diff-changeLog.xml
outputChangeLogFile=src/main/resources/liquibase-changeLog.xml