From 1188b5361d8755d3a663e2edeff45dc3c1f1ed40 Mon Sep 17 00:00:00 2001 From: Vusumuzi Khoza Date: Thu, 23 Oct 2025 09:10:54 +0200 Subject: [PATCH] fixing bug on transaction instances --- .../qantra/tm/domain/models/BaseEntity.java | 2 +- .../EconetConfirmationProcessor.java | 1 - .../handlers/CalculateChargesHandler.java | 148 ------------------ .../handlers/ConfirmationLabelHandler.java | 33 ---- .../handlers/ConfirmationLogicHandler.java | 18 --- .../handlers/FormattingHandler.java | 59 ------- .../domain/workflows/TransactionWorkflow.java | 19 ++- .../workflows/handlers/CleanupHandler.java | 3 +- .../workflows/handlers/ConfirmHandler.java | 6 +- .../handlers/RecipientsUpdateHandler.java | 60 +++++++ 10 files changed, 81 insertions(+), 268 deletions(-) delete mode 100644 src/main/java/zw/qantra/tm/domain/services/processors/handlers/CalculateChargesHandler.java delete mode 100644 src/main/java/zw/qantra/tm/domain/services/processors/handlers/ConfirmationLabelHandler.java delete mode 100644 src/main/java/zw/qantra/tm/domain/services/processors/handlers/ConfirmationLogicHandler.java delete mode 100644 src/main/java/zw/qantra/tm/domain/services/processors/handlers/FormattingHandler.java create mode 100644 src/main/java/zw/qantra/tm/domain/workflows/handlers/RecipientsUpdateHandler.java diff --git a/src/main/java/zw/qantra/tm/domain/models/BaseEntity.java b/src/main/java/zw/qantra/tm/domain/models/BaseEntity.java index e004deb..12f0251 100644 --- a/src/main/java/zw/qantra/tm/domain/models/BaseEntity.java +++ b/src/main/java/zw/qantra/tm/domain/models/BaseEntity.java @@ -40,5 +40,5 @@ public class BaseEntity { @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS") private LocalDateTime updatedAt; // Or Date, Timestamp - private Boolean deleted; + private Boolean deleted = false; } diff --git a/src/main/java/zw/qantra/tm/domain/services/processors/confirmations/EconetConfirmationProcessor.java b/src/main/java/zw/qantra/tm/domain/services/processors/confirmations/EconetConfirmationProcessor.java index e7fd825..8716ffc 100644 --- a/src/main/java/zw/qantra/tm/domain/services/processors/confirmations/EconetConfirmationProcessor.java +++ b/src/main/java/zw/qantra/tm/domain/services/processors/confirmations/EconetConfirmationProcessor.java @@ -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()) diff --git a/src/main/java/zw/qantra/tm/domain/services/processors/handlers/CalculateChargesHandler.java b/src/main/java/zw/qantra/tm/domain/services/processors/handlers/CalculateChargesHandler.java deleted file mode 100644 index d8d447c..0000000 --- a/src/main/java/zw/qantra/tm/domain/services/processors/handlers/CalculateChargesHandler.java +++ /dev/null @@ -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 { - 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 chargeList = chargeService.getChargeRepository().findAllByCurrency(transaction.getDebitCurrency()); - - List appropriateCharges = new ArrayList<>(); - for (Charge charge : chargeList) { - boolean isPresent = false; - for (ChargeCondition chargeCondition: charge.getIncludes()){ - List 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 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); - } - } -} diff --git a/src/main/java/zw/qantra/tm/domain/services/processors/handlers/ConfirmationLabelHandler.java b/src/main/java/zw/qantra/tm/domain/services/processors/handlers/ConfirmationLabelHandler.java deleted file mode 100644 index 6b945c0..0000000 --- a/src/main/java/zw/qantra/tm/domain/services/processors/handlers/ConfirmationLabelHandler.java +++ /dev/null @@ -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 { - 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; - } -} diff --git a/src/main/java/zw/qantra/tm/domain/services/processors/handlers/ConfirmationLogicHandler.java b/src/main/java/zw/qantra/tm/domain/services/processors/handlers/ConfirmationLogicHandler.java deleted file mode 100644 index c537d7a..0000000 --- a/src/main/java/zw/qantra/tm/domain/services/processors/handlers/ConfirmationLogicHandler.java +++ /dev/null @@ -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 { - @Override - public Transaction process(Transaction transaction) { - if(transaction.getType().equals(RequestType.CONFIRM)) { - transaction.setTrace(Utils.getUid()); - transaction.setReference(UUID.randomUUID().toString()); - } - return transaction; - } -} diff --git a/src/main/java/zw/qantra/tm/domain/services/processors/handlers/FormattingHandler.java b/src/main/java/zw/qantra/tm/domain/services/processors/handlers/FormattingHandler.java deleted file mode 100644 index 4261aa0..0000000 --- a/src/main/java/zw/qantra/tm/domain/services/processors/handlers/FormattingHandler.java +++ /dev/null @@ -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 { - @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; - } -} diff --git a/src/main/java/zw/qantra/tm/domain/workflows/TransactionWorkflow.java b/src/main/java/zw/qantra/tm/domain/workflows/TransactionWorkflow.java index 2c2e332..33d744f 100644 --- a/src/main/java/zw/qantra/tm/domain/workflows/TransactionWorkflow.java +++ b/src/main/java/zw/qantra/tm/domain/workflows/TransactionWorkflow.java @@ -1,5 +1,6 @@ package zw.qantra.tm.domain.workflows; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import io.nflow.engine.workflow.curated.State; import io.nflow.engine.workflow.definition.NextAction; @@ -17,6 +18,7 @@ import zw.qantra.tm.domain.workflows.handlers.HandlerInterface; import zw.qantra.tm.domain.workflows.handlers.WorkflowHandlerFactory; import zw.qantra.tm.utils.Utils; +import java.io.IOException; import java.util.LinkedHashMap; @Component @@ -35,6 +37,7 @@ public class TransactionWorkflow extends WorkflowDefinition { public static final State CALCULATE_CHARGES = new State("calculateCharges", WorkflowStateType.start); public static final State CLEANUP = new State("cleanup"); public static final State CONFIRM = new State("confirm"); + public static final State RECIPIENTS_UPDATE = new State("recipientsUpdate"); public static final State CONFIRM_SUCCESS = new State("confirmSuccess", WorkflowStateType.manual); public static final State SALES_INVOICE = new State("salesInvoice"); public static final State GATEWAY_PAYMENT = new State("gatewayPayment"); @@ -54,7 +57,8 @@ public class TransactionWorkflow extends WorkflowDefinition { permit(CALCULATE_CHARGES, CLEANUP); permit(CLEANUP, CONFIRM); - permit(CONFIRM, CONFIRM_SUCCESS, FAILED); + permit(CONFIRM, RECIPIENTS_UPDATE, FAILED); + permit(RECIPIENTS_UPDATE, CONFIRM_SUCCESS); permit(CONFIRM_SUCCESS, SALES_INVOICE); permit(SALES_INVOICE, GATEWAY_PAYMENT); permit(GATEWAY_PAYMENT, GATEWAY_PAYMENT_SUCCESS, FAILED); @@ -100,13 +104,24 @@ public class TransactionWorkflow extends WorkflowDefinition { transaction.setWorkflowId(execution.getWorkflowInstanceId()); transaction = process(transaction, "confirm"); execution.setVariable("body", Utils.toJson(transaction)); - return NextAction.moveToState(CONFIRM_SUCCESS, "Transaction confirmed successfully"); + return NextAction.moveToState(RECIPIENTS_UPDATE, "Recipients updated successfully"); } catch (Exception e) { e.printStackTrace(); return NextAction.moveToState(FAILED, e.getMessage()); } } + public NextAction recipientsUpdate(StateExecution execution) throws IOException { + LinkedHashMap map = execution.getVariable("body", LinkedHashMap.class); + byte[] json = objectMapper.writeValueAsBytes(map); + Transaction transaction = objectMapper.readValue(json, Transaction.class); + + transaction.setWorkflowId(execution.getWorkflowInstanceId()); + transaction = process(transaction, "recipientsUpdate"); + execution.setVariable("body", Utils.toJson(transaction)); + return NextAction.moveToState(CONFIRM_SUCCESS, "Transaction confirmed successfully"); + } + public void confirmSuccess(StateExecution execution) { log.info("Confirm complete"); } diff --git a/src/main/java/zw/qantra/tm/domain/workflows/handlers/CleanupHandler.java b/src/main/java/zw/qantra/tm/domain/workflows/handlers/CleanupHandler.java index ddf3949..b7929c7 100644 --- a/src/main/java/zw/qantra/tm/domain/workflows/handlers/CleanupHandler.java +++ b/src/main/java/zw/qantra/tm/domain/workflows/handlers/CleanupHandler.java @@ -13,7 +13,6 @@ import zw.qantra.tm.domain.enums.Status; 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.domain.services.processors.handlers.ConfirmationLabelHandler; import zw.qantra.tm.exceptions.ApiException; import zw.qantra.tm.utils.Utils; @@ -22,7 +21,7 @@ import java.util.UUID; @Service("cleanup") @RequiredArgsConstructor public class CleanupHandler implements HandlerInterface { - private final Logger logger = LoggerFactory.getLogger(ConfirmationLabelHandler.class); + private final Logger logger = LoggerFactory.getLogger(CleanupHandler.class); private final ProviderService providerService; diff --git a/src/main/java/zw/qantra/tm/domain/workflows/handlers/ConfirmHandler.java b/src/main/java/zw/qantra/tm/domain/workflows/handlers/ConfirmHandler.java index 89a39d2..9c9c6e7 100644 --- a/src/main/java/zw/qantra/tm/domain/workflows/handlers/ConfirmHandler.java +++ b/src/main/java/zw/qantra/tm/domain/workflows/handlers/ConfirmHandler.java @@ -20,7 +20,7 @@ public class ConfirmHandler implements HandlerInterface { @Override public Object process(Transaction transaction) throws Exception { - transactionService.save(transaction); + transaction = transactionService.save(transaction); TransactionEvent transactionEvent = TransactionEvent.builder() .transactionId(transaction.getId().toString()) @@ -46,14 +46,12 @@ public class ConfirmHandler implements HandlerInterface { transaction.setIntegrationStatus(Status.FAILED); transaction.setResponseCode("01"); transaction.setErrorMessage(e.getMessage()); - transactionService.save(transaction); - return transaction; } transactionEvent.setStatus(transaction.getStatus()); transactionEvent.setMessage(transaction.getErrorMessage()); transactionEventService.save(transactionEvent); - return transaction; + return transactionService.save(transaction); } } diff --git a/src/main/java/zw/qantra/tm/domain/workflows/handlers/RecipientsUpdateHandler.java b/src/main/java/zw/qantra/tm/domain/workflows/handlers/RecipientsUpdateHandler.java new file mode 100644 index 0000000..bc34949 --- /dev/null +++ b/src/main/java/zw/qantra/tm/domain/workflows/handlers/RecipientsUpdateHandler.java @@ -0,0 +1,60 @@ +package zw.qantra.tm.domain.workflows.handlers; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import zw.qantra.tm.domain.enums.Status; +import zw.qantra.tm.domain.models.Recipient; +import zw.qantra.tm.domain.models.Transaction; +import zw.qantra.tm.domain.services.RecipientService; +import zw.qantra.tm.domain.services.processors.handlers.LogicHandler; + +import java.util.List; + +@Service("recipientsUpdate") +@RequiredArgsConstructor +public class RecipientsUpdateHandler implements HandlerInterface { + + private final RecipientService recipientService; + + @Override + public Transaction process(Transaction transaction) { + System.out.println("processing recipients"); + + try { + if(transaction.getStatus() == Status.SUCCESS){ + List recipients = recipientService.getRecipient(transaction.getCreditAccount(), transaction.getUserId()); + + Recipient recipient = null; + if(recipients.size() > 0){ + recipient = recipients.get(0); + }else{ + recipient = new Recipient(); + recipient.setAccount(transaction.getCreditAccount()); + recipient.setUserId(transaction.getUserId()); + } + + recipient.setLatestProviderLabel(transaction.getProviderLabel()); + if(transaction.getCreditName() != null && !transaction.getCreditName().isEmpty()){ + recipient.setName(transaction.getCreditName()); + } + if(transaction.getCreditEmail() != null && !transaction.getCreditEmail().isEmpty()){ + recipient.setEmail(transaction.getCreditEmail()); + } + if(transaction.getCreditPhone() != null && !transaction.getCreditPhone().isEmpty()){ + recipient.setPhoneNumber(transaction.getCreditPhone()); + } + + String stringToInitial = transaction.getCreditName() != null && !transaction.getCreditName().isEmpty() + ? transaction.getCreditName() : transaction.getBillName(); + + recipient.setInitials(stringToInitial.substring(0, 2).toUpperCase()); + recipientService.save(recipient); + + } + }catch (Exception e) { + e.printStackTrace(); + } + + return transaction; + } +} \ No newline at end of file