From 683da1074837a5fbdf8206dde9091788f69cf64f Mon Sep 17 00:00:00 2001 From: Vusumuzi Khoza Date: Sun, 2 Nov 2025 15:32:42 +0200 Subject: [PATCH] updating sequence of flows --- .../controllers/TransactonController.java | 60 ++++---- .../domain/workflows/TransactionWorkflow.java | 142 +++++++++--------- .../handlers/IntegrationHandler.java | 3 + .../workflows/handlers/PollStatusHandler.java | 4 +- 4 files changed, 108 insertions(+), 101 deletions(-) diff --git a/src/main/java/zw/qantra/tm/domain/controllers/TransactonController.java b/src/main/java/zw/qantra/tm/domain/controllers/TransactonController.java index 7c03429..e7c7805 100644 --- a/src/main/java/zw/qantra/tm/domain/controllers/TransactonController.java +++ b/src/main/java/zw/qantra/tm/domain/controllers/TransactonController.java @@ -89,12 +89,44 @@ public class TransactonController { @GetMapping("/integration/{id}") public ResponseEntity integration(@PathVariable UUID id) throws ExecutionException, InterruptedException { + LogUtils.logPrettyJson(logger, "incoming integration", id); Transaction transaction = transactionService.findById(id); // Create an update to move the workflow to the next state WorkflowInstance update = new WorkflowInstance.Builder() .setId(transaction.getWorkflowId()) .setState("integration") // The state you want to transition to .setNextActivation(DateTime.now()) // Schedule immediate execution + .setStateVariables(new HashMap<>(){{ put("body", Utils.toJson(transaction)); }}) + .setStateText("Resumed from manual state") + .build(); + + WorkflowInstanceAction action = new WorkflowInstanceAction.Builder(update) + .setType(WorkflowInstanceAction.WorkflowActionType.externalChange) + .setExecutionEnd(DateTime.now()) + .build(); + + // Apply the update + workflowInstanceService.updateWorkflowInstance(update, action); + + CompletableFuture future = workflowUtils + .waitForState(transaction.getWorkflowId(), new String[]{"purchaseInvoicePaymentSuccess", "failed"}, + 15000, 50); + Object response = future.get(); + LogUtils.logPrettyJson(logger, "outgoing transaction", response); + + return ResponseEntity.ok(response); + } + + @GetMapping("/poll/{id}") + public ResponseEntity poll(@PathVariable UUID id) throws ExecutionException, InterruptedException { + LogUtils.logPrettyJson(logger, "incoming poll", id); + Transaction transaction = transactionService.findById(id); + // Create an update to move the workflow to the next state + WorkflowInstance update = new WorkflowInstance.Builder() + .setId(transaction.getWorkflowId()) + .setState("pollStatus") // The state you want to transition to + .setNextActivation(DateTime.now()) // Schedule immediate execution + .setStateVariables(new HashMap<>(){{ put("body", Utils.toJson(transaction)); }}) .setStateText("Resumed from manual state") .build(); @@ -115,34 +147,6 @@ public class TransactonController { return ResponseEntity.ok(response); } - @GetMapping("/poll/{id}") - public ResponseEntity poll(@PathVariable UUID id) throws ExecutionException, InterruptedException { - Transaction transaction = transactionService.findById(id); - // Create an update to move the workflow to the next state - WorkflowInstance update = new WorkflowInstance.Builder() - .setId(transaction.getWorkflowId()) - .setState("pollStatus") // The state you want to transition to - .setNextActivation(DateTime.now()) // Schedule immediate execution - .setStateText("Resumed from manual state") - .build(); - - WorkflowInstanceAction action = new WorkflowInstanceAction.Builder(update) - .setType(WorkflowInstanceAction.WorkflowActionType.externalChange) - .setExecutionEnd(DateTime.now()) - .build(); - - // Apply the update - workflowInstanceService.updateWorkflowInstance(update, action); - - CompletableFuture future = workflowUtils - .waitForState(transaction.getWorkflowId(), new String[]{"purchaseInvoice", "failed"}, - 15000, 50); - Object response = future.get(); - LogUtils.logPrettyJson(logger, "outgoing transaction", response); - - return ResponseEntity.ok(response); - } - @GetMapping("/request/{id}/{authType}") public ResponseEntity request(@PathVariable UUID id, @PathVariable AuthType authType) throws ExecutionException, InterruptedException { 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 0193713..c0a2db4 100644 --- a/src/main/java/zw/qantra/tm/domain/workflows/TransactionWorkflow.java +++ b/src/main/java/zw/qantra/tm/domain/workflows/TransactionWorkflow.java @@ -47,9 +47,9 @@ public class TransactionWorkflow extends WorkflowDefinition { public static final State JOURNAL_ENTRY = new State("journalEntry"); public static final State SALES_PAYMENT = new State("salesPayment"); public static final State PURCHASE_INVOICE = new State("purchaseInvoice"); - public static final State PURCHASE_INVOICE_SUCCESS = new State("purchaseInvoiceSuccess", WorkflowStateType.manual); public static final State INTEGRATION = new State("integration"); public static final State PURCHASE_INVOICE_PAYMENT = new State("purchaseInvoicePayment"); + public static final State PURCHASE_INVOICE_PAYMENT_SUCCESS = new State("purchaseInvoicePaymentSuccess", WorkflowStateType.manual); public static final State TRAN_FAILED = new State("failed", WorkflowStateType.manual); public static final State DONE = new State("done", WorkflowStateType.end); @@ -63,15 +63,15 @@ public class TransactionWorkflow extends WorkflowDefinition { permit(RECIPIENTS_UPDATE, CONFIRM_SUCCESS); permit(CONFIRM_SUCCESS, GATEWAY_PAYMENT); permit(GATEWAY_PAYMENT, GATEWAY_PAYMENT_SUCCESS, TRAN_FAILED); - permit(GATEWAY_PAYMENT_SUCCESS, POLL_STATUS); + permit(GATEWAY_PAYMENT_SUCCESS, INTEGRATION); + permit(INTEGRATION, PURCHASE_INVOICE, TRAN_FAILED); + permit(PURCHASE_INVOICE, PURCHASE_INVOICE_PAYMENT); + permit(PURCHASE_INVOICE_PAYMENT, PURCHASE_INVOICE_PAYMENT_SUCCESS); + permit(PURCHASE_INVOICE_PAYMENT_SUCCESS, POLL_STATUS); permit(POLL_STATUS, SALES_INVOICE, TRAN_FAILED); permit(SALES_INVOICE, JOURNAL_ENTRY); permit(JOURNAL_ENTRY, SALES_PAYMENT); - permit(SALES_PAYMENT, PURCHASE_INVOICE); - permit(PURCHASE_INVOICE, PURCHASE_INVOICE_SUCCESS); - permit(PURCHASE_INVOICE_SUCCESS, INTEGRATION); - permit(INTEGRATION, PURCHASE_INVOICE_PAYMENT, TRAN_FAILED); - permit(PURCHASE_INVOICE_PAYMENT, DONE); + permit(SALES_PAYMENT, DONE); } public NextAction calculateCharges(StateExecution execution) { @@ -169,6 +169,68 @@ public class TransactionWorkflow extends WorkflowDefinition { log.info("Gateway payment initiation complete"); } + public NextAction integration(StateExecution execution) throws IOException { + LinkedHashMap map = execution.getVariable("body", LinkedHashMap.class); + byte[] json = objectMapper.writeValueAsBytes(map); + Transaction transaction = objectMapper.readValue(json, Transaction.class); + + try { + HandlerInterface handlerInterface = + (HandlerInterface) workflowHandlerFactory.getWorkflowHandler("integration"); + transaction = (Transaction) handlerInterface.process(transaction); + + execution.setVariable("body", Utils.toJson(transaction)); + return NextAction.moveToState(PURCHASE_INVOICE, "Integration request successful"); + } catch (Exception e) { + log.info(e.getMessage()); + execution.setVariable("body", Utils.toJson(transaction)); + return NextAction.moveToState(TRAN_FAILED, e.getMessage()); + } + } + + public NextAction purchaseInvoice(StateExecution execution) throws Exception { + try { + LinkedHashMap map = execution.getVariable("body", LinkedHashMap.class); + byte[] json = objectMapper.writeValueAsBytes(map); + Transaction transaction = objectMapper.readValue(json, Transaction.class); + + HandlerInterface handlerInterface = + (HandlerInterface) workflowHandlerFactory.getWorkflowHandler("purchaseInvoice"); + transaction = (Transaction) handlerInterface.process(transaction); + + execution.setVariable("body", Utils.toJson(transaction)); + return NextAction.moveToState(PURCHASE_INVOICE_PAYMENT, + "Purchase invoice successful - id: " + transaction.getErpPurchaseRef()); + + }catch (Exception e) { + e.printStackTrace(); + return NextAction.retryAfter(new DateTime().plusMinutes(1), e.getMessage()); + } + } + + public NextAction purchaseInvoicePayment(StateExecution execution) { + try { + LinkedHashMap map = execution.getVariable("body", LinkedHashMap.class); + byte[] json = objectMapper.writeValueAsBytes(map); + Transaction transaction = objectMapper.readValue(json, Transaction.class); + + HandlerInterface handlerInterface = + (HandlerInterface) workflowHandlerFactory.getWorkflowHandler("purchaseInvoicePayment"); + transaction = (Transaction) handlerInterface.process(transaction); + + execution.setVariable("body", Utils.toJson(transaction)); + return NextAction.moveToState(PURCHASE_INVOICE_PAYMENT_SUCCESS, + "Purchase invoice payment generated - id: " + transaction.getErpPurchasePaymentRef()); + }catch (Exception e) { + e.printStackTrace(); + return NextAction.retryAfter(new DateTime().plusMinutes(1), e.getMessage()); + } + } + + public void purchaseInvoicePaymentSuccess(StateExecution execution) { + log.info("Purchase invoice payment complete"); + } + public NextAction pollStatus(StateExecution execution) throws IOException { // cannot cast direct to Transaction object because of some jackson mapper hullabaloo :( LinkedHashMap map = execution.getVariable("body", LinkedHashMap.class); @@ -185,7 +247,7 @@ public class TransactionWorkflow extends WorkflowDefinition { } catch (Exception e) { log.info(e.getMessage()); execution.setVariable("body", Utils.toJson(transaction)); - return NextAction.moveToState(TRAN_FAILED, e.getMessage()); + return NextAction.retryAfter(DateTime.now().plusSeconds(30), e.getMessage()); } } @@ -239,73 +301,11 @@ public class TransactionWorkflow extends WorkflowDefinition { transaction = (Transaction) handlerInterface.process(transaction); execution.setVariable("body", Utils.toJson(transaction)); - return NextAction.moveToState(PURCHASE_INVOICE, + return NextAction.moveToState(DONE, "Sales payment successful - id: " + transaction.getErpSalesPaymentRef()); }catch (Exception e) { e.printStackTrace(); return NextAction.retryAfter(new DateTime().plusMinutes(1), e.getMessage()); } } - - public NextAction purchaseInvoice(StateExecution execution) throws Exception { - try { - LinkedHashMap map = execution.getVariable("body", LinkedHashMap.class); - byte[] json = objectMapper.writeValueAsBytes(map); - Transaction transaction = objectMapper.readValue(json, Transaction.class); - - HandlerInterface handlerInterface = - (HandlerInterface) workflowHandlerFactory.getWorkflowHandler("purchaseInvoice"); - transaction = (Transaction) handlerInterface.process(transaction); - - execution.setVariable("body", Utils.toJson(transaction)); - return NextAction.moveToState(PURCHASE_INVOICE_SUCCESS, - "Purchase invoice successful - id: " + transaction.getErpPurchaseRef()); - - }catch (Exception e) { - e.printStackTrace(); - return NextAction.retryAfter(new DateTime().plusMinutes(1), e.getMessage()); - } - } - - public void purchaseInvoiceSuccess(StateExecution execution) { - log.info("Purchase invoice payment complete"); - } - - public NextAction integration(StateExecution execution) throws IOException { - LinkedHashMap map = execution.getVariable("body", LinkedHashMap.class); - byte[] json = objectMapper.writeValueAsBytes(map); - Transaction transaction = objectMapper.readValue(json, Transaction.class); - - try { - HandlerInterface handlerInterface = - (HandlerInterface) workflowHandlerFactory.getWorkflowHandler("integration"); - transaction = (Transaction) handlerInterface.process(transaction); - - execution.setVariable("body", Utils.toJson(transaction)); - return NextAction.moveToState(PURCHASE_INVOICE_PAYMENT, "Integration request successful"); - } catch (Exception e) { - log.info(e.getMessage()); - execution.setVariable("body", Utils.toJson(transaction)); - return NextAction.moveToState(TRAN_FAILED, e.getMessage()); - } - } - - public NextAction purchaseInvoicePayment(StateExecution execution) { - try { - LinkedHashMap map = execution.getVariable("body", LinkedHashMap.class); - byte[] json = objectMapper.writeValueAsBytes(map); - Transaction transaction = objectMapper.readValue(json, Transaction.class); - - HandlerInterface handlerInterface = - (HandlerInterface) workflowHandlerFactory.getWorkflowHandler("purchaseInvoicePayment"); - transaction = (Transaction) handlerInterface.process(transaction); - - execution.setVariable("body", Utils.toJson(transaction)); - return NextAction.moveToState(DONE, - "Purchase invoice payment generated - id: " + transaction.getErpPurchasePaymentRef()); - }catch (Exception e) { - e.printStackTrace(); - return NextAction.retryAfter(new DateTime().plusMinutes(1), e.getMessage()); - } - } } diff --git a/src/main/java/zw/qantra/tm/domain/workflows/handlers/IntegrationHandler.java b/src/main/java/zw/qantra/tm/domain/workflows/handlers/IntegrationHandler.java index 558f881..64646b5 100644 --- a/src/main/java/zw/qantra/tm/domain/workflows/handlers/IntegrationHandler.java +++ b/src/main/java/zw/qantra/tm/domain/workflows/handlers/IntegrationHandler.java @@ -75,6 +75,9 @@ public class IntegrationHandler implements HandlerInterface { throw new ApiException("Transaction failed: " + transaction.getErrorMessage() ); }else { transaction.setErrorMessage(null); + // if we are here it means payment was a success + // make sure it is marked appropriately + transaction.setPaymentStatus(transaction.getStatus()); } Transaction t1 = Utils.deepCopy(transaction, Transaction.class); diff --git a/src/main/java/zw/qantra/tm/domain/workflows/handlers/PollStatusHandler.java b/src/main/java/zw/qantra/tm/domain/workflows/handlers/PollStatusHandler.java index 4b0884e..d208c00 100644 --- a/src/main/java/zw/qantra/tm/domain/workflows/handlers/PollStatusHandler.java +++ b/src/main/java/zw/qantra/tm/domain/workflows/handlers/PollStatusHandler.java @@ -27,7 +27,7 @@ public class PollStatusHandler implements HandlerInterface { Logger logger = LoggerFactory.getLogger(TransactionService.class); // if the transaction is already successful, return it - if(transaction.getIntegrationStatus() == Status.SUCCESS){ + if(transaction.getPollingStatus() == Status.SUCCESS){ return transaction; } @@ -58,7 +58,7 @@ public class PollStatusHandler implements HandlerInterface { // if transaction wasn't successful, throw and halt workflow if(transaction.getStatus() != Status.SUCCESS){ transactionService.save(transaction); - throw new ApiException("Transaction failed: " + transaction.getErrorMessage() ); + throw new ApiException("Verification pending: " + transaction.getErrorMessage() ); }else { transaction.setErrorMessage(null); }