package com.billpaymentservice.bbps.util; import java.security.SecureRandom; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Optional; import java.util.function.Function; import org.modelmapper.ModelMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import com.billpaymentservice.bbps.dto.AdditionalInfoBillDTO; import com.billpaymentservice.bbps.dto.AdditionalInfoDTO; import com.billpaymentservice.bbps.dto.CustomerParamDAO; import com.billpaymentservice.bbps.dto.CustomerParamDTO; import com.billpaymentservice.bbps.dto.ResponseModel; import com.billpaymentservice.bbps.entites.AdditionalInfoBill; import com.billpaymentservice.bbps.entites.BBPSMastersData; import com.billpaymentservice.bbps.exception.CustomException; import com.billpaymentservice.bbps.repository.BBPSMasterDataRepo; import com.billpaymentservice.bbps.repository.BillMasterRepository; import com.billpaymentservice.bbps.repository.BillerDataRepository; import com.billpaymentservice.bbps.repository.InboundTraceLogRepository; import com.billpaymentservice.bbps.dto.request.BillFetchByBillIdRequest; import com.billpaymentservice.bbps.dto.request.BillerDetailRequest; import com.billpaymentservice.bbps.dto.request.MerchantRequestModel; import com.billpaymentservice.bbps.dto.response.BillDataResponse; import com.billpaymentservice.bbps.entites.BBPSMerchantValidation; import com.billpaymentservice.bbps.entites.BillMaster; import com.billpaymentservice.bbps.entites.BillerDetails; import com.billpaymentservice.bbps.entites.BillerResponse; import com.billpaymentservice.bbps.entites.CustomerParamBill; import com.billpaymentservice.bbps.entites.InboundTraceLog; import com.billpaymentservice.bbps.entites.OutboundTraceLog; import com.billpaymentservice.bbps.entites.PaymentChannel; import com.billpaymentservice.bbps.entites.PaymentMode; import com.billpaymentservice.bbps.exception.CustomException; import com.billpaymentservice.bbps.repository.MerchantRepository; import com.billpaymentservice.bbps.repository.OutboundTraceLogRepository; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; @Component public class Helper { private static final Logger LOGGER = LoggerFactory.getLogger(Helper.class); private final InboundTraceLogRepository inboundTraceLogRepository; private final OutboundTraceLogRepository outboundTraceLogRepository; private static final String CHARACTERS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; private static final SecureRandom secureRandom = new SecureRandom(); private final ObjectMapper objectMapper = new ObjectMapper(); private final BBPSMasterDataRepo bbpsMasterDataRepo; private final BillerDataRepository billerDataRespository; private final BillMasterRepository billMasterRepository; // Constructor Injection public Helper(BBPSMasterDataRepo bbpsMasterDataRepo, InboundTraceLogRepository inboundTraceLogRepository, OutboundTraceLogRepository outboundTraceLogRepository,BillerDataRepository billerDataRepository, BillMasterRepository billMasterRepository) { this.bbpsMasterDataRepo = bbpsMasterDataRepo; this.inboundTraceLogRepository = inboundTraceLogRepository; this.outboundTraceLogRepository = outboundTraceLogRepository; this.billerDataRespository=billerDataRepository; this.billMasterRepository=billMasterRepository; } /** * Validates whether the merchant exists and is active. * * @param requestModel MerchantRequestModel containing merchant details * @return true if merchant exists and is active * @throws CustomException if merchant is invalid or inactive * * * */ public void startPolling(T requestObject, // Request object (any type) Function apiCallFunction, // Function to call API int intervalSeconds, // gap between two polls int maxDurationSeconds // total polling time ) { new Thread(() -> { try { int maxAttempts = maxDurationSeconds / intervalSeconds; for (int i = 0; i < maxAttempts; i++) { LOGGER.info("⏳ Polling attempt {}...", i + 1); ResponseModel response = apiCallFunction.apply(requestObject); LOGGER.info("📥 Poll response: {}", response); if ("SUCCESS".equalsIgnoreCase(response.getStatus())) { LOGGER.info("✅ Polling successful, stopping early."); break; } Thread.sleep(intervalSeconds * 1000L); } } catch (Exception e) { LOGGER.error("❌ Error in polling thread: {}", e.getMessage(), e); } }).start(); } public void validateMerchant(String mid) { if (mid == null || mid == "") { LOGGER.warn("MerchantRequestModel or MID is null"); throw new CustomException("Mid is required", "FAILED", BBPSConstant.BADREQUEST, null); } BBPSMastersData merchantValidation = bbpsMasterDataRepo.findByMid(mid); if (merchantValidation == null) { LOGGER.warn("Merchant not found for MID: {}", mid); throw new CustomException("Merchant not found by id", "FAILED", BBPSConstant.BADREQUEST, null); } if (merchantValidation.getApiKey() == null && merchantValidation.getSecreteKey() == null) { LOGGER.warn("Merchant BBPS service is not active MID: {}", mid); throw new CustomException("Merchant BBPS Service is not active by mid", "FAILED", BBPSConstant.BADREQUEST, null); } if (!Boolean.TRUE.equals(merchantValidation.getIsActive())) { LOGGER.warn("Merchant inactive for MID: {}", mid); throw new CustomException("Merchant is inactive", "FAILED", BBPSConstant.BADREQUEST, null); } LOGGER.info("Merchant is valid and active for MID: {}", mid); // Agar exception nahi throw hua, merchant valid hai } public static String generateTxnId(int n) { StringBuilder sb = new StringBuilder(n); for (int i = 0; i < n; i++) { int index = secureRandom.nextInt(CHARACTERS.length()); sb.append(CHARACTERS.charAt(index)); } return sb.toString(); // Prefix } public String convertObjectToJson(Object obj) { if (obj == null) return "null"; try { return new com.fasterxml.jackson.databind.ObjectMapper().writeValueAsString(obj); } catch (com.fasterxml.jackson.core.JsonProcessingException e) { // log appropriately (use your existing logger) System.err.println("JSON conversion failed: " + e.getMessage()); return "{}"; // or "N/A" depending on your needs } } /* ---------- Trace helper methods ---------- */ public InboundTraceLog createInboundLog(String requestId, String txnId, String apiName, String mid, String httpMethod, String requestPayload) { if(inboundTraceLogRepository.findByRequestId(requestId) != null) { throw new CustomException("Duplicate Request ID: " + requestId); } InboundTraceLog inboundLog = new InboundTraceLog(requestId, txnId, apiName, mid == null ? "N/A" : mid, "BBPS_ORG", httpMethod, requestPayload == null ? "N/A" : requestPayload); inboundTraceLogRepository.save(inboundLog); return inboundLog; } public void updateInboundLogSuccess(InboundTraceLog inboundLog, String responsePayload, String responseCode) { if (inboundLog == null) return; inboundLog.setResponsePayload(responsePayload); inboundLog.setResponseCode(responseCode); inboundLog.setStatus("SUCCESS"); inboundLog.setResponseTime(LocalDateTime.now()); inboundTraceLogRepository.save(inboundLog); } public void updateInboundLogFailure(InboundTraceLog inboundLog, String responsePayload, String responseCode) { if (inboundLog == null) return; inboundLog.setResponsePayload(responsePayload); inboundLog.setResponseCode(responseCode); inboundLog.setStatus("FAILED"); inboundLog.setResponseTime(LocalDateTime.now()); inboundTraceLogRepository.save(inboundLog); } public OutboundTraceLog createOutboundLog(String txnId, String requestId, String apiName, String url, String httpMethod, String requestPayload) { OutboundTraceLog outboundLog = new OutboundTraceLog(txnId, requestId, apiName, url, httpMethod, requestPayload == null ? "N/A" : requestPayload); outboundTraceLogRepository.save(outboundLog); return outboundLog; } public void updateOutboundLogResponse(OutboundTraceLog outboundLog, String responsePayload) { if (outboundLog == null) return; outboundLog.setResponsePayload(responsePayload); outboundLog.setResponseReceiveTime(LocalDateTime.now()); outboundTraceLogRepository.save(outboundLog); } /* ---------- End helpers ---------- */ public void saveOrUpdateBillerData(BillerDetails newEntity) { Optional existingOpt = billerDataRespository.findByBillerId(newEntity.getBillerId()); if (existingOpt.isPresent()) { BillerDetails existing = existingOpt.get(); // Update base biller fields existing.setBillerName(newEntity.getBillerName()); existing.setBillerAliasName(newEntity.getBillerAliasName()); existing.setBillerDescription(newEntity.getBillerDescription()); existing.setStatus(newEntity.getStatus()); existing.setBillerCoverage(newEntity.getBillerCoverage()); // ... (update other simple fields as needed) // ✅ Clear and replace nested lists to avoid duplicate inserts existing.getBillerPaymentModes().clear(); if (newEntity.getBillerPaymentModes() != null) { for (PaymentMode mode : newEntity.getBillerPaymentModes()) { mode.setBiller(existing); existing.getBillerPaymentModes().add(mode); } } existing.getBillerPaymentChannels().clear(); if (newEntity.getBillerPaymentChannels() != null) { for (PaymentChannel channel : newEntity.getBillerPaymentChannels()) { channel.setBiller(existing); existing.getBillerPaymentChannels().add(channel); } } billerDataRespository.save(existing); } else { // brand new biller billerDataRespository.save(newEntity); } } public void saveOrUpdateBillerData(BillDataResponse responseWrapper, BillFetchByBillIdRequest billFetchByBillIdRequest) { BillMaster billMaster =new BillMaster(); billMaster.setBillId("12333333hjsdfjsfdj"); billMaster.setRefId(responseWrapper.getRefId()); billMaster.setBillerId(responseWrapper.getBillerId()); billMaster.setStatus(responseWrapper.getStatus()); billMaster.setActionType(responseWrapper.getActionType()); billMaster.setUserId(responseWrapper.getUserId()); billMaster.setReqId(billFetchByBillIdRequest.getReqId()); // if available from your API request billMaster.setTxnId("jhnjhbhj"); // give it from method // ---- BILLER RESPONSE ---- BillerResponse billerResponse = new BillerResponse(); if (responseWrapper.getBillerResponse() != null) { billerResponse.setBillId(responseWrapper.getBillerResponse().getBillId()); billerResponse.setAmount(responseWrapper.getBillerResponse().getAmount()); billerResponse.setBillDate(responseWrapper.getBillerResponse().getBillDate()); billerResponse.setBillNumber(responseWrapper.getBillerResponse().getBillNumber()); billerResponse.setBillPeriod(responseWrapper.getBillerResponse().getBillPeriod()); billerResponse.setCustConvFee(responseWrapper.getBillerResponse().getCustConvFee()); billerResponse.setCouCustConvFee(responseWrapper.getBillerResponse().getCouCustConvFee()); billerResponse.setDueDate(responseWrapper.getBillerResponse().getDueDate()); billerResponse.setCustomerName(responseWrapper.getBillerResponse().getCustomerName()); billerResponse.setAmountOption(responseWrapper.getBillerResponse().isAmountOption()); billerResponse.setBillMaster(billMaster); // maintain mapping } billMaster.setBillerResponse(billerResponse); // ---- ADDITIONAL INFO ---- List additionalInfos = new ArrayList<>(); if (responseWrapper.getAdditionalInfo() != null) { for (AdditionalInfoBillDTO info : responseWrapper.getAdditionalInfo()) { AdditionalInfoBill entity = new AdditionalInfoBill(); entity.setName(info.getName()); entity.setValue(info.getValue()); entity.setBillMaster(billMaster); // maintain mapping additionalInfos.add(entity); } } billMaster.setAdditionalInfo(additionalInfos); // ---- CUSTOMER PARAMS ---- List customerParams = new ArrayList<>(); if (responseWrapper.getCustomerParams() != null) { for (CustomerParamDAO param : responseWrapper.getCustomerParams()) { CustomerParamBill entity = new CustomerParamBill(); entity.setName(param.getName()); entity.setValue(param.getValue()); entity.setType(param.getType()); entity.setBillMaster(billMaster); customerParams.add(entity); } } billMaster.setCustomerParams(customerParams); // ---- DATES ---- billMaster.setCreationDate(responseWrapper.getCreationDate()); billMaster.setModifiedDate(responseWrapper.getModifiedDate()); billMaster.setCreatedEpochDate(responseWrapper.getCreatedEpochDate()); billMaster.setModifiedEpochDate(responseWrapper.getModifiedEpochDate()); billMasterRepository.save(billMaster); } }