implement zesa currency swap feature

This commit is contained in:
2026-06-01 20:34:48 +02:00
parent 92276c07d8
commit 461dc7e7e4
17 changed files with 257 additions and 48 deletions

View File

@@ -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");
}
} }

View File

@@ -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;
} }

View File

@@ -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;

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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));
}
}

View File

@@ -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);

View File

@@ -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
}
} }

View File

@@ -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);

View File

@@ -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");

View File

@@ -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();

View File

@@ -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");

View File

@@ -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.");

View 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());
}
}
}
}

View File

@@ -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();
@@ -62,4 +67,4 @@ public class LiveConfigSeeder {
log.info("Live configuration seeding complete."); log.info("Live configuration seeding complete.");
} }
} }

View File

@@ -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")

View 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>