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;
@RestController
@RequestMapping("/categories")
@RequestMapping("/public/categories")
@RequiredArgsConstructor
public class CategoryController {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -59,6 +59,19 @@ public class Transaction extends BaseEntity {
private String providerImage;
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 erpJournalRef;
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.models.Transaction;
import java.util.Optional;
import java.util.UUID;
@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.erp.PurchaseInvoiceHandler;
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.utils.Utils;
@@ -45,10 +47,18 @@ public class TransactionService {
private final ChargeService chargeService;
private final AdditionalDataService additionalDataService;
private final RecipientService recipientService;
private final TransactionEventService transactionEventService;
public Transaction integration(Transaction transaction) {
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 =
new LogicPipeline<>(new ValidationHandler());
transaction = transactionLogic.execute(transaction);
@@ -81,6 +91,10 @@ public class TransactionService {
LogicPipeline<Transaction, Transaction> postTransactionLogic =
new LogicPipeline<>(new PurchaseInvoiceHandler());
transaction = postTransactionLogic.execute(transaction);
transactionEvent.setStatus(transaction.getStatus());
transactionEvent.setMessage(transaction.getErrorMessage());
transactionEventService.save(transactionEvent);
return transaction;
}
@@ -96,6 +110,13 @@ public class TransactionService {
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
String label = getLabel(transaction);
logger.info("label: {}", label);
@@ -106,12 +127,14 @@ public class TransactionService {
// find out if the gateway tran was successful
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){
Transaction transactionCopy = Utils.deepCopy(transaction, Transaction.class);
transactionCopy.setType(RequestType.INTEGRATION);
transactionCopy.setId(null);
integration(transactionCopy);
transaction.setType(RequestType.INTEGRATION);
integration(transaction);
}
} catch (Exception e) {
e.printStackTrace();
@@ -119,11 +142,25 @@ public class TransactionService {
transaction.setResponseCode("01");
transaction.setErrorMessage(e.getMessage());
}
return transaction;
}
public Transaction execute(Transaction transaction) {
transaction.setReference(UUID.randomUUID().toString());
public Transaction execute(Transaction incomingTransaction) {
// 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
LogicPipeline<Transaction, Transaction> preTransactionLogic =
@@ -143,6 +180,12 @@ public class TransactionService {
// MAIN TRANSACTION LOGIC!
transactionProcessorInterface.process(transaction);
if(transaction.getType() == RequestType.CONFIRM){
transaction.setConfirmationStatus(transaction.getStatus());
}else{
transaction.setPaymentStatus(transaction.getStatus());
}
}catch (Exception e){
e.printStackTrace();
transaction.setStatus(Status.FAILED);
@@ -158,13 +201,16 @@ public class TransactionService {
.addHandler(new SalesInvoiceHandler());
transaction = postTransactionLogic.execute(transaction);
transactionEvent.setStatus(transaction.getStatus());
transactionEvent.setMessage(transaction.getErrorMessage());
transactionEventService.save(transactionEvent);
return transaction;
}
private String getLabel(Transaction transaction){
String label = "";
List<RequestType> list = Arrays.asList(RequestType.CONFIRM);
if(list.contains(transaction.getType())){
if(transaction.getType() == RequestType.CONFIRM){
// get processor label from billClientId
Provider provider = providerService.getProviderRepository()
.findByClientId(transaction.getBillClientId());
@@ -173,6 +219,8 @@ public class TransactionService {
}
label = transaction.getType() + "_" +
provider.getLabel();
transaction.setIntegrationProcessorLabel(provider.getIntegrationProcessorLabel());
}else{
label = transaction.getType() + "_" +
transaction.getPaymentProcessorLabel() + "_" +

View File

@@ -1,5 +1,7 @@
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;
@@ -9,6 +11,7 @@ public class ConfirmationLogicHandler implements LogicHandler<Transaction, Trans
public Transaction process(Transaction transaction) {
if(transaction.getType().equals(RequestType.CONFIRM)) {
transaction.setTrace(Utils.getUid());
transaction.setReference(UUID.randomUUID().toString());
}
return transaction;
}