completed initial charge logic
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<ChargeCondition> 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<Charge> 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");
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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<List<Transaction>> getAll() {
|
||||
return ResponseEntity.ok(transactionService.getTransactionRepository().findAll());
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
public ResponseEntity<Transaction> getById(@PathVariable UUID id) {
|
||||
return transactionService.getTransactionRepository().findById(id)
|
||||
.map(ResponseEntity::ok)
|
||||
.orElse(ResponseEntity.notFound().build());
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public ResponseEntity<Transaction> create(@RequestBody Transaction transaction) {
|
||||
return ResponseEntity.ok(transactionService.execute(transaction));
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
public ResponseEntity<Transaction> 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<Void> delete(@PathVariable UUID id) {
|
||||
if (!transactionService.getTransactionRepository().existsById(id)) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
transactionService.getTransactionRepository().deleteById(id);
|
||||
return ResponseEntity.noContent().build();
|
||||
}
|
||||
}
|
||||
|
||||
22
src/main/java/zw/qantra/tm/domain/dtos/SBZProductDto.java
Normal file
22
src/main/java/zw/qantra/tm/domain/dtos/SBZProductDto.java
Normal file
@@ -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;
|
||||
}
|
||||
28
src/main/java/zw/qantra/tm/domain/models/BaseEntity.java
Normal file
28
src/main/java/zw/qantra/tm/domain/models/BaseEntity.java
Normal file
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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<ChargeCondition, UUID> {
|
||||
}
|
||||
@@ -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<Charge, UUID> {
|
||||
List<Charge> findAllByCurrency(CurrencyType currency);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -8,4 +8,8 @@ import zw.qantra.tm.domain.repositories.CurrencyRepository;
|
||||
@RequiredArgsConstructor
|
||||
public class CurrencyService {
|
||||
private final CurrencyRepository currencyRepository;
|
||||
|
||||
public CurrencyRepository getCurrencyRepository() {
|
||||
return currencyRepository;
|
||||
}
|
||||
}
|
||||
@@ -8,4 +8,8 @@ import zw.qantra.tm.domain.repositories.IntegrationProcessorRepository;
|
||||
@RequiredArgsConstructor
|
||||
public class IntegrationProcessorService {
|
||||
private final IntegrationProcessorRepository integrationProcessorRepository;
|
||||
|
||||
public IntegrationProcessorRepository getIntegrationProcessorRepository() {
|
||||
return integrationProcessorRepository;
|
||||
}
|
||||
}
|
||||
@@ -8,4 +8,8 @@ import zw.qantra.tm.domain.repositories.PaymentProcessorRepository;
|
||||
@RequiredArgsConstructor
|
||||
public class PaymentProcessorService {
|
||||
private final PaymentProcessorRepository paymentProcessorRepository;
|
||||
|
||||
public PaymentProcessorRepository getPaymentProcessorRepository() {
|
||||
return paymentProcessorRepository;
|
||||
}
|
||||
}
|
||||
@@ -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<SBZProductDto> 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<LinkedHashMap> content = (List<LinkedHashMap>) body.get("content");
|
||||
|
||||
List<SBZProductDto> 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<Provider> getProviders() {
|
||||
return providerRepository.findAll();
|
||||
}
|
||||
|
||||
public Optional<Provider> 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;
|
||||
}
|
||||
}
|
||||
@@ -8,4 +8,8 @@ import zw.qantra.tm.domain.repositories.SettingRepository;
|
||||
@RequiredArgsConstructor
|
||||
public class SettingService {
|
||||
private final SettingRepository settingRepository;
|
||||
|
||||
public SettingRepository getSettingRepository() {
|
||||
return settingRepository;
|
||||
}
|
||||
}
|
||||
@@ -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<Transaction, Transaction> transactionLogic =
|
||||
new LogicPipeline<>(new CalculateChargesHandler())
|
||||
new LogicPipeline<>(calculateChargesHandler)
|
||||
.addHandler(new ValidationHandler());
|
||||
|
||||
transaction = transactionLogic.execute(transaction);
|
||||
|
||||
String label = transaction.getType() + "_" + transaction.getIntegrationProcessorLabel();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<Transaction, Transaction> {
|
||||
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<Charge> chargeList = chargeService.getChargeRepository().findAllByCurrency(transaction.getDebitCurrency());
|
||||
|
||||
List<Charge> appropriateCharges = new ArrayList<>();
|
||||
for (Charge charge : chargeList) {
|
||||
boolean isPresent = false;
|
||||
for (ChargeCondition chargeCondition: charge.getIncludes()){
|
||||
List<String> 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<String> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<String, String> payload = Map.of(
|
||||
"clientId", clientId,
|
||||
"clientSecret", clientSecret
|
||||
);
|
||||
|
||||
HttpEntity<Map<String, String>> entity = new HttpEntity<>(payload, headers);
|
||||
Map response = this.restTemplate.postForObject(aggregatorUrl + "/merchant/client/token",entity, Map.class);
|
||||
|
||||
Map<String, String> body = (Map<String, String>) response.get("body");
|
||||
|
||||
return (String) body.get("access_token");
|
||||
}
|
||||
|
||||
public Map getToken(MultiValueMap payload){
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
|
||||
@@ -120,6 +145,12 @@ public class RestService {
|
||||
return this.restTemplate.postForObject(path, entity, clazz);
|
||||
}
|
||||
|
||||
public <T> T getAs(String path, HttpHeaders headers, Class<T> clazz){
|
||||
// build the request
|
||||
HttpEntity entity = new HttpEntity<>(headers);
|
||||
return this.restTemplate.exchange(path, HttpMethod.GET, entity, clazz).getBody();
|
||||
}
|
||||
|
||||
public <T> T post(String path, Object payload, Class<T> clazz){
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package zw.qantra.tm.rest;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
@@ -19,11 +20,10 @@ 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;
|
||||
private final Gson gson = new GsonBuilder().setPrettyPrinting().create();
|
||||
|
||||
@Override
|
||||
public ClientHttpResponse intercept(
|
||||
@@ -35,18 +35,20 @@ public class RestTemplateInterceptor
|
||||
|
||||
// we don't log file data
|
||||
if(request.getHeaders().getContentLength() < 10000){
|
||||
logger.info("Request: "
|
||||
+ request.getMethod() + ": "
|
||||
+ request.getURI(), "http", gson.fromJson(requestBody, HashMap.class));
|
||||
logger.info("Request: {}: {}\n{}\n{}",
|
||||
request.getMethod(),
|
||||
request.getURI(),
|
||||
request.getHeaders().toSingleValueMap(),
|
||||
gson.toJson(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));
|
||||
logger.info("Response body:\n{}", gson.toJson(gson.fromJson(body, HashMap.class)));
|
||||
}catch (Exception exception){
|
||||
logger.info("Problem serializing resp: {}", "http", exception.getMessage());
|
||||
logger.info("Problem serializing resp: {}", exception.getMessage());
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
@@ -6,11 +6,14 @@ logging.level.org.springframework.boot.autoconfigure=INFO
|
||||
server.port=6950
|
||||
|
||||
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
|
||||
spring.datasource.driver-class-name=org.postgresql.Driver
|
||||
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
|
||||
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
|
||||
|
||||
spring.datasource.url=jdbc:mysql://localhost:3306/qpay?useLegacyDatetimeCode=false
|
||||
spring.datasource.username=root
|
||||
spring.datasource.url=jdbc:postgresql://localhost:5432/qpay?useLegacyDatetimeCode=false
|
||||
spring.datasource.username=postgres
|
||||
spring.datasource.password=password
|
||||
|
||||
sbz.aggregator.url=http://192.168.16.29:24050/v1
|
||||
sbz.aggregator.client-id=small_bakery
|
||||
sbz.aggregator.client-secret=tmtCUeJA1dQezn405HSrYSdhV81XiBEi
|
||||
@@ -9,11 +9,13 @@ logging.level.org.springframework.boot.autoconfigure=INFO
|
||||
server.port=6950
|
||||
|
||||
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
|
||||
spring.datasource.driver-class-name=org.postgresql.Driver
|
||||
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
|
||||
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
|
||||
|
||||
spring.datasource.url=jdbc:mysql://localhost:3306/qpay?useLegacyDatetimeCode=false
|
||||
spring.datasource.username=root
|
||||
|
||||
spring.datasource.url=jdbc:postgresql://localhost:5432/qpay?useLegacyDatetimeCode=false
|
||||
spring.datasource.username=postgres
|
||||
spring.datasource.password=password
|
||||
|
||||
sbz.aggregator.url=http://192.168.16.29:24050/v1
|
||||
@@ -1,9 +1,9 @@
|
||||
changeLogFile=src/main/resources/liquibase-diff-changeLog.xml
|
||||
url=jdbc:mysql://localhost:3306/qpay?serverTimezone=Africa/Harare&useLegacyDatetimeCode=false
|
||||
url=jdbc:postgresql://localhost:5432/qpay?serverTimezone=Africa/Harare&useLegacyDatetimeCode=false
|
||||
username=root
|
||||
password=password
|
||||
|
||||
driver=com.mysql.cj.jdbc.Driver
|
||||
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
|
||||
driver=org.postgresql.Driver
|
||||
referenceUrl=hibernate:spring:zw.qantra.tm.domain.models?dialect=org.hibernate.dialect.PostgreSQLDialect&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
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
changeLogFile=src/main/resources/liquibase-diff-changeLog.xml
|
||||
url=jdbc:mysql://localhost:3306/qpay?serverTimezone=Africa/Harare&useLegacyDatetimeCode=false
|
||||
url=jdbc:postgresql://localhost:5432/qpay?serverTimezone=Africa/Harare&useLegacyDatetimeCode=false
|
||||
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
|
||||
driver=org.postgresql.Driver
|
||||
referenceUrl=hibernate:spring:zw.qantra.tm.domain.models?dialect=org.hibernate.dialect.PostgreSQLDialect&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
|
||||
|
||||
Reference in New Issue
Block a user