diff --git a/.gitignore b/.gitignore index 7712e53..001eafd 100644 --- a/.gitignore +++ b/.gitignore @@ -31,4 +31,5 @@ build/ ### VS Code ### .vscode/ -logs/ \ No newline at end of file +logs/ +data/ diff --git a/data/jobrunr.mv.db b/data/jobrunr.mv.db deleted file mode 100644 index fdd1312..0000000 Binary files a/data/jobrunr.mv.db and /dev/null differ diff --git a/data/jobrunr.trace.db b/data/jobrunr.trace.db deleted file mode 100644 index 9c3c351..0000000 --- a/data/jobrunr.trace.db +++ /dev/null @@ -1,64 +0,0 @@ -2026-06-11 11:30:43.191867+02:00 database: flush -org.h2.message.DbException: General error: "org.h2.mvstore.MVStoreException: The file is locked: /Users/khoza/dev/qantra/qpay-transaction-manager-spring-boot/data/jobrunr.mv.db [2.2.224/7]" [50000-224] - at org.h2.message.DbException.get(DbException.java:212) - at org.h2.message.DbException.convert(DbException.java:407) - at org.h2.mvstore.db.Store.lambda$new$0(Store.java:122) - at org.h2.mvstore.MVStore.handleException(MVStore.java:1546) - at org.h2.mvstore.MVStore.panic(MVStore.java:371) - at org.h2.mvstore.MVStore.(MVStore.java:291) - at org.h2.mvstore.MVStore$Builder.open(MVStore.java:2035) - at org.h2.mvstore.db.Store.(Store.java:133) - at org.h2.engine.Database.(Database.java:326) - at org.h2.engine.Engine.openSession(Engine.java:92) - at org.h2.engine.Engine.openSession(Engine.java:222) - at org.h2.engine.Engine.createSession(Engine.java:201) - at org.h2.engine.SessionRemote.connectEmbeddedOrServer(SessionRemote.java:343) - at org.h2.jdbc.JdbcConnection.(JdbcConnection.java:125) - at org.h2.Driver.connect(Driver.java:59) - at com.zaxxer.hikari.util.DriverDataSource.getConnection(DriverDataSource.java:137) - at com.zaxxer.hikari.pool.PoolBase.newConnection(PoolBase.java:360) - at com.zaxxer.hikari.pool.PoolBase.newPoolEntry(PoolBase.java:202) - at com.zaxxer.hikari.pool.HikariPool.createPoolEntry(HikariPool.java:461) - at com.zaxxer.hikari.pool.HikariPool.checkFailFast(HikariPool.java:550) - at com.zaxxer.hikari.pool.HikariPool.(HikariPool.java:98) - at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:111) - at org.jobrunr.storage.sql.common.SqlStorageProviderFactory.getStorageProviderUsingDataSource(SqlStorageProviderFactory.java:40) - at org.jobrunr.storage.sql.common.SqlStorageProviderFactory.using(SqlStorageProviderFactory.java:36) - at org.jobrunr.storage.sql.common.SqlStorageProviderFactory.using(SqlStorageProviderFactory.java:31) - at org.jobrunr.spring.autoconfigure.storage.JobRunrSqlStorageAutoConfiguration.sqlStorageProvider(JobRunrSqlStorageAutoConfiguration.java:33) - at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) - at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) - at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) - at java.base/java.lang.reflect.Method.invoke(Method.java:569) - at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:146) - at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:644) - at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:636) - at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1355) - at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1185) - at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:562) - at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:522) - at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:337) - at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) - at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:335) - at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) - at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:975) - at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:971) - at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:625) - at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) - at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754) - at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:456) - at org.springframework.boot.SpringApplication.run(SpringApplication.java:335) - at org.springframework.boot.SpringApplication.run(SpringApplication.java:1363) - at org.springframework.boot.SpringApplication.run(SpringApplication.java:1352) - at zw.qantra.tm.TmApplication.main(TmApplication.java:20) -Caused by: org.h2.jdbc.JdbcSQLNonTransientException: General error: "org.h2.mvstore.MVStoreException: The file is locked: /Users/khoza/dev/qantra/qpay-transaction-manager-spring-boot/data/jobrunr.mv.db [2.2.224/7]" [50000-224] - at org.h2.message.DbException.getJdbcSQLException(DbException.java:566) - at org.h2.message.DbException.getJdbcSQLException(DbException.java:489) - ... 51 more -Caused by: org.h2.mvstore.MVStoreException: The file is locked: /Users/khoza/dev/qantra/qpay-transaction-manager-spring-boot/data/jobrunr.mv.db [2.2.224/7] - at org.h2.mvstore.DataUtils.newMVStoreException(DataUtils.java:996) - at org.h2.mvstore.SingleFileStore.lockFileChannel(SingleFileStore.java:143) - at org.h2.mvstore.SingleFileStore.open(SingleFileStore.java:117) - at org.h2.mvstore.SingleFileStore.open(SingleFileStore.java:81) - at org.h2.mvstore.MVStore.(MVStore.java:286) - ... 45 more diff --git a/src/main/java/zw/qantra/tm/domain/controllers/AccountController.java b/src/main/java/zw/qantra/tm/domain/controllers/AccountController.java index dcf1887..10ddc1e 100644 --- a/src/main/java/zw/qantra/tm/domain/controllers/AccountController.java +++ b/src/main/java/zw/qantra/tm/domain/controllers/AccountController.java @@ -8,8 +8,6 @@ import zw.qantra.tm.domain.dtos.erp.VelocityAccountDto; import zw.qantra.tm.domain.dtos.erp.VelocityStatementDto; import zw.qantra.tm.domain.services.VelocityAccountService; -import java.util.List; - @RestController @RequestMapping("/accounts") @RequiredArgsConstructor @@ -19,10 +17,10 @@ public class AccountController { @GetMapping("/phone/{currencyPhoneConcat}") @PreAuthorize("hasRole('ACCOUNT_READ')") - public ResponseEntity getAccountByPhone( + public ResponseEntity getAccountByPhone( @PathVariable String currencyPhoneConcat) { try { - Object account = velocityAccountService.getAccountByPhone(currencyPhoneConcat); + VelocityAccountDto account = velocityAccountService.getAccountByPhone(currencyPhoneConcat); return ResponseEntity.ok(account); } catch (Exception e) { return ResponseEntity.badRequest().build(); diff --git a/src/main/java/zw/qantra/tm/domain/dtos/erp/VelocityAccountDto.java b/src/main/java/zw/qantra/tm/domain/dtos/erp/VelocityAccountDto.java index 4712749..0db6947 100644 --- a/src/main/java/zw/qantra/tm/domain/dtos/erp/VelocityAccountDto.java +++ b/src/main/java/zw/qantra/tm/domain/dtos/erp/VelocityAccountDto.java @@ -2,15 +2,12 @@ package zw.qantra.tm.domain.dtos.erp; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; -import jakarta.persistence.*; -import jakarta.validation.constraints.NotNull; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import java.math.BigDecimal; -import java.sql.Timestamp; @Data @Builder @@ -27,11 +24,10 @@ public class VelocityAccountDto { private String type; private String category; private String ledger; - private Timestamp deactivatedAt; private BigDecimal balance; private String partyType; private String party; - private String currency; + private Object currency; private String customerStringId; private String companyStringId; } \ No newline at end of file diff --git a/src/main/java/zw/qantra/tm/domain/services/GroupBatchService.java b/src/main/java/zw/qantra/tm/domain/services/GroupBatchService.java index 0de57df..20aa6f1 100644 --- a/src/main/java/zw/qantra/tm/domain/services/GroupBatchService.java +++ b/src/main/java/zw/qantra/tm/domain/services/GroupBatchService.java @@ -253,8 +253,10 @@ public class GroupBatchService { CompletableFuture future = workflowUtils .waitForState(batch.getWorkflowId(), new String[]{"done", "failed"}, 15000, 50); - future.get(); - return getBatch(batchId, userId); + StateResponse response = (StateResponse) future.get(); + String id = response.getBody().toString(); // this returns Batch Transaction ID + String cleaned = id.replaceAll("^\"|\"$", ""); + return transactionService.findById(UUID.fromString(cleaned)); } catch (InterruptedException | ExecutionException e) { throw new RuntimeException(e); } diff --git a/src/main/java/zw/qantra/tm/domain/services/GroupBatchWorkflowService.java b/src/main/java/zw/qantra/tm/domain/services/GroupBatchWorkflowService.java index 44d6d9a..e83ea74 100644 --- a/src/main/java/zw/qantra/tm/domain/services/GroupBatchWorkflowService.java +++ b/src/main/java/zw/qantra/tm/domain/services/GroupBatchWorkflowService.java @@ -133,7 +133,6 @@ public class GroupBatchWorkflowService { return groupBatchRepository.save(batch); } - @Transactional public GroupBatch processPoll(UUID batchId) throws Exception { GroupBatch batch = getBatchById(batchId); if (batch.getStatus() != GroupBatchStatus.REQUESTED && batch.getStatus() != GroupBatchStatus.POLL_FAILED) { diff --git a/src/main/java/zw/qantra/tm/domain/services/ProviderService.java b/src/main/java/zw/qantra/tm/domain/services/ProviderService.java index 31b55e3..c94195a 100644 --- a/src/main/java/zw/qantra/tm/domain/services/ProviderService.java +++ b/src/main/java/zw/qantra/tm/domain/services/ProviderService.java @@ -102,7 +102,7 @@ public class ProviderService { try { response = restService.getAs(stockUrl, headers, LinkedHashMap.class); } catch (Exception e) { - log.error("Error fetching products for provider " + providerId, e); + log.error("Error fetching products for provider " + providerId); return Collections.emptyList(); } diff --git a/src/main/java/zw/qantra/tm/domain/services/VelocityAccountService.java b/src/main/java/zw/qantra/tm/domain/services/VelocityAccountService.java index 26ffd1e..1db6290 100644 --- a/src/main/java/zw/qantra/tm/domain/services/VelocityAccountService.java +++ b/src/main/java/zw/qantra/tm/domain/services/VelocityAccountService.java @@ -26,7 +26,7 @@ public class VelocityAccountService { * @param currencyPhoneConcat The concatenation of currency and phone (e.g. "USD+263771234567"). * @return VelocityAccountDto with account details. */ - public Object getAccountByPhone(String currencyPhoneConcat) { + public VelocityAccountDto getAccountByPhone(String currencyPhoneConcat) { try { LinkedHashMap responseMap = velocityPaymentService.makeRequest( HttpMethod.GET, @@ -37,7 +37,7 @@ public class VelocityAccountService { log.error("Error fetching account"); throw new IllegalStateException("Error fetching account"); } - return responseMap; + return Utils.deepCopy(responseMap, VelocityAccountDto.class); } catch (Exception e) { log.error("Error fetching account by phone {}: {}", currencyPhoneConcat, e.getMessage(), e); throw new IllegalStateException("Error fetching account: " + e.getMessage(), e); diff --git a/src/main/java/zw/qantra/tm/domain/services/VelocityPaymentService.java b/src/main/java/zw/qantra/tm/domain/services/VelocityPaymentService.java index 3d361d6..6a530db 100644 --- a/src/main/java/zw/qantra/tm/domain/services/VelocityPaymentService.java +++ b/src/main/java/zw/qantra/tm/domain/services/VelocityPaymentService.java @@ -205,8 +205,10 @@ public class VelocityPaymentService { int maxRetries = settingService.getIntegerSetting("MAX_RETRIES"); int retries = transaction.getRetries() == null ? 0 : transaction.getRetries(); - if (retries >= maxRetries) + if (retries >= maxRetries) { + transaction.setErrorMessage("Maximum retries reached"); return transaction; + } transaction.setRetries(retries + 1); diff --git a/src/main/java/zw/qantra/tm/domain/services/processors/payments/CityWalletRemotePaymentProcessor.java b/src/main/java/zw/qantra/tm/domain/services/processors/payments/CityWalletRemotePaymentProcessor.java index 2fc3de9..f0664b0 100644 --- a/src/main/java/zw/qantra/tm/domain/services/processors/payments/CityWalletRemotePaymentProcessor.java +++ b/src/main/java/zw/qantra/tm/domain/services/processors/payments/CityWalletRemotePaymentProcessor.java @@ -70,7 +70,6 @@ public class CityWalletRemotePaymentProcessor implements TransactionProcessorInt try { transaction = velocityPaymentService.pollPaymentStatus(transaction); - } catch (Exception e) { e.printStackTrace(); logger.error("Network error during payment processing: {}", e.getMessage(), e); @@ -88,6 +87,7 @@ public class CityWalletRemotePaymentProcessor implements TransactionProcessorInt try { velocityPaymentService.updateTransaction(transaction.getOrderTransactionId(), code); + transaction.setStatus(Status.SUCCESS); } catch (Exception e) { e.printStackTrace(); logger.error("Network error during payment update: {}", e.getMessage(), e); diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 75782d7..8c6bd2b 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -28,7 +28,7 @@ sbz.aggregator.encryption-key=91b5b3c7-860f-473c-ac5c-b12d9121819b steward.payment.processor.url=${sbz.merchant.url} -mpgs.return-url=https://vdt.co.zw/#/return +mpgs.return-url=https://pay.velocityafrica.net/return # JWT Configuration jwt.secret=your-super-secret-jwt-key-here-make-it-very-long-and-secure-in-production diff --git a/src/main/resources/liquibase-diff-changeLog.xml b/src/main/resources/liquibase-diff-changeLog.xml deleted file mode 100644 index 6c626da..0000000 --- a/src/main/resources/liquibase-diff-changeLog.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - - -