package com.billpaymentservice.bbps.serviceImpl; import java.time.LocalDateTime; import java.util.Map; 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 @Slf4j 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; 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) { String apiName = "InitiateBillFetch"; String txnId = helper.generateTxnId(15); String requestId = fetchBillRequest.getReqId(); String url = 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, url, "POST", requestJson); try { Map responseMap = httpService.postRequest(url, requestJson, headers); String responseData = responseMap.get("data"); log.info("📦 Bill Fetch API 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")) ); } BillMaster billMaster = new BillMaster(); billMaster.setBillId((String) dataMap.get("billId")); billMaster.setReqId(fetchBillRequest.getReqId()); billMaster.setTxnId(txnId); billMasterRepository.save(billMaster); FetchBillResponse fetchBillResponse = objectMapper.convertValue(dataMap, FetchBillResponse.class); GeneralResp generalResponse = fetchBillResponse.getGenericResponse(); helper.updateInboundLogSuccess(inboundLog, responseData, generalResponse.getStatus()); //save intitate bill here BillInitiate billInitiate = new BillInitiate(); billInitiate.setBillId((String) dataMap.get("billId")); billInitiate.setReqId(fetchBillRequest.getReqId()); billInitiate.setTxnId(txnId); billIntiateRepository.save(billInitiate); BillFetchByBillIdRequest billFetchByBillIdRequest = new BillFetchByBillIdRequest(); billFetchByBillIdRequest.setBillId(fetchBillResponse.getBillId()); billFetchByBillIdRequest.setMid(fetchBillRequest.getMid()); billFetchByBillIdRequest.setReqId(requestId); try { Thread.sleep(2000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); // restore interrupted flag log.warn("Thread interrupted during delay: {}", e.getMessage()); } return fetchBillById(billFetchByBillIdRequest, inboundLog); } catch (JsonProcessingException e) { log.error("❌ JSON Parsing Error: {}", e.getMessage()); helper.updateInboundLogFailure(inboundLog, e.getMessage(), "JSON_ERROR"); throw new CustomException("Invalid JSON received from API"); } } 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); 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 // ✅ Final successful response helper.updateInboundLogSuccess(inboundLog, responseData, generalResp.getStatus()); helper.saveOrUpdateBillerData(responseWrapper,billFetchByBillIdRequest); 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"); } } @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"; try { 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); Map responseMap = httpService.getRequest(fullUrl, headers); String responseData = responseMap.get("data"); log.info("data is :{}", responseData); ObjectMapper objectMapper = new ObjectMapper(); Map dataMap = objectMapper.readValue(responseData, new TypeReference>() { }); if (!dataMap.containsKey("genericResponse")) { throw new CustomException(String.valueOf(dataMap.get("message")), // message String.valueOf(dataMap.get("status")), // status Integer.parseInt(String.valueOf(dataMap.get("statusCode"))), // statusCode (int) 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); return responseModel; } catch (JsonProcessingException e) { log.error("Error parsing responseData JSON: {}", e.getMessage()); throw new CustomException("Invalid JSON received from API"); } } @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"); } } * } */ }