diff --git a/src/main/java/com/billpaymentservice/bbps/controller/PaymentController.java b/src/main/java/com/billpaymentservice/bbps/controller/PaymentController.java index 326d536..ae246ba 100644 --- a/src/main/java/com/billpaymentservice/bbps/controller/PaymentController.java +++ b/src/main/java/com/billpaymentservice/bbps/controller/PaymentController.java @@ -36,7 +36,7 @@ public class PaymentController { @PostMapping(value = "/intiatePayment", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity requestBillFetch( - @Validated @RequestBody BillPaymentRequest billPaymentRequest) { + @RequestBody BillPaymentRequest billPaymentRequest) { ResponseModel response = paymentService.initiatePayment(billPaymentRequest); return ResponseEntity.ok(response); diff --git a/src/main/java/com/billpaymentservice/bbps/dto/request/BillPaymentRequest.java b/src/main/java/com/billpaymentservice/bbps/dto/request/BillPaymentRequest.java index d34d13a..5a87b3b 100644 --- a/src/main/java/com/billpaymentservice/bbps/dto/request/BillPaymentRequest.java +++ b/src/main/java/com/billpaymentservice/bbps/dto/request/BillPaymentRequest.java @@ -11,43 +11,33 @@ import lombok.Setter; @Setter public class BillPaymentRequest extends BaseClass { - @NotBlank(message = "ref id cannot be null or empty") private String refId; - @NotBlank(message = "bill id cannot be null or empty") private String billId; // Change the field to Boolean (wrapper) type to allow @NotNull validation - @NotNull(message = "Amount options cannot be null.") private boolean amountOptions; // Changed to Boolean (wrapper type) - @NotBlank(message = "payment mode cannot be null or empty") private String paymentMode; - @NotBlank(message = "payment information cannot be null or empty") private String paymentInformation; - @NotBlank(message = "biller id cannot be null or empty") private String billerId; - @NotBlank(message = "biller name cannot be null or empty") private String billerName; - @NotBlank(message = "biller category cannot be null or empty") private String billerCategory; private String macAdress; private String customerMobNo; - @NotNull(message = "Transaction amount cannot be null.") - private Integer txnAmount; // Changed to @NotNull as it's an Integer (non-nullable) + private String txnAmount; // Changed to @NotNull as it's an Integer (non-nullable) private String paymentRefId; private String clientReqId; - @NotBlank(message = "payment channel cannot be null or empty") private String paymentChannel; private List deviceBlockTags; diff --git a/src/main/java/com/billpaymentservice/bbps/dto/sentRequest/BillPaymentForwardRequest.java b/src/main/java/com/billpaymentservice/bbps/dto/sentRequest/BillPaymentForwardRequest.java index 047432e..e5df46c 100644 --- a/src/main/java/com/billpaymentservice/bbps/dto/sentRequest/BillPaymentForwardRequest.java +++ b/src/main/java/com/billpaymentservice/bbps/dto/sentRequest/BillPaymentForwardRequest.java @@ -29,7 +29,7 @@ public class BillPaymentForwardRequest { private String billerCategory; private String macAdress; private String customerMobNo; - private Integer txnAmount; + private String txnAmount; private String paymentRefId; private String clientReqId; private String paymentChannel; diff --git a/src/main/java/com/billpaymentservice/bbps/entites/AuditListener.java b/src/main/java/com/billpaymentservice/bbps/entites/AuditListener.java index a3d72d5..ebf0511 100644 --- a/src/main/java/com/billpaymentservice/bbps/entites/AuditListener.java +++ b/src/main/java/com/billpaymentservice/bbps/entites/AuditListener.java @@ -28,7 +28,6 @@ public class AuditListener { // Ensure isDeleted / deleted exists and is set to false on create setAlways(entity, "isDeleted", Boolean.FALSE); - setAlways(entity, "deleted", Boolean.FALSE); } @PreUpdate diff --git a/src/main/java/com/billpaymentservice/bbps/entites/PaymentChannel.java b/src/main/java/com/billpaymentservice/bbps/entites/PaymentChannel.java index 831435a..74c77dd 100644 --- a/src/main/java/com/billpaymentservice/bbps/entites/PaymentChannel.java +++ b/src/main/java/com/billpaymentservice/bbps/entites/PaymentChannel.java @@ -8,7 +8,6 @@ import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; import jakarta.validation.constraints.Min; -import jakarta.validation.constraints.NotBlank; import lombok.Getter; import lombok.Setter; import lombok.ToString; @@ -17,8 +16,8 @@ import lombok.ToString; @Setter @Entity @ToString -@Table(name = "payment_channel") -public class PaymentChannel extends Auditable { +@Table(name = "bbps_payment_channel") +public class PaymentChannel { @Id @Column(name = "payment_channel_limit_id") @@ -42,4 +41,4 @@ public class PaymentChannel extends Auditable { @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "biller_id") private BillerDetails biller; -} \ No newline at end of file +} diff --git a/src/main/java/com/billpaymentservice/bbps/exception/GlobalExceptionHandler.java b/src/main/java/com/billpaymentservice/bbps/exception/GlobalExceptionHandler.java index 49330b4..a904a52 100644 --- a/src/main/java/com/billpaymentservice/bbps/exception/GlobalExceptionHandler.java +++ b/src/main/java/com/billpaymentservice/bbps/exception/GlobalExceptionHandler.java @@ -9,76 +9,145 @@ import org.springframework.http.*; import org.springframework.validation.FieldError; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.*; +import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.NoHandlerFoundException; import com.billpaymentservice.bbps.dto.ResponseModel; +import jakarta.servlet.http.HttpServletRequest; + @ControllerAdvice public class GlobalExceptionHandler { private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class); - - - @ExceptionHandler(MethodArgumentNotValidException.class) - public ResponseEntity> handleValidation(MethodArgumentNotValidException ex) { - List> fieldErrors = ex.getBindingResult().getFieldErrors() - .stream() - .map(fe -> Map.of("field", fe.getField(), "message", fe.getDefaultMessage())) - .collect(Collectors.toList()); - logger.warn("Validation failed: {}", fieldErrors); - ResponseModel>> resp = new ResponseModel<>( - "Validation failed for request", - "FAILED", - HttpStatus.BAD_REQUEST.value(), - null + /** + * 🔥 Common logger formatter for all exceptions + */ + private String buildErrorContext(Exception ex, HttpServletRequest request, HandlerMethod handlerMethod) { + + // API endpoint + String endpoint = (request != null) + ? request.getMethod() + " " + request.getRequestURI() + : "unknown"; + + // Controller method + String controllerMethod = (handlerMethod != null) + ? handlerMethod.getBeanType().getSimpleName() + "." + handlerMethod.getMethod().getName() + : "NoControllerMethodMatched"; + + // Root cause (actual throw point) + StackTraceElement root = ex.getStackTrace()[0]; + String rootCause = root.getClassName() + "." + root.getMethodName() + "():" + root.getLineNumber(); + + return String.format( + "API: %s | Controller: %s | Thrown From: %s | Exception: %s", + endpoint, + controllerMethod, + rootCause, + ex.getClass().getSimpleName() ); - // put validation errors into data or duplicate field in ResponseModel as required - resp.setData(fieldErrors); + } + + // ------------------------- VALIDATION EXCEPTION ------------------------ + @ExceptionHandler(MethodArgumentNotValidException.class) + public ResponseEntity> handleValidation( + MethodArgumentNotValidException ex, + HttpServletRequest request, + HandlerMethod handlerMethod) { + + String context = buildErrorContext(ex, request, handlerMethod); + logger.warn("ValidationException → {}", context); + + List> fieldErrors = ex.getBindingResult() + .getFieldErrors() + .stream() + .map(fe -> Map.of("field", fe.getField(), "message", fe.getDefaultMessage())) + .collect(Collectors.toList()); + + ResponseModel>> resp = new ResponseModel<>( + "Validation failed", + "FAILED", + HttpStatus.BAD_REQUEST.value(), + fieldErrors + ); + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(resp); } - + + // ------------------------- NO HANDLER (404 NOT FOUND) ------------------------ @ExceptionHandler(NoHandlerFoundException.class) public ResponseEntity> handleNotFound(NoHandlerFoundException ex) { - logger.warn("Endpoint not found: {}", ex.getRequestURL()); + + logger.warn("404 Not Found → {}", ex.getRequestURL()); + ResponseModel resp = new ResponseModel<>( - "Endpoint not found: " + ex.getRequestURL(), - "FAILED", - HttpStatus.NOT_FOUND.value(), - null + "Endpoint not found: " + ex.getRequestURL(), + "FAILED", + HttpStatus.NOT_FOUND.value(), + null ); + return ResponseEntity.status(HttpStatus.NOT_FOUND).body(resp); } + // ------------------------- CUSTOM APP EXCEPTION ------------------------ @ExceptionHandler(AppException.class) - public ResponseEntity> handleAppException(AppException ex) { - logger.warn("AppException: status={}, message={}", ex.getStatus(), ex.getMessage()); - ResponseModel resp = new ResponseModel<>(ex.getMessage(), ex.getStatus(), ex.getStatusCode(), ex.getData()); + public ResponseEntity> handleAppException( + AppException ex, + HttpServletRequest request, + HandlerMethod handlerMethod) { + + String context = buildErrorContext(ex, request, handlerMethod); + logger.warn("AppException → {}", context); + + ResponseModel resp = new ResponseModel<>( + ex.getMessage(), + ex.getStatus(), + ex.getStatusCode(), + ex.getData() + ); + return ResponseEntity.status(HttpStatus.valueOf(ex.getStatusCode())).body(resp); } - + + // ------------------------- CUSTOMException ------------------------ @ExceptionHandler(CustomException.class) - public ResponseEntity> handleCustomException(CustomException ex) { - logger.warn("CustomException: {}", ex.getMessage()); + public ResponseEntity> handleCustomException( + CustomException ex, + HttpServletRequest request, + HandlerMethod handlerMethod) { + + String context = buildErrorContext(ex, request, handlerMethod); + logger.warn("CustomException → {}", context); + ResponseModel resp = new ResponseModel<>( - ex.getMessage(), - "FAILED", - HttpStatus.BAD_REQUEST.value(), - null + ex.getMessage(), + "FAILED", + HttpStatus.BAD_REQUEST.value(), + null ); + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(resp); } - + // ------------------------- GENERIC EXCEPTION ------------------------ @ExceptionHandler(Exception.class) - public ResponseEntity> handleAll(Exception ex) { - logger.error("Unhandled exception", ex); + public ResponseEntity> handleAll( + Exception ex, + HttpServletRequest request, + HandlerMethod handlerMethod) { + + String context = buildErrorContext(ex, request, handlerMethod); + logger.error("UnhandledException → {}", context, ex); + ResponseModel response = new ResponseModel<>( - "Service temporarily unavailable. Please try again later.", - "FAILED", - HttpStatus.INTERNAL_SERVER_ERROR.value(), - null + "Internal Server Error", + "FAILED", + HttpStatus.INTERNAL_SERVER_ERROR.value(), + null ); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response); } } diff --git a/src/main/java/com/billpaymentservice/bbps/serviceImpl/MasterDataServiceImpl.java b/src/main/java/com/billpaymentservice/bbps/serviceImpl/MasterDataServiceImpl.java index 5b3f4ab..3769d72 100644 --- a/src/main/java/com/billpaymentservice/bbps/serviceImpl/MasterDataServiceImpl.java +++ b/src/main/java/com/billpaymentservice/bbps/serviceImpl/MasterDataServiceImpl.java @@ -113,7 +113,6 @@ public class MasterDataServiceImpl implements MasterDataService { @Override public ResponseModel getMasterData(MasterRequest masterRequest) { - String apiName = BBPSConstant.API_GET_MASTER_DATA; String requestId = masterRequest.getReqId(); String mid = masterRequest.getMid(); @@ -888,7 +887,7 @@ public class MasterDataServiceImpl implements MasterDataService { LOGGER.info("[{}] END | reqId={} | status={}", apiName, requestId, generalResp.getStatus()); return responseModel; } catch (JsonProcessingException e) { - LOGGER.error("Error parsing responseData JSON: {}", e.getMessage()); + LOGGER.error("Error parsing responseData JSON: {} :: class :: method ", e.getMessage()); helper.updateInboundLogFailure(inboundLog, e.getMessage(), BBPSConstant.JSON_ERROR); throw new CustomException("Invalid JSON received from API"); } diff --git a/src/main/java/com/billpaymentservice/bbps/serviceImpl/PaymentServiceImpl.java b/src/main/java/com/billpaymentservice/bbps/serviceImpl/PaymentServiceImpl.java index 6b016ab..a6fbb43 100644 --- a/src/main/java/com/billpaymentservice/bbps/serviceImpl/PaymentServiceImpl.java +++ b/src/main/java/com/billpaymentservice/bbps/serviceImpl/PaymentServiceImpl.java @@ -90,11 +90,14 @@ public class PaymentServiceImpl implements PaymentService { @Override public ResponseModel initiatePayment(BillPaymentRequest billPaymentRequest) { String apiName = BBPSConstant.API_INITIATE_PAYMENT; + LOGGER.info("[{}] START | reqId={} | mid={}", apiName, billPaymentRequest.getReqId(), + billPaymentRequest.getMid()); String txnId = helper.generateTxnId(15); String requestId = billPaymentRequest.getReqId(); String userId = billPaymentRequest.getClientReqId(); String url = paymentUrl + BBPSConstant.PAYMENT_URL; billPaymentRequest.setClientReqId(txnId); + LOGGER.info("Initiating payment for reqId={} and txnId={} and userId={}", requestId, txnId, userId); BharatConnectMaster headersData = beneficiaryDetailsRepository.findByOrgId(BBPSConstant.ORG_ID); diff --git a/src/main/java/com/billpaymentservice/bbps/util/Helper.java b/src/main/java/com/billpaymentservice/bbps/util/Helper.java index 026fdd0..b10ee93 100644 --- a/src/main/java/com/billpaymentservice/bbps/util/Helper.java +++ b/src/main/java/com/billpaymentservice/bbps/util/Helper.java @@ -92,7 +92,6 @@ public class Helper { 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));