hotrecharge integration complete
This commit is contained in:
8
pom.xml
8
pom.xml
@@ -175,6 +175,14 @@
|
|||||||
<artifactId>loki-logback-appender</artifactId>
|
<artifactId>loki-logback-appender</artifactId>
|
||||||
<version>2.0.1</version> <!-- Use the latest version -->
|
<version>2.0.1</version> <!-- Use the latest version -->
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-cache</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.ben-manes.caffeine</groupId>
|
||||||
|
<artifactId>caffeine</artifactId>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package zw.qantra.tm;
|
|||||||
import io.nflow.rest.config.RestConfiguration;
|
import io.nflow.rest.config.RestConfiguration;
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.cache.annotation.EnableCaching;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
@@ -10,6 +11,7 @@ import org.springframework.context.annotation.Import;
|
|||||||
|
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
@Import(RestConfiguration.class)
|
@Import(RestConfiguration.class)
|
||||||
|
@EnableCaching
|
||||||
public class TmApplication {
|
public class TmApplication {
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
|||||||
25
src/main/java/zw/qantra/tm/config/CacheConfig.java
Normal file
25
src/main/java/zw/qantra/tm/config/CacheConfig.java
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package zw.qantra.tm.config;
|
||||||
|
|
||||||
|
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||||
|
import org.springframework.cache.CacheManager;
|
||||||
|
import org.springframework.cache.annotation.EnableCaching;
|
||||||
|
import org.springframework.cache.caffeine.CaffeineCacheManager;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableCaching
|
||||||
|
public class CacheConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public CacheManager cacheManager() {
|
||||||
|
CaffeineCacheManager cacheManager = new CaffeineCacheManager("providerProducts");
|
||||||
|
cacheManager.setCaffeine(Caffeine.newBuilder()
|
||||||
|
.expireAfterWrite(24, TimeUnit.HOURS)
|
||||||
|
.maximumSize(10000));
|
||||||
|
return cacheManager;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@ public class ProviderController {
|
|||||||
|
|
||||||
@GetMapping("/{id}/products")
|
@GetMapping("/{id}/products")
|
||||||
public ResponseEntity getProducts(@PathVariable UUID id) {
|
public ResponseEntity getProducts(@PathVariable UUID id) {
|
||||||
return ResponseEntity.ok(providerService.getProviderProducts(id));
|
return ResponseEntity.ok(providerService.getProviderProducts(id.toString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/{id}/products/{productId}")
|
@GetMapping("/{id}/products/{productId}")
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ public class TransactonController {
|
|||||||
workflowInstanceService.updateWorkflowInstance(update, action);
|
workflowInstanceService.updateWorkflowInstance(update, action);
|
||||||
|
|
||||||
CompletableFuture<Object> future = workflowUtils
|
CompletableFuture<Object> future = workflowUtils
|
||||||
.waitForState(transaction.getWorkflowId(), new String[]{"purchaseInvoicePaymentSuccess", "failed"},
|
.waitForState(transaction.getWorkflowId(), new String[]{"integrationStatusSuccess", "done", "failed"},
|
||||||
15000, 50);
|
15000, 50);
|
||||||
Object response = future.get();
|
Object response = future.get();
|
||||||
LogUtils.logPrettyJson(logger, "outgoing transaction", response);
|
LogUtils.logPrettyJson(logger, "outgoing transaction", response);
|
||||||
@@ -139,7 +139,7 @@ public class TransactonController {
|
|||||||
workflowInstanceService.updateWorkflowInstance(update, action);
|
workflowInstanceService.updateWorkflowInstance(update, action);
|
||||||
|
|
||||||
CompletableFuture<Object> future = workflowUtils
|
CompletableFuture<Object> future = workflowUtils
|
||||||
.waitForState(transaction.getWorkflowId(), new String[]{"done", "failed"},
|
.waitForState(transaction.getWorkflowId(), new String[]{"pollStatusSuccess", "done", "failed"},
|
||||||
15000, 50);
|
15000, 50);
|
||||||
Object response = future.get();
|
Object response = future.get();
|
||||||
LogUtils.logPrettyJson(logger, "outgoing transaction", response);
|
LogUtils.logPrettyJson(logger, "outgoing transaction", response);
|
||||||
|
|||||||
@@ -21,4 +21,5 @@ public class SBZProductDto {
|
|||||||
private BigDecimal defaultAmount;
|
private BigDecimal defaultAmount;
|
||||||
private boolean requiresAmount;
|
private boolean requiresAmount;
|
||||||
private UUID uid;
|
private UUID uid;
|
||||||
|
private String externalId;
|
||||||
}
|
}
|
||||||
@@ -16,12 +16,17 @@ import org.hibernate.annotations.SQLRestriction;
|
|||||||
public class Provider extends BaseEntity {
|
public class Provider extends BaseEntity {
|
||||||
private String clientId;
|
private String clientId;
|
||||||
private String label;
|
private String label;
|
||||||
|
private String name;
|
||||||
private String description;
|
private String description;
|
||||||
private String externalId;
|
private String externalId;
|
||||||
private String image;
|
private String image;
|
||||||
private String category;
|
private String category;
|
||||||
private String accountFieldName;
|
private String accountFieldName;
|
||||||
private String integrationProcessorLabel;
|
private String integrationProcessorLabel;
|
||||||
|
private String uid;
|
||||||
|
private Boolean requiresAmount;
|
||||||
|
private Boolean requiresPhone;
|
||||||
|
private Boolean requiresAccount;
|
||||||
private double percentageCommission;
|
private double percentageCommission;
|
||||||
@Column(columnDefinition = "TEXT")
|
@Column(columnDefinition = "TEXT")
|
||||||
private String meta;
|
private String meta;
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ package zw.qantra.tm.domain.services;
|
|||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.text.WordUtils;
|
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.cache.annotation.Cacheable;
|
||||||
import org.springframework.http.HttpHeaders;
|
import org.springframework.http.HttpHeaders;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
@@ -15,28 +15,37 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
|||||||
import zw.qantra.tm.domain.dtos.SBZProductDto;
|
import zw.qantra.tm.domain.dtos.SBZProductDto;
|
||||||
import zw.qantra.tm.domain.models.Provider;
|
import zw.qantra.tm.domain.models.Provider;
|
||||||
import zw.qantra.tm.domain.repositories.ProviderRepository;
|
import zw.qantra.tm.domain.repositories.ProviderRepository;
|
||||||
|
import zw.qantra.tm.exceptions.ApiException;
|
||||||
import zw.qantra.tm.rest.RestService;
|
import zw.qantra.tm.rest.RestService;
|
||||||
|
import zw.qantra.tm.utils.Utils;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
|
@Slf4j
|
||||||
public class ProviderService {
|
public class ProviderService {
|
||||||
@Getter
|
@Getter
|
||||||
private final ProviderRepository providerRepository;
|
private final ProviderRepository providerRepository;
|
||||||
private final RestService restService;
|
private final RestService restService;
|
||||||
|
private final HotRechargeTokenService hotRechargeTokenService;
|
||||||
|
|
||||||
|
@Value("${hot.api.url:https://ssl.hot.co.zw/api/v3}")
|
||||||
|
private String hotApiUrl;
|
||||||
|
|
||||||
@Value("${sbz.aggregator.url}")
|
@Value("${sbz.aggregator.url}")
|
||||||
private String aggregatorUrl;
|
private String aggregatorUrl;
|
||||||
|
|
||||||
public SBZProductDto getProviderProduct(UUID providerId, UUID productId) {
|
public SBZProductDto getProviderProduct(UUID providerId, UUID productId) {
|
||||||
return getProviderProducts(providerId).stream()
|
return getProviderProducts(providerId.toString()).stream()
|
||||||
.filter(product -> product.getUid().equals(productId))
|
.filter(product -> product.getUid().equals(productId))
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public List<SBZProductDto> getProviderProducts(UUID providerId) {
|
public List<SBZProductDto> getProviderProducts(UUID providerId) {
|
||||||
String token = restService.getMerchantToken();
|
String token = restService.getMerchantToken();
|
||||||
|
|
||||||
@@ -64,6 +73,74 @@ public class ProviderService {
|
|||||||
return products;
|
return products;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Cacheable(value = "providerProducts", key = "#providerId")
|
||||||
|
public List<SBZProductDto> getProviderProducts(String providerId) {
|
||||||
|
try {
|
||||||
|
Provider provider = providerRepository.findByClientId(providerId);
|
||||||
|
if (provider == null) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
String stockUrl = hotApiUrl + "/query/stock/" + 111;
|
||||||
|
HttpHeaders headers = new HttpHeaders();
|
||||||
|
String token = hotRechargeTokenService.getToken();
|
||||||
|
headers.setBearerAuth(token);
|
||||||
|
|
||||||
|
LinkedHashMap<String, Object> response = restService.getAs(stockUrl, headers, LinkedHashMap.class);
|
||||||
|
List stock = (List) response.get("stock");
|
||||||
|
|
||||||
|
List<SBZProductDto> products = new ArrayList<>();
|
||||||
|
for (Object item : stock) {
|
||||||
|
LinkedHashMap<String, Object> stockItem = (LinkedHashMap<String, Object>) item;
|
||||||
|
String stockProviderId = (String) stockItem.get("providerId");
|
||||||
|
if (stockProviderId.equals(providerId)) {
|
||||||
|
log.info("Found matching provider in stock: " + stockItem);
|
||||||
|
|
||||||
|
SBZProductDto sbzProductDto = new SBZProductDto();
|
||||||
|
sbzProductDto.setName((String) stockItem.get("name"));
|
||||||
|
sbzProductDto.setDescription((String) stockItem.get("description"));
|
||||||
|
sbzProductDto.setDisplayName((String) stockItem.get("name"));
|
||||||
|
sbzProductDto.setDefaultAmount(new BigDecimal(stockItem.get("amount").toString()));
|
||||||
|
sbzProductDto.setRequiresAmount(true); // always true for now
|
||||||
|
sbzProductDto.setUid(UUID.fromString(Utils.getUid()));
|
||||||
|
sbzProductDto.setExternalId((String) stockItem.get("productCode"));
|
||||||
|
products.add(sbzProductDto);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return products;
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getProviderId(String billClientId) {
|
||||||
|
Provider provider = getProviderRepository()
|
||||||
|
.findByClientId(billClientId);
|
||||||
|
if(provider == null){
|
||||||
|
throw new ApiException("Provider not found for billClientId: " + billClientId);
|
||||||
|
}
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Map<String, String> meta = Utils.fromJson(provider.getMeta(), Map.class);
|
||||||
|
return meta.get("hotProductId");
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getExternalId(String providerId, String productUuid) {
|
||||||
|
try {
|
||||||
|
UUID productUid = UUID.fromString(productUuid);
|
||||||
|
|
||||||
|
List<SBZProductDto> products = getProviderProducts(providerId);
|
||||||
|
|
||||||
|
return products.stream()
|
||||||
|
.filter(product -> product.getUid().equals(productUid))
|
||||||
|
.map(SBZProductDto::getExternalId)
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
log.error("Invalid UUID format: " + productUuid, e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Object getProviderByClientId(String clientId) {
|
public Object getProviderByClientId(String clientId) {
|
||||||
Provider provider = providerRepository.findByClientId(clientId);
|
Provider provider = providerRepository.findByClientId(clientId);
|
||||||
if (provider != null) {
|
if (provider != null) {
|
||||||
@@ -81,7 +158,7 @@ public class ProviderService {
|
|||||||
existingProviders = getProviderRepository().findAll();
|
existingProviders = getProviderRepository().findAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
return getProviders(existingProviders);
|
return existingProviders;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object getProviders(List<Provider> existingProviders) {
|
public Object getProviders(List<Provider> existingProviders) {
|
||||||
|
|||||||
@@ -1,29 +1,17 @@
|
|||||||
package zw.qantra.tm.domain.services.processors.confirmations.hotrecharge;
|
package zw.qantra.tm.domain.services.processors.confirmations.hotrecharge;
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import zw.qantra.tm.domain.enums.Status;
|
import zw.qantra.tm.domain.services.AdditionalDataService;
|
||||||
import zw.qantra.tm.domain.models.Transaction;
|
import zw.qantra.tm.domain.services.HotRechargeTokenService;
|
||||||
import zw.qantra.tm.domain.services.processors.TransactionProcessorInterface;
|
import zw.qantra.tm.rest.RestService;
|
||||||
|
|
||||||
|
|
||||||
@Service("CONFIRM_ECONET_BUNDLES_HOT")
|
@Service("CONFIRM_ECONET_BUNDLES_HOT")
|
||||||
@RequiredArgsConstructor
|
public class EconetBundlesConfirmationHotProcessor extends NetoneConfirmationHotProcessor {
|
||||||
public class EconetBundlesConfirmationHotProcessor implements TransactionProcessorInterface {
|
public EconetBundlesConfirmationHotProcessor(
|
||||||
@Override
|
AdditionalDataService additionalDataService,
|
||||||
public Object process(Transaction transaction) throws Exception {
|
HotRechargeTokenService hotRechargeTokenService,
|
||||||
transaction.setStatus(Status.SUCCESS);
|
RestService restService) {
|
||||||
transaction.setResponseCode("00");
|
super(additionalDataService, hotRechargeTokenService, restService);
|
||||||
return transaction;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object reverse(Transaction transaction) {
|
|
||||||
return transaction;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object poll(Transaction transaction) throws Exception {
|
|
||||||
return transaction;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,29 +1,17 @@
|
|||||||
package zw.qantra.tm.domain.services.processors.confirmations.hotrecharge;
|
package zw.qantra.tm.domain.services.processors.confirmations.hotrecharge;
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import zw.qantra.tm.domain.enums.Status;
|
import zw.qantra.tm.domain.services.AdditionalDataService;
|
||||||
import zw.qantra.tm.domain.models.Transaction;
|
import zw.qantra.tm.domain.services.HotRechargeTokenService;
|
||||||
import zw.qantra.tm.domain.services.processors.TransactionProcessorInterface;
|
import zw.qantra.tm.rest.RestService;
|
||||||
|
|
||||||
|
|
||||||
@Service("CONFIRM_ECONET_HOT")
|
@Service("CONFIRM_ECONET_HOT")
|
||||||
@RequiredArgsConstructor
|
public class EconetConfirmationHotProcessor extends NetoneConfirmationHotProcessor {
|
||||||
public class EconetConfirmationHotProcessor implements TransactionProcessorInterface {
|
public EconetConfirmationHotProcessor(
|
||||||
@Override
|
AdditionalDataService additionalDataService,
|
||||||
public Object process(Transaction transaction) throws Exception {
|
HotRechargeTokenService hotRechargeTokenService,
|
||||||
transaction.setStatus(Status.SUCCESS);
|
RestService restService) {
|
||||||
transaction.setResponseCode("00");
|
super(additionalDataService, hotRechargeTokenService, restService);
|
||||||
return transaction;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object reverse(Transaction transaction) {
|
|
||||||
return transaction;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object poll(Transaction transaction) throws Exception {
|
|
||||||
return transaction;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,68 @@
|
|||||||
package zw.qantra.tm.domain.services.processors.confirmations.hotrecharge;
|
package zw.qantra.tm.domain.services.processors.confirmations.hotrecharge;
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
import zw.qantra.tm.domain.enums.RequestType;
|
||||||
import zw.qantra.tm.domain.enums.Status;
|
import zw.qantra.tm.domain.enums.Status;
|
||||||
|
import zw.qantra.tm.domain.models.AdditionalData;
|
||||||
import zw.qantra.tm.domain.models.Transaction;
|
import zw.qantra.tm.domain.models.Transaction;
|
||||||
|
import zw.qantra.tm.domain.services.AdditionalDataService;
|
||||||
|
import zw.qantra.tm.domain.services.HotRechargeTokenService;
|
||||||
import zw.qantra.tm.domain.services.processors.TransactionProcessorInterface;
|
import zw.qantra.tm.domain.services.processors.TransactionProcessorInterface;
|
||||||
|
import zw.qantra.tm.rest.RestService;
|
||||||
|
import zw.qantra.tm.utils.Utils;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
|
||||||
@Service("CONFIRM_NETONE_HOT")
|
@Service("CONFIRM_NETONE_HOT")
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
|
@Slf4j
|
||||||
public class NetoneConfirmationHotProcessor implements TransactionProcessorInterface {
|
public class NetoneConfirmationHotProcessor implements TransactionProcessorInterface {
|
||||||
|
private final AdditionalDataService additionalDataService;
|
||||||
|
private final HotRechargeTokenService hotRechargeTokenService;
|
||||||
|
private final RestService restService;
|
||||||
|
|
||||||
|
@Value("${hot.api.url:https://ssl.hot.co.zw/api/v3}")
|
||||||
|
private String hotApiUrl;
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object process(Transaction transaction) throws Exception {
|
public Object process(Transaction transaction) throws Exception {
|
||||||
|
|
||||||
|
String token = hotRechargeTokenService.getToken();
|
||||||
|
if (!checkBalance(token, transaction.getAmount())) {
|
||||||
|
transaction.setStatus(Status.FAILED);
|
||||||
|
transaction.setResponseCode("92");
|
||||||
|
transaction.setErrorMessage("There's a technical issue on our end. Please try again later.");
|
||||||
|
return transaction;
|
||||||
|
}
|
||||||
|
|
||||||
transaction.setStatus(Status.SUCCESS);
|
transaction.setStatus(Status.SUCCESS);
|
||||||
transaction.setResponseCode("00");
|
transaction.setResponseCode("00");
|
||||||
|
|
||||||
|
List<Map> additionalDataList = new ArrayList<>();
|
||||||
|
additionalDataList.add(new HashMap() {{
|
||||||
|
put("name", "Provider");
|
||||||
|
put("value", transaction.getBillName());
|
||||||
|
}});
|
||||||
|
additionalDataList.add(new HashMap() {{
|
||||||
|
put("name", "Destination Phone Number");
|
||||||
|
put("value", transaction.getCreditAccount());
|
||||||
|
}});
|
||||||
|
transaction.setAdditionalData(additionalDataList);
|
||||||
|
|
||||||
|
AdditionalData additionalData = AdditionalData.builder()
|
||||||
|
.transactionId(transaction.getId().toString())
|
||||||
|
.requestType(RequestType.CONFIRM)
|
||||||
|
.jsonString(Utils.toJson(additionalDataList))
|
||||||
|
.build();
|
||||||
|
additionalDataService.getAdditionalDataRepository().save(additionalData);
|
||||||
|
|
||||||
return transaction;
|
return transaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,4 +75,27 @@ public class NetoneConfirmationHotProcessor implements TransactionProcessorInter
|
|||||||
public Object poll(Transaction transaction) throws Exception {
|
public Object poll(Transaction transaction) throws Exception {
|
||||||
return transaction;
|
return transaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean checkBalance(String token, BigDecimal amount) {
|
||||||
|
try {
|
||||||
|
String balanceUrl = hotApiUrl + "/account/balance/4";
|
||||||
|
HttpHeaders headers = new HttpHeaders();
|
||||||
|
headers.setBearerAuth(token);
|
||||||
|
|
||||||
|
LinkedList responseEntity = restService.getAs(balanceUrl, headers, LinkedList.class);
|
||||||
|
LinkedHashMap response = (LinkedHashMap) responseEntity.get(0);
|
||||||
|
log.info("Response: {}", response);
|
||||||
|
|
||||||
|
if (response != null && response.containsKey("balance")) {
|
||||||
|
Object balance = response.get("balance");
|
||||||
|
log.info("Current balance: {}", balance);
|
||||||
|
return (new BigDecimal(balance.toString())).compareTo(amount) > 0;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Failed to check balance: {}", e.getMessage(), e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,8 +52,8 @@ public class ZesaConfirmationHotProcessor implements TransactionProcessorInterfa
|
|||||||
|
|
||||||
if (!checkBalance(token, transaction.getAmount())) {
|
if (!checkBalance(token, transaction.getAmount())) {
|
||||||
transaction.setStatus(Status.FAILED);
|
transaction.setStatus(Status.FAILED);
|
||||||
transaction.setResponseCode("91");
|
transaction.setResponseCode("92");
|
||||||
transaction.setErrorMessage("Insufficient balance");
|
transaction.setErrorMessage("There's a technical issue on our end. Please try again later.");
|
||||||
return transaction;
|
return transaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,12 +91,12 @@ public class ZesaConfirmationHotProcessor implements TransactionProcessorInterfa
|
|||||||
private List<Map<String, String>> buildAdditionalData(Map<String, Object> customerData) {
|
private List<Map<String, String>> buildAdditionalData(Map<String, Object> customerData) {
|
||||||
List<Map<String, String>> additionalDataList = new ArrayList<>();
|
List<Map<String, String>> additionalDataList = new ArrayList<>();
|
||||||
if (customerData != null) {
|
if (customerData != null) {
|
||||||
additionalDataList.add(Map.of("key", "accountNumber",
|
additionalDataList.add(Map.of("name", "MeterNumber",
|
||||||
"value", customerData.getOrDefault("accountNumber", "").toString()));
|
"value", customerData.getOrDefault("accountNumber", "").toString()));
|
||||||
Map<String, Object> details = (Map<String, Object>) customerData.get("details");
|
Map<String, Object> details = (Map<String, Object>) customerData.get("details");
|
||||||
if (details != null) {
|
if (details != null) {
|
||||||
for (Map.Entry<String, Object> entry : details.entrySet()) {
|
for (Map.Entry<String, Object> entry : details.entrySet()) {
|
||||||
additionalDataList.add(Map.of("key", entry.getKey(), "value", entry.getValue().toString()));
|
additionalDataList.add(Map.of("name", entry.getKey(), "value", entry.getValue().toString()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,11 +13,9 @@ import org.springframework.web.client.ResourceAccessException;
|
|||||||
import zw.qantra.tm.domain.enums.RequestType;
|
import zw.qantra.tm.domain.enums.RequestType;
|
||||||
import zw.qantra.tm.domain.enums.Status;
|
import zw.qantra.tm.domain.enums.Status;
|
||||||
import zw.qantra.tm.domain.models.AdditionalData;
|
import zw.qantra.tm.domain.models.AdditionalData;
|
||||||
import zw.qantra.tm.domain.models.Provider;
|
|
||||||
import zw.qantra.tm.domain.models.Transaction;
|
import zw.qantra.tm.domain.models.Transaction;
|
||||||
import zw.qantra.tm.domain.services.*;
|
import zw.qantra.tm.domain.services.*;
|
||||||
import zw.qantra.tm.domain.services.processors.TransactionProcessorInterface;
|
import zw.qantra.tm.domain.services.processors.TransactionProcessorInterface;
|
||||||
import zw.qantra.tm.exceptions.ApiException;
|
|
||||||
import zw.qantra.tm.rest.RestService;
|
import zw.qantra.tm.rest.RestService;
|
||||||
import zw.qantra.tm.utils.Utils;
|
import zw.qantra.tm.utils.Utils;
|
||||||
|
|
||||||
@@ -73,8 +71,8 @@ public class HotRechargeIntegrationProcessor implements TransactionProcessorInte
|
|||||||
|
|
||||||
if (!checkBalance(token, transaction.getAmount())) {
|
if (!checkBalance(token, transaction.getAmount())) {
|
||||||
transaction.setStatus(Status.FAILED);
|
transaction.setStatus(Status.FAILED);
|
||||||
transaction.setResponseCode("91");
|
transaction.setResponseCode("92");
|
||||||
transaction.setErrorMessage("Insufficient balance");
|
transaction.setErrorMessage("There's a technical issue on our end. Please try again later.");
|
||||||
return transaction;
|
return transaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,17 +153,18 @@ public class HotRechargeIntegrationProcessor implements TransactionProcessorInte
|
|||||||
|
|
||||||
Map<String, Object> request = new LinkedHashMap<>();
|
Map<String, Object> request = new LinkedHashMap<>();
|
||||||
request.put("AgentReference", UUID.randomUUID().toString());
|
request.put("AgentReference", UUID.randomUUID().toString());
|
||||||
request.put("ProductId", getProductId(transaction.getBillClientId()));
|
request.put("ProductId", providerService.getProviderId(transaction.getBillClientId()));
|
||||||
request.put("Target", transaction.getCreditAccount());
|
request.put("Target", transaction.getCreditAccount());
|
||||||
request.put("Amount", transaction.getAmount());
|
request.put("Amount", transaction.getAmount());
|
||||||
|
|
||||||
if(getProductId(transaction.getBillClientId()).equals("41")) {
|
// do this for zesa
|
||||||
|
if(providerService.getProviderId(transaction.getBillClientId()).equals("41")) {
|
||||||
List<Map<String, String>> rechargeOptions = new ArrayList<>();
|
List<Map<String, String>> rechargeOptions = new ArrayList<>();
|
||||||
if (transaction.getCreditPhone() != null) {
|
if (transaction.getDebitPhone() != null) {
|
||||||
Map<String, String> option = new LinkedHashMap<>();
|
Map<String, String> option = new LinkedHashMap<>();
|
||||||
option.put("Name", "NotifyNumber");
|
option.put("Name", "NotifyNumber");
|
||||||
option.put("ParameterType", "String");
|
option.put("ParameterType", "String");
|
||||||
option.put("Value", transaction.getCreditPhone());
|
option.put("Value", transaction.getDebitPhone());
|
||||||
rechargeOptions.add(option);
|
rechargeOptions.add(option);
|
||||||
}
|
}
|
||||||
if (!rechargeOptions.isEmpty()) {
|
if (!rechargeOptions.isEmpty()) {
|
||||||
@@ -173,6 +172,18 @@ public class HotRechargeIntegrationProcessor implements TransactionProcessorInte
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// do this for Econet bundles
|
||||||
|
if(providerService.getProviderId(transaction.getBillClientId()).equals("111")) {
|
||||||
|
String productCode = providerService.getExternalId(
|
||||||
|
providerService.getProviderId(transaction.getBillClientId()), transaction.getProductUid());
|
||||||
|
List<Map<String, String>> rechargeOptions = new ArrayList<>();
|
||||||
|
Map<String, String> option = new LinkedHashMap<>();
|
||||||
|
option.put("Name", "ProductCode");
|
||||||
|
option.put("ParameterType", "String");
|
||||||
|
option.put("Value", productCode);
|
||||||
|
rechargeOptions.add(option);
|
||||||
|
}
|
||||||
|
|
||||||
logger.info("Sending recharge request: {}", request);
|
logger.info("Sending recharge request: {}", request);
|
||||||
ResponseEntity<?> response = restService.postAsEntity(rechargeUrl, request, headers, LinkedHashMap.class);
|
ResponseEntity<?> response = restService.postAsEntity(rechargeUrl, request, headers, LinkedHashMap.class);
|
||||||
logger.info("Recharge response: {}", response.getBody());
|
logger.info("Recharge response: {}", response.getBody());
|
||||||
@@ -213,25 +224,16 @@ public class HotRechargeIntegrationProcessor implements TransactionProcessorInte
|
|||||||
private List<Map<String, String>> buildAdditionalDataList(Map<String, Object> rechargeData) {
|
private List<Map<String, String>> buildAdditionalDataList(Map<String, Object> rechargeData) {
|
||||||
List<Map<String, String>> additionalDataList = new ArrayList<>();
|
List<Map<String, String>> additionalDataList = new ArrayList<>();
|
||||||
for (Map.Entry<String, Object> entry : rechargeData.entrySet()) {
|
for (Map.Entry<String, Object> entry : rechargeData.entrySet()) {
|
||||||
|
if(entry.getKey().equals("Cost"))
|
||||||
|
continue;
|
||||||
Map<String, String> dataItem = new LinkedHashMap<>();
|
Map<String, String> dataItem = new LinkedHashMap<>();
|
||||||
dataItem.put("key", entry.getKey());
|
dataItem.put("name", entry.getKey());
|
||||||
dataItem.put("value", entry.getValue().toString());
|
dataItem.put("value", entry.getValue().toString());
|
||||||
additionalDataList.add(dataItem);
|
additionalDataList.add(dataItem);
|
||||||
}
|
}
|
||||||
return additionalDataList;
|
return additionalDataList;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getProductId(String billClientId) {
|
|
||||||
Provider provider = providerService.getProviderRepository()
|
|
||||||
.findByClientId(billClientId);
|
|
||||||
if(provider == null){
|
|
||||||
throw new ApiException("Provider not found for billClientId: " + billClientId);
|
|
||||||
}
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
Map<String, String> meta = Utils.fromJson(provider.getMeta(), Map.class);
|
|
||||||
return meta.get("hotProductId");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object reverse(Transaction transaction) {
|
public Object reverse(Transaction transaction) {
|
||||||
return transaction;
|
return transaction;
|
||||||
|
|||||||
@@ -42,6 +42,15 @@ public class EcocashRemotePaymentProcessor implements TransactionProcessorInterf
|
|||||||
@Override
|
@Override
|
||||||
public Object process(Transaction transaction) throws Exception {
|
public Object process(Transaction transaction) throws Exception {
|
||||||
logger.info("processing transaction");
|
logger.info("processing transaction");
|
||||||
|
|
||||||
|
if(settingService.getBooleanSetting("SIMULATE_ECOCASH_SUCCESS")) {
|
||||||
|
transaction.setStatus(Status.SUCCESS);
|
||||||
|
transaction.setResponseCode("00");
|
||||||
|
transaction.setPaymentProcessorRef(RandomStringUtils.randomNumeric(10));
|
||||||
|
transaction.setDebitRef(RandomStringUtils.randomNumeric(10));
|
||||||
|
return transaction;
|
||||||
|
}
|
||||||
|
|
||||||
String token = restService.getMerchantToken();
|
String token = restService.getMerchantToken();
|
||||||
|
|
||||||
// send total less gateway charges
|
// send total less gateway charges
|
||||||
@@ -83,15 +92,6 @@ public class EcocashRemotePaymentProcessor implements TransactionProcessorInterf
|
|||||||
LinkedHashMap body = (LinkedHashMap) response.get("body");
|
LinkedHashMap body = (LinkedHashMap) response.get("body");
|
||||||
transaction.setSdkActionId((String) body.get("uid"));
|
transaction.setSdkActionId((String) body.get("uid"));
|
||||||
|
|
||||||
// todo: REMOVE THIS
|
|
||||||
if(settingService.getBooleanSetting("SIMULATE_ECOCASH_SUCCESS")){
|
|
||||||
body.put("status", "SUCCESS");
|
|
||||||
LinkedHashMap hashMap = new LinkedHashMap();
|
|
||||||
hashMap.put("debitReference", RandomStringUtils.randomNumeric(10));
|
|
||||||
body.put("transaction", hashMap);
|
|
||||||
body.put("targetUrl", "/home");
|
|
||||||
}
|
|
||||||
|
|
||||||
if(body.get("status").equals("SUCCESS")){
|
if(body.get("status").equals("SUCCESS")){
|
||||||
transaction.setStatus(Status.SUCCESS);
|
transaction.setStatus(Status.SUCCESS);
|
||||||
transaction.setResponseCode("00");
|
transaction.setResponseCode("00");
|
||||||
|
|||||||
Reference in New Issue
Block a user