half commited
This commit is contained in:
@@ -0,0 +1,162 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user