Compare commits
10 Commits
d53a427a5a
...
be0f0ae49c
| Author | SHA1 | Date | |
|---|---|---|---|
| be0f0ae49c | |||
| ba796527ae | |||
| 1de89364be | |||
| fbc3967215 | |||
| 94a30633ca | |||
| 5d3cfde7f6 | |||
|
|
b9efd83762 | ||
| 220c19123f | |||
| a4d210393e | |||
| 6d48472425 |
14
compose.yml
Normal file
14
compose.yml
Normal file
@@ -0,0 +1,14 @@
|
||||
services:
|
||||
velocity-pay-backend:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
image: velocity-pay-backend:0.0.2-RELEASE
|
||||
restart: always
|
||||
container_name: velocity-pay-backend
|
||||
ports:
|
||||
- 8058:8058
|
||||
- 6950:6950
|
||||
network_mode: host
|
||||
environment:
|
||||
- TZ=Africa/Harare
|
||||
18
keys/velocityafrica.csr
Normal file
18
keys/velocityafrica.csr
Normal file
@@ -0,0 +1,18 @@
|
||||
-----BEGIN CERTIFICATE REQUEST-----
|
||||
MIIC7TCCAdUCAQAwgacxCzAJBgNVBAYTAlpXMQ8wDQYDVQQIDAZIYXJhcmUxDzAN
|
||||
BgNVBAcMBkhhcmFyZTEYMBYGA1UECgwPV2FsbGV0IEV4Y2hhbmdlMRkwFwYDVQQL
|
||||
DBBEaWdpdGFsIFBheW1lbnRzMR0wGwYDVQQDDBQqLnZlbG9jaXR5YWZyaWNhLm5l
|
||||
dDEiMCAGCSqGSIb3DQEJARYTdnVzYWtob3phQGdtYWlsLmNvbTCCASIwDQYJKoZI
|
||||
hvcNAQEBBQADggEPADCCAQoCggEBALl1Pvoh+NUK9A8KlwgLkAIiDs3l7Kbs2Zt5
|
||||
FZvyOgpjD6Cl/IJy7DC8oJ7Ty+eJkoVKPjyX7urnf1wQgstQMgryA7YMVnQPcYJe
|
||||
Zm3px+lPhbkEEeVc5veNktCqL590LLMrL6IZbGR1Xh8Phd7niLn1+o+ZebgWH6XD
|
||||
nIb8JFrBTy5ZigU/dGBpII/NhciIVQDF4TPy5DwfNIBAAKZdp/CiYpTFN0iV7/UA
|
||||
0g43zfS3CbuEK+Mj6BkkSquWHFd76W58BAhDa2S50sd6VF+jq4EFCdWmJXmxVmPI
|
||||
rxUMFi9DbI7Cd9DB56pkfnSIYx0t60SjXKy3gkNdB6zcNcFZ860CAwEAAaAAMA0G
|
||||
CSqGSIb3DQEBCwUAA4IBAQBK4YsEvIQolqsVeqUSxUforA1qo/iKhK+yetlQ8YdN
|
||||
hR/v+4fIIoaIZLjdSTOLDTDIJ3AUBGlKvF7mf64X+JQpyQVqUIx58S92Zr645lfU
|
||||
LADsliDXCNQp0nzWDavAC9HvbA4ibl+Z1X+z/uojf3fPZEmAli2howpWYiLIsQSj
|
||||
quS5L3kkv8XhYBnE0h3L4GL3kp7bFAMRzJq3392pU3xAGiJhBS3oKyBel8mfWrTE
|
||||
MQDsFgK6bjy2JWRgmb5lqKSV1KlqbG5zmBkSgFcEkrB9tm7DtQbo5oPSDKO5otWf
|
||||
Wn40iNGAnSRhsc0jjXR1s1oqGQh7SrEhOvm9JI76LfGh
|
||||
-----END CERTIFICATE REQUEST-----
|
||||
28
keys/velocityafrica.key
Normal file
28
keys/velocityafrica.key
Normal file
@@ -0,0 +1,28 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC5dT76IfjVCvQP
|
||||
CpcIC5ACIg7N5eym7NmbeRWb8joKYw+gpfyCcuwwvKCe08vniZKFSj48l+7q539c
|
||||
EILLUDIK8gO2DFZ0D3GCXmZt6cfpT4W5BBHlXOb3jZLQqi+fdCyzKy+iGWxkdV4f
|
||||
D4Xe54i59fqPmXm4Fh+lw5yG/CRawU8uWYoFP3RgaSCPzYXIiFUAxeEz8uQ8HzSA
|
||||
QACmXafwomKUxTdIle/1ANION830twm7hCvjI+gZJEqrlhxXe+lufAQIQ2tkudLH
|
||||
elRfo6uBBQnVpiV5sVZjyK8VDBYvQ2yOwnfQweeqZH50iGMdLetEo1yst4JDXQes
|
||||
3DXBWfOtAgMBAAECggEAE8cSIUCwNaAGaPg7JQt+nwucwo5rj12g0zi1ejz5vZPU
|
||||
3c/1vr+cupHTGz0A+L8uFfLV8kZNn1VyupG0MRRrUcl/ZdGpApWHCF0i1UPiItpt
|
||||
K07ozan7o5jAeGo9y+ZqweywFrxZV9lAYVWZdC0C4rn9r4Sxe1U4qMfUkHyhL+BQ
|
||||
o2Ot2qxfHbbV/mNZcSlQHLByRu1tuVT8TxS5ZOqdu0ar8QnyoHr9tk6+x6WgCONr
|
||||
K/Hvjej9BL6KrB2/vK52A8PclPJAqb8w5W8c0jQHhS9dAJV5t1KHXAejBz396vn3
|
||||
PowurUTyu7RrVVK49j9/q0utPl2tosTOhPNobzY6wQKBgQD8iCyuCLMawh84i8qq
|
||||
SFqyNI0bc2nWuF74QrCc/3NScUdOM8oMzUd5vskEyAa3yl1Xf0Ffg/nMO70E/3Ra
|
||||
rvLoFnfS/WXHrY8TkSpNI6TY/gtf/F8LOW2HZemWSBZ4Q/iatfTX+gL/3cZFE9F6
|
||||
dAbA8wcmGNR3GBKbDorxKhSv3QKBgQC8AUKI5ijfmqCnilqNrpiegxcAdGT8au1W
|
||||
uIW9CjdXS9c1D/+cPFGRc+vLIqWUE7vStkSl6rzY4Va8uqh7t8P9MksE8ru8QcbD
|
||||
Sq2RwO1vBPnNmb7eS/ooyRbYFOXlrSx7mntPkXUZE/c7Lpqe7XxeC+R/wsZc8fyd
|
||||
H8RXlCH+EQKBgQCJNhBYy19NpbZDCimnFkM5WrZk+PPD+E+Zwtqlo2Lso6Ijyp35
|
||||
ELzKWLBuiaSkzi6jcQB7PoFGyBqvEYIOF8rh8ftPR+G6egIolpKOcVSLlpVlpQGb
|
||||
Xqu1itk9j5Ot6vi0TzNm+nAQyFaHh/ok99AHnMUAnTMsNoc9esxhtwSgfQKBgAoF
|
||||
MyuiwMjyL9/eIudsR9wi9oXirkM5276EhTRHKB3x/91nJeOI33wWJpokFZkn2Bnp
|
||||
9wy0zy3sDLhwZmx5peuH07HG0jS9eL+N6mRbWQxSwpGvwRmOCR5FYX9Q1dQA/97y
|
||||
o+HgydH1W3b/C+SumbNVhO8+TV5E04b4W3mjfpBRAoGBAJMnFV0ivN+TN4/czmAx
|
||||
4JulU6mh0qCfax6tMbx5RiXmlHitAXQvPooFpzItLWlrRnXj6aHHqXOs5uH0kspj
|
||||
4pTnDGnQO9VxhRziEjPIAVS6vFntxOs0zshaROreLcaVoDSkmz6YkFTeiSbtzumo
|
||||
rGOjofBa6j9pYfi9YCi8nSnK
|
||||
-----END PRIVATE KEY-----
|
||||
@@ -0,0 +1,19 @@
|
||||
package zw.qantra.tm.domain.dtos.sms;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class SmsPopCampaignRequest {
|
||||
private String name;
|
||||
private String message;
|
||||
private String sender_id;
|
||||
@Builder.Default
|
||||
private String contact_import_method = "manual";
|
||||
private String manual_contacts;
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package zw.qantra.tm.domain.models;
|
||||
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import jakarta.persistence.*;
|
||||
import lombok.*;
|
||||
import zw.qantra.tm.domain.enums.Status;
|
||||
@@ -16,6 +17,7 @@ import zw.qantra.tm.domain.enums.Status;
|
||||
@NoArgsConstructor
|
||||
public class Otp extends BaseEntity{
|
||||
private String username;
|
||||
@JsonIgnore
|
||||
private String otp;
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "otp_status")
|
||||
|
||||
@@ -72,6 +72,7 @@ public class Transaction extends BaseEntity {
|
||||
@Enumerated(EnumType.STRING)
|
||||
private CurrencyType creditCurrency;
|
||||
private BigDecimal creditAmount;
|
||||
private BigDecimal creditTotalAmount;
|
||||
private String creditName;
|
||||
private String creditCard;
|
||||
private String creditRef;
|
||||
|
||||
@@ -22,5 +22,5 @@ public interface TransactionRepository extends JpaRepository<Transaction, UUID>,
|
||||
List<Transaction> findAllByWorkspaceIdIsNullAndUserIdIsNotNull();
|
||||
List<Transaction> findAllByConfirmationStatusAndPaymentStatusAndIntegrationStatus(
|
||||
Status s1, Status s2, Status s3);
|
||||
List<Transaction> findAllByPaymentStatusAndPollingStatus(Status s1, Status s2);
|
||||
List<Transaction> findAllByPollingStatus(Status s2);
|
||||
}
|
||||
@@ -35,7 +35,7 @@ public class JobService {
|
||||
|
||||
public void checkOrphanedTransactions() {
|
||||
List<Transaction> transactions = transactionService.getTransactionRepository()
|
||||
.findAllByPaymentStatusAndPollingStatus(Status.SUCCESS, Status.PENDING);
|
||||
.findAllByPollingStatus(Status.PENDING);
|
||||
if (!transactions.isEmpty())
|
||||
log.info("Transaction count {}", transactions.size());
|
||||
|
||||
@@ -47,8 +47,13 @@ public class JobService {
|
||||
int maxRetries = settingService.getIntegerSetting("MAX_RETRIES");
|
||||
int retries = transaction.getRetries() == null ? 0 : transaction.getRetries();
|
||||
|
||||
if (retries >= maxRetries)
|
||||
break;
|
||||
if (retries >= maxRetries) {
|
||||
transaction.setPaymentStatus(Status.FAILED);
|
||||
transaction.setPollingStatus(Status.FAILED);
|
||||
transaction.setErrorMessage("Maximum retries reached");
|
||||
transactionService.save(transaction);
|
||||
continue;
|
||||
}
|
||||
|
||||
transaction.setRetries(retries + 1);
|
||||
// polling logic
|
||||
@@ -106,8 +111,8 @@ public class JobService {
|
||||
|
||||
success++;
|
||||
}else {
|
||||
transaction.setPollingStatus(Status.FAILED);
|
||||
transaction.setIntegrationStatus(Status.FAILED);
|
||||
transaction.setPollingStatus(Status.PENDING);
|
||||
transaction.setIntegrationStatus(Status.PENDING);
|
||||
transactionService.save(transaction);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,16 +12,14 @@ import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import jakarta.mail.internet.MimeMessage;
|
||||
import zw.qantra.tm.domain.dtos.sms.SmsApiResponse;
|
||||
import zw.qantra.tm.domain.dtos.sms.SmsBatchRequest;
|
||||
import zw.qantra.tm.domain.dtos.sms.SmsMessage;
|
||||
import zw.qantra.tm.domain.dtos.sms.SmsPopCampaignRequest;
|
||||
import zw.qantra.tm.rest.RestService;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collections;
|
||||
import java.util.UUID;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
@@ -32,13 +30,18 @@ public class NotificationService {
|
||||
private final RestService restService;
|
||||
private final JavaMailSender emailSender;
|
||||
|
||||
@Value("${zss.sms.api.url}")
|
||||
private String smsApiUrl;
|
||||
@Value("${zss.sms.api.auth}")
|
||||
private String smsApiAuth;
|
||||
@Value("${sms-pop.url}")
|
||||
private String smsPopUrl;
|
||||
@Value("${sms-pop.apikey}")
|
||||
private String smsPopApiKey;
|
||||
@Value("${sms.debug}")
|
||||
private Boolean smsDebug;
|
||||
|
||||
@Value("${wa.api.url}")
|
||||
private String waApiUrl;
|
||||
@Value("${wa.api.key}")
|
||||
private String waApiKey;
|
||||
|
||||
@Async
|
||||
public void sendEmail(String to, String subject, String text) {
|
||||
sendEmailWithHtml(to, subject, text, null);
|
||||
@@ -101,6 +104,24 @@ public class NotificationService {
|
||||
}
|
||||
}
|
||||
|
||||
@Async
|
||||
public void sendWA(String chatId, String text) {
|
||||
try {
|
||||
Map<String, String> payload = new HashMap<>();
|
||||
payload.put("chatId", String.format("%s@c.us", chatId));
|
||||
payload.put("text", text);
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.set("X-API-Key", waApiKey);
|
||||
|
||||
restService.postWithExternalAuth(waApiUrl, payload, headers, Object.class);
|
||||
|
||||
logger.info("WA message sent to {}: {}", chatId, text);
|
||||
} catch (Exception e) {
|
||||
logger.error("Failed to send WA message to {}: {}", chatId, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Async
|
||||
public void sendSms(String to, String message) {
|
||||
if(smsDebug) {
|
||||
@@ -108,28 +129,25 @@ public class NotificationService {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
SmsMessage smsMessage = SmsMessage.builder()
|
||||
.reference(UUID.randomUUID().toString())
|
||||
.message(message)
|
||||
.msisdn(to)
|
||||
.build();
|
||||
String phoneNumber = to;
|
||||
if (phoneNumber.startsWith("+")) {
|
||||
phoneNumber = phoneNumber.substring(1);
|
||||
}
|
||||
|
||||
SmsBatchRequest batchRequest = SmsBatchRequest.builder()
|
||||
.batchReference(UUID.randomUUID().toString())
|
||||
.smsList(Collections.singletonList(smsMessage))
|
||||
SmsPopCampaignRequest campaignRequest = SmsPopCampaignRequest.builder()
|
||||
.name("Velocity Payments")
|
||||
.message(message)
|
||||
.sender_id("Velocity")
|
||||
.contact_import_method("manual")
|
||||
.manual_contacts(phoneNumber)
|
||||
.build();
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.set("Authorization", smsApiAuth);
|
||||
headers.set("Authorization", "Bearer " + smsPopApiKey);
|
||||
|
||||
SmsApiResponse response = restService.postWithExternalAuth(
|
||||
smsApiUrl,
|
||||
batchRequest,
|
||||
headers,
|
||||
SmsApiResponse.class
|
||||
);
|
||||
restService.postAs(smsPopUrl, campaignRequest, headers, Object.class);
|
||||
|
||||
logger.info("SMS sent to {} - Status: {}, Result: {}", to, response.getStatus(), response.getResult());
|
||||
logger.info("SMS sent to {} via SMSPOP", to);
|
||||
} catch (Exception e) {
|
||||
logger.error("Failed to send SMS to {}: {}", to, e.getMessage(), e);
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import zw.qantra.tm.domain.enums.CurrencyType;
|
||||
import zw.qantra.tm.domain.enums.RequestType;
|
||||
import zw.qantra.tm.domain.models.AdditionalData;
|
||||
import zw.qantra.tm.domain.models.Currency;
|
||||
@@ -33,23 +34,20 @@ public class TransactionService {
|
||||
private final AdditionalDataService additionalDataService;
|
||||
private final CurrencyService currencyService;
|
||||
|
||||
public BigDecimal calculateCreditAmount(Transaction transaction) {
|
||||
if (transaction.getCreditCurrency() == null) {
|
||||
public BigDecimal calculateCurrencyExchangeValue(CurrencyType from, CurrencyType to, BigDecimal amount) {
|
||||
if (to == null) {
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
|
||||
Currency debitCurrency = currencyService.findCurrencyByIsoCode(transaction.getDebitCurrency().name());
|
||||
Currency creditCurrency = currencyService.findCurrencyByIsoCode(transaction.getCreditCurrency().name());
|
||||
Currency debitCurrency = currencyService.findCurrencyByIsoCode(from.name());
|
||||
Currency creditCurrency = currencyService.findCurrencyByIsoCode(to.name());
|
||||
|
||||
BigDecimal debitBankRate = new BigDecimal(debitCurrency.getBankRate());
|
||||
BigDecimal creditBankRate = new BigDecimal(creditCurrency.getBankRate());
|
||||
|
||||
BigDecimal bankRate = creditBankRate.divide(debitBankRate, 4, RoundingMode.HALF_UP);
|
||||
|
||||
BigDecimal creditAmount = transaction.getAmount().multiply(bankRate).setScale(4, RoundingMode.HALF_UP);
|
||||
|
||||
transaction.setCreditAmount(creditAmount);
|
||||
return creditAmount;
|
||||
return amount.multiply(bankRate).setScale(4, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
public void reassignTransactions(UUID oldWorkspaceId, UUID newWorkspaceId){
|
||||
|
||||
@@ -181,7 +181,7 @@ public class VelocityPaymentService {
|
||||
? "VMC" : transaction.getPaymentProcessorLabel();
|
||||
VelocityTransactionDto payload = VelocityTransactionDto.builder()
|
||||
.salesOrderId(transaction.getOrderId())
|
||||
.amount(transaction.getCreditAmount())
|
||||
.amount(transaction.getCreditTotalAmount())
|
||||
.debitPhone(transaction.getDebitPhone())
|
||||
.debitRegion("ZW")
|
||||
.type("REQUEST")
|
||||
|
||||
@@ -56,8 +56,9 @@ public class NetoneConfirmationHotProcessor implements TransactionProcessorInter
|
||||
}
|
||||
}
|
||||
|
||||
transaction.setCreditCurrency(CurrencyType.USD);
|
||||
transaction.setCreditCurrency(transaction.getDebitCurrency());
|
||||
transaction.setCreditAmount(transaction.getAmount());
|
||||
transaction.setCreditTotalAmount(transaction.getTotalAmount());
|
||||
transaction.setCreditAccount(Utils.formatPhoneNumber(transaction.getCreditAccount(), "ZW"));
|
||||
transaction.setStatus(Status.SUCCESS);
|
||||
transaction.setResponseCode("00");
|
||||
|
||||
@@ -5,14 +5,11 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
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.Status;
|
||||
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.services.*;
|
||||
import zw.qantra.tm.domain.services.processors.TransactionProcessorInterface;
|
||||
@@ -20,7 +17,6 @@ import zw.qantra.tm.exceptions.ApiException;
|
||||
import zw.qantra.tm.rest.RestService;
|
||||
import zw.qantra.tm.utils.Utils;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.*;
|
||||
|
||||
@Service("CONFIRM_ZESA_HOT")
|
||||
@@ -69,7 +65,14 @@ public class ZesaConfirmationHotProcessor implements TransactionProcessorInterfa
|
||||
transaction.setCreditCurrency(CurrencyType.USD);
|
||||
}
|
||||
|
||||
transaction.setCreditAmount(transactionService.calculateCreditAmount(transaction));
|
||||
transaction.setCreditAmount(transactionService.calculateCurrencyExchangeValue(
|
||||
transaction.getDebitCurrency(),
|
||||
transaction.getCreditCurrency(),
|
||||
transaction.getAmount()));
|
||||
transaction.setCreditTotalAmount(transactionService.calculateCurrencyExchangeValue(
|
||||
transaction.getDebitCurrency(),
|
||||
transaction.getCreditCurrency(),
|
||||
transaction.getTotalAmount()));
|
||||
|
||||
// token should've been updated by checkCustomer
|
||||
if (!hotRechargeBalanceService.checkBalance(token, transaction.getCreditAmount(), accId)) {
|
||||
|
||||
@@ -40,6 +40,7 @@ public class CityWalletRemotePaymentProcessor implements TransactionProcessorInt
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
logger.error("Network error during payment processing: {}", e.getMessage(), e);
|
||||
transaction.setPaymentStatus(Status.FAILED);
|
||||
transaction.setStatus(Status.FAILED);
|
||||
transaction.setResponseCode("99");
|
||||
transaction.setSystemErrorMessage("Network error: " + e.getMessage());
|
||||
@@ -71,6 +72,7 @@ public class CityWalletRemotePaymentProcessor implements TransactionProcessorInt
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
logger.error("Network error during payment processing: {}", e.getMessage(), e);
|
||||
transaction.setPollingStatus(Status.FAILED);
|
||||
transaction.setStatus(Status.FAILED);
|
||||
transaction.setResponseCode("99");
|
||||
transaction.setSystemErrorMessage("Network error: " + e.getMessage());
|
||||
|
||||
@@ -46,6 +46,7 @@ public class EcocashRemotePaymentProcessor implements TransactionProcessorInterf
|
||||
e.printStackTrace();
|
||||
logger.error("Network error during payment processing: {}", e.getMessage(), e);
|
||||
transaction.setStatus(Status.FAILED);
|
||||
transaction.setPaymentStatus(Status.FAILED);
|
||||
transaction.setResponseCode("99");
|
||||
transaction.setSystemErrorMessage("Network error: " + e.getMessage());
|
||||
transaction.setErrorMessage("Payment failed. Please try again.");
|
||||
@@ -77,6 +78,7 @@ public class EcocashRemotePaymentProcessor implements TransactionProcessorInterf
|
||||
e.printStackTrace();
|
||||
logger.error("Network error during payment processing: {}", e.getMessage(), e);
|
||||
transaction.setStatus(Status.FAILED);
|
||||
transaction.setPollingStatus(Status.FAILED);
|
||||
transaction.setResponseCode("99");
|
||||
transaction.setSystemErrorMessage("Network error: " + e.getMessage());
|
||||
transaction.setErrorMessage("Payment failed. Please try again.");
|
||||
|
||||
@@ -48,6 +48,7 @@ public class MPGSWebPaymentProcessor implements TransactionProcessorInterface {
|
||||
e.printStackTrace();
|
||||
logger.error("Network error during payment processing: {}", e.getMessage(), e);
|
||||
transaction.setStatus(Status.FAILED);
|
||||
transaction.setPaymentStatus(Status.FAILED);
|
||||
transaction.setResponseCode("99");
|
||||
transaction.setSystemErrorMessage("Network error: " + e.getMessage());
|
||||
transaction.setErrorMessage("Payment failed. Please try again");
|
||||
@@ -62,6 +63,7 @@ public class MPGSWebPaymentProcessor implements TransactionProcessorInterface {
|
||||
|
||||
if(settingService.getBooleanSetting("SIMULATE_MPGS_SUCCESS")) {
|
||||
transaction.setStatus(Status.SUCCESS);
|
||||
transaction.setPollingStatus(Status.SUCCESS);
|
||||
transaction.setResponseCode("02");
|
||||
transaction.setTargetUrl("/home");
|
||||
return transaction;
|
||||
@@ -74,6 +76,7 @@ public class MPGSWebPaymentProcessor implements TransactionProcessorInterface {
|
||||
e.printStackTrace();
|
||||
logger.error("Network error during payment processing: {}", e.getMessage(), e);
|
||||
transaction.setStatus(Status.FAILED);
|
||||
transaction.setPollingStatus(Status.FAILED);
|
||||
transaction.setResponseCode("99");
|
||||
transaction.setSystemErrorMessage("Network error: " + e.getMessage());
|
||||
transaction.setErrorMessage("Payment failed. Please try again");
|
||||
|
||||
@@ -120,8 +120,8 @@ public class RegistrationWorkflow extends WorkflowDefinition {
|
||||
Otp otp = otpService.generateOtp(username, "REGISTRATION");
|
||||
|
||||
notificationService.sendOtpEmail(email, username, otp.getOtp());
|
||||
notificationService.sendSms(registerRequest.getPhone(),
|
||||
"Your Velocity Pay verification code is: " + otp.getOtp());
|
||||
String message = "Your Velocity Pay verification code is: " + otp.getOtp();
|
||||
notificationService.sendSms(registerRequest.getPhone(), message);
|
||||
|
||||
log.info("otp generated successfully: {}", otp.getOtp());
|
||||
} catch (Exception e) {
|
||||
@@ -146,8 +146,8 @@ public class RegistrationWorkflow extends WorkflowDefinition {
|
||||
Otp otp = otpService.generateOtp(otpRequest.getUsername(), "REGISTRATION");
|
||||
// The username field in OtpRequest may actually contain the email; use it directly
|
||||
notificationService.sendOtpEmail(otpRequest.getUsername(), otpRequest.getUsername(), otp.getOtp());
|
||||
notificationService.sendSms(otpRequest.getPhone(),
|
||||
"Your Velocity Pay verification code is: " + otp.getOtp());
|
||||
String message = "Your Velocity Pay verification code is: " + otp.getOtp();
|
||||
notificationService.sendSms(otpRequest.getPhone(), message);
|
||||
|
||||
log.info("otp resent successfully: {}", otp.getOtp());
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package zw.qantra.tm.domain.services.processors.workflows.handlers;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
import zw.qantra.tm.domain.enums.Status;
|
||||
import zw.qantra.tm.domain.models.Transaction;
|
||||
@@ -22,6 +23,10 @@ public class GatewayPaymentHandler implements HandlerInterface {
|
||||
private final RecipientService recipientService;
|
||||
private final TransactionEventService transactionEventService;
|
||||
private final TransactionService transactionService;
|
||||
private final NotificationService notificationService;
|
||||
|
||||
@Value("${wa.api.phone}")
|
||||
private String waPhone;
|
||||
|
||||
@Override
|
||||
public Object process(Transaction incomingTransaction) throws Exception {
|
||||
@@ -73,7 +78,12 @@ public class GatewayPaymentHandler implements HandlerInterface {
|
||||
transaction.setResponseCode("01");
|
||||
transaction.setErrorMessage(e.getMessage());
|
||||
transactionService.save(transaction);
|
||||
return transaction;
|
||||
}
|
||||
|
||||
if(Status.FAILED.equals(transaction.getPaymentStatus())) {
|
||||
notificationService.sendWA(waPhone, String.format(
|
||||
"Payment failed: Transaction: %s, Customer message: %s, System message: %s",
|
||||
transaction.getId(), transaction.getErrorMessage(), transaction.getSystemErrorMessage()));
|
||||
}
|
||||
|
||||
transactionEvent.setStatus(transaction.getStatus());
|
||||
|
||||
@@ -3,6 +3,7 @@ package zw.qantra.tm.domain.services.processors.workflows.handlers;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
import zw.qantra.tm.domain.enums.RequestType;
|
||||
import zw.qantra.tm.domain.enums.Status;
|
||||
@@ -28,6 +29,10 @@ public class IntegrationHandler implements HandlerInterface {
|
||||
private final RecipientService recipientService;
|
||||
private final TransactionEventService transactionEventService;
|
||||
private final TransactionService transactionService;
|
||||
private final NotificationService notificationService;
|
||||
|
||||
@Value("${wa.api.phone}")
|
||||
private String waPhone;
|
||||
|
||||
List<String> lock = new ArrayList<>();
|
||||
|
||||
@@ -86,6 +91,12 @@ public class IntegrationHandler implements HandlerInterface {
|
||||
|
||||
transaction.setIntegrationStatus(transaction.getStatus());
|
||||
|
||||
if(!Status.SUCCESS.equals(transaction.getIntegrationStatus())) {
|
||||
notificationService.sendWA(waPhone, String.format(
|
||||
"Integration failed: Transaction %s, Customer message: %s, System message: %s",
|
||||
transaction.getId(), transaction.getErrorMessage(), transaction.getSystemErrorMessage()));
|
||||
}
|
||||
|
||||
removeLock(transaction.getId().toString());
|
||||
if(transaction.getStatus() != Status.SUCCESS){
|
||||
transactionService.save(transaction);
|
||||
|
||||
@@ -84,9 +84,14 @@ jobrunr.datasource.password=
|
||||
app.reports.storage-path=/srv/data/reports
|
||||
app.base-url=https://payapi.velocityafrica.net
|
||||
|
||||
zss.sms.api.url=https://secure.zss.co.zw/vportal/cnm/vsms/json/batch
|
||||
zss.sms.api.auth=Basic UWFudHJhdGVjaDpVbUk3OUpwUDh3QTY=
|
||||
sms.debug=false
|
||||
|
||||
sms-pop.url=https://smspop.co.zw/api/campaigns
|
||||
sms-pop.apikey=108|ypBpUtJGlPzelxJl2aySp672I9KKbyaQPtcxMPBrfb70be27
|
||||
|
||||
cloudflare-token=cfut_U0qhvhzAATLqHQa9lad7oRmBmPoJ6n9uIoCBbNOWc9884f18
|
||||
cloudflare-zone-id=378cea8e50c0d9a158816109b3b1fd8d
|
||||
|
||||
wa.api.url=http://173.212.247.232:2785/api/sessions/70eae8dc-2be8-465a-a54b-b6c1d3313699/messages/send-text
|
||||
wa.api.key=owa_k1_3d2198d81573303f0b48c3c4a6981fd13261b1d0099758eabf4efb53beaba77d
|
||||
wa.api.phone=263773591219
|
||||
@@ -1,23 +1,8 @@
|
||||
<?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="1782340507017-3">
|
||||
<changeSet author="khoza (generated)" id="1783202068979-3">
|
||||
<addColumn tableName="transaction">
|
||||
<column name="batch_description" type="varchar(255)"/>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
<changeSet author="khoza (generated)" id="1782340507017-4">
|
||||
<addColumn tableName="group_batch_item">
|
||||
<column name="credit_address" type="varchar(255)"/>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
<changeSet author="khoza (generated)" id="1782340507017-5">
|
||||
<addColumn tableName="payment_processor">
|
||||
<column name="currency" type="varchar(255)"/>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
<changeSet author="khoza (generated)" id="1782340507017-6">
|
||||
<addColumn tableName="transaction">
|
||||
<column name="username" type="varchar(255)"/>
|
||||
<column name="credit_total_amount" type="numeric(38, 2)"/>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
||||
|
||||
@@ -121,7 +121,7 @@ class TransactionServiceTest {
|
||||
|
||||
@Test
|
||||
@DisplayName("Should calculate credit amount using bank rate")
|
||||
void shouldCalculateCreditAmount() {
|
||||
void shouldCalculateCurrencyExchangeValue() {
|
||||
Currency currencyUsd = new Currency();
|
||||
currencyUsd.setBankRate("1.0");
|
||||
Currency currencyZwl = new Currency();
|
||||
@@ -130,10 +130,12 @@ class TransactionServiceTest {
|
||||
when(currencyService.findCurrencyByIsoCode("USD")).thenReturn(currencyUsd);
|
||||
when(currencyService.findCurrencyByIsoCode("ZWL")).thenReturn(currencyZwl);
|
||||
|
||||
BigDecimal creditAmount = transactionService.calculateCreditAmount(transaction);
|
||||
BigDecimal creditAmount = transactionService.calculateCurrencyExchangeValue(
|
||||
transaction.getDebitCurrency(),
|
||||
transaction.getCreditCurrency(),
|
||||
transaction.getAmount());
|
||||
|
||||
assertEquals(new BigDecimal("1500.0000"), creditAmount);
|
||||
assertEquals(new BigDecimal("1500.0000"), transaction.getCreditAmount());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -141,7 +143,10 @@ class TransactionServiceTest {
|
||||
void shouldReturnZeroWhenCreditCurrencyNull() {
|
||||
transaction.setCreditCurrency(null);
|
||||
|
||||
BigDecimal result = transactionService.calculateCreditAmount(transaction);
|
||||
BigDecimal result = transactionService.calculateCurrencyExchangeValue(
|
||||
transaction.getDebitCurrency(),
|
||||
transaction.getCreditCurrency(),
|
||||
transaction.getAmount());
|
||||
|
||||
assertEquals(BigDecimal.ZERO, result);
|
||||
}
|
||||
|
||||
@@ -49,6 +49,9 @@ class GatewayPaymentHandlerTest {
|
||||
@Mock
|
||||
private TransactionProcessorInterface transactionProcessor;
|
||||
|
||||
@Mock
|
||||
private NotificationService notificationService;
|
||||
|
||||
@InjectMocks
|
||||
private GatewayPaymentHandler handler;
|
||||
|
||||
@@ -63,7 +66,7 @@ class GatewayPaymentHandlerTest {
|
||||
transaction.setType(RequestType.REQUEST);
|
||||
transaction.setAuthType(AuthType.MOBILE);
|
||||
transaction.setPaymentProcessorLabel("ecocash");
|
||||
transaction.setPaymentStatus(null);
|
||||
transaction.setPaymentStatus(Status.PENDING);
|
||||
|
||||
lenient().when(transactionService.getTransactionRepository()).thenReturn(transactionRepository);
|
||||
}
|
||||
|
||||
@@ -48,6 +48,9 @@ class IntegrationHandlerTest {
|
||||
@Mock
|
||||
private TransactionProcessorInterface transactionProcessor;
|
||||
|
||||
@Mock
|
||||
private NotificationService notificationService;
|
||||
|
||||
@InjectMocks
|
||||
private IntegrationHandler handler;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user