package com.billpaymentservice.bbps.serviceImpl; import java.util.Map; 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.stereotype.Service; import org.springframework.web.client.RestClient; import com.billpaymentservice.bbps.dto.GeneralResp; import com.billpaymentservice.bbps.dto.ResponseModel; import com.billpaymentservice.bbps.dto.request.BillFetchByBillIdRequest; import com.billpaymentservice.bbps.dto.request.FetchBillRequest; import com.billpaymentservice.bbps.dto.request.FetchBillValidatestRequest; import com.billpaymentservice.bbps.dto.response.BillDataResponse; import com.billpaymentservice.bbps.dto.response.FetchBillResponse; import com.billpaymentservice.bbps.dto.response.FetchBillValidatestResponse; import com.billpaymentservice.bbps.entites.BharatConnectMaster; import com.billpaymentservice.bbps.entites.BillInitiate; import com.billpaymentservice.bbps.entites.BillMaster; import com.billpaymentservice.bbps.entites.InboundTraceLog; import com.billpaymentservice.bbps.entites.OutboundTraceLog; import com.billpaymentservice.bbps.exception.CustomException; import com.billpaymentservice.bbps.repository.BharatConnectMasterRepository; import com.billpaymentservice.bbps.repository.BillInitateRepository; import com.billpaymentservice.bbps.repository.BillMasterRepository; import com.billpaymentservice.bbps.repository.InboundTraceLogRepository; import com.billpaymentservice.bbps.repository.OutboundTraceLogRepository; import com.billpaymentservice.bbps.service.AgentService; import com.billpaymentservice.bbps.util.BBPSConstant; import com.billpaymentservice.bbps.util.Helper; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; @Service public class AgentServiceImpl implements AgentService { @Value("${agent.data.url}") private String agentDtaUrl; private final RestClient restClient; private final HttpService httpService; private final Helper helper; private final BharatConnectMasterRepository beneficiaryDetailsRepository; private final BillMasterRepository billMasterRepository; private final BillInitateRepository billIntiateRepository; private static final Logger LOGGER = LoggerFactory.getLogger(AgentServiceImpl.class); public AgentServiceImpl(RestClient restClient, HttpService httpService, Helper helper, BharatConnectMasterRepository beneficiaryDetailsRepository,InboundTraceLogRepository inboundTraceLogRepository, OutboundTraceLogRepository outboundTraceLogRepository, BillMasterRepository billMasterRepository,BillInitateRepository billIntiateRepository) { this.restClient = restClient; this.httpService = httpService; this.helper = helper; this.beneficiaryDetailsRepository = beneficiaryDetailsRepository; this.billMasterRepository = billMasterRepository; this. billIntiateRepository= billIntiateRepository; } @Override public ResponseModel initiateBillFetch(FetchBillRequest fetchBillRequest) { LOGGER.info("Initiating bill fetch for request ID: {} and MID", fetchBillRequest.getReqId(), fetchBillRequest.getMid()); String apiName = "InitiateBillFetch"; String txnId = helper.generateTxnId(15); String requestId = fetchBillRequest.getReqId(); String fullUrl = agentDtaUrl + BBPSConstant.BILL_FETCH_INTI; helper.validateMerchant(fetchBillRequest.getMid()); BharatConnectMaster headersData = beneficiaryDetailsRepository.findByOrgId("ORG001"); HttpHeaders headers = new HttpHeaders(); headers.set("x-digiledge-key", headersData.getXDigiledgeKey()); headers.set("agentId", headersData.getAgentId()); headers.set("tenantId", headersData.getTenantId()); headers.setContentType(MediaType.APPLICATION_JSON); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); String requestJson = helper.convertObjectToJson(fetchBillRequest); // ✅ Create inbound & outbound logs InboundTraceLog inboundLog = helper.createInboundLog(requestId, txnId, apiName, fetchBillRequest.getMid(), "POST", requestJson); OutboundTraceLog outboundLog = helper.createOutboundLog(txnId, requestId, apiName, fullUrl, "POST", requestJson); LOGGER.debug("Outbound log created successfully for txnId: {} and reqId: {}", txnId, requestId); try { LOGGER.info("Calling external API: {}", fullUrl); Map responseMap = httpService.postRequest(fullUrl, requestJson, headers); String responseData = responseMap.get("data"); helper.updateOutboundLogResponse(outboundLog, responseData); LOGGER.debug("Response received from BBPS: {}", responseData); Map dataMap = objectMapper.readValue(responseData, new TypeReference>() {}); if (!dataMap.containsKey("genericResponse")) { helper.updateInboundLogFailure(inboundLog, responseData, "INVALID_RESPONSE"); LOGGER.warn("Invalid response received for txnId: {}", txnId); throw new CustomException( String.valueOf(dataMap.get("message")), String.valueOf(dataMap.get("status")), Integer.parseInt(String.valueOf(dataMap.get("statusCode"))), String.valueOf(dataMap.get("remarks")) ); } BillMaster billMaster = new BillMaster(); billMaster.setBillId((String) dataMap.get("billId")); billMaster.setReqId(fetchBillRequest.getReqId()); billMaster.setTxnId(txnId); billMasterRepository.save(billMaster); LOGGER.info("BillMaster saved for billId: {}", billMaster.getBillId()); FetchBillResponse fetchBillResponse = objectMapper.convertValue(dataMap, FetchBillResponse.class); GeneralResp generalResponse = fetchBillResponse.getGenericResponse(); helper.updateInboundLogSuccess(inboundLog, responseData, generalResponse.getStatus()); BillInitiate billInitiate = new BillInitiate(); billInitiate.setBillId((String) dataMap.get("billId")); billInitiate.setReqId(fetchBillRequest.getReqId()); billInitiate.setTxnId(txnId); billIntiateRepository.save(billInitiate); LOGGER.info("BillInitiate entry saved for billId: {}", billInitiate.getBillId()); BillFetchByBillIdRequest billFetchByBillIdRequest = new BillFetchByBillIdRequest(); billFetchByBillIdRequest.setBillId(fetchBillResponse.getBillId()); billFetchByBillIdRequest.setMid(fetchBillRequest.getMid()); billFetchByBillIdRequest.setReqId(requestId); LOGGER.info("Waiting for 2 seconds before fetching bill by ID..."); try { Thread.sleep(2000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); LOGGER.warn("Thread interrupted during delay: {}", e.getMessage()); } LOGGER.info("Calling internal method Fetching bill by ID: {}", billFetchByBillIdRequest.getBillId()); return fetchBillById(billFetchByBillIdRequest, inboundLog); } catch (JsonProcessingException e) { LOGGER.error("JSON Parsing Error: {}", e.getMessage(), e); helper.updateInboundLogFailure(inboundLog, e.getMessage(), "JSON_ERROR"); throw new CustomException("Invalid JSON received from API"); } } public ResponseModel fetchBillById(BillFetchByBillIdRequest billFetchByBillIdRequest, InboundTraceLog inboundLog) { LOGGER.info("Fetching bill by BILLID for request ID={} and MID={}", billFetchByBillIdRequest.getReqId(), billFetchByBillIdRequest.getMid()); String apiName = "FetchBillById"; String txnId = helper.generateTxnId(15); String reqId = billFetchByBillIdRequest.getReqId(); String billId = billFetchByBillIdRequest.getBillId(); String fullUrl = agentDtaUrl + BBPSConstant.BILL_FETCH_BY_ID + "1ced7566-1add-4e01-9ed6-04cd67ac9684"; helper.validateMerchant(billFetchByBillIdRequest.getMid()); BharatConnectMaster headersData = beneficiaryDetailsRepository.findByOrgId("ORG001"); HttpHeaders headers = new HttpHeaders(); headers.set("x-digiledge-key", headersData.getXDigiledgeKey()); headers.set("agentId", headersData.getAgentId()); headers.set("tenantId", headersData.getTenantId()); headers.setContentType(MediaType.APPLICATION_JSON); ObjectMapper objectMapper = new ObjectMapper(); OutboundTraceLog outboundLog = helper.createOutboundLog(txnId, reqId, apiName, fullUrl, "GET", null); LOGGER.debug("Outbound log created successfully for FetchBillById: {}", fullUrl); try { LOGGER.info("Calling external API: {}", fullUrl); Map responseMap = httpService.getRequest(fullUrl, headers); String responseData = responseMap.get("data"); LOGGER.info("Bill FetchById Response: {}", responseData); helper.updateOutboundLogResponse(outboundLog, responseData); Map dataMap = objectMapper.readValue(responseData, new TypeReference>() {}); if (!dataMap.containsKey("genericResponse")) { helper.updateInboundLogFailure(inboundLog, responseData, "INVALID_RESPONSE"); LOGGER.warn("Invalid response structure for txnId: {}", txnId); throw new CustomException( String.valueOf(dataMap.get("message")), String.valueOf(dataMap.get("status")), Integer.parseInt(String.valueOf(dataMap.get("statusCode"))), String.valueOf(dataMap.get("remarks")) ); } BillDataResponse responseWrapper = objectMapper.convertValue(dataMap, BillDataResponse.class); GeneralResp generalResp = responseWrapper.getGenericResponse(); String status = generalResp.getStatus(); LOGGER.info("Current Bill Status: {}", status); helper.updateInboundLogSuccess(inboundLog, responseData, generalResp.getStatus()); helper.saveOrUpdateBillerData(responseWrapper,billFetchByBillIdRequest); LOGGER.info("Bill data saved successfully for billId: {}", billId); return new ResponseModel( generalResp.getMessage(), generalResp.getStatus(), Integer.parseInt(generalResp.getStatusCode()), responseWrapper ); } catch (JsonProcessingException e) { LOGGER.error("Error parsing BillById JSON: {}", e.getMessage(), e); helper.updateInboundLogFailure(inboundLog, e.getMessage(), "JSON_ERROR"); throw new CustomException("Invalid JSON received from API"); } } @Override public ResponseModel fetchBillById(BillFetchByBillIdRequest billFetchByBillIdRequest) { String apiName = "FetchBillById"; String txnId = helper.generateTxnId(15); String reqId = billFetchByBillIdRequest.getReqId(); String billId = billFetchByBillIdRequest.getBillId(); String fullUrl = agentDtaUrl + BBPSConstant.BILL_FETCH_BY_ID + "1ced7566-1add-4e01-9ed6-04cd67ac9684"; LOGGER.info("Initiating {} API for billId={} and reqId={}", apiName, billId, reqId); try { helper.validateMerchant(billFetchByBillIdRequest.getMid()); LOGGER.debug("Merchant validated successfully for MID={}", billFetchByBillIdRequest.getMid()); BharatConnectMaster headersData = beneficiaryDetailsRepository.findByOrgId("ORG001"); HttpHeaders headers = new HttpHeaders(); headers.set("x-digiledge-key", headersData.getXDigiledgeKey()); headers.set("agentId", headersData.getAgentId()); headers.set("tenantId", headersData.getTenantId()); headers.setContentType(MediaType.APPLICATION_JSON); LOGGER.info("Calling external BBPS API for FetchBillById, URL={}", fullUrl); Map responseMap = httpService.getRequest(fullUrl, headers); String responseData = responseMap.get("data"); LOGGER.info("Response received from BBPS for billId={} : {}", billId, responseData); ObjectMapper objectMapper = new ObjectMapper(); Map dataMap = objectMapper.readValue(responseData, new TypeReference>() {}); if (!dataMap.containsKey("genericResponse")) { LOGGER.warn("Invalid response structure received for billId={} | Response={}", billId, responseData); throw new CustomException( String.valueOf(dataMap.get("message")), // message String.valueOf(dataMap.get("status")), // status Integer.parseInt(String.valueOf(dataMap.get("statusCode"))), // statusCode String.valueOf(dataMap.get("remarks")) ); } BillDataResponse responseWrapper = objectMapper.convertValue(dataMap, BillDataResponse.class); GeneralResp generalResp = responseWrapper.getGenericResponse(); ResponseModel responseModel = new ResponseModel( generalResp.getMessage(), generalResp.getStatus(), Integer.parseInt(generalResp.getStatusCode()), responseWrapper ); helper.saveOrUpdateBillerData(responseWrapper, billFetchByBillIdRequest); LOGGER.info("Bill data saved successfully for billId={} | Status={}", billId, generalResp.getStatus()); return responseModel; } catch (JsonProcessingException e) { LOGGER.error("Error parsing responseData JSON for billId={} : {}", billId, e.getMessage(), e); throw new CustomException("Invalid JSON received from API"); } } @Override public ResponseModel initiateBillValidate(FetchBillValidatestRequest fetchBillValidateRequest) { // TODO Auto-generated method stub return null; } @Override public ResponseModel initiateBillValidateByBillId(BillFetchByBillIdRequest billRequest) { // TODO Auto-generated method stub return null; } /* * @Override public ResponseModel * initiateBillValidate(FetchBillValidatestRequest fetchBillValidateRequest) { * * ResponseModel responseModel = new ResponseModel(); try { * * helper.validateMerchant(fetchBillValidateRequest.getMid()); * * BharatConnectMaster headersData = * beneficiaryDetailsRepository.findByOrgId("ORG001"); * * if (headersData == null) { responseModel = new * ResponseModel("Tenant Id Is Null", "Not Found", BBPSConstant.BADREQUEST, * headersData); return responseModel; } * * HttpHeaders headers = new HttpHeaders(); headers.set("x-digiledge-key", * headersData.getXDigiledgeKey()); headers.set("agentId", * headersData.getAgentId()); headers.set("tenantId", * headersData.getTenantId()); * headers.setContentType(MediaType.APPLICATION_JSON); * * ObjectMapper objectMapper = new ObjectMapper(); * objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); String * json = objectMapper.writeValueAsString(fetchBillValidateRequest); String url * = agentDtaUrl + BBPSConstant.BILLER_VALIDATE_URL; * * Map responseMap = httpService.postRequest(url, json, * headers); String responseData = responseMap.get("data"); * * log.info("📦 Bill Fetch API Response: {}", responseData); * * if (responseData == null || responseData.isEmpty()) { throw new * CustomException("Empty response from BBPS server"); } * * // ✅ Convert JSON string → Map Map dataMap = * objectMapper.readValue(responseData, new TypeReference>() * {}); * * // ✅ Validate generalResp if (!dataMap.containsKey("genericResponse")) { * throw new CustomException("Invalid response structure from BBPS"); } * * // ✅ Convert to your custom DTO FetchBillValidatestResponse fetchBillResponse * = objectMapper.convertValue(dataMap, FetchBillValidatestResponse.class); * GeneralResp generalResponse = fetchBillResponse.getGenericResponse(); * * // ✅ Build final ResponseModel responseModel = new ResponseModel( * generalResponse.getMessage(), generalResponse.getStatus(), * Integer.parseInt(generalResponse.getStatusCode()), fetchBillResponse ); * * } catch (CustomException ex) { log.warn("Merchant validation failed: {}", * ex.getMessage()); return ex.getResponseModel(); // Directly return * ResponseModel from exception } catch (Exception e) { * log.error("❌ Exception in initiateBillFetch(): {}", e.getMessage(), e); * responseModel.setStatus("500"); * responseModel.setMessage("Exception occurred"); * responseModel.setData(e.getMessage()); } * * return responseModel; } * * @Override public ResponseModel * initiateBillValidateByBillId(BillFetchByBillIdRequest billRequest) { * * ResponseModel responseModel = new ResponseModel(); try { * helper.validateMerchant(billRequest.getMid()); * * BharatConnectMaster headersData = * beneficiaryDetailsRepository.findByOrgId("ORG001"); * * if (headersData == null) { responseModel = new * ResponseModel("Tenant Id Is Null", "Not Found", BBPSConstant.BADREQUEST, * headersData); return responseModel; } * * HttpHeaders headers = new HttpHeaders(); headers.set("x-digiledge-key", * headersData.getXDigiledgeKey()); headers.set("agentId", * headersData.getAgentId()); headers.set("tenantId", * headersData.getTenantId()); * headers.setContentType(MediaType.APPLICATION_JSON); * * ObjectMapper objectMapper = new ObjectMapper(); * objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); String * json = objectMapper.writeValueAsString(billRequest); String url = agentDtaUrl * + BBPSConstant.BILL_ID_URL + billRequest.getBillId(); * * log.info("Full URL: {}", url); * objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); * * // ✅ Call the API using httpService Map responseMap = * httpService.getRequest(url, headers); String responseData = * responseMap.get("data"); * * log.info("📦 Bill Fetch API Response: {}", responseData); * * if (responseData == null || responseData.isEmpty()) { throw new * CustomException("Empty response from BBPS server"); } * * // ✅ Convert JSON string → Map Map dataMap = * objectMapper.readValue(responseData, new TypeReference>() * {}); * * // ✅ Validate generalResp if (!dataMap.containsKey("genericResponse")) { * throw new CustomException("Invalid response structure from BBPS"); } * * // ✅ Convert to your custom DTO BillDataResponse fetchBillResponse = * objectMapper.convertValue(dataMap, BillDataResponse.class); GeneralResp * generalResponse = fetchBillResponse.getGenericResponse(); * * // ✅ Build final ResponseModel responseModel = new ResponseModel( * generalResponse.getRemarks(), generalResponse.getStatus(), * Integer.parseInt(generalResponse.getStatusCode()), fetchBillResponse ); * * } catch (CustomException ex) { log.warn("Merchant validation failed: {}", * ex.getMessage()); return ex.getResponseModel(); // Directly return * ResponseModel from exception } catch (Exception e) { * log.error("❌ Exception in initiateBillFetch(): {}", e.getMessage(), e); * responseModel.setStatus("500"); * responseModel.setMessage("Exception occurred"); * responseModel.setData(e.getMessage()); } * * return responseModel; } */ /* POLLING METHOD * public ResponseModel fetchBillById(BillFetchByBillIdRequest * billFetchByBillIdRequest, InboundTraceLog inboundLog) { String apiName = * "FetchBillById"; String txnId = helper.generateTxnId(15); String reqId = * billFetchByBillIdRequest.getReqId(); String billId = * billFetchByBillIdRequest.getBillId(); String url = agentDtaUrl + * BBPSConstant.BILL_FETCH_BY_ID + "1ced7566-1add-4e01-9ed6-04cd67ac9684"; * * helper.validateMerchant(billFetchByBillIdRequest.getMid()); * * BharatConnectMaster headersData = * beneficiaryDetailsRepository.findByOrgId("ORG001"); HttpHeaders headers = new * HttpHeaders(); headers.set("x-digiledge-key", * headersData.getXDigiledgeKey()); headers.set("agentId", * headersData.getAgentId()); headers.set("tenantId", * headersData.getTenantId()); * headers.setContentType(MediaType.APPLICATION_JSON); * * ObjectMapper objectMapper = new ObjectMapper(); OutboundTraceLog outboundLog * = helper.createOutboundLog(txnId, reqId, apiName, url, "GET", null); * * // ✅ Loop setup long startTime = System.currentTimeMillis(); long * maxWaitTimeMs = 40000; // 40 seconds long intervalMs = 5000; // 5 seconds * * while (true) { try { Map responseMap = * httpService.getRequest(url, headers); String responseData = * responseMap.get("data"); log.info("📦 Bill FetchById Response: {}", * responseData); * * helper.updateOutboundLogResponse(outboundLog, responseData); * * Map dataMap = objectMapper.readValue(responseData, new * TypeReference>() {}); * * if (!dataMap.containsKey("genericResponse")) { * helper.updateInboundLogFailure(inboundLog, responseData, "INVALID_RESPONSE"); * throw new CustomException( String.valueOf(dataMap.get("message")), * String.valueOf(dataMap.get("status")), * Integer.parseInt(String.valueOf(dataMap.get("statusCode"))), * String.valueOf(dataMap.get("remarks")) ); } * * BillDataResponse responseWrapper = objectMapper.convertValue(dataMap, * BillDataResponse.class); GeneralResp generalResp = * responseWrapper.getGenericResponse(); * * String status = generalResp.getStatus(); * log.info("📡 Current Bill Status: {}", status); // ✅ Check for "INPROGRESS" * or pending state if ("INPROGRESS".equalsIgnoreCase(status)) { if * (System.currentTimeMillis() - startTime > maxWaitTimeMs) { * helper.updateInboundLogFailure(inboundLog, "Timeout after 40 seconds", * "FAILED"); throw new CustomException("Timeout", "FAILED", 408, * "Bill fetch still in progress"); } Thread.sleep(intervalMs); continue; // * retry again } // ✅ Final successful response * helper.updateInboundLogSuccess(inboundLog, responseData, * generalResp.getStatus()); return new ResponseModel( generalResp.getMessage(), * generalResp.getStatus(), Integer.parseInt(generalResp.getStatusCode()), * responseWrapper ); } catch (JsonProcessingException e) { * log.error("Error parsing BillById JSON: {}", e.getMessage()); * helper.updateInboundLogFailure(inboundLog, e.getMessage(), "JSON_ERROR"); * throw new CustomException("Invalid JSON received from API"); } catch * (InterruptedException e) { Thread.currentThread().interrupt(); * helper.updateInboundLogFailure(inboundLog, e.getMessage(), "INTERRUPTED"); * throw new CustomException("Thread interrupted during bill status check"); } } * } */ }