package com.billpaymentservice.bbps.serviceImpl; import java.util.Map; import java.util.Optional; import org.modelmapper.ModelMapper; import org.modelmapper.convention.MatchingStrategies; 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 com.billpaymentservice.bbps.dto.GeneralResp; import com.billpaymentservice.bbps.dto.ResponseModel; import com.billpaymentservice.bbps.dto.request.BillPaymentRequest; import com.billpaymentservice.bbps.dto.request.TransactionStatusRequest; import com.billpaymentservice.bbps.dto.request.TrasactionStatusByClientReqIdRequest; import com.billpaymentservice.bbps.dto.response.BillPaymentResponse; import com.billpaymentservice.bbps.dto.response.TransactionStatusResponse; import com.billpaymentservice.bbps.dto.sentRequest.BillPaymentForwardRequest; import com.billpaymentservice.bbps.entites.AgentFundTransfer; import com.billpaymentservice.bbps.entites.BharatConnectMaster; import com.billpaymentservice.bbps.entites.BillPayment; import com.billpaymentservice.bbps.entites.InboundTraceLog; import com.billpaymentservice.bbps.entites.OutboundTraceLog; import com.billpaymentservice.bbps.entites.PaymentInitiate; import com.billpaymentservice.bbps.entites.TransactionMaster; import com.billpaymentservice.bbps.exception.CustomException; import com.billpaymentservice.bbps.repository.AgentFundTransferRepository; import com.billpaymentservice.bbps.repository.BharatConnectMasterRepository; import com.billpaymentservice.bbps.repository.InboundTraceLogRepository; import com.billpaymentservice.bbps.repository.OutboundTraceLogRepository; import com.billpaymentservice.bbps.repository.PaymentIntiateRepository; import com.billpaymentservice.bbps.repository.PaymentRepository; import com.billpaymentservice.bbps.repository.TransactionMasterRepository; import com.billpaymentservice.bbps.service.PaymentService; import com.billpaymentservice.bbps.util.BBPSConstant; import com.billpaymentservice.bbps.util.EntityRelationshipUtil; 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; @Service public class PaymentServiceImpl implements PaymentService { @Value("${payment.data.url}") private String paymentUrl; private final BharatConnectMasterRepository beneficiaryDetailsRepository; private final HttpService httpService; private final Helper helper; private final PaymentRepository paymentRepository; private final InboundTraceLogRepository inboundTraceLogRepository; private final OutboundTraceLogRepository outboundTraceLogRepository; private final PaymentIntiateRepository paymentIntiateRepository; private final TransactionMasterRepository transactionMasterRepository; private final AgentFundTransferRepository agentFundTransferRepository; private static final Logger LOGGER = LoggerFactory.getLogger(PaymentServiceImpl.class); public PaymentServiceImpl(BharatConnectMasterRepository beneficiaryDetailsRepository, HttpService httpService, Helper helper, PaymentRepository paymentRepository, InboundTraceLogRepository inboundTraceLogRepository, OutboundTraceLogRepository outboundTraceLogRepository, PaymentIntiateRepository paymentIntiateRepository, TransactionMasterRepository transactionMasterRepository, AgentFundTransferRepository agentFundTransferRepository) { this.beneficiaryDetailsRepository = beneficiaryDetailsRepository; this.httpService = httpService; this.helper = helper; this.paymentRepository = paymentRepository; this.inboundTraceLogRepository = inboundTraceLogRepository; this.outboundTraceLogRepository = outboundTraceLogRepository; this.paymentIntiateRepository = paymentIntiateRepository; this.transactionMasterRepository = transactionMasterRepository; this.agentFundTransferRepository = agentFundTransferRepository; } @Override public ResponseModel initiatePayment(BillPaymentRequest billPaymentRequest) { String apiName = "InitiatePayment"; String txnId = helper.generateTxnId(15); String requestId = billPaymentRequest.getReqId(); String url = paymentUrl + BBPSConstant.PAYMENT_URL; billPaymentRequest.setClientReqId(txnId); LOGGER.info("Initiating payment for reqId={} and txnId={}", requestId, txnId); 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); BillPaymentForwardRequest forwardReq = helper.billPaymentForwardRequest(billPaymentRequest); String forwardjson = helper.convertObjectToJson(forwardReq); String requestJson = helper.convertObjectToJson(billPaymentRequest); InboundTraceLog inboundLog = helper.createInboundLog(requestId, txnId, apiName, billPaymentRequest.getMid(), "POST", requestJson); OutboundTraceLog outboundLog = helper.createOutboundLog(txnId, requestId, apiName, url, "POST",forwardjson); try { LOGGER.info("Calling BBPS payment URL: {}", url); Map responseMap = httpService.postRequest(url, forwardjson, headers); String responseData = responseMap.get("data"); LOGGER.info("Payment API Response: {}", responseData); helper.updateOutboundLogResponse(outboundLog, responseData); if (responseData == null || responseData.isEmpty()) { helper.updateInboundLogFailure(inboundLog, "Empty response from BBPS", "FAILED"); LOGGER.warn("Empty response received from BBPS for txnId={}", txnId); throw new CustomException("Empty response from BBPS server"); } Map dataMap = objectMapper.readValue(responseData, new TypeReference>() {}); if (!dataMap.containsKey("genericResponse")) { helper.updateInboundLogFailure(inboundLog, responseData, "INVALID_STRUCTURE"); LOGGER.warn("Invalid response structure received for txnId={}", txnId); throw new CustomException("Invalid response structure from BBPS"); } BillPaymentResponse billPaymentResponse = objectMapper.convertValue(dataMap, BillPaymentResponse.class); GeneralResp generalResponse = billPaymentResponse.getGenericResponse(); ModelMapper mapper = new ModelMapper(); mapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT); BillPayment entity = mapper.map(billPaymentResponse, BillPayment.class); paymentRepository.save(entity); LOGGER.info("BillPayment entity saved successfully for txnId={}", txnId); helper.updateInboundLogSuccess(inboundLog, responseData, generalResponse.getStatus()); PaymentInitiate paymentInitiate = new PaymentInitiate(); paymentInitiate.setTxnId(txnId); paymentInitiate.setReqId(requestId); paymentInitiate.setBankTxnId(billPaymentResponse.getTxnId()); paymentInitiate.setStatus(billPaymentResponse.getStatus()); paymentIntiateRepository.save(paymentInitiate); LOGGER.info("PaymentInitiate entry saved successfully for txnId={}", txnId); TransactionStatusRequest transactionStatusRequest = new TransactionStatusRequest(); transactionStatusRequest.setTxnId(billPaymentResponse.getTxnId()); transactionStatusRequest.setReqId(requestId); try { Thread.sleep(9000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); LOGGER.warn("Thread interrupted during payment delay: {}", e.getMessage()); } LOGGER.info("Checking transaction status for txnId={}", billPaymentResponse.getTxnId()); return checkTrasactionStatus(transactionStatusRequest, inboundLog); } catch (JsonProcessingException e) { LOGGER.error("JSON Parsing Error in initiatePayment: {}", e.getMessage(), e); helper.updateInboundLogFailure(inboundLog, e.getMessage(), "JSON_ERROR"); throw new CustomException("Invalid JSON received from API"); } } public ResponseModel checkTrasactionStatus(TransactionStatusRequest transactionStatusRequest, InboundTraceLog inboundLog) { String apiName = "CheckTransactionStatus"; String txnId = helper.generateTxnId(15); String requestId = transactionStatusRequest.getReqId(); String url = paymentUrl + BBPSConstant.CHECK_TRANSACTION_URL + transactionStatusRequest.getTxnId(); LOGGER.info("Checking transaction status (internal) for txnId={} and reqId={}", transactionStatusRequest.getTxnId(), requestId); 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, requestId, apiName, url, "GET", null); Map responseMap = httpService.getRequest(url, headers); String responseData = responseMap.get("data"); LOGGER.info("Transaction status response: {}", responseData); helper.updateOutboundLogResponse(outboundLog, responseData); try { Map dataMap = objectMapper.readValue(responseData, new TypeReference>() {}); if (!dataMap.containsKey("generalResp")) { helper.updateInboundLogFailure(inboundLog, responseData, "INVALID_RESPONSE"); LOGGER.warn("Invalid transaction status response for txnId={}", transactionStatusRequest.getTxnId()); 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")) ); } TransactionStatusResponse responseWrapper = objectMapper.convertValue(dataMap, TransactionStatusResponse.class); GeneralResp generalResp = responseWrapper.getGeneralResp(); LOGGER.info("Transaction status received: {}", generalResp.getStatus()); ModelMapper mapper = new ModelMapper(); mapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT); TransactionMaster transactionMaster = mapper.map(responseWrapper.getTransactionDAO(), TransactionMaster.class); transactionMaster.setTxnId(txnId); transactionMaster.setReqId(transactionStatusRequest.getReqId()); EntityRelationshipUtil.linkTransactionMasterRelationships(transactionMaster); transactionMasterRepository.save(transactionMaster); LOGGER.info("TransactionMaster saved successfully for txnId={}", txnId); AgentFundTransfer agentFundTransfer = mapper.map(responseWrapper.getAgentFundTransfer(), AgentFundTransfer.class); agentFundTransfer.setTxnId(txnId); agentFundTransfer.setReqId(transactionStatusRequest.getReqId()); agentFundTransfer.setTransactionMasterId(transactionMaster.getBankTxnId()); agentFundTransferRepository.save(agentFundTransfer); LOGGER.info("AgentFundTransfer entry saved successfully for txnId={}", txnId); ResponseModel responseModel = new ResponseModel( generalResp.getMessage(), generalResp.getStatus(), Integer.parseInt(generalResp.getStatusCode()), responseWrapper ); helper.updateInboundLogSuccess(inboundLog, responseData, generalResp.getStatus()); return responseModel; } catch (JsonProcessingException e) { LOGGER.error("Error parsing transaction status JSON: {}", e.getMessage(), e); helper.updateInboundLogFailure(inboundLog, e.getMessage(), "JSON_ERROR"); throw new CustomException("Invalid JSON received from API"); } } @Override public ResponseModel checkTrasactionStatus(TransactionStatusRequest transactionStatusRequest) { helper.validateMerchant(transactionStatusRequest.getMid()); String txnID = helper.generateTxnId(15); String apiName = "checkTrasactionStatus"; String requestId = transactionStatusRequest.getReqId(); String requestJson = helper.convertObjectToJson(transactionStatusRequest); String url = paymentUrl + BBPSConstant.CHECK_TRANSACTION_URL + transactionStatusRequest.getTxnId(); LOGGER.info("Checking transaction status (public) for txnId={} and reqId={}", transactionStatusRequest.getTxnId(), requestId); 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); InboundTraceLog inboundLog = helper.createInboundLog(requestId, txnID, apiName, transactionStatusRequest.getMid(), "POST", requestJson); OutboundTraceLog outboundLog = helper.createOutboundLog(txnID, requestId, apiName, url, "GET", null); ObjectMapper objectMapper = new ObjectMapper(); Map responseMap = httpService.getRequest(url, headers); String responseData = responseMap.get("data"); LOGGER.info("Transaction status response data: {}", responseData); helper.updateOutboundLogResponse(outboundLog, responseData); try { Map dataMap = objectMapper.readValue(responseData, new TypeReference>() {}); if (!dataMap.containsKey("generalResp")) { helper.updateInboundLogFailure(inboundLog, responseData, String.valueOf(dataMap.get("status"))); LOGGER.warn("Invalid transaction status response for txnId={} from BBPS", transactionStatusRequest.getTxnId()); 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")) ); } TransactionStatusResponse responseWrapper = objectMapper.convertValue(dataMap, TransactionStatusResponse.class); GeneralResp generalResp = responseWrapper.getGeneralResp(); String status = generalResp.getStatus(); LOGGER.info("Current transaction status: {}", status); ResponseModel responseModel = new ResponseModel( generalResp.getMessage(), generalResp.getStatus(), Integer.parseInt(generalResp.getStatusCode()), responseWrapper ); helper.updateInboundLogSuccess(inboundLog, responseData, generalResp.getStatus()); return responseModel; } catch (JsonProcessingException e) { LOGGER.error("Error parsing responseData JSON: {}", e.getMessage(), e); helper.updateInboundLogFailure(inboundLog, e.getMessage(), "JSON_ERROR"); throw new CustomException("Invalid JSON received from API"); } } @Override public ResponseModel checkTrasactionStatusByClientReqId(TrasactionStatusByClientReqIdRequest transactionStatusRequest) { helper.validateMerchant(transactionStatusRequest.getMid()); String txnID = helper.generateTxnId(15); String apiName = "checkTrasactionStatusbyClientReqId"; String requestId = transactionStatusRequest.getReqId(); String clientReqId = transactionStatusRequest.getClientReqId(); String requestJson = helper.convertObjectToJson(transactionStatusRequest); LOGGER.info("Checking transaction status for clientReqId={} and reqId={}", clientReqId, requestId); // Fetch Txn from DB Optional paymentInitiate = paymentIntiateRepository.findByReqId(clientReqId); if (paymentInitiate.isEmpty()) { throw new CustomException("No Transaction Found for given Client Req Id"); } String url = paymentUrl + BBPSConstant.CHECK_TRANSACTION_BY_CLIENT_REQ_ID_URL + paymentInitiate.get().getTxnId(); // Prepare headers 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); // 🔵 Create Inbound + Outbound Logs InboundTraceLog inboundLog = helper.createInboundLog( requestId, txnID, apiName, transactionStatusRequest.getMid(), "POST", requestJson); OutboundTraceLog outboundLog = helper.createOutboundLog( txnID, requestId, apiName, url, "GET", null); ObjectMapper objectMapper = new ObjectMapper(); try { LOGGER.info("Calling BBPS Transaction Status API URL={}", url); Map responseMap = httpService.getRequest(url, headers); String responseData = responseMap.get("data"); LOGGER.info("Transaction Status Response: {}", responseData); helper.updateOutboundLogResponse(outboundLog, responseData); // Parse response Map dataMap = objectMapper.readValue( responseData, new TypeReference>() {}); // Validate response if (!dataMap.containsKey("generalResp")) { helper.updateInboundLogFailure(inboundLog, responseData, "INVALID_RESPONSE"); LOGGER.warn("Invalid transaction status response for clientReqId={} | Response={}", clientReqId, responseData); 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")) ); } // Convert into proper DTO TransactionStatusResponse responseWrapper = objectMapper.convertValue(dataMap, TransactionStatusResponse.class); GeneralResp generalResp = responseWrapper.getGeneralResp(); LOGGER.info("Transaction Status for clientReqId={} => {}", clientReqId, generalResp.getStatus()); helper.updateInboundLogSuccess(inboundLog, responseData, generalResp.getStatus()); return new ResponseModel( generalResp.getMessage(), generalResp.getStatus(), Integer.parseInt(generalResp.getStatusCode()), responseWrapper ); } catch (JsonProcessingException e) { LOGGER.error("Error parsing JSON in transaction status API: {}", e.getMessage(), e); helper.updateInboundLogFailure(inboundLog, e.getMessage(), "JSON_ERROR"); throw new CustomException("Invalid JSON received from API"); } } } /* PollBack logic for the fetchTxnByTxnId API * @Override public ResponseModel fetchTxnByTxnId(BillFetchByBillIdRequest * billFetchByBillIdRequest) { * * String billId = billFetchByBillIdRequest.getBillId(); String fullUrl = * agentDtaUrl + BBPSConstant.BILL_FETCH_BY_ID+billId; * System.out.print(fullUrl); * * BeneficiaryDetails * 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(); * * try { 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); * * return responseModel; * * } catch (JsonProcessingException e) { * LOGGER.error("Error parsing responseData JSON: {}", e.getMessage()); throw * new CustomException("Invalid JSON received from API"); } * * } */ /* * public ResponseModel checkTrasactionStatus(TransactionStatusRequest * transactionStatusRequest,InboundTraceLog inboundLog) { String apiName = * "CheckTransactionStatus"; String txnId = helper.generateTxnId(15); //our * genrated txnId String requestId = transactionStatusRequest.getReqId(); String * url = paymentUrl + BBPSConstant.CHECK_TRANSACTION_URL + * transactionStatusRequest.getTxnId(); ; * * 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, requestId, * apiName, url, "GET", null); * * long startTime = System.currentTimeMillis(); long maxWaitTimeMs = 40000; long * intervalMs = 5000; * * while (true) { Map responseMap = httpService.getRequest(url, * headers); String responseData = responseMap.get("data"); * LOGGER.info("Transaction status response: {}", responseData); * * helper.updateOutboundLogResponse(outboundLog, responseData); * * try { Map dataMap = objectMapper.readValue(responseData, new * TypeReference>() {}); * * if (!dataMap.containsKey("generalResp")) { * 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")) ); } * * TransactionStatusResponse responseWrapper = * objectMapper.convertValue(dataMap, TransactionStatusResponse.class); * GeneralResp generalResp = responseWrapper.getGeneralResp(); * * String status = generalResp.getStatus(); * LOGGER.info("Current transaction status: {}", status); * * if ("INPROGRESS".equalsIgnoreCase(status)) { if (System.currentTimeMillis() - * startTime > maxWaitTimeMs) { helper.updateInboundLogFailure(inboundLog, * "Timeout after 40 seconds", "FAILED"); throw new * CustomException("Timeout waiting for transaction completion", "FAILED", 408, * "Transaction still in progress"); } * * Thread.sleep(intervalMs); continue; } * * ResponseModel responseModel = new ResponseModel( generalResp.getMessage(), * generalResp.getStatus(), Integer.parseInt(generalResp.getStatusCode()), * responseWrapper ); * * helper.updateInboundLogSuccess(inboundLog, responseData, * generalResp.getStatus()); return responseModel; * * } catch (JsonProcessingException e) { * LOGGER.error("Error parsing transaction status 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 transaction check"); } } * } * * */