progress on aligning transaction payloads

This commit is contained in:
2025-09-01 20:16:32 +02:00
parent 61f5503bdb
commit 0ee5c2297d
16 changed files with 143 additions and 18 deletions

View File

@@ -9,7 +9,7 @@ import zw.qantra.tm.domain.services.CategoryService;
import java.util.UUID; import java.util.UUID;
@RestController @RestController
@RequestMapping("/categories") @RequestMapping("/public/categories")
@RequiredArgsConstructor @RequiredArgsConstructor
public class CategoryController { public class CategoryController {

View File

@@ -10,7 +10,7 @@ import zw.qantra.tm.domain.models.Transaction;
import zw.qantra.tm.domain.services.TransactionService; import zw.qantra.tm.domain.services.TransactionService;
@RestController @RestController
@RequestMapping("/confirm/transaction") @RequestMapping("/public/confirm/transaction")
@RequiredArgsConstructor @RequiredArgsConstructor
public class ConfirmationController { public class ConfirmationController {
private final TransactionService transactionService; private final TransactionService transactionService;

View File

@@ -8,7 +8,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
@RestController @RestController
@RequestMapping("/integration/transaction") @RequestMapping("/public/integration/transaction")
@RequiredArgsConstructor @RequiredArgsConstructor
public class IntegrationController { public class IntegrationController {
@PostMapping() @PostMapping()

View File

@@ -11,7 +11,7 @@ import java.util.Optional;
import java.util.UUID; import java.util.UUID;
@RestController @RestController
@RequestMapping("/payment-processors") @RequestMapping("/public/payment-processors")
@RequiredArgsConstructor @RequiredArgsConstructor
public class PaymentProcessorController { public class PaymentProcessorController {

View File

@@ -9,7 +9,7 @@ import zw.qantra.tm.domain.services.ProviderService;
import java.util.UUID; import java.util.UUID;
@RestController @RestController
@RequestMapping("/providers") @RequestMapping("/public/providers")
@RequiredArgsConstructor @RequiredArgsConstructor
public class ProviderController { public class ProviderController {

View File

@@ -16,7 +16,7 @@ import java.util.List;
import java.util.UUID; import java.util.UUID;
@RestController @RestController
@RequestMapping("/recipients") @RequestMapping("/public/recipients")
@RequiredArgsConstructor @RequiredArgsConstructor
public class RecipientController { public class RecipientController {

View File

@@ -2,7 +2,6 @@ package zw.qantra.tm.domain.controllers;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import net.kaczmarzyk.spring.data.jpa.domain.Equal; import net.kaczmarzyk.spring.data.jpa.domain.Equal;
import net.kaczmarzyk.spring.data.jpa.web.annotation.And;
import net.kaczmarzyk.spring.data.jpa.web.annotation.Or; import net.kaczmarzyk.spring.data.jpa.web.annotation.Or;
import net.kaczmarzyk.spring.data.jpa.web.annotation.Spec; import net.kaczmarzyk.spring.data.jpa.web.annotation.Spec;
import net.kaczmarzyk.spring.data.jpa.web.annotation.Conjunction; import net.kaczmarzyk.spring.data.jpa.web.annotation.Conjunction;
@@ -21,7 +20,7 @@ import zw.qantra.tm.utils.LogUtils;
import java.util.UUID; import java.util.UUID;
@RestController @RestController
@RequestMapping("/transaction") @RequestMapping("/public/transaction")
@RequiredArgsConstructor @RequiredArgsConstructor
public class TransactonController { public class TransactonController {
Logger logger = LoggerFactory.getLogger(TransactonController.class); Logger logger = LoggerFactory.getLogger(TransactonController.class);

View File

@@ -3,5 +3,6 @@ package zw.qantra.tm.domain.enums;
public enum RequestType { public enum RequestType {
CONFIRM, CONFIRM,
REQUEST, REQUEST,
POLL,
INTEGRATION INTEGRATION
} }

View File

@@ -5,5 +5,8 @@ public enum Status {
PENDING, PENDING,
FAILED, FAILED,
CANCELLED, CANCELLED,
REVERSED REVERSED,
AUTHORIZED,
REJECTED,
PROCESSING
} }

View File

@@ -59,6 +59,19 @@ public class Transaction extends BaseEntity {
private String providerImage; private String providerImage;
private String paymentProcessorImage; private String paymentProcessorImage;
@Enumerated(EnumType.STRING)
private Status authorizationStatus;
@Enumerated(EnumType.STRING)
private Status confirmationStatus;
@Enumerated(EnumType.STRING)
private Status paymentStatus;
@Enumerated(EnumType.STRING)
private Status integrationStatus;
@Enumerated(EnumType.STRING)
private Status reversalStatus;
@Enumerated(EnumType.STRING)
private Status pollingStatus;
private String erpSalesRef; private String erpSalesRef;
private String erpJournalRef; private String erpJournalRef;
private String erpSalesPaymentRef; private String erpSalesPaymentRef;

View File

@@ -0,0 +1,29 @@
package zw.qantra.tm.domain.models;
import com.fasterxml.jackson.annotation.JsonInclude;
import jakarta.persistence.Entity;
import jakarta.persistence.Enumerated;
import jakarta.persistence.EnumType;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import zw.qantra.tm.domain.enums.Status;
@Entity
@Getter
@Setter
@Builder
@AllArgsConstructor
@NoArgsConstructor
@JsonInclude(JsonInclude.Include.NON_NULL)
public class TransactionEvent extends BaseEntity {
private String transactionId;
private String event;
@Enumerated(EnumType.STRING)
private Status status;
private String message;
private String data;
}

View File

@@ -0,0 +1,10 @@
package zw.qantra.tm.domain.repositories;
import java.util.List;
import java.util.UUID;
import org.springframework.data.jpa.repository.JpaRepository;
import zw.qantra.tm.domain.models.TransactionEvent;
public interface TransactionEventRepository extends JpaRepository<TransactionEvent, UUID> {
List<TransactionEvent> findByTransactionId(UUID transactionId);
}

View File

@@ -7,6 +7,7 @@ import org.springframework.stereotype.Repository;
import zw.qantra.tm.domain.enums.RequestType; import zw.qantra.tm.domain.enums.RequestType;
import zw.qantra.tm.domain.models.Transaction; import zw.qantra.tm.domain.models.Transaction;
import java.util.Optional;
import java.util.UUID; import java.util.UUID;
@Repository @Repository

View File

@@ -0,0 +1,18 @@
package zw.qantra.tm.domain.services;
import org.springframework.stereotype.Service;
import lombok.RequiredArgsConstructor;
import zw.qantra.tm.domain.models.TransactionEvent;
import zw.qantra.tm.domain.repositories.TransactionEventRepository;
@Service
@RequiredArgsConstructor
public class TransactionEventService {
private final TransactionEventRepository transactionEventRepository;
public void save(TransactionEvent transactionEvent) {
transactionEventRepository.save(transactionEvent);
}
}

View File

@@ -30,6 +30,8 @@ import zw.qantra.tm.domain.services.processors.handlers.RecipientsUpdateHandler;
import zw.qantra.tm.domain.services.processors.handlers.ValidationHandler; import zw.qantra.tm.domain.services.processors.handlers.ValidationHandler;
import zw.qantra.tm.domain.services.processors.handlers.erp.PurchaseInvoiceHandler; import zw.qantra.tm.domain.services.processors.handlers.erp.PurchaseInvoiceHandler;
import zw.qantra.tm.domain.services.processors.handlers.erp.SalesInvoiceHandler; import zw.qantra.tm.domain.services.processors.handlers.erp.SalesInvoiceHandler;
import zw.qantra.tm.domain.models.TransactionEvent;
import zw.qantra.tm.domain.services.TransactionEventService;
import zw.qantra.tm.exceptions.ApiException; import zw.qantra.tm.exceptions.ApiException;
import zw.qantra.tm.utils.Utils; import zw.qantra.tm.utils.Utils;
@@ -45,10 +47,18 @@ public class TransactionService {
private final ChargeService chargeService; private final ChargeService chargeService;
private final AdditionalDataService additionalDataService; private final AdditionalDataService additionalDataService;
private final RecipientService recipientService; private final RecipientService recipientService;
private final TransactionEventService transactionEventService;
public Transaction integration(Transaction transaction) { public Transaction integration(Transaction transaction) {
transaction.setType(RequestType.INTEGRATION); transaction.setType(RequestType.INTEGRATION);
TransactionEvent transactionEvent = TransactionEvent.builder()
.transactionId(transaction.getId().toString())
.event(transaction.getType().name())
.status(Status.PROCESSING)
.build();
transactionEventService.save(transactionEvent);
LogicPipeline<Transaction, Transaction> transactionLogic = LogicPipeline<Transaction, Transaction> transactionLogic =
new LogicPipeline<>(new ValidationHandler()); new LogicPipeline<>(new ValidationHandler());
transaction = transactionLogic.execute(transaction); transaction = transactionLogic.execute(transaction);
@@ -81,6 +91,10 @@ public class TransactionService {
LogicPipeline<Transaction, Transaction> postTransactionLogic = LogicPipeline<Transaction, Transaction> postTransactionLogic =
new LogicPipeline<>(new PurchaseInvoiceHandler()); new LogicPipeline<>(new PurchaseInvoiceHandler());
transaction = postTransactionLogic.execute(transaction); transaction = postTransactionLogic.execute(transaction);
transactionEvent.setStatus(transaction.getStatus());
transactionEvent.setMessage(transaction.getErrorMessage());
transactionEventService.save(transactionEvent);
return transaction; return transaction;
} }
@@ -96,6 +110,13 @@ public class TransactionService {
return transaction; return transaction;
} }
TransactionEvent transactionEvent = TransactionEvent.builder()
.transactionId(transaction.getId().toString())
.event(RequestType.POLL.name())
.status(Status.PROCESSING)
.build();
transactionEventService.save(transactionEvent);
// no persistence happens during polling unless the transaction status changes // no persistence happens during polling unless the transaction status changes
String label = getLabel(transaction); String label = getLabel(transaction);
logger.info("label: {}", label); logger.info("label: {}", label);
@@ -106,12 +127,14 @@ public class TransactionService {
// find out if the gateway tran was successful // find out if the gateway tran was successful
transactionProcessorInterface.poll(transaction); transactionProcessorInterface.poll(transaction);
// if tran was successful deep copy transaction & push integration to the aggregator transactionEvent.setStatus(transaction.getStatus());
transactionEvent.setMessage(transaction.getErrorMessage());
transactionEventService.save(transactionEvent);
// if tran was successful push integration to the aggregator
if(transaction.getStatus() == Status.SUCCESS){ if(transaction.getStatus() == Status.SUCCESS){
Transaction transactionCopy = Utils.deepCopy(transaction, Transaction.class); transaction.setType(RequestType.INTEGRATION);
transactionCopy.setType(RequestType.INTEGRATION); integration(transaction);
transactionCopy.setId(null);
integration(transactionCopy);
} }
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
@@ -119,11 +142,25 @@ public class TransactionService {
transaction.setResponseCode("01"); transaction.setResponseCode("01");
transaction.setErrorMessage(e.getMessage()); transaction.setErrorMessage(e.getMessage());
} }
return transaction; return transaction;
} }
public Transaction execute(Transaction transaction) { public Transaction execute(Transaction incomingTransaction) {
transaction.setReference(UUID.randomUUID().toString()); // if payment request (not confirm), then fetch already existing transaction record
Transaction transaction = incomingTransaction;
if(incomingTransaction.getId() != null){
transaction = getTransactionRepository().findById(incomingTransaction.getId()).orElseThrow(() ->
new ApiException("Transaction not found for ID: " + incomingTransaction.getId()));
transaction.setType(incomingTransaction.getType()); // type should be REQUEST here
}
TransactionEvent transactionEvent = TransactionEvent.builder()
.transactionId(transaction.getId().toString())
.event(transaction.getType().name())
.status(Status.PROCESSING)
.build();
transactionEventService.save(transactionEvent);
// run pre transaction logic // run pre transaction logic
LogicPipeline<Transaction, Transaction> preTransactionLogic = LogicPipeline<Transaction, Transaction> preTransactionLogic =
@@ -143,6 +180,12 @@ public class TransactionService {
// MAIN TRANSACTION LOGIC! // MAIN TRANSACTION LOGIC!
transactionProcessorInterface.process(transaction); transactionProcessorInterface.process(transaction);
if(transaction.getType() == RequestType.CONFIRM){
transaction.setConfirmationStatus(transaction.getStatus());
}else{
transaction.setPaymentStatus(transaction.getStatus());
}
}catch (Exception e){ }catch (Exception e){
e.printStackTrace(); e.printStackTrace();
transaction.setStatus(Status.FAILED); transaction.setStatus(Status.FAILED);
@@ -158,13 +201,16 @@ public class TransactionService {
.addHandler(new SalesInvoiceHandler()); .addHandler(new SalesInvoiceHandler());
transaction = postTransactionLogic.execute(transaction); transaction = postTransactionLogic.execute(transaction);
transactionEvent.setStatus(transaction.getStatus());
transactionEvent.setMessage(transaction.getErrorMessage());
transactionEventService.save(transactionEvent);
return transaction; return transaction;
} }
private String getLabel(Transaction transaction){ private String getLabel(Transaction transaction){
String label = ""; String label = "";
List<RequestType> list = Arrays.asList(RequestType.CONFIRM); if(transaction.getType() == RequestType.CONFIRM){
if(list.contains(transaction.getType())){
// get processor label from billClientId // get processor label from billClientId
Provider provider = providerService.getProviderRepository() Provider provider = providerService.getProviderRepository()
.findByClientId(transaction.getBillClientId()); .findByClientId(transaction.getBillClientId());
@@ -173,6 +219,8 @@ public class TransactionService {
} }
label = transaction.getType() + "_" + label = transaction.getType() + "_" +
provider.getLabel(); provider.getLabel();
transaction.setIntegrationProcessorLabel(provider.getIntegrationProcessorLabel());
}else{ }else{
label = transaction.getType() + "_" + label = transaction.getType() + "_" +
transaction.getPaymentProcessorLabel() + "_" + transaction.getPaymentProcessorLabel() + "_" +

View File

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