implement zesa currency swap feature
This commit is contained in:
@@ -23,7 +23,8 @@ public class ProviderController {
|
|||||||
@GetMapping
|
@GetMapping
|
||||||
public ResponseEntity getProviders(
|
public ResponseEntity getProviders(
|
||||||
@And({
|
@And({
|
||||||
@Spec(path = "category", spec = Equal.class)
|
@Spec(path = "category", spec = Equal.class),
|
||||||
|
@Spec(path = "currency", spec = Equal.class),
|
||||||
}) Specification<Provider> spec, Pageable pageable) {
|
}) Specification<Provider> spec, Pageable pageable) {
|
||||||
return ResponseEntity.ok(providerService.findAll(spec, pageable));
|
return ResponseEntity.ok(providerService.findAll(spec, pageable));
|
||||||
}
|
}
|
||||||
@@ -71,4 +72,10 @@ public class ProviderController {
|
|||||||
}
|
}
|
||||||
return ResponseEntity.notFound().build();
|
return ResponseEntity.notFound().build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DeleteMapping
|
||||||
|
public ResponseEntity evictCache() {
|
||||||
|
providerService.evictProviderProductsCache();
|
||||||
|
return ResponseEntity.ok("Cache evicted");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,4 +13,6 @@ public class Currency extends BaseEntity {
|
|||||||
private String isoCode;
|
private String isoCode;
|
||||||
private String name;
|
private String name;
|
||||||
private String displayName;
|
private String displayName;
|
||||||
|
private String bankRate;
|
||||||
|
private String forecastRate;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,11 @@ package zw.qantra.tm.domain.models;
|
|||||||
|
|
||||||
import jakarta.persistence.Column;
|
import jakarta.persistence.Column;
|
||||||
import jakarta.persistence.Entity;
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.EnumType;
|
||||||
|
import jakarta.persistence.Enumerated;
|
||||||
import lombok.*;
|
import lombok.*;
|
||||||
import org.hibernate.annotations.SQLRestriction;
|
import org.hibernate.annotations.SQLRestriction;
|
||||||
|
import zw.qantra.tm.domain.enums.CurrencyType;
|
||||||
|
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@@ -19,6 +22,8 @@ public class Provider extends BaseEntity {
|
|||||||
private String name;
|
private String name;
|
||||||
private String description;
|
private String description;
|
||||||
private String externalId;
|
private String externalId;
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
|
private CurrencyType currency;
|
||||||
private String image;
|
private String image;
|
||||||
private String category;
|
private String category;
|
||||||
private String accountFieldName;
|
private String accountFieldName;
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ public class Transaction extends BaseEntity {
|
|||||||
private String creditAccount;
|
private String creditAccount;
|
||||||
@Enumerated(EnumType.STRING)
|
@Enumerated(EnumType.STRING)
|
||||||
private CurrencyType creditCurrency;
|
private CurrencyType creditCurrency;
|
||||||
|
private BigDecimal creditAmount;
|
||||||
private String creditName;
|
private String creditName;
|
||||||
private String creditCard;
|
private String creditCard;
|
||||||
private String creditRef;
|
private String creditRef;
|
||||||
|
|||||||
@@ -4,8 +4,10 @@ import org.springframework.data.jpa.repository.JpaRepository;
|
|||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
import zw.qantra.tm.domain.models.Currency;
|
import zw.qantra.tm.domain.models.Currency;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
public interface CurrencyRepository extends JpaRepository<Currency, UUID> {
|
public interface CurrencyRepository extends JpaRepository<Currency, UUID> {
|
||||||
|
Optional<Currency> findByIsoCode(String isoCode);
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,9 @@ package zw.qantra.tm.domain.services;
|
|||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
import zw.qantra.tm.domain.models.Currency;
|
||||||
import zw.qantra.tm.domain.repositories.CurrencyRepository;
|
import zw.qantra.tm.domain.repositories.CurrencyRepository;
|
||||||
|
import zw.qantra.tm.exceptions.ApiException;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@@ -12,4 +14,9 @@ public class CurrencyService {
|
|||||||
public CurrencyRepository getCurrencyRepository() {
|
public CurrencyRepository getCurrencyRepository() {
|
||||||
return currencyRepository;
|
return currencyRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Currency findCurrencyByIsoCode(String isoCode) {
|
||||||
|
return currencyRepository.findByIsoCode(isoCode)
|
||||||
|
.orElseThrow(() -> new ApiException("Currency not found for ISO code: " + isoCode));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -26,9 +26,9 @@ public class HotRechargeBalanceService {
|
|||||||
private String hotApiUrl;
|
private String hotApiUrl;
|
||||||
|
|
||||||
@Cacheable(value = "hotrechargeBalance", key = "#token + '_' + #amount.toString()")
|
@Cacheable(value = "hotrechargeBalance", key = "#token + '_' + #amount.toString()")
|
||||||
public boolean checkBalance(String token, BigDecimal amount) {
|
public boolean checkBalance(String token, BigDecimal amount, String accId) {
|
||||||
try {
|
try {
|
||||||
String balanceUrl = hotApiUrl + "/account/balance/4";
|
String balanceUrl = hotApiUrl + "/account/balance/" + accId;
|
||||||
HttpHeaders headers = new HttpHeaders();
|
HttpHeaders headers = new HttpHeaders();
|
||||||
headers.setBearerAuth(token);
|
headers.setBearerAuth(token);
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import lombok.RequiredArgsConstructor;
|
|||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.cache.annotation.CacheEvict;
|
||||||
import org.springframework.cache.annotation.Cacheable;
|
import org.springframework.cache.annotation.Cacheable;
|
||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
import org.springframework.data.jpa.domain.Specification;
|
import org.springframework.data.jpa.domain.Specification;
|
||||||
@@ -81,7 +82,8 @@ public class ProviderService {
|
|||||||
return products;
|
return products;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Cacheable(value = "providerProducts", key = "#providerUid")
|
// todo: figure out caching for different providers
|
||||||
|
// @Cacheable(value = "providerProducts", key = "#providerUid")
|
||||||
public List<SBZProductDto> getProviderProducts(String providerUid) {
|
public List<SBZProductDto> getProviderProducts(String providerUid) {
|
||||||
try {
|
try {
|
||||||
Provider provider = providerRepository.findById(UUID.fromString(providerUid)).orElse(null);
|
Provider provider = providerRepository.findById(UUID.fromString(providerUid)).orElse(null);
|
||||||
@@ -126,6 +128,7 @@ public class ProviderService {
|
|||||||
|
|
||||||
return products;
|
return products;
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
|
log.error("Error fetching products: " + providerUid, e);
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -159,11 +162,7 @@ public class ProviderService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Object getProviderByClientId(String clientId) {
|
public Object getProviderByClientId(String clientId) {
|
||||||
Provider provider = providerRepository.findByClientId(clientId);
|
return providerRepository.findByClientId(clientId);
|
||||||
if (provider != null) {
|
|
||||||
return ((List)getProviders(Collections.singletonList(provider))).stream().findFirst().orElse(null);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object findAll(Specification<Provider> specification, Pageable pageable) {
|
public Object findAll(Specification<Provider> specification, Pageable pageable) {
|
||||||
@@ -237,4 +236,17 @@ public class ProviderService {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Evict ALL providerProducts cache entries
|
||||||
|
@CacheEvict(value = "providerProducts", allEntries = true)
|
||||||
|
public void evictProviderProductsCache() {
|
||||||
|
// empty - annotation handles the eviction
|
||||||
|
}
|
||||||
|
|
||||||
|
// Or evict a SINGLE entry by key
|
||||||
|
@CacheEvict(value = "providerProducts", key = "#providerUid")
|
||||||
|
public void evictProviderProductCache(String providerUid) {
|
||||||
|
// only evicts cache for this specific providerUid
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -3,6 +3,8 @@ package zw.qantra.tm.domain.services;
|
|||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.math.RoundingMode;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
@@ -15,6 +17,7 @@ import org.springframework.stereotype.Service;
|
|||||||
|
|
||||||
import zw.qantra.tm.domain.enums.RequestType;
|
import zw.qantra.tm.domain.enums.RequestType;
|
||||||
import zw.qantra.tm.domain.models.AdditionalData;
|
import zw.qantra.tm.domain.models.AdditionalData;
|
||||||
|
import zw.qantra.tm.domain.models.Currency;
|
||||||
import zw.qantra.tm.domain.models.Transaction;
|
import zw.qantra.tm.domain.models.Transaction;
|
||||||
import zw.qantra.tm.domain.repositories.TransactionRepository;
|
import zw.qantra.tm.domain.repositories.TransactionRepository;
|
||||||
import zw.qantra.tm.exceptions.ApiException;
|
import zw.qantra.tm.exceptions.ApiException;
|
||||||
@@ -28,6 +31,21 @@ public class TransactionService {
|
|||||||
@Getter
|
@Getter
|
||||||
private final TransactionRepository transactionRepository;
|
private final TransactionRepository transactionRepository;
|
||||||
private final AdditionalDataService additionalDataService;
|
private final AdditionalDataService additionalDataService;
|
||||||
|
private final CurrencyService currencyService;
|
||||||
|
|
||||||
|
public BigDecimal calculateCreditAmount(Transaction transaction) {
|
||||||
|
if (transaction.getCreditCurrency() == null) {
|
||||||
|
return BigDecimal.ZERO;
|
||||||
|
}
|
||||||
|
|
||||||
|
Currency currency = currencyService.findCurrencyByIsoCode(transaction.getCreditCurrency().name());
|
||||||
|
|
||||||
|
BigDecimal bankRate = new BigDecimal(currency.getBankRate());
|
||||||
|
BigDecimal creditAmount = transaction.getAmount().multiply(bankRate).setScale(4, RoundingMode.HALF_EVEN);
|
||||||
|
|
||||||
|
transaction.setCreditAmount(creditAmount);
|
||||||
|
return creditAmount;
|
||||||
|
}
|
||||||
|
|
||||||
public void reassignTransactions(String oldUid, String newUid){
|
public void reassignTransactions(String oldUid, String newUid){
|
||||||
List<Transaction> transactions = transactionRepository.findAllByUserId(oldUid);
|
List<Transaction> transactions = transactionRepository.findAllByUserId(oldUid);
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.http.HttpHeaders;
|
import org.springframework.http.HttpHeaders;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
import zw.qantra.tm.domain.enums.CurrencyType;
|
||||||
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;
|
||||||
@@ -41,7 +42,13 @@ public class NetoneConfirmationHotProcessor implements TransactionProcessorInter
|
|||||||
// do this if we not simulating success, if simulating success, we skip balance check and just return success
|
// do this if we not simulating success, if simulating success, we skip balance check and just return success
|
||||||
if(!settingService.getBooleanSetting("SIMULATE_HOT_SUCCESS")){
|
if(!settingService.getBooleanSetting("SIMULATE_HOT_SUCCESS")){
|
||||||
String token = hotRechargeTokenService.getToken();
|
String token = hotRechargeTokenService.getToken();
|
||||||
if (!hotRechargeBalanceService.checkBalance(token, transaction.getAmount())) {
|
|
||||||
|
// Account Type Id Account Name
|
||||||
|
// 1 ZWG
|
||||||
|
// 2 ZWG Non-VAT (Utilitiies)
|
||||||
|
// 3 USD
|
||||||
|
// 4 USD Non-VAT (Utilitiies)
|
||||||
|
if (!hotRechargeBalanceService.checkBalance(token, transaction.getAmount(), "3")) {
|
||||||
transaction.setStatus(Status.FAILED);
|
transaction.setStatus(Status.FAILED);
|
||||||
transaction.setResponseCode("92");
|
transaction.setResponseCode("92");
|
||||||
transaction.setErrorMessage("There's a technical issue on our end. Please try again later.");
|
transaction.setErrorMessage("There's a technical issue on our end. Please try again later.");
|
||||||
@@ -49,6 +56,8 @@ public class NetoneConfirmationHotProcessor implements TransactionProcessorInter
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
transaction.setCreditCurrency(CurrencyType.USD);
|
||||||
|
transaction.setCreditAmount(transaction.getAmount());
|
||||||
transaction.setCreditAccount(Utils.formatPhoneNumber(transaction.getCreditAccount(), "ZW"));
|
transaction.setCreditAccount(Utils.formatPhoneNumber(transaction.getCreditAccount(), "ZW"));
|
||||||
transaction.setStatus(Status.SUCCESS);
|
transaction.setStatus(Status.SUCCESS);
|
||||||
transaction.setResponseCode("00");
|
transaction.setResponseCode("00");
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import org.springframework.http.HttpHeaders;
|
|||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
import zw.qantra.tm.domain.enums.CurrencyType;
|
||||||
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;
|
||||||
@@ -33,6 +34,7 @@ public class ZesaConfirmationHotProcessor implements TransactionProcessorInterfa
|
|||||||
private final HotRechargeTokenService hotRechargeTokenService;
|
private final HotRechargeTokenService hotRechargeTokenService;
|
||||||
private final HotRechargeBalanceService hotRechargeBalanceService;
|
private final HotRechargeBalanceService hotRechargeBalanceService;
|
||||||
private final SettingService settingService;
|
private final SettingService settingService;
|
||||||
|
private final TransactionService transactionService;
|
||||||
|
|
||||||
@Value("${hot.api.url:https://ssl.hot.co.zw/api/v3}")
|
@Value("${hot.api.url:https://ssl.hot.co.zw/api/v3}")
|
||||||
private String hotApiUrl;
|
private String hotApiUrl;
|
||||||
@@ -57,13 +59,6 @@ public class ZesaConfirmationHotProcessor implements TransactionProcessorInterfa
|
|||||||
return transaction;
|
return transaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hotRechargeBalanceService.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;
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, Object> customerData = checkCustomer(token, transaction);
|
Map<String, Object> customerData = checkCustomer(token, transaction);
|
||||||
if (customerData == null) {
|
if (customerData == null) {
|
||||||
transaction.setStatus(Status.FAILED);
|
transaction.setStatus(Status.FAILED);
|
||||||
@@ -72,6 +67,30 @@ public class ZesaConfirmationHotProcessor implements TransactionProcessorInterfa
|
|||||||
return transaction;
|
return transaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Account Type Id Account Name
|
||||||
|
// 1 ZWG
|
||||||
|
// 2 ZWG Non-VAT (Utilitiies)
|
||||||
|
// 3 USD
|
||||||
|
// 4 USD Non-VAT (Utilitiies)
|
||||||
|
String accId = "4";
|
||||||
|
String currency = extractCurrency(customerData);
|
||||||
|
logger.info("Customer currency: {}", currency.isEmpty() ? "UNKNOWN" : currency);
|
||||||
|
if(currency.equals("ZWG")){
|
||||||
|
accId = "2";
|
||||||
|
transaction.setCreditCurrency(CurrencyType.ZWG);
|
||||||
|
}else {
|
||||||
|
transaction.setCreditCurrency(CurrencyType.USD);
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction.setCreditAmount(transactionService.calculateCreditAmount(transaction));
|
||||||
|
|
||||||
|
if (!hotRechargeBalanceService.checkBalance(token, transaction.getAmount(), accId)) {
|
||||||
|
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");
|
||||||
|
|
||||||
@@ -110,15 +129,27 @@ public class ZesaConfirmationHotProcessor implements TransactionProcessorInterfa
|
|||||||
return additionalDataList;
|
return additionalDataList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String extractCurrency(Map<String, Object> customerData) {
|
||||||
|
if (customerData == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
Object detailsObj = customerData.get("details");
|
||||||
|
if (!(detailsObj instanceof Map<?, ?> details)) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
Object currency = details.get("Currency");
|
||||||
|
if (currency == null) {
|
||||||
|
currency = details.get("currency");
|
||||||
|
}
|
||||||
|
|
||||||
|
return currency == null ? "" : currency.toString().trim();
|
||||||
|
}
|
||||||
|
|
||||||
private Map<String, Object> checkCustomer(String token, Transaction transaction) {
|
private Map<String, Object> checkCustomer(String token, Transaction transaction) {
|
||||||
try {
|
try {
|
||||||
Provider provider = providerService.getProviderRepository()
|
String hotProductId = providerService.getProviderId(transaction.getBillClientId());
|
||||||
.findByClientId(transaction.getBillClientId());
|
|
||||||
if(provider == null){
|
|
||||||
throw new ApiException("Provider not found for billClientId: " + transaction.getBillClientId());
|
|
||||||
}
|
|
||||||
Map meta = Utils.fromJson(provider.getMeta(), Map.class);
|
|
||||||
String hotProductId = (String) meta.get("hotProductId");
|
|
||||||
|
|
||||||
String customerUrl = hotApiUrl + "/query/customer/" + hotProductId + "/" + transaction.getCreditAccount();
|
String customerUrl = hotApiUrl + "/query/customer/" + hotProductId + "/" + transaction.getCreditAccount();
|
||||||
HttpHeaders headers = new HttpHeaders();
|
HttpHeaders headers = new HttpHeaders();
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import org.springframework.http.ResponseEntity;
|
|||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.web.client.HttpClientErrorException;
|
import org.springframework.web.client.HttpClientErrorException;
|
||||||
import org.springframework.web.client.ResourceAccessException;
|
import org.springframework.web.client.ResourceAccessException;
|
||||||
|
import zw.qantra.tm.domain.enums.CurrencyType;
|
||||||
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;
|
||||||
@@ -36,6 +37,8 @@ public class HotRechargeIntegrationProcessor implements TransactionProcessorInte
|
|||||||
@Value("${hot.api.url:https://ssl.hot.co.zw/api/v3}")
|
@Value("${hot.api.url:https://ssl.hot.co.zw/api/v3}")
|
||||||
private String hotApiUrl;
|
private String hotApiUrl;
|
||||||
|
|
||||||
|
private static final String ZESA_ZWG_CLIENT = "zesa_prepaid_zwg";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
public Object process(Transaction transaction) throws Exception {
|
public Object process(Transaction transaction) throws Exception {
|
||||||
@@ -70,12 +73,8 @@ public class HotRechargeIntegrationProcessor implements TransactionProcessorInte
|
|||||||
return transaction;
|
return transaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hotRechargeBalanceService.checkBalance(token, transaction.getAmount())) {
|
// todo: a balance check might be necessary here. Removing
|
||||||
transaction.setStatus(Status.FAILED);
|
// for now so not to spam hotrecharge with requests
|
||||||
transaction.setResponseCode("92");
|
|
||||||
transaction.setErrorMessage("There's a technical issue on our end. Please try again later.");
|
|
||||||
return transaction;
|
|
||||||
}
|
|
||||||
|
|
||||||
LinkedHashMap<String, Object> rechargeResponse = performRecharge(token, transaction);
|
LinkedHashMap<String, Object> rechargeResponse = performRecharge(token, transaction);
|
||||||
if (rechargeResponse == null) {
|
if (rechargeResponse == null) {
|
||||||
@@ -132,19 +131,30 @@ public class HotRechargeIntegrationProcessor implements TransactionProcessorInte
|
|||||||
headers.setBearerAuth(token);
|
headers.setBearerAuth(token);
|
||||||
headers.setContentType(org.springframework.http.MediaType.APPLICATION_JSON);
|
headers.setContentType(org.springframework.http.MediaType.APPLICATION_JSON);
|
||||||
|
|
||||||
|
String providerId = providerService.getProviderId(transaction.getBillClientId());
|
||||||
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", providerService.getProviderId(transaction.getBillClientId()));
|
request.put("ProductId", providerId);
|
||||||
request.put("Target", transaction.getCreditAccount());
|
request.put("Target", transaction.getCreditAccount());
|
||||||
request.put("Amount", transaction.getAmount());
|
request.put("Amount", transaction.getCreditAmount());
|
||||||
|
|
||||||
// for zesa skip this
|
// 41 = zesa
|
||||||
if(!providerService.getProviderId(transaction.getBillClientId()).equals("41")) {
|
if(providerId.equals("41")) {
|
||||||
|
request.put("CustomerSMS", "A ZETDC token was purchased for " +
|
||||||
|
"%ACOUNTNAME% (%METERNUMBER%) that resulted in %KWH% units.");
|
||||||
|
}
|
||||||
|
else {
|
||||||
request.put("CustomerSMS", "%COMPANYNAME% topped up your account with $%AMOUNT%.");
|
request.put("CustomerSMS", "%COMPANYNAME% topped up your account with $%AMOUNT%.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// do this for zesa only
|
// do this for zesa only
|
||||||
if(providerService.getProviderId(transaction.getBillClientId()).equals("41")) {
|
if(providerId.equals("41")) {
|
||||||
|
// update product to zwg product id
|
||||||
|
if(transaction.getCreditCurrency().equals(CurrencyType.ZWG)) {
|
||||||
|
String zwgProviderId = providerService.getProviderId(ZESA_ZWG_CLIENT);
|
||||||
|
request.put("ProductId", zwgProviderId);
|
||||||
|
}
|
||||||
|
|
||||||
List<Map<String, String>> rechargeOptions = new ArrayList<>();
|
List<Map<String, String>> rechargeOptions = new ArrayList<>();
|
||||||
if (transaction.getDebitPhone() != null) {
|
if (transaction.getDebitPhone() != null) {
|
||||||
Map<String, String> option = new LinkedHashMap<>();
|
Map<String, String> option = new LinkedHashMap<>();
|
||||||
@@ -159,9 +169,8 @@ public class HotRechargeIntegrationProcessor implements TransactionProcessorInte
|
|||||||
}
|
}
|
||||||
|
|
||||||
// do this for Econet bundles
|
// do this for Econet bundles
|
||||||
if(providerService.getProviderId(transaction.getBillClientId()).equals("111")) {
|
if(providerId.equals("111")) {
|
||||||
String productCode = providerService.getExternalId(
|
String productCode = providerService.getExternalId(providerId, transaction.getProductUid());
|
||||||
providerService.getProviderId(transaction.getBillClientId()), transaction.getProductUid());
|
|
||||||
List<Map<String, String>> rechargeOptions = new ArrayList<>();
|
List<Map<String, String>> rechargeOptions = new ArrayList<>();
|
||||||
Map<String, String> option = new LinkedHashMap<>();
|
Map<String, String> option = new LinkedHashMap<>();
|
||||||
option.put("Name", "ProductCode");
|
option.put("Name", "ProductCode");
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ public class ChargeSeeder implements Seeder {
|
|||||||
.chargeLabel(ChargeLabelEnum.GATEWAY_FEE)
|
.chargeLabel(ChargeLabelEnum.GATEWAY_FEE)
|
||||||
.currency(CurrencyType.USD)
|
.currency(CurrencyType.USD)
|
||||||
.percentageRate(new BigDecimal("1"))
|
.percentageRate(new BigDecimal("1"))
|
||||||
|
.min(new BigDecimal("0.01"))
|
||||||
.includes(new HashSet<>(Arrays.asList(ecocashCondition)))
|
.includes(new HashSet<>(Arrays.asList(ecocashCondition)))
|
||||||
.build());
|
.build());
|
||||||
log.info("Charge 'Ecocash Gateway fee (1%)' seeded.");
|
log.info("Charge 'Ecocash Gateway fee (1%)' seeded.");
|
||||||
|
|||||||
53
src/main/java/zw/qantra/tm/seeders/CurrencySeeder.java
Normal file
53
src/main/java/zw/qantra/tm/seeders/CurrencySeeder.java
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
package zw.qantra.tm.seeders;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import zw.qantra.tm.domain.models.Currency;
|
||||||
|
import zw.qantra.tm.domain.repositories.CurrencyRepository;
|
||||||
|
import zw.qantra.tm.utils.Utils;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Seeds Currency entities.
|
||||||
|
* Priority: Should run early before providers (currencies are referenced by providers).
|
||||||
|
* Only seeds entities that don't already exist (checked by isoCode).
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class CurrencySeeder implements Seeder {
|
||||||
|
|
||||||
|
private final CurrencyRepository currencyRepository;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void seed() {
|
||||||
|
List<Currency> currencies = Arrays.asList(
|
||||||
|
Currency.builder()
|
||||||
|
.isoCode("USD")
|
||||||
|
.name("USD")
|
||||||
|
.displayName("USD")
|
||||||
|
.bankRate("1")
|
||||||
|
.forecastRate("1")
|
||||||
|
.build(),
|
||||||
|
Currency.builder()
|
||||||
|
.isoCode("ZWG")
|
||||||
|
.name("ZWG")
|
||||||
|
.displayName("ZWG")
|
||||||
|
.bankRate("26.5")
|
||||||
|
.forecastRate("31")
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
|
||||||
|
for (Currency currency : currencies) {
|
||||||
|
if (currencyRepository.findByIsoCode(currency.getIsoCode()).isPresent()) {
|
||||||
|
log.info("Currency '{}' already exists, skipping.", currency.getIsoCode());
|
||||||
|
} else {
|
||||||
|
currencyRepository.save(currency);
|
||||||
|
log.info("Currency '{}' seeded.", currency.getIsoCode());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,19 +10,21 @@ import org.springframework.context.annotation.Configuration;
|
|||||||
* Orchestrates all seeders in the correct priority order matching
|
* Orchestrates all seeders in the correct priority order matching
|
||||||
* ConfigController.seedLiveConfig() execution sequence:
|
* ConfigController.seedLiveConfig() execution sequence:
|
||||||
* <p>
|
* <p>
|
||||||
* 1. PaymentProcessorSeeder
|
* 1. CurrencySeeder
|
||||||
* 2. IntegrationProcessorSeeder
|
* 2. PaymentProcessorSeeder
|
||||||
* 3. CategorySeeder
|
* 3. IntegrationProcessorSeeder
|
||||||
* 4. ProviderSeeder
|
* 4. CategorySeeder
|
||||||
* 5. ChargeConditionSeeder
|
* 5. ProviderSeeder
|
||||||
* 6. ChargeSeeder
|
* 6. ChargeConditionSeeder
|
||||||
* 7. SettingSeeder
|
* 7. ChargeSeeder
|
||||||
|
* 8. SettingSeeder
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Configuration
|
@Configuration
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class LiveConfigSeeder {
|
public class LiveConfigSeeder {
|
||||||
|
|
||||||
|
private final CurrencySeeder currencySeeder;
|
||||||
private final PaymentProcessorSeeder paymentProcessorSeeder;
|
private final PaymentProcessorSeeder paymentProcessorSeeder;
|
||||||
private final IntegrationProcessorSeeder integrationProcessorSeeder;
|
private final IntegrationProcessorSeeder integrationProcessorSeeder;
|
||||||
private final CategorySeeder categorySeeder;
|
private final CategorySeeder categorySeeder;
|
||||||
@@ -39,6 +41,9 @@ public class LiveConfigSeeder {
|
|||||||
public void seedAll() {
|
public void seedAll() {
|
||||||
log.info("Starting live configuration seeding...");
|
log.info("Starting live configuration seeding...");
|
||||||
|
|
||||||
|
log.info("Seeding Currencies...");
|
||||||
|
currencySeeder.seed();
|
||||||
|
|
||||||
log.info("Seeding Payment Processors...");
|
log.info("Seeding Payment Processors...");
|
||||||
paymentProcessorSeeder.seed();
|
paymentProcessorSeeder.seed();
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package zw.qantra.tm.seeders;
|
|||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
import zw.qantra.tm.domain.enums.CurrencyType;
|
||||||
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.utils.Utils;
|
import zw.qantra.tm.utils.Utils;
|
||||||
@@ -29,6 +30,7 @@ public class ProviderSeeder implements Seeder {
|
|||||||
.name("Econet Airtime")
|
.name("Econet Airtime")
|
||||||
.description("Econet Airtime")
|
.description("Econet Airtime")
|
||||||
.clientId("econet_airtime")
|
.clientId("econet_airtime")
|
||||||
|
.currency(CurrencyType.USD)
|
||||||
.label("ECONET")
|
.label("ECONET")
|
||||||
.category("AIRTIME")
|
.category("AIRTIME")
|
||||||
.image("econet.png")
|
.image("econet.png")
|
||||||
@@ -47,6 +49,7 @@ public class ProviderSeeder implements Seeder {
|
|||||||
.name("NetOne Airtime")
|
.name("NetOne Airtime")
|
||||||
.description("NetOne Airtime")
|
.description("NetOne Airtime")
|
||||||
.clientId("netone_usd_airtime")
|
.clientId("netone_usd_airtime")
|
||||||
|
.currency(CurrencyType.USD)
|
||||||
.label("NETONE")
|
.label("NETONE")
|
||||||
.category("AIRTIME")
|
.category("AIRTIME")
|
||||||
.image("netone.png")
|
.image("netone.png")
|
||||||
@@ -61,10 +64,30 @@ public class ProviderSeeder implements Seeder {
|
|||||||
.priority(3)
|
.priority(3)
|
||||||
.meta("{\"hotProductId\": \"35\"}")
|
.meta("{\"hotProductId\": \"35\"}")
|
||||||
.build(),
|
.build(),
|
||||||
|
Provider.builder()
|
||||||
|
.name("Zesa Prepaid")
|
||||||
|
.description("Prepaid Zesa")
|
||||||
|
.clientId("zesa_prepaid_zwg")
|
||||||
|
.currency(CurrencyType.ZWG)
|
||||||
|
.label("ZESA")
|
||||||
|
.category("UTILITIES")
|
||||||
|
.image("zesa.png")
|
||||||
|
.externalId("5468b1ed-4e11-4348-9bc1-f422890fc2c2")
|
||||||
|
.integrationProcessorLabel("HOT")
|
||||||
|
.accountFieldName("Meter Number")
|
||||||
|
.percentageCommission(1.5)
|
||||||
|
.requiresAccount(true)
|
||||||
|
.requiresAmount(true)
|
||||||
|
.requiresPhone(true)
|
||||||
|
.priority(1)
|
||||||
|
.meta("{\"hotProductId\": \"24\"}")
|
||||||
|
.uid(Utils.getUid())
|
||||||
|
.build(),
|
||||||
Provider.builder()
|
Provider.builder()
|
||||||
.name("Zesa Prepaid")
|
.name("Zesa Prepaid")
|
||||||
.description("Prepaid Zesa")
|
.description("Prepaid Zesa")
|
||||||
.clientId("zesa_prepaid_usd")
|
.clientId("zesa_prepaid_usd")
|
||||||
|
.currency(CurrencyType.USD)
|
||||||
.label("ZESA")
|
.label("ZESA")
|
||||||
.category("UTILITIES")
|
.category("UTILITIES")
|
||||||
.image("zesa.png")
|
.image("zesa.png")
|
||||||
@@ -83,6 +106,7 @@ public class ProviderSeeder implements Seeder {
|
|||||||
.name("Econet Bundles")
|
.name("Econet Bundles")
|
||||||
.description("Econet Bundles")
|
.description("Econet Bundles")
|
||||||
.clientId("econet_bundles_usd")
|
.clientId("econet_bundles_usd")
|
||||||
|
.currency(CurrencyType.USD)
|
||||||
.label("ECONET_BUNDLES")
|
.label("ECONET_BUNDLES")
|
||||||
.category("AIRTIME")
|
.category("AIRTIME")
|
||||||
.image("econet.png")
|
.image("econet.png")
|
||||||
|
|||||||
23
src/main/resources/liquibase-diff-changeLog.xml
Normal file
23
src/main/resources/liquibase-diff-changeLog.xml
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
|
||||||
|
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext" xmlns:pro="http://www.liquibase.org/xml/ns/pro" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/pro http://www.liquibase.org/xml/ns/pro/liquibase-pro-latest.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
|
||||||
|
<changeSet author="khoza (generated)" id="1780329348054-3">
|
||||||
|
<addColumn tableName="currency">
|
||||||
|
<column name="bank_rate" type="varchar(255)"/>
|
||||||
|
</addColumn>
|
||||||
|
</changeSet>
|
||||||
|
<changeSet author="khoza (generated)" id="1780329348054-4">
|
||||||
|
<addColumn tableName="transaction">
|
||||||
|
<column name="credit_amount" type="numeric(38, 2)"/>
|
||||||
|
</addColumn>
|
||||||
|
</changeSet>
|
||||||
|
<changeSet author="khoza (generated)" id="1780329348054-5">
|
||||||
|
<addColumn tableName="provider">
|
||||||
|
<column name="currency" type="varchar(255)"/>
|
||||||
|
</addColumn>
|
||||||
|
</changeSet>
|
||||||
|
<changeSet author="khoza (generated)" id="1780329348054-6">
|
||||||
|
<addColumn tableName="currency">
|
||||||
|
<column name="forecast_rate" type="varchar(255)"/>
|
||||||
|
</addColumn>
|
||||||
|
</changeSet>
|
||||||
|
</databaseChangeLog>
|
||||||
Reference in New Issue
Block a user