From fdd3a224edd26f2f9fd14c4f36ebb9f11d83e35d Mon Sep 17 00:00:00 2001 From: Vus Khoza Date: Mon, 23 Jun 2025 10:45:05 +0200 Subject: [PATCH] progress on integration to sbz --- pom.xml | 5 ++ .../controllers/CategoryController.java | 52 +++++++++++++++++++ .../controllers/ProviderController.java | 4 +- .../qantra/tm/domain/dtos/BillPaymentDto.java | 31 +++++++++++ .../qantra/tm/domain/dtos/SBZProductDto.java | 4 +- .../qantra/tm/domain/dtos/SBZProviderDto.java | 19 +++++++ .../zw/qantra/tm/domain/models/Category.java | 17 ++++++ .../tm/domain/models/PaymentProcessor.java | 10 +--- .../zw/qantra/tm/domain/models/Provider.java | 2 + .../repositories/CategoryRepository.java | 11 ++++ .../repositories/ProviderRepository.java | 2 + .../tm/domain/services/CategoryService.java | 46 ++++++++++++++++ .../tm/domain/services/ProviderService.java | 40 +++++++++++--- .../ZesaConfirmationProcessor.java | 23 ++++++++ 14 files changed, 247 insertions(+), 19 deletions(-) create mode 100644 src/main/java/zw/qantra/tm/domain/controllers/CategoryController.java create mode 100644 src/main/java/zw/qantra/tm/domain/dtos/BillPaymentDto.java create mode 100644 src/main/java/zw/qantra/tm/domain/dtos/SBZProviderDto.java create mode 100644 src/main/java/zw/qantra/tm/domain/models/Category.java create mode 100644 src/main/java/zw/qantra/tm/domain/repositories/CategoryRepository.java create mode 100644 src/main/java/zw/qantra/tm/domain/services/CategoryService.java 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; }