Rev 35458 | Rev 35654 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed
package com.spice.profitmandi.web.controller;import com.fasterxml.jackson.databind.ObjectMapper;import com.razorpay.Utils;import com.spice.profitmandi.common.exception.ProfitMandiBusinessException;import com.spice.profitmandi.common.web.util.ResponseSender;import com.spice.profitmandi.dao.entity.fofo.Customer;import com.spice.profitmandi.dao.entity.fofo.FofoOrder;import com.spice.profitmandi.dao.entity.fofo.UpsellRazorpayPaymentStatus;import com.spice.profitmandi.dao.repository.cs.AgentRecordingRepository;import com.spice.profitmandi.dao.repository.fofo.CustomerRepository;import com.spice.profitmandi.dao.repository.fofo.FofoOrderRepository;import com.spice.profitmandi.dao.repository.fofo.UpsellRazorpayPaymentStatusRepository;import com.spice.profitmandi.service.integrations.kommuno.RecordingService;import com.spice.profitmandi.service.integrations.smartping.SmartPingService;import com.spice.profitmandi.service.integrations.smartping.model.CallDetailModel;import com.spice.profitmandi.service.integrations.smartping.model.PushCallLogModel;import com.spice.profitmandi.web.util.MVCResponseSender;import org.apache.logging.log4j.LogManager;import org.apache.logging.log4j.Logger;import org.json.JSONObject;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Value;import org.springframework.http.HttpStatus;import org.springframework.http.MediaType;import org.springframework.http.ResponseEntity;import org.springframework.stereotype.Controller;import org.springframework.transaction.annotation.Transactional;import org.springframework.ui.Model;import org.springframework.web.bind.annotation.ModelAttribute;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;import javax.servlet.http.HttpServletRequest;@Controller@Transactional(rollbackFor = Throwable.class)public class WebHookController {@AutowiredMVCResponseSender mvcResponseSender;@Autowiredprivate ResponseSender<?> responseSender;@Autowiredprivate FofoOrderRepository fofoOrderRepository;@Autowiredprivate CustomerRepository customerRepository;private static final Logger LOGGER = LogManager.getLogger(WebHookController.class);@AutowiredSmartPingService smartPingService;@AutowiredRecordingService recordingService;@AutowiredAgentRecordingRepository agentRecordingRepository;/*** Knowlarity Click2Call Webhook - First Event (Call Report Handler).* <p>* This endpoint is called by Knowlarity when a click-to-call event is initiated or completed.* Knowlarity sends call detail data as form-urlencoded POST, which is mapped to {@link CallDetailModel}.* <p>* The incoming third-party data from Knowlarity may not always match our expected model fields.* If Knowlarity changes their payload structure or sends unexpected/null values,* errors can occur during binding or in {@link RecordingService#updateAgentRecording}.* <p>* URL: POST /click2call/report-handler* Content-Type: application/x-www-form-urlencoded* Third-party: Knowlarity*/@RequestMapping(value = "/click2call/report-handler", method = RequestMethod.POST, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)public ResponseEntity<?> click2callReportHandlerPost(HttpServletRequest request, Model model, @ModelAttribute CallDetailModel callDetail) throws Exception {// Log raw parameters from Knowlarity before processing, so we can see exactly what they sentLOGGER.info("report-handler raw params from Knowlarity: {}", request.getParameterMap());try {LOGGER.info("first event call detail (mapped): {}", callDetail);recordingService.updateAgentRecording(callDetail);} catch (Exception e) {LOGGER.error("Error processing report-handler webhook. Raw params: {}", request.getParameterMap(), e);}return responseSender.ok(true);}/*** Knowlarity Click2Call Webhook - Recording URL Update.** This endpoint is called by Knowlarity after a call recording is available.* Knowlarity sends updated call detail (including the recording URL) as form-urlencoded POST,* mapped to {@link CallDetailModel}.** The incoming third-party data from Knowlarity may not always match our expected model fields.* If Knowlarity changes their payload structure or sends unexpected/null values,* errors can occur during binding or in {@link RecordingService#updateAgentRecordingUrl}.** URL: POST /click2call/report-handler/recording-url* Content-Type: application/x-www-form-urlencoded* Third-party: Knowlarity*/@RequestMapping(value = "/click2call/report-handler/recording-url", method = RequestMethod.POST, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)public ResponseEntity<?> click2callReportHandlerUpdateRecordingUrlPost(HttpServletRequest request, Model model, @ModelAttribute CallDetailModel callDetail) throws Exception {// Log raw parameters from Knowlarity before processing, so we can see exactly what they sentLOGGER.info("recording-url raw params from Knowlarity: {}", request.getParameterMap());try {LOGGER.info("update call detail (mapped): {}", callDetail);recordingService.updateAgentRecordingUrl(callDetail);} catch (Exception e) {LOGGER.error("Error processing recording-url webhook. Raw params: {}", request.getParameterMap(), e);}return responseSender.ok(true);}/*** Knowlarity Click2Call Webhook - Push Call Log.** This endpoint is called by Knowlarity to push call log data in JSON format,* mapped to {@link PushCallLogModel}.** The incoming third-party data from Knowlarity may not always match our expected model fields.* If Knowlarity changes their JSON payload structure or sends unexpected/null values,* errors can occur during deserialization or in {@link RecordingService#updateAgentCallLog}.** URL: POST /click2call/push-call-log-handler* Content-Type: application/json* Third-party: Knowlarity*/@RequestMapping(value = "/click2call/push-call-log-handler", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)public ResponseEntity<?> click2callPushLogHandler(HttpServletRequest request, @RequestBody String rawBody) throws Exception {// Log raw JSON body from Knowlarity before processing, so we can see exactly what they sentLOGGER.info("push-call-log raw JSON from Knowlarity: {}", rawBody);try {ObjectMapper objectMapper = new ObjectMapper();PushCallLogModel pushCallLogModel = objectMapper.readValue(rawBody, PushCallLogModel.class);LOGGER.info("update call detail - push log (mapped): {}", pushCallLogModel);recordingService.updateAgentCallLog(pushCallLogModel);} catch (Exception e) {LOGGER.error("Error processing push-call-log webhook. Raw JSON: {}", rawBody, e);}return responseSender.ok("true");}@Value("${razorpay.account.keySecret}")private String razorpaySecret;@Autowiredprivate UpsellRazorpayPaymentStatusRepository upsellRazorpayPaymentStatusRepository;@RequestMapping(value = "/upsellPayment/callback", method = RequestMethod.GET)public ResponseEntity<?> handleCallback(HttpServletRequest request) {try {// Retrieve the Razorpay parameters from the query stringString paymentId = request.getParameter("razorpay_payment_id");String razorpaySignature = request.getParameter("razorpay_signature");String paymentLinkId = request.getParameter("razorpay_payment_link_id");String paymentLinkStatus = request.getParameter("razorpay_payment_link_status");String paymentLinkReferenceId = request.getParameter("razorpay_payment_link_reference_id");String orderId = request.getParameter("orderId");String insuranceAmount = request.getParameter("insuranceAmount");// Check for required parametersif (paymentId == null || razorpaySignature == null || orderId == null || insuranceAmount == null) {return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("Missing required parameters");}JSONObject options = new JSONObject();options.put("payment_link_reference_id", paymentLinkReferenceId);options.put("razorpay_payment_id", paymentId);options.put("payment_link_status", paymentLinkStatus);options.put("payment_link_id", paymentLinkId);options.put("razorpay_signature", razorpaySignature);boolean status = Utils.verifyPaymentLink(options, razorpaySecret);LOGGER.info("status signature {}", status);if (!status) {return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("Invalid signature");}FofoOrder fofoOrder = fofoOrderRepository.selectByOrderId(Integer.parseInt(orderId));Customer customer = customerRepository.selectById(fofoOrder.getCustomerId());// Handle the payment status (you may need to add more logic depending on the status)updatePaymentStatus(paymentId, paymentLinkReferenceId, "captured", orderId, insuranceAmount);// Construct the HTML responseString htmlResponse = "<html>" +"<head>" +"<style>" +" .container { background-color: #f0f0f0; padding: 20px; text-align: center; border-radius: 8px;max-width:600px;width:auto;margin:10px auto; }" +" .success-icon { color: green; font-size:30px; margin-right: 10px;border-radius: 50%;border: 2px solid;padding: 1px 7px; }" +" .message { font-size: 18px; margin-top: 10px; }" +"</style>" +"</head>" +"<body>" +" <div class='container'>" +" <span class='success-icon'>✓</span>" +" <div class='message'>" +" Hi " + customer.getFirstName() + ",<br>" +" Your payment of " + insuranceAmount + " was successfully completed.<br>" +" Your payment ID is: " + paymentId + "." +" </div>" +" </div>" +"</body>" +"</html>";return ResponseEntity.ok().contentType(MediaType.TEXT_HTML).body(htmlResponse);} catch (Exception e) {e.printStackTrace();return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Error processing callback");}}private void updatePaymentStatus(String paymentId, String paymentLinkReferenceId, String status, String orderId, String amount) throws ProfitMandiBusinessException {int id = Integer.parseInt(paymentLinkReferenceId.replaceFirst("^0+(?!$)", ""));UpsellRazorpayPaymentStatus upsellRazorpayPaymentStatus = upsellRazorpayPaymentStatusRepository.selectById(id);upsellRazorpayPaymentStatus.setPaymentId(paymentId);upsellRazorpayPaymentStatus.setReferenceId(paymentLinkReferenceId);upsellRazorpayPaymentStatus.setPaymentStatus(status);upsellRazorpayPaymentStatus.setPayment(Float.parseFloat(amount));}}