diff --git a/pom.xml b/pom.xml
index 30ced52..e2ad341 100644
--- a/pom.xml
+++ b/pom.xml
@@ -103,6 +103,11 @@
postgresql
runtime
+
+ net.kaczmarzyk
+ specification-arg-resolver
+ 3.1.0
+
diff --git a/src/main/java/zw/qantra/tm/domain/controllers/CategoryController.java b/src/main/java/zw/qantra/tm/domain/controllers/CategoryController.java
new file mode 100644
index 0000000..44fa0a6
--- /dev/null
+++ b/src/main/java/zw/qantra/tm/domain/controllers/CategoryController.java
@@ -0,0 +1,52 @@
+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.Category;
+import zw.qantra.tm.domain.services.CategoryService;
+import zw.qantra.tm.rest.ApiResponse;
+
+import java.util.UUID;
+
+@RestController
+@RequestMapping("/categories")
+@RequiredArgsConstructor
+public class CategoryController {
+
+ private final CategoryService categoryService;
+
+ @GetMapping
+ public ResponseEntity getAllCategories() {
+ return ApiResponse.ok(categoryService.getAllCategories());
+ }
+
+ @GetMapping("/{id}")
+ public ResponseEntity getCategory(@PathVariable UUID id) {
+ return categoryService.getCategory(id)
+ .map(ApiResponse::ok)
+ .orElse(ApiResponse.notFound());
+ }
+
+ @PostMapping
+ public ResponseEntity createCategory(@RequestBody Category category) {
+ return ApiResponse.ok(categoryService.createCategory(category));
+ }
+
+ @PutMapping("/{id}")
+ public ResponseEntity updateCategory(@PathVariable UUID id, @RequestBody Category category) {
+ Category updatedCategory = categoryService.updateCategory(id, category);
+ if (updatedCategory != null) {
+ return ApiResponse.ok(updatedCategory);
+ }
+ return ApiResponse.notFound();
+ }
+
+ @DeleteMapping("/{id}")
+ public ResponseEntity deleteCategory(@PathVariable UUID id) {
+ if (categoryService.deleteCategory(id)) {
+ return ApiResponse.ok("Category deleted successfully");
+ }
+ return ApiResponse.notFound();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/zw/qantra/tm/domain/controllers/ProviderController.java b/src/main/java/zw/qantra/tm/domain/controllers/ProviderController.java
index ebca345..7e9c0b4 100644
--- a/src/main/java/zw/qantra/tm/domain/controllers/ProviderController.java
+++ b/src/main/java/zw/qantra/tm/domain/controllers/ProviderController.java
@@ -17,8 +17,8 @@ public class ProviderController {
private final ProviderService providerService;
@GetMapping
- public ResponseEntity getProviders() {
- return ApiResponse.ok(providerService.getProviders());
+ public ResponseEntity getProviders(@RequestParam(required = false) String category) {
+ return ApiResponse.ok(providerService.getProviders(category));
}
@GetMapping("/{id}/products")
diff --git a/src/main/java/zw/qantra/tm/domain/dtos/BillPaymentDto.java b/src/main/java/zw/qantra/tm/domain/dtos/BillPaymentDto.java
new file mode 100644
index 0000000..96029b6
--- /dev/null
+++ b/src/main/java/zw/qantra/tm/domain/dtos/BillPaymentDto.java
@@ -0,0 +1,31 @@
+package zw.qantra.tm.domain.dtos;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import zw.qantra.tm.domain.enums.CurrencyType;
+import zw.qantra.tm.domain.enums.RequestType;
+
+import java.math.BigDecimal;
+
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class BillPaymentDto {
+ private RequestType type;
+ private String billClientId;
+ private String debitRef;
+ private CurrencyType debitCurrency;
+ private BigDecimal amount;
+ private String aggregatorId;
+ private String debitPhone;
+ private String debitAccount;
+ private String creditAccount;
+ private CurrencyType creditCurrency;
+ private String creditPhone;
+ private String billName;
+}
\ No newline at end of file
diff --git a/src/main/java/zw/qantra/tm/domain/dtos/SBZProductDto.java b/src/main/java/zw/qantra/tm/domain/dtos/SBZProductDto.java
index c75b97b..2ec6e6d 100644
--- a/src/main/java/zw/qantra/tm/domain/dtos/SBZProductDto.java
+++ b/src/main/java/zw/qantra/tm/domain/dtos/SBZProductDto.java
@@ -1,5 +1,6 @@
package zw.qantra.tm.domain.dtos;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
@@ -12,6 +13,7 @@ import java.util.UUID;
@Builder
@NoArgsConstructor
@AllArgsConstructor
+@JsonIgnoreProperties(ignoreUnknown = true)
public class SBZProductDto {
private String name;
private String description;
@@ -19,4 +21,4 @@ public class SBZProductDto {
private BigDecimal defaultAmount;
private boolean requiresAmount;
private UUID uid;
-}
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/src/main/java/zw/qantra/tm/domain/dtos/SBZProviderDto.java b/src/main/java/zw/qantra/tm/domain/dtos/SBZProviderDto.java
new file mode 100644
index 0000000..46eb4a7
--- /dev/null
+++ b/src/main/java/zw/qantra/tm/domain/dtos/SBZProviderDto.java
@@ -0,0 +1,19 @@
+package zw.qantra.tm.domain.dtos;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import lombok.Data;
+
+import java.util.UUID;
+
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class SBZProviderDto {
+ private String clientId;
+ private boolean requiresAccount;
+ private boolean requiresAmount;
+ private boolean requiresAmountFromMerchant;
+ private boolean requiresPhone;
+ private boolean requiresReversal;
+ private String additionalDataString;
+ private String processorType;
+}
\ No newline at end of file
diff --git a/src/main/java/zw/qantra/tm/domain/models/Category.java b/src/main/java/zw/qantra/tm/domain/models/Category.java
new file mode 100644
index 0000000..8b918cb
--- /dev/null
+++ b/src/main/java/zw/qantra/tm/domain/models/Category.java
@@ -0,0 +1,17 @@
+package zw.qantra.tm.domain.models;
+
+import jakarta.persistence.Entity;
+import lombok.*;
+
+@Entity
+@Setter
+@Getter
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class Category extends BaseEntity {
+ private String name;
+ private String image;
+ private String description;
+ private String label;
+}
\ No newline at end of file
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 470d981..b30e326 100644
--- a/src/main/java/zw/qantra/tm/domain/models/PaymentProcessor.java
+++ b/src/main/java/zw/qantra/tm/domain/models/PaymentProcessor.java
@@ -1,13 +1,8 @@
package zw.qantra.tm.domain.models;
import jakarta.persistence.Entity;
-import jakarta.persistence.GeneratedValue;
-import jakarta.persistence.GenerationType;
-import jakarta.persistence.Column;
import lombok.*;
-import java.util.UUID;
-
@Entity
@Getter
@Setter
@@ -15,12 +10,9 @@ import java.util.UUID;
@AllArgsConstructor
@NoArgsConstructor
public class PaymentProcessor extends BaseEntity {
- @GeneratedValue(strategy = GenerationType.UUID)
- @Column(columnDefinition = "VARCHAR(36)")
- private UUID id;
-
private String name;
private String description;
private String label;
private String type;
+ private String image;
}
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 b88e648..4d8bbd4 100644
--- a/src/main/java/zw/qantra/tm/domain/models/Provider.java
+++ b/src/main/java/zw/qantra/tm/domain/models/Provider.java
@@ -15,4 +15,6 @@ public class Provider extends BaseEntity {
private String label;
private String description;
private String externalId;
+ private String image;
+ private String category;
}
diff --git a/src/main/java/zw/qantra/tm/domain/repositories/CategoryRepository.java b/src/main/java/zw/qantra/tm/domain/repositories/CategoryRepository.java
new file mode 100644
index 0000000..d1f3612
--- /dev/null
+++ b/src/main/java/zw/qantra/tm/domain/repositories/CategoryRepository.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.Category;
+
+import java.util.UUID;
+
+@Repository
+public interface CategoryRepository 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
index ff6b980..a014887 100644
--- a/src/main/java/zw/qantra/tm/domain/repositories/ProviderRepository.java
+++ b/src/main/java/zw/qantra/tm/domain/repositories/ProviderRepository.java
@@ -4,8 +4,10 @@ import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import zw.qantra.tm.domain.models.Provider;
+import java.util.List;
import java.util.UUID;
@Repository
public interface ProviderRepository extends JpaRepository {
+ List findByCategory(String category);
}
\ No newline at end of file
diff --git a/src/main/java/zw/qantra/tm/domain/services/CategoryService.java b/src/main/java/zw/qantra/tm/domain/services/CategoryService.java
new file mode 100644
index 0000000..2155e21
--- /dev/null
+++ b/src/main/java/zw/qantra/tm/domain/services/CategoryService.java
@@ -0,0 +1,46 @@
+package zw.qantra.tm.domain.services;
+
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import zw.qantra.tm.domain.models.Category;
+import zw.qantra.tm.domain.repositories.CategoryRepository;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.UUID;
+
+@Getter
+@Service
+@RequiredArgsConstructor
+public class CategoryService {
+ private final CategoryRepository categoryRepository;
+
+ public List getAllCategories() {
+ return categoryRepository.findAll();
+ }
+
+ public Optional getCategory(UUID id) {
+ return categoryRepository.findById(id);
+ }
+
+ public Category createCategory(Category category) {
+ return categoryRepository.save(category);
+ }
+
+ public Category updateCategory(UUID id, Category category) {
+ if (categoryRepository.existsById(id)) {
+ category.setId(id);
+ return categoryRepository.save(category);
+ }
+ return null;
+ }
+
+ public boolean deleteCategory(UUID id) {
+ if (categoryRepository.existsById(id)) {
+ categoryRepository.deleteById(id);
+ return true;
+ }
+ return false;
+ }
+}
\ 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 2ce20ab..6936227 100644
--- a/src/main/java/zw/qantra/tm/domain/services/ProviderService.java
+++ b/src/main/java/zw/qantra/tm/domain/services/ProviderService.java
@@ -1,5 +1,6 @@
package zw.qantra.tm.domain.services;
+import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
@@ -11,25 +12,22 @@ 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.*;
+import java.util.stream.Collectors;
@Service
@RequiredArgsConstructor
public class ProviderService {
+ @Getter
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();
@@ -57,8 +55,36 @@ public class ProviderService {
return products;
}
- public List getProviders() {
- return providerRepository.findAll();
+ public Object getProviders(String category) {
+ String token = restService.getMerchantToken();
+
+ HttpHeaders headers = new HttpHeaders();
+ headers.setContentType(MediaType.APPLICATION_JSON);
+ headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
+ headers.setBearerAuth(token);
+
+ List existingProviders;
+ if (category != null) {
+ existingProviders = getProviderRepository().findByCategory(category);
+ } else {
+ existingProviders = getProviderRepository().findAll();
+ }
+
+ List existingProviderIds = existingProviders.stream()
+ .map(Provider::getClientId)
+ .collect(Collectors.toList());
+ String existingProviderIdsString = String.join(",", existingProviderIds);
+
+ LinkedHashMap response = this.restService.getAs(
+ aggregatorUrl + "/merchant/aggregation/providers?supportedCurrencies=USD&clientIdIn=" + existingProviderIdsString,
+ headers,
+ LinkedHashMap.class
+ );
+
+ LinkedHashMap body = (LinkedHashMap) response.get("body");
+ List content = (List) body.get("content");
+
+ return content;
}
public Optional getProvider(UUID id) {
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 d4dde96..f55f31d 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
@@ -3,10 +3,14 @@ package zw.qantra.tm.domain.services.processors.confirmations;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
+import zw.qantra.tm.domain.dtos.BillPaymentDto;
+import zw.qantra.tm.domain.enums.RequestType;
import zw.qantra.tm.domain.models.Transaction;
import zw.qantra.tm.domain.services.processors.TransactionProcessorInterface;
import zw.qantra.tm.rest.RestService;
+import java.util.Map;
+
@Service("CONFIRM_ZESA")
@RequiredArgsConstructor
public class ZesaConfirmationProcessor implements TransactionProcessorInterface {
@@ -18,8 +22,27 @@ public class ZesaConfirmationProcessor implements TransactionProcessorInterface
@Override
public Object process(Transaction transaction) throws Exception {
System.out.println("processing transaction");
+ String token = restService.getMerchantToken();
+ BillPaymentDto request = BillPaymentDto.builder()
+ .type(RequestType.CONFIRM)
+ .billClientId(transaction.getBillClientId())
+ .debitRef(transaction.getDebitRef())
+ .debitCurrency(transaction.getDebitCurrency())
+ .amount(transaction.getAmount())
+ .aggregatorId(transaction.getAggregatorId())
+ .debitPhone(transaction.getDebitPhone())
+ .debitAccount(transaction.getDebitAccount())
+ .creditAccount(transaction.getCreditAccount())
+ .creditPhone(transaction.getCreditPhone())
+ .billName(transaction.getBillName())
+ .build();
+ String url = aggregatorUrl + "/v1/merchant/aggregation/transaction";
+
+ Map response = restService.postWithToken(token, url, request, Map.class);
+
+ System.out.println(response);
return transaction;
}