completed initial charge logic

This commit is contained in:
2025-06-18 09:28:01 +02:00
parent b214d42d2f
commit 9ae78635c7
34 changed files with 614 additions and 102 deletions

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -8,4 +8,8 @@ import zw.qantra.tm.domain.repositories.CurrencyRepository;
@RequiredArgsConstructor
public class CurrencyService {
private final CurrencyRepository currencyRepository;
public CurrencyRepository getCurrencyRepository() {
return currencyRepository;
}
}

View File

@@ -8,4 +8,8 @@ import zw.qantra.tm.domain.repositories.IntegrationProcessorRepository;
@RequiredArgsConstructor
public class IntegrationProcessorService {
private final IntegrationProcessorRepository integrationProcessorRepository;
public IntegrationProcessorRepository getIntegrationProcessorRepository() {
return integrationProcessorRepository;
}
}

View File

@@ -8,4 +8,8 @@ import zw.qantra.tm.domain.repositories.PaymentProcessorRepository;
@RequiredArgsConstructor
public class PaymentProcessorService {
private final PaymentProcessorRepository paymentProcessorRepository;
public PaymentProcessorRepository getPaymentProcessorRepository() {
return paymentProcessorRepository;
}
}

View File

@@ -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;
}
}

View File

@@ -8,4 +8,8 @@ import zw.qantra.tm.domain.repositories.SettingRepository;
@RequiredArgsConstructor
public class SettingService {
private final SettingRepository settingRepository;
public SettingRepository getSettingRepository() {
return settingRepository;
}
}

View File

@@ -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();

View File

@@ -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

View File

@@ -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);
}
}
}