162 lines
7.8 KiB
Java
162 lines
7.8 KiB
Java
package com.billpaymentservice.bbps.scheduler;
|
|
|
|
import java.util.Arrays;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Optional;
|
|
|
|
import org.modelmapper.ModelMapper;
|
|
import org.slf4j.Logger;
|
|
import org.slf4j.LoggerFactory;
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
import org.springframework.http.HttpHeaders;
|
|
import org.springframework.scheduling.annotation.Scheduled;
|
|
import org.springframework.stereotype.Component;
|
|
|
|
import com.billpaymentservice.bbps.dto.GeneralResp;
|
|
import com.billpaymentservice.bbps.dto.response.BillPaymentResponse;
|
|
import com.billpaymentservice.bbps.dto.request.TransactionStatusRequest;
|
|
import com.billpaymentservice.bbps.entites.BillPayment;
|
|
import com.billpaymentservice.bbps.entites.BharatConnectMaster;
|
|
import com.billpaymentservice.bbps.entites.OutboundTraceLog;
|
|
import com.billpaymentservice.bbps.entites.TransactionMaster;
|
|
import com.billpaymentservice.bbps.repository.BharatConnectMasterRepository;
|
|
import com.billpaymentservice.bbps.repository.OutboundTraceLogRepository;
|
|
import com.billpaymentservice.bbps.repository.TransactionMasterRepository;
|
|
import com.billpaymentservice.bbps.repository.PaymentRepository;
|
|
import com.billpaymentservice.bbps.service.PaymentService;
|
|
import com.billpaymentservice.bbps.serviceImpl.HttpService;
|
|
import com.billpaymentservice.bbps.util.BBPSConstant;
|
|
import com.billpaymentservice.bbps.util.Helper;
|
|
import com.fasterxml.jackson.core.type.TypeReference;
|
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
|
|
@Component
|
|
public class PaymentRetryScheduler {
|
|
|
|
private static final Logger LOGGER = LoggerFactory.getLogger(PaymentRetryScheduler.class);
|
|
|
|
private final TransactionMasterRepository transactionMasterRepository;
|
|
private final OutboundTraceLogRepository outboundTraceLogRepository;
|
|
private final BharatConnectMasterRepository beneficiaryDetailsRepository;
|
|
private final Helper helper;
|
|
private final HttpService httpService;
|
|
private final PaymentRepository paymentRepository;
|
|
private final PaymentService paymentService;
|
|
private final ObjectMapper objectMapper;
|
|
private final ModelMapper modelMapper;
|
|
|
|
@Autowired
|
|
public PaymentRetryScheduler(TransactionMasterRepository transactionMasterRepository,
|
|
OutboundTraceLogRepository outboundTraceLogRepository,
|
|
BharatConnectMasterRepository beneficiaryDetailsRepository, Helper helper, HttpService httpService,
|
|
PaymentRepository paymentRepository, PaymentService paymentService) {
|
|
this.transactionMasterRepository = transactionMasterRepository;
|
|
this.outboundTraceLogRepository = outboundTraceLogRepository;
|
|
this.beneficiaryDetailsRepository = beneficiaryDetailsRepository;
|
|
this.helper = helper;
|
|
this.httpService = httpService;
|
|
this.paymentRepository = paymentRepository;
|
|
this.paymentService = paymentService;
|
|
this.objectMapper = new ObjectMapper();
|
|
this.modelMapper = new ModelMapper();
|
|
}
|
|
|
|
// runs every 60 seconds by default; configurable via property scheduler.retry.delay.ms
|
|
@Scheduled(fixedDelayString = "${scheduler.retry.delay.ms:60000}")
|
|
public void retryPendingPayments() {
|
|
LOGGER.info("PaymentRetryScheduler triggered");
|
|
List<String> statuses = Arrays.asList("PENDING_UPSTREAM", "INVALID_RESPONSE_FROM_BBPS",
|
|
"INVALID_JSON_FROM_UPSTREAM", "Failed", "FAILED");
|
|
|
|
List<TransactionMaster> pendingList = transactionMasterRepository.findByStatusIn(statuses);
|
|
if (pendingList == null || pendingList.isEmpty()) {
|
|
LOGGER.debug("No pending TransactionMaster entries found");
|
|
return;
|
|
}
|
|
|
|
for (TransactionMaster tm : pendingList) {
|
|
try {
|
|
LOGGER.info("Retrying transaction txnId={} status={}", tm.getTxnId(), tm.getStatus());
|
|
|
|
Optional<OutboundTraceLog> outOpt = outboundTraceLogRepository
|
|
.findTopByTxnIdAndApiNameOrderByIdDesc(tm.getTxnId(), BBPSConstant.API_INITIATE_PAYMENT);
|
|
if (outOpt.isEmpty()) {
|
|
LOGGER.warn("No outbound trace log found for txnId={}", tm.getTxnId());
|
|
continue;
|
|
}
|
|
|
|
OutboundTraceLog outbound = outOpt.get();
|
|
String payload = outbound.getRequestPayload();
|
|
if (payload == null || payload.isEmpty()) {
|
|
LOGGER.warn("Outbound request payload empty for txnId={}", tm.getTxnId());
|
|
continue;
|
|
}
|
|
|
|
BharatConnectMaster headersData = beneficiaryDetailsRepository.findByOrgId(BBPSConstant.ORG_ID);
|
|
HttpHeaders headers = helper.buildHeaders(headersData);
|
|
|
|
LOGGER.info("Resending request to BBPS for txnId={} url={}", tm.getTxnId(), outbound.getEndpointUrl());
|
|
Map<String, String> responseMap = httpService.postRequest(outbound.getEndpointUrl(), payload, headers);
|
|
String responseData = responseMap.get(BBPSConstant.RESPONSE_DATA_KEY);
|
|
LOGGER.info("Retry response for txnId={} => {}", tm.getTxnId(), responseData);
|
|
|
|
helper.updateOutboundLogResponse(outbound, responseData);
|
|
outboundTraceLogRepository.save(outbound);
|
|
|
|
if (responseData == null || responseData.isEmpty()) {
|
|
LOGGER.warn("Empty response on retry for txnId={}", tm.getTxnId());
|
|
// leave as pending for next run
|
|
continue;
|
|
}
|
|
|
|
Map<String, Object> dataMap = objectMapper.readValue(responseData, new TypeReference<Map<String, Object>>() {});
|
|
if (!dataMap.containsKey(BBPSConstant.RESPONSE_GENERIC_KEY)) {
|
|
LOGGER.warn("Retry response invalid structure for txnId={}", tm.getTxnId());
|
|
// update TransactionMaster status for visibility
|
|
tm.setStatus("INVALID_RESPONSE_FROM_BBPS");
|
|
transactionMasterRepository.save(tm);
|
|
continue;
|
|
}
|
|
|
|
BillPaymentResponse billResp = objectMapper.convertValue(dataMap, BillPaymentResponse.class);
|
|
GeneralResp gen = billResp.getGenericResponse();
|
|
|
|
if (gen == null) {
|
|
LOGGER.warn("Generic response null after retry for txnId={}", tm.getTxnId());
|
|
continue;
|
|
}
|
|
|
|
if (!"Failed".equalsIgnoreCase(gen.getStatus())) {
|
|
// success path
|
|
BillPayment entity = modelMapper.map(billResp, BillPayment.class);
|
|
paymentRepository.save(entity);
|
|
|
|
tm.setBankTxnId(billResp.getTxnId());
|
|
tm.setStatus(billResp.getStatus());
|
|
transactionMasterRepository.save(tm);
|
|
|
|
// trigger transaction status check (best-effort)
|
|
try {
|
|
TransactionStatusRequest tsReq = new TransactionStatusRequest();
|
|
tsReq.setTxnId(billResp.getTxnId());
|
|
tsReq.setReqId(tm.getReqId());
|
|
tsReq.setMid(tm.getMid());
|
|
tsReq.setClientUserId(tm.getClientUserId());
|
|
paymentService.checkTrasactionStatus(tsReq);
|
|
} catch (Exception e) {
|
|
LOGGER.warn("Transaction status check failed after retry for txnId={} : {}", tm.getTxnId(), e.getMessage());
|
|
}
|
|
} else {
|
|
// upstream returned failed status
|
|
tm.setBankTxnId(billResp.getTxnId());
|
|
tm.setStatus(gen.getStatus());
|
|
transactionMasterRepository.save(tm);
|
|
}
|
|
|
|
} catch (Exception ex) {
|
|
LOGGER.error("Error while retrying transaction txnId={}: {}", tm.getTxnId(), ex.getMessage(), ex);
|
|
}
|
|
}
|
|
}
|
|
} |