diff --git a/pom.xml b/pom.xml
index 5df47dc..30ced52 100644
--- a/pom.xml
+++ b/pom.xml
@@ -58,11 +58,6 @@
org.springframework.boot
spring-boot-starter-web
-
- com.mysql
- mysql-connector-j
- runtime
-
org.projectlombok
lombok
@@ -103,6 +98,12 @@
json
20231013
+
+ org.postgresql
+ postgresql
+ runtime
+
+
diff --git a/src/main/java/zw/qantra/tm/TmApplication.java b/src/main/java/zw/qantra/tm/TmApplication.java
index 3a8f8f7..ec94f6d 100644
--- a/src/main/java/zw/qantra/tm/TmApplication.java
+++ b/src/main/java/zw/qantra/tm/TmApplication.java
@@ -2,6 +2,9 @@ package zw.qantra.tm;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.Bean;
+
+import com.google.gson.Gson;
@SpringBootApplication
public class TmApplication {
@@ -10,4 +13,8 @@ public class TmApplication {
SpringApplication.run(TmApplication.class, args);
}
+ @Bean
+ public Gson gson() {
+ return new Gson();
+ }
}
diff --git a/src/main/java/zw/qantra/tm/domain/controllers/ConfigController.java b/src/main/java/zw/qantra/tm/domain/controllers/ConfigController.java
new file mode 100644
index 0000000..03a386f
--- /dev/null
+++ b/src/main/java/zw/qantra/tm/domain/controllers/ConfigController.java
@@ -0,0 +1,83 @@
+package zw.qantra.tm.domain.controllers;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import zw.qantra.tm.domain.enums.ChargeConditionType;
+import zw.qantra.tm.domain.enums.ChargeLabelEnum;
+import zw.qantra.tm.domain.enums.CurrencyType;
+import zw.qantra.tm.domain.models.Charge;
+import zw.qantra.tm.domain.models.ChargeCondition;
+import zw.qantra.tm.domain.services.ChargeConditionService;
+import zw.qantra.tm.domain.services.ChargeService;
+
+import java.math.BigDecimal;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+
+@RestController
+@RequestMapping("/configs")
+@RequiredArgsConstructor
+public class ConfigController {
+ private final ChargeConditionService chargeConditionService;
+ private final ChargeService chargeService;
+
+ @GetMapping("/charges")
+ public ResponseEntity charges(){
+ List chargeConditions = Arrays.asList(
+ ChargeCondition.builder()
+ .name("Ecocash Gateway")
+ .type(ChargeConditionType.GATEWAY)
+ .codes("ECOCASH")
+ .build(),
+ ChargeCondition.builder()
+ .name("Steward Gateway")
+ .type(ChargeConditionType.GATEWAY)
+ .codes("STEWARD")
+ .build(),
+ ChargeCondition.builder()
+ .name("Mastercard Gateway")
+ .type(ChargeConditionType.GATEWAY)
+ .codes("MPGS")
+ .build()
+ );
+ chargeService.getChargeRepository().deleteAll();
+ chargeConditionService.getChargeConditionRepository().deleteAll();
+ chargeConditionService.getChargeConditionRepository().saveAll(chargeConditions);
+
+ List charges = Arrays.asList(
+ Charge.builder()
+ .description("Gateway fee")
+ .chargeLabel(ChargeLabelEnum.GATEWAY_FEE)
+ .currency(CurrencyType.USD)
+ .percentageRate(new BigDecimal("1"))
+ .includes(new HashSet<>() {{
+ add(chargeConditions.get(0));
+ add(chargeConditions.get(1));
+ add(chargeConditions.get(2));
+ }})
+ .build(),
+ Charge.builder()
+ .description("Tax")
+ .chargeLabel(ChargeLabelEnum.TAX)
+ .currency(CurrencyType.USD)
+ .percentageRate(new BigDecimal("2"))
+ .max(new BigDecimal("10150"))
+ .minimumAmount(new BigDecimal("5"))
+ .maximumAmount(new BigDecimal("500000"))
+ .includes(new HashSet<>() {{
+ add(chargeConditions.get(0));
+ add(chargeConditions.get(1));
+ add(chargeConditions.get(2));
+ }})
+ .build()
+ );
+
+ chargeService.getChargeRepository().saveAll(charges);
+
+ return ResponseEntity.ok("Charges configuration complete");
+ }
+}
diff --git a/src/main/java/zw/qantra/tm/domain/controllers/ProviderController.java b/src/main/java/zw/qantra/tm/domain/controllers/ProviderController.java
new file mode 100644
index 0000000..ebca345
--- /dev/null
+++ b/src/main/java/zw/qantra/tm/domain/controllers/ProviderController.java
@@ -0,0 +1,57 @@
+package zw.qantra.tm.domain.controllers;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+import zw.qantra.tm.domain.models.Provider;
+import zw.qantra.tm.domain.services.ProviderService;
+import zw.qantra.tm.rest.ApiResponse;
+
+import java.util.UUID;
+
+@RestController
+@RequestMapping("/providers")
+@RequiredArgsConstructor
+public class ProviderController {
+
+ private final ProviderService providerService;
+
+ @GetMapping
+ public ResponseEntity getProviders() {
+ return ApiResponse.ok(providerService.getProviders());
+ }
+
+ @GetMapping("/{id}/products")
+ public ResponseEntity getProducts(@PathVariable UUID id) {
+ return ApiResponse.ok(providerService.getProviderProducts(id));
+ }
+
+ @GetMapping("/{id}")
+ public ResponseEntity getProvider(@PathVariable UUID id) {
+ return providerService.getProvider(id)
+ .map(ApiResponse::ok)
+ .orElse(ApiResponse.notFound());
+ }
+
+ @PostMapping
+ public ResponseEntity createProvider(@RequestBody Provider provider) {
+ return ApiResponse.ok(providerService.createProvider(provider));
+ }
+
+ @PutMapping("/{id}")
+ public ResponseEntity updateProvider(@PathVariable UUID id, @RequestBody Provider provider) {
+ Provider updatedProvider = providerService.updateProvider(id, provider);
+ if (updatedProvider != null) {
+ return ApiResponse.ok(updatedProvider);
+ }
+ return ApiResponse.notFound();
+ }
+
+ @DeleteMapping("/{id}")
+ public ResponseEntity deleteProvider(@PathVariable UUID id) {
+ if (providerService.deleteProvider(id)) {
+ return ApiResponse.ok("Provider deleted successfully");
+ }
+ return ApiResponse.notFound();
+ }
+}
diff --git a/src/main/java/zw/qantra/tm/domain/controllers/TransactonController.java b/src/main/java/zw/qantra/tm/domain/controllers/TransactonController.java
index 0885d41..162c508 100644
--- a/src/main/java/zw/qantra/tm/domain/controllers/TransactonController.java
+++ b/src/main/java/zw/qantra/tm/domain/controllers/TransactonController.java
@@ -1,18 +1,53 @@
package zw.qantra.tm.domain.controllers;
import lombok.RequiredArgsConstructor;
+import zw.qantra.tm.domain.services.TransactionService;
+import zw.qantra.tm.domain.models.Transaction;
+
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;
+import org.springframework.web.bind.annotation.*;
+import java.util.List;
+import java.util.UUID;
@RestController
@RequestMapping("/transaction")
@RequiredArgsConstructor
public class TransactonController {
- @PostMapping()
- public ResponseEntity post(@RequestBody Object transactionRequest) {
- return ResponseEntity.ok("Transaction endpoint is working");
+
+ private final TransactionService transactionService;
+
+ @GetMapping
+ public ResponseEntity> getAll() {
+ return ResponseEntity.ok(transactionService.getTransactionRepository().findAll());
+ }
+
+ @GetMapping("/{id}")
+ public ResponseEntity getById(@PathVariable UUID id) {
+ return transactionService.getTransactionRepository().findById(id)
+ .map(ResponseEntity::ok)
+ .orElse(ResponseEntity.notFound().build());
+ }
+
+ @PostMapping
+ public ResponseEntity create(@RequestBody Transaction transaction) {
+ return ResponseEntity.ok(transactionService.execute(transaction));
+ }
+
+ @PutMapping("/{id}")
+ public ResponseEntity update(@PathVariable UUID id, @RequestBody Transaction transaction) {
+ if (!transactionService.getTransactionRepository().existsById(id)) {
+ return ResponseEntity.notFound().build();
+ }
+ transaction.setId(id);
+ return ResponseEntity.ok(transactionService.execute(transaction));
+ }
+
+ @DeleteMapping("/{id}")
+ public ResponseEntity delete(@PathVariable UUID id) {
+ if (!transactionService.getTransactionRepository().existsById(id)) {
+ return ResponseEntity.notFound().build();
+ }
+ transactionService.getTransactionRepository().deleteById(id);
+ return ResponseEntity.noContent().build();
}
}
diff --git a/src/main/java/zw/qantra/tm/domain/dtos/SBZProductDto.java b/src/main/java/zw/qantra/tm/domain/dtos/SBZProductDto.java
new file mode 100644
index 0000000..c75b97b
--- /dev/null
+++ b/src/main/java/zw/qantra/tm/domain/dtos/SBZProductDto.java
@@ -0,0 +1,22 @@
+package zw.qantra.tm.domain.dtos;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.math.BigDecimal;
+import java.util.UUID;
+
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class SBZProductDto {
+ private String name;
+ private String description;
+ private String displayName;
+ private BigDecimal defaultAmount;
+ private boolean requiresAmount;
+ private UUID uid;
+}
\ No newline at end of file
diff --git a/src/main/java/zw/qantra/tm/domain/models/BaseEntity.java b/src/main/java/zw/qantra/tm/domain/models/BaseEntity.java
new file mode 100644
index 0000000..4870587
--- /dev/null
+++ b/src/main/java/zw/qantra/tm/domain/models/BaseEntity.java
@@ -0,0 +1,28 @@
+package zw.qantra.tm.domain.models;
+
+import jakarta.persistence.Column;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.Id;
+import jakarta.persistence.MappedSuperclass;
+import lombok.Getter;
+import lombok.Setter;
+import org.hibernate.annotations.GenericGenerator;
+import org.hibernate.annotations.JdbcTypeCode;
+import org.hibernate.type.SqlTypes;
+
+import java.util.UUID;
+
+@Getter
+@Setter
+@MappedSuperclass
+public class BaseEntity {
+ @Id
+ @GeneratedValue(generator = "UUID")
+ @GenericGenerator(
+ name = "UUID",
+ strategy = "org.hibernate.id.UUIDGenerator"
+ )
+ @JdbcTypeCode(SqlTypes.UUID)
+ @Column(name = "id", updatable = false, nullable = false)
+ private UUID id;
+}
diff --git a/src/main/java/zw/qantra/tm/domain/models/Charge.java b/src/main/java/zw/qantra/tm/domain/models/Charge.java
index 3e317b5..a0f30a8 100644
--- a/src/main/java/zw/qantra/tm/domain/models/Charge.java
+++ b/src/main/java/zw/qantra/tm/domain/models/Charge.java
@@ -10,7 +10,6 @@ import zw.qantra.tm.domain.enums.TargetEvent;
import java.math.BigDecimal;
import java.util.HashSet;
import java.util.Set;
-import java.util.UUID;
@Entity
@Getter
@@ -18,11 +17,7 @@ import java.util.UUID;
@Builder
@AllArgsConstructor
@NoArgsConstructor
-public class Charge {
- @Id
- @GeneratedValue(strategy = GenerationType.UUID)
- private UUID id;
-
+public class Charge extends BaseEntity {
private String description;
// % rate to charge
diff --git a/src/main/java/zw/qantra/tm/domain/models/ChargeCondition.java b/src/main/java/zw/qantra/tm/domain/models/ChargeCondition.java
index 80a5ee1..e7d2f3b 100644
--- a/src/main/java/zw/qantra/tm/domain/models/ChargeCondition.java
+++ b/src/main/java/zw/qantra/tm/domain/models/ChargeCondition.java
@@ -5,7 +5,6 @@ import lombok.*;
import zw.qantra.tm.domain.enums.ChargeConditionType;
import java.util.List;
-import java.util.UUID;
@Entity
@Getter
@@ -13,15 +12,11 @@ import java.util.UUID;
@Builder
@AllArgsConstructor
@NoArgsConstructor
-public class ChargeCondition {
- @Id
- @GeneratedValue(strategy = GenerationType.UUID)
- private UUID id;
-
+public class ChargeCondition extends BaseEntity {
private String name;
@Enumerated(EnumType.STRING)
private ChargeConditionType type;
- @Column(columnDefinition = "longtext")
+ @Column(columnDefinition = "TEXT")
private String codes;
@ManyToMany(mappedBy = "includes")
diff --git a/src/main/java/zw/qantra/tm/domain/models/Currency.java b/src/main/java/zw/qantra/tm/domain/models/Currency.java
index 6c350b0..3d75768 100644
--- a/src/main/java/zw/qantra/tm/domain/models/Currency.java
+++ b/src/main/java/zw/qantra/tm/domain/models/Currency.java
@@ -1,11 +1,6 @@
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
@@ -14,11 +9,7 @@ import lombok.*;
@Builder
@AllArgsConstructor
@NoArgsConstructor
-public class Currency {
- @Id
- @GeneratedValue(strategy = GenerationType.UUID)
- private UUID id;
-
+public class Currency extends BaseEntity {
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
index 527132b..958aa5e 100644
--- a/src/main/java/zw/qantra/tm/domain/models/IntegrationProcessor.java
+++ b/src/main/java/zw/qantra/tm/domain/models/IntegrationProcessor.java
@@ -1,11 +1,6 @@
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
@@ -14,12 +9,8 @@ import lombok.*;
@Builder
@AllArgsConstructor
@NoArgsConstructor
-public class IntegrationProcessor {
- @Id
- @GeneratedValue(strategy = GenerationType.UUID)
- private UUID id;
-
+public class IntegrationProcessor extends BaseEntity {
private String name;
private String description;
- private String type;
+ private String label;
}
diff --git a/src/main/java/zw/qantra/tm/domain/models/PaymentProcessor.java b/src/main/java/zw/qantra/tm/domain/models/PaymentProcessor.java
index 49ef204..470d981 100644
--- a/src/main/java/zw/qantra/tm/domain/models/PaymentProcessor.java
+++ b/src/main/java/zw/qantra/tm/domain/models/PaymentProcessor.java
@@ -3,7 +3,7 @@ package zw.qantra.tm.domain.models;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
-import jakarta.persistence.Id;
+import jakarta.persistence.Column;
import lombok.*;
import java.util.UUID;
@@ -14,9 +14,9 @@ import java.util.UUID;
@Builder
@AllArgsConstructor
@NoArgsConstructor
-public class PaymentProcessor {
- @Id
+public class PaymentProcessor extends BaseEntity {
@GeneratedValue(strategy = GenerationType.UUID)
+ @Column(columnDefinition = "VARCHAR(36)")
private UUID id;
private String name;
diff --git a/src/main/java/zw/qantra/tm/domain/models/Provider.java b/src/main/java/zw/qantra/tm/domain/models/Provider.java
index 3df2252..b88e648 100644
--- a/src/main/java/zw/qantra/tm/domain/models/Provider.java
+++ b/src/main/java/zw/qantra/tm/domain/models/Provider.java
@@ -1,12 +1,8 @@
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
@@ -14,12 +10,9 @@ import java.util.UUID;
@Builder
@AllArgsConstructor
@NoArgsConstructor
-public class Provider {
- @Id
- @GeneratedValue(strategy = GenerationType.UUID)
- private UUID id;
-
+public class Provider extends BaseEntity {
private String clientId;
- private String name;
+ private String label;
private String description;
+ private String externalId;
}
diff --git a/src/main/java/zw/qantra/tm/domain/models/Setting.java b/src/main/java/zw/qantra/tm/domain/models/Setting.java
index 95ae24c..25b7aeb 100644
--- a/src/main/java/zw/qantra/tm/domain/models/Setting.java
+++ b/src/main/java/zw/qantra/tm/domain/models/Setting.java
@@ -1,11 +1,6 @@
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;
@@ -18,11 +13,7 @@ import lombok.Setter;
@Builder
@AllArgsConstructor
@NoArgsConstructor
-public class Setting {
- @Id
- @GeneratedValue(strategy = GenerationType.UUID)
- private UUID id;
-
+public class Setting extends BaseEntity {
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
index c4044c9..a6228de 100644
--- a/src/main/java/zw/qantra/tm/domain/models/Transaction.java
+++ b/src/main/java/zw/qantra/tm/domain/models/Transaction.java
@@ -9,7 +9,6 @@ import zw.qantra.tm.domain.enums.Status;
import java.math.BigDecimal;
import java.util.List;
-import java.util.UUID;
@Entity
@Getter
@@ -18,11 +17,7 @@ import java.util.UUID;
@AllArgsConstructor
@NoArgsConstructor
@JsonInclude(JsonInclude.Include.NON_NULL)
-public class Transaction {
- @Id
- @GeneratedValue(strategy = GenerationType.UUID)
- private UUID id;
-
+public class Transaction extends BaseEntity {
private String trace;
private BigDecimal amount;
private BigDecimal charge;
@@ -34,6 +29,7 @@ public class Transaction {
private String productUid;
private String paymentProcessorLabel;
private String integrationProcessorLabel;
+ private String providerLabel;
private String rrn;
private String channelName;
private String channel;
diff --git a/src/main/java/zw/qantra/tm/domain/repositories/ChargeConditionRepository.java b/src/main/java/zw/qantra/tm/domain/repositories/ChargeConditionRepository.java
new file mode 100644
index 0000000..62e36d1
--- /dev/null
+++ b/src/main/java/zw/qantra/tm/domain/repositories/ChargeConditionRepository.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.ChargeCondition;
+
+import java.util.UUID;
+
+@Repository
+public interface ChargeConditionRepository extends JpaRepository {
+}
\ No newline at end of file
diff --git a/src/main/java/zw/qantra/tm/domain/repositories/ChargeRepository.java b/src/main/java/zw/qantra/tm/domain/repositories/ChargeRepository.java
new file mode 100644
index 0000000..7652933
--- /dev/null
+++ b/src/main/java/zw/qantra/tm/domain/repositories/ChargeRepository.java
@@ -0,0 +1,14 @@
+package zw.qantra.tm.domain.repositories;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+import zw.qantra.tm.domain.enums.CurrencyType;
+import zw.qantra.tm.domain.models.Charge;
+
+import java.util.List;
+import java.util.UUID;
+
+@Repository
+public interface ChargeRepository extends JpaRepository {
+ List findAllByCurrency(CurrencyType currency);
+}
\ No newline at end of file
diff --git a/src/main/java/zw/qantra/tm/domain/services/ChargeConditionService.java b/src/main/java/zw/qantra/tm/domain/services/ChargeConditionService.java
new file mode 100644
index 0000000..edfede5
--- /dev/null
+++ b/src/main/java/zw/qantra/tm/domain/services/ChargeConditionService.java
@@ -0,0 +1,20 @@
+package zw.qantra.tm.domain.services;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import zw.qantra.tm.domain.models.ChargeCondition;
+import zw.qantra.tm.domain.repositories.ChargeConditionRepository;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.UUID;
+
+@Service
+@RequiredArgsConstructor
+public class ChargeConditionService {
+ private final ChargeConditionRepository chargeConditionRepository;
+
+ public ChargeConditionRepository getChargeConditionRepository() {
+ return chargeConditionRepository;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/zw/qantra/tm/domain/services/ChargeService.java b/src/main/java/zw/qantra/tm/domain/services/ChargeService.java
new file mode 100644
index 0000000..3b7c3e7
--- /dev/null
+++ b/src/main/java/zw/qantra/tm/domain/services/ChargeService.java
@@ -0,0 +1,20 @@
+package zw.qantra.tm.domain.services;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import zw.qantra.tm.domain.models.Charge;
+import zw.qantra.tm.domain.repositories.ChargeRepository;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.UUID;
+
+@Service
+@RequiredArgsConstructor
+public class ChargeService {
+ private final ChargeRepository chargeRepository;
+
+ public ChargeRepository getChargeRepository() {
+ return chargeRepository;
+ }
+}
\ 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
index b94af52..5a1abc2 100644
--- a/src/main/java/zw/qantra/tm/domain/services/CurrencyService.java
+++ b/src/main/java/zw/qantra/tm/domain/services/CurrencyService.java
@@ -8,4 +8,8 @@ import zw.qantra.tm.domain.repositories.CurrencyRepository;
@RequiredArgsConstructor
public class CurrencyService {
private final CurrencyRepository currencyRepository;
+
+ public CurrencyRepository getCurrencyRepository() {
+ return 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
index 77e82c7..e4f0440 100644
--- a/src/main/java/zw/qantra/tm/domain/services/IntegrationProcessorService.java
+++ b/src/main/java/zw/qantra/tm/domain/services/IntegrationProcessorService.java
@@ -8,4 +8,8 @@ import zw.qantra.tm.domain.repositories.IntegrationProcessorRepository;
@RequiredArgsConstructor
public class IntegrationProcessorService {
private final IntegrationProcessorRepository integrationProcessorRepository;
+
+ public IntegrationProcessorRepository getIntegrationProcessorRepository() {
+ return 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
index 4b2bee4..0c8f473 100644
--- a/src/main/java/zw/qantra/tm/domain/services/PaymentProcessorService.java
+++ b/src/main/java/zw/qantra/tm/domain/services/PaymentProcessorService.java
@@ -8,4 +8,8 @@ import zw.qantra.tm.domain.repositories.PaymentProcessorRepository;
@RequiredArgsConstructor
public class PaymentProcessorService {
private final PaymentProcessorRepository paymentProcessorRepository;
+
+ public PaymentProcessorRepository getPaymentProcessorRepository() {
+ return 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
index 17afbf8..2ce20ab 100644
--- a/src/main/java/zw/qantra/tm/domain/services/ProviderService.java
+++ b/src/main/java/zw/qantra/tm/domain/services/ProviderService.java
@@ -1,11 +1,87 @@
package zw.qantra.tm.domain.services;
import lombok.RequiredArgsConstructor;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import zw.qantra.tm.domain.dtos.SBZProductDto;
+import zw.qantra.tm.domain.models.Provider;
+import zw.qantra.tm.domain.repositories.ChargeRepository;
import zw.qantra.tm.domain.repositories.ProviderRepository;
+import zw.qantra.tm.rest.RestService;
+
+import java.util.*;
@Service
@RequiredArgsConstructor
public class ProviderService {
private final ProviderRepository providerRepository;
+ private final RestService restService;
+
+ @Value("${sbz.aggregator.url}")
+ private String aggregatorUrl;
+
+ public ProviderRepository getProviderRepository() {
+ return providerRepository;
+ }
+
+ public List getProviderProducts(UUID providerId) {
+ String token = restService.getMerchantToken();
+
+ HttpHeaders headers = new HttpHeaders();
+ headers.setContentType(MediaType.APPLICATION_JSON);
+ headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
+ headers.setBearerAuth(token);
+
+ LinkedHashMap response = this.restService.getAs(
+ aggregatorUrl + "/merchant/aggregation/products?size=100&clientUid=" + providerId.toString(),
+ headers,
+ LinkedHashMap.class
+ );
+
+ LinkedHashMap body = (LinkedHashMap) response.get("body");
+ List content = (List) body.get("content");
+
+ List products = new ArrayList<>();
+ for (LinkedHashMap product : content) {
+ ObjectMapper mapper = new ObjectMapper();
+ SBZProductDto productDto = mapper.convertValue(product, SBZProductDto.class);
+ products.add(productDto);
+ }
+
+ return products;
+ }
+
+ public List getProviders() {
+ return providerRepository.findAll();
+ }
+
+ public Optional getProvider(UUID id) {
+ return providerRepository.findById(id);
+ }
+
+ public Provider createProvider(Provider provider) {
+ return providerRepository.save(provider);
+ }
+
+ public Provider updateProvider(UUID id, Provider provider) {
+ if (providerRepository.existsById(id)) {
+ provider.setId(id);
+ return providerRepository.save(provider);
+ }
+ return null;
+ }
+
+ public boolean deleteProvider(UUID id) {
+ if (providerRepository.existsById(id)) {
+ providerRepository.deleteById(id);
+ return true;
+ }
+ return false;
+ }
}
\ 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
index 4b36c93..7fdd001 100644
--- a/src/main/java/zw/qantra/tm/domain/services/SettingService.java
+++ b/src/main/java/zw/qantra/tm/domain/services/SettingService.java
@@ -8,4 +8,8 @@ import zw.qantra.tm.domain.repositories.SettingRepository;
@RequiredArgsConstructor
public class SettingService {
private final SettingRepository settingRepository;
+
+ public SettingRepository getSettingRepository() {
+ return 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
index e50c0dc..c116808 100644
--- a/src/main/java/zw/qantra/tm/domain/services/TransactionService.java
+++ b/src/main/java/zw/qantra/tm/domain/services/TransactionService.java
@@ -3,7 +3,6 @@ package zw.qantra.tm.domain.services;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import zw.qantra.tm.domain.enums.Status;
-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;
@@ -17,11 +16,18 @@ import zw.qantra.tm.domain.services.processors.handlers.ValidationHandler;
public class TransactionService {
private final TransactionRepository transactionRepository;
private final PaymentProcessorFactory paymentProcessorFactory;
+ private final CalculateChargesHandler calculateChargesHandler;
+
+ public TransactionRepository getTransactionRepository() {
+ return transactionRepository;
+ }
public Transaction execute(Transaction transaction) {
+
LogicPipeline transactionLogic =
- new LogicPipeline<>(new CalculateChargesHandler())
+ new LogicPipeline<>(calculateChargesHandler)
.addHandler(new ValidationHandler());
+
transaction = transactionLogic.execute(transaction);
String label = transaction.getType() + "_" + transaction.getIntegrationProcessorLabel();
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
index 7bb28e0..d4dde96 100644
--- 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
@@ -18,8 +18,10 @@ public class ZesaConfirmationProcessor implements TransactionProcessorInterface
@Override
public Object process(Transaction transaction) throws Exception {
System.out.println("processing transaction");
- throw new Exception("Challenge encountered, please contact support.");
-// return transaction;
+
+
+
+ return transaction;
}
@Override
diff --git a/src/main/java/zw/qantra/tm/domain/services/processors/handlers/CalculateChargesHandler.java b/src/main/java/zw/qantra/tm/domain/services/processors/handlers/CalculateChargesHandler.java
index b3afe66..420056d 100644
--- a/src/main/java/zw/qantra/tm/domain/services/processors/handlers/CalculateChargesHandler.java
+++ b/src/main/java/zw/qantra/tm/domain/services/processors/handlers/CalculateChargesHandler.java
@@ -1,11 +1,135 @@
package zw.qantra.tm.domain.services.processors.handlers;
+import lombok.AllArgsConstructor;
+import org.springframework.stereotype.Service;
+import zw.qantra.tm.domain.models.Charge;
+import zw.qantra.tm.domain.models.ChargeCondition;
import zw.qantra.tm.domain.models.Transaction;
+import zw.qantra.tm.domain.services.ChargeConditionService;
+import zw.qantra.tm.domain.services.ChargeService;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+
+@AllArgsConstructor
+@Service
public class CalculateChargesHandler implements LogicHandler {
+ private ChargeConditionService chargeConditionService;
+ private ChargeService chargeService;
+
@Override
public Transaction process(Transaction transaction) {
System.out.println("processing charges");
+ transaction.setCharge(BigDecimal.ZERO);
+ transaction.setTax(BigDecimal.ZERO);
+ transaction.setGatewayCharge(BigDecimal.ZERO);
+
+ BigDecimal amountToCharge = transaction.getAmount();
+ BigDecimal chargeAmount = BigDecimal.ZERO;
+
+ List chargeList = chargeService.getChargeRepository().findAllByCurrency(transaction.getDebitCurrency());
+
+ List appropriateCharges = new ArrayList<>();
+ for (Charge charge : chargeList) {
+ boolean isPresent = false;
+ for (ChargeCondition chargeCondition: charge.getIncludes()){
+ List conditionTypes = Arrays.asList(chargeCondition.getCodes().split(","));
+ switch (chargeCondition.getType()){
+ case GATEWAY:
+ isPresent = conditionTypes.contains(transaction.getPaymentProcessorLabel());
+ break;
+ case PROVIDER:
+ isPresent = conditionTypes.contains(transaction.getIntegrationProcessorLabel());
+ break;
+ }
+ if(isPresent) break; // we've found a matching condition, no need to check rest of conditions
+ }
+ if(!isPresent) continue; // if we did not find a match, means this charge does not apply to this transaction
+
+ boolean isExempt = false;
+ for (ChargeCondition chargeCondition: charge.getExcludes()){
+ List conditionTypes = Arrays.asList(chargeCondition.getCodes().split(","));
+ switch (chargeCondition.getType()){
+ case GATEWAY:
+ isExempt = conditionTypes.contains(transaction.getPaymentProcessorLabel());
+ break;
+ case PROVIDER:
+ isExempt = conditionTypes.contains(transaction.getIntegrationProcessorLabel());
+ break;
+ }
+ if(isExempt) break; // we've found a matching condition, no need to check rest of conditions
+ }
+ if(isExempt) continue; // if we found a match, means this transaction is exempt from this charge
+
+ appropriateCharges.add(charge);
+ }
+
+ for (Charge charge : appropriateCharges) {
+ // if minimum amount chargeable is lower than the amount to charge, apply the min charge
+ if(charge.getMinimumAmount() != null) {
+ if(charge.getMinimumAmount().compareTo(amountToCharge) > 0) {
+ applyCharge(transaction, charge, charge.getMin());
+ continue;
+ }
+ }
+
+ // if maximum amount chargeable is higher than the amount to charge, apply the max charge
+ if(charge.getMaximumAmount() != null) {
+ if(charge.getMaximumAmount().compareTo(amountToCharge) < 0) {
+ applyCharge(transaction, charge, charge.getMax());
+ continue;
+ }
+ }
+
+ // calculate unrestricted charge amount
+ // if percentage rate is not present then it's a flat charge, else it's a percentage
+ if(charge.getPercentageRate() == null) {
+ if(charge.getFlat() == null)
+ continue;
+
+ chargeAmount = charge.getFlat();
+ }else {
+ BigDecimal fraction = charge.getPercentageRate().divide(
+ new BigDecimal(100), 4, RoundingMode.CEILING);
+ chargeAmount = amountToCharge.multiply(fraction)
+ .setScale(2, RoundingMode.CEILING);
+
+ // if it's a composite charge then add the flat component as well
+ if(charge.isComposite()) {
+ chargeAmount = chargeAmount.add(charge.getFlat())
+ .setScale(2, RoundingMode.CEILING);
+ }
+ }
+
+ // if minimum charge is set, check if the charge amount is less than the minimum
+ if(charge.getMin() != null) {
+ if(charge.getMin().compareTo(chargeAmount) > 0){
+ chargeAmount = charge.getMin();
+ }
+ }
+
+ // if maximum charge is set, check if the charge amount is more than the maximum
+ if(charge.getMax() != null) {
+ if(charge.getMax().compareTo(chargeAmount) < 0) {
+ chargeAmount = charge.getMax();
+ }
+ }
+
+ applyCharge(transaction, charge, chargeAmount);
+ }
+
return transaction;
}
+
+ void applyCharge(Transaction transaction, Charge charge, BigDecimal chargeAmount) {
+ switch (charge.getChargeLabel()) {
+ case FEE -> transaction.setCharge(chargeAmount);
+ case TAX -> transaction.setTax(chargeAmount);
+ case GATEWAY_FEE -> transaction.setGatewayCharge(chargeAmount);
+ }
+ }
}
diff --git a/src/main/java/zw/qantra/tm/exceptions/RestTemplateResponseErrorHandler.java b/src/main/java/zw/qantra/tm/exceptions/RestTemplateResponseErrorHandler.java
index e34e715..e9a13ba 100755
--- a/src/main/java/zw/qantra/tm/exceptions/RestTemplateResponseErrorHandler.java
+++ b/src/main/java/zw/qantra/tm/exceptions/RestTemplateResponseErrorHandler.java
@@ -40,7 +40,7 @@ public class RestTemplateResponseErrorHandler
BufferedReader reader = new BufferedReader(new InputStreamReader(httpResponse.getBody()));
String httpBodyResponse = reader.lines().collect(Collectors.joining(""));
- throw new RestException(httpBodyResponse, "Client Error");
+ throw new RestException(httpBodyResponse, "Client Error: " + httpResponse.getStatusCode());
}
}
}
\ No newline at end of file
diff --git a/src/main/java/zw/qantra/tm/rest/RestService.java b/src/main/java/zw/qantra/tm/rest/RestService.java
index c66a2b0..4c6802e 100755
--- a/src/main/java/zw/qantra/tm/rest/RestService.java
+++ b/src/main/java/zw/qantra/tm/rest/RestService.java
@@ -39,6 +39,13 @@ public class RestService {
public final RestTemplate restTemplate;
+ @Value("${sbz.aggregator.client-id}")
+ private String clientId;
+ @Value("${sbz.aggregator.client-secret}")
+ private String clientSecret;
+ @Value("${sbz.aggregator.url}")
+ private String aggregatorUrl;
+
public RestService() {
this.restTemplate = new RestTemplate(
new BufferingClientHttpRequestFactory(
@@ -54,7 +61,7 @@ public class RestService {
if (CollectionUtils.isEmpty(interceptors)) {
interceptors = new ArrayList<>();
}
- interceptors.add(new RestTemplateInterceptor(gson));
+ interceptors.add(new RestTemplateInterceptor());
this.restTemplate.setInterceptors(interceptors);
}
@@ -81,6 +88,24 @@ public class RestService {
return clientToken;
}
+ public String getMerchantToken() {
+ HttpHeaders headers = new HttpHeaders();
+ headers.setContentType(MediaType.APPLICATION_JSON);
+ headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
+
+ Map payload = Map.of(
+ "clientId", clientId,
+ "clientSecret", clientSecret
+ );
+
+ HttpEntity