fixing bug on transaction instances

This commit is contained in:
2025-10-23 09:10:54 +02:00
parent ed1b05f9b8
commit 1188b5361d
10 changed files with 81 additions and 268 deletions

View File

@@ -66,7 +66,6 @@ public class EconetConfirmationProcessor implements TransactionProcessorInterfac
transaction.setStatus(Status.SUCCESS);
transaction.setResponseCode((String) body.get("responseCode"));
transaction.setAdditionalData(body.get("additionalData"));
transactionService.save(transaction);
AdditionalData additionalData = AdditionalData.builder()
.transactionId(transaction.getId().toString())

View File

@@ -1,148 +0,0 @@
package zw.qantra.tm.domain.services.processors.handlers;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.ChargeService;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@Service
@RequiredArgsConstructor
public class CalculateChargesHandler implements LogicHandler<Transaction, Transaction> {
Logger logger = LoggerFactory.getLogger(CalculateChargesHandler.class);
private final ChargeService chargeService;
@Override
public Transaction process(Transaction transaction) {
logger.info("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 we did not find a match, means this charge does not apply to this transaction
// also implies there is no need to check exemptions
if(!isPresent) continue;
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);
}
// calculate total amount for ease of use later
transaction.setTotalAmount(
transaction.getAmount().add(
transaction.getCharge().add(
transaction.getTax().add(
transaction.getGatewayCharge()))));
return transaction;
}
void applyCharge(Transaction transaction, Charge charge, BigDecimal chargeAmount) {
if(chargeAmount == null) return;
switch (charge.getChargeLabel()) {
case FEE -> transaction.setCharge(chargeAmount);
case TAX -> transaction.setTax(chargeAmount);
case GATEWAY_FEE -> transaction.setGatewayCharge(chargeAmount);
}
}
}

View File

@@ -1,33 +0,0 @@
package zw.qantra.tm.domain.services.processors.handlers;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import lombok.RequiredArgsConstructor;
import zw.qantra.tm.domain.models.Provider;
import zw.qantra.tm.domain.models.Transaction;
import zw.qantra.tm.domain.services.ProviderService;
import zw.qantra.tm.exceptions.ApiException;
@Service
@RequiredArgsConstructor
public class ConfirmationLabelHandler implements LogicHandler<Transaction, Transaction> {
private final Logger logger = LoggerFactory.getLogger(ConfirmationLabelHandler.class);
private final ProviderService providerService;
@Override
public Transaction process(Transaction transaction) {
Provider provider = providerService.getProviderRepository()
.findByClientId(transaction.getBillClientId());
if(provider == null){
throw new ApiException("Provider not found for billClientId: " + transaction.getBillClientId());
}
String label = transaction.getType() + "_" + provider.getLabel();
transaction.setConfirmationProcessorLabel(label);
logger.info("label: {}", label);
return transaction;
}
}

View File

@@ -1,18 +0,0 @@
package zw.qantra.tm.domain.services.processors.handlers;
import java.util.UUID;
import zw.qantra.tm.domain.enums.RequestType;
import zw.qantra.tm.domain.models.Transaction;
import zw.qantra.tm.utils.Utils;
public class ConfirmationLogicHandler implements LogicHandler<Transaction, Transaction> {
@Override
public Transaction process(Transaction transaction) {
if(transaction.getType().equals(RequestType.CONFIRM)) {
transaction.setTrace(Utils.getUid());
transaction.setReference(UUID.randomUUID().toString());
}
return transaction;
}
}

View File

@@ -1,59 +0,0 @@
package zw.qantra.tm.domain.services.processors.handlers;
import com.google.i18n.phonenumbers.NumberParseException;
import com.google.i18n.phonenumbers.PhoneNumberUtil;
import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber;
import zw.qantra.tm.domain.models.Transaction;
public class FormattingHandler implements LogicHandler<Transaction, Transaction> {
@Override
public Transaction process(Transaction transaction) {
System.out.println("processing formatting");
// strip credit account and make sure there's nothing funny
String creditAccount = transaction.getCreditAccount();
if (creditAccount != null && !creditAccount.isEmpty()) {
// Remove all non-hexadecimal characters (keep only 0-9, a-f, A-F)
String strippedAccount = creditAccount.replaceAll("[^0-9a-fA-F]", "");
// Only set if the stripped result is not empty
if (!strippedAccount.isEmpty()) {
transaction.setCreditAccount(strippedAccount);
} else {
// If stripping results in empty string, set to null
transaction.setCreditAccount(null);
}
}
// sanitize phone number formatting
String debitPhoneNumber = transaction.getDebitPhone();
String creditPhoneNumber = transaction.getCreditPhone();
PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
if(debitPhoneNumber != null && !debitPhoneNumber.isEmpty()){
try {
// phone must begin with '+'
PhoneNumber zimPhoneNumber = phoneUtil.parse(debitPhoneNumber, "");
String formattedPhoneNumber = phoneUtil.format(zimPhoneNumber, PhoneNumberUtil.PhoneNumberFormat.E164);
transaction.setDebitPhone(formattedPhoneNumber);
} catch (NumberParseException e) {
System.err.println("NumberParseException was thrown: " + e.toString());
}
}
if(creditPhoneNumber != null && !creditPhoneNumber.isEmpty()){
try {
PhoneNumber zimPhoneNumber = phoneUtil.parse(creditPhoneNumber, "");
String formattedPhoneNumber = phoneUtil.format(zimPhoneNumber, PhoneNumberUtil.PhoneNumberFormat.E164);
transaction.setCreditPhone(formattedPhoneNumber);
} catch (NumberParseException e) {
System.err.println("NumberParseException was thrown: " + e.toString());
}
}
return transaction;
}
}