From 6079ce48a3392f1f3b7136114865cb55737b05cf Mon Sep 17 00:00:00 2001 From: Lincpay Tech Date: Thu, 11 Dec 2025 17:35:26 +0530 Subject: [PATCH 1/2] updated audit table accroding to merchant panel --- .../bbps/entites/AuditListener.java | 62 ++++++++----------- .../bbps/entites/Auditable.java | 30 ++++----- .../bbps/entites/BillCustomerParams.java | 1 + .../exception/GlobalExceptionHandler.java | 9 +-- .../bbps/serviceImpl/PaymentServiceImpl.java | 1 - .../bbps/util/BBPSConstant.java | 2 +- .../bbps/util/BBPS_RESPONSE_COD_CONSTANT.java | 5 ++ .../bbps/util/BillerMapperHelper.java | 13 +--- .../billpaymentservice/bbps/util/Helper.java | 2 - .../resources/application-local.properties | 11 ++-- src/main/resources/application.properties | 2 +- 11 files changed, 63 insertions(+), 75 deletions(-) create mode 100644 src/main/java/com/billpaymentservice/bbps/util/BBPS_RESPONSE_COD_CONSTANT.java diff --git a/src/main/java/com/billpaymentservice/bbps/entites/AuditListener.java b/src/main/java/com/billpaymentservice/bbps/entites/AuditListener.java index 707592d..4249710 100644 --- a/src/main/java/com/billpaymentservice/bbps/entites/AuditListener.java +++ b/src/main/java/com/billpaymentservice/bbps/entites/AuditListener.java @@ -6,75 +6,67 @@ import java.time.LocalDateTime; import jakarta.persistence.PrePersist; import jakarta.persistence.PreUpdate; -/** - * Generic JPA entity listener that populates common audit fields on entities. - * - * Behavior: - * - On PrePersist: sets createdAt/creationDate/createdEpochDate if present (only when null), - * and forces isDeleted/deleted to false if those fields exist. - * - On PreUpdate: sets modifiedDate/modifiedEpochDate/updatedAt/updatedOn if present. - * - * This uses reflection so entities don't need to extend a specific base class. - */ public class AuditListener { @PrePersist public void prePersist(Object entity) { LocalDateTime now = LocalDateTime.now(); - setIfNull(entity, "createdAt", now); - setIfNull(entity, "createdOn", now); - setIfNull(entity, "creationDate", now == null ? null : now.toString()); + + // Creation fields + setIfNull(entity, "createDateTime", now); + setIfNull(entity, "createdBy", null); // handle manually in service if needed setIfNull(entity, "createdEpochDate", System.currentTimeMillis()); - // Ensure isDeleted / deleted exists and is set to false on create + // Set isDeleted = false on creation setAlways(entity, "isDeleted", Boolean.FALSE); } - /*@PreUpdate - public void preUpdate(Object entity) { - LocalDateTime now = LocalDateTime.now(); - setIfNull(entity, "modifiedDate", now == null ? null : now.toString()); - setIfNull(entity, "modifiedEpochDate", System.currentTimeMillis()); - setIfNull(entity, "updatedAt", now); - setIfNull(entity, "updatedOn", now); - } -*/ + /* + * @PreUpdate public void preUpdate(Object entity) { LocalDateTime now = + * LocalDateTime.now(); + * + * // Update fields setAlways(entity, "updatedDateTime", now); setIfNull(entity, + * "updatedBy", null); // you can override manually in service setAlways(entity, + * "modifiedEpochDate", System.currentTimeMillis()); } + */ private void setIfNull(Object entity, String fieldName, Object value) { try { Field f = findField(entity.getClass(), fieldName); if (f == null) return; + boolean accessible = f.canAccess(entity); f.setAccessible(true); - Object current = f.get(entity); - if (current == null && value != null) { + + Object currentValue = f.get(entity); + if (currentValue == null) { f.set(entity, value); } + f.setAccessible(accessible); - } catch (Exception e) { - // ignore reflection failures - } + } catch (Exception ignored) {} } private void setAlways(Object entity, String fieldName, Object value) { try { Field f = findField(entity.getClass(), fieldName); if (f == null) return; + boolean accessible = f.canAccess(entity); f.setAccessible(true); + f.set(entity, value); + f.setAccessible(accessible); - } catch (Exception e) { - // ignore reflection failures - } + } catch (Exception ignored) {} } private Field findField(Class cls, String name) { - Class cur = cls; - while (cur != null && cur != Object.class) { + Class current = cls; + while (current != null && current != Object.class) { try { - return cur.getDeclaredField(name); + return current.getDeclaredField(name); } catch (NoSuchFieldException e) { - cur = cur.getSuperclass(); + current = current.getSuperclass(); } } return null; diff --git a/src/main/java/com/billpaymentservice/bbps/entites/Auditable.java b/src/main/java/com/billpaymentservice/bbps/entites/Auditable.java index 9cc1bf2..2359759 100644 --- a/src/main/java/com/billpaymentservice/bbps/entites/Auditable.java +++ b/src/main/java/com/billpaymentservice/bbps/entites/Auditable.java @@ -17,25 +17,25 @@ import lombok.Setter; @Getter @Setter public abstract class Auditable { + @Column(name = "create_date_time") + private LocalDateTime createDateTime; - @Column(name = "created_at") - private LocalDateTime createdAt; + @Column(name = "updated_date_time") + private LocalDateTime updatedDateTime; - @Column(name = "updated_at") - private LocalDateTime updatedAt; + @Column(name = "create_by") + private LocalDateTime createdBy; - @Column(name = "created_on") - private LocalDateTime createdOn; + @Column(name = "updated_by") + private LocalDateTime updatedBy; - @Column(name = "updated_on") - private LocalDateTime updatedOn; + @Column(name = "created_epoch_date") + private Long createdEpochDate; - @Column(name = "created_epoch_date") - private Long createdEpochDate; + @Column(name = "modified_epoch_date") + private Long modifiedEpochDate; - @Column(name = "modified_epoch_date") - private Long modifiedEpochDate; - - @Column(name = "is_deleted") - private Boolean isDeleted = Boolean.FALSE; + @Column(name = "is_deleted") + private Boolean isDeleted = Boolean.FALSE; + } \ No newline at end of file diff --git a/src/main/java/com/billpaymentservice/bbps/entites/BillCustomerParams.java b/src/main/java/com/billpaymentservice/bbps/entites/BillCustomerParams.java index 361fb5c..427d88d 100644 --- a/src/main/java/com/billpaymentservice/bbps/entites/BillCustomerParams.java +++ b/src/main/java/com/billpaymentservice/bbps/entites/BillCustomerParams.java @@ -1,5 +1,6 @@ package com.billpaymentservice.bbps.entites; + import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; diff --git a/src/main/java/com/billpaymentservice/bbps/exception/GlobalExceptionHandler.java b/src/main/java/com/billpaymentservice/bbps/exception/GlobalExceptionHandler.java index 6fac62d..3b09f07 100644 --- a/src/main/java/com/billpaymentservice/bbps/exception/GlobalExceptionHandler.java +++ b/src/main/java/com/billpaymentservice/bbps/exception/GlobalExceptionHandler.java @@ -90,16 +90,13 @@ public class GlobalExceptionHandler { logger.warn("Invalid JSON received for endpoint {}: {}", endpoint, shortMsg); - Map data = Map.of( - "exception", ex.getClass().getName(), - "message", shortMsg - ); + ResponseModel resp = new ResponseModel<>( - "[" + endpoint + "] Invalid JSON received", + "Invalid JSON received", "FAILED", HttpStatus.BAD_REQUEST.value(), - data + null ); return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(resp); diff --git a/src/main/java/com/billpaymentservice/bbps/serviceImpl/PaymentServiceImpl.java b/src/main/java/com/billpaymentservice/bbps/serviceImpl/PaymentServiceImpl.java index d195103..a93c6d5 100644 --- a/src/main/java/com/billpaymentservice/bbps/serviceImpl/PaymentServiceImpl.java +++ b/src/main/java/com/billpaymentservice/bbps/serviceImpl/PaymentServiceImpl.java @@ -192,7 +192,6 @@ public class PaymentServiceImpl implements PaymentService { String apiName = BBPSConstant.API_CHECK_TRANSACTION_STATUS; String txnId = helper.generateTxnId(15); String requestId = transactionStatusRequest.getReqId(); - // START log for internal transaction status check LOGGER.info("[{}] START (internal) | checkTxnId={} | reqId={} | bankTxnId={}", apiName, txnId, requestId, transactionStatusRequest.getTxnId()); diff --git a/src/main/java/com/billpaymentservice/bbps/util/BBPSConstant.java b/src/main/java/com/billpaymentservice/bbps/util/BBPSConstant.java index 62e598d..553d2ba 100644 --- a/src/main/java/com/billpaymentservice/bbps/util/BBPSConstant.java +++ b/src/main/java/com/billpaymentservice/bbps/util/BBPSConstant.java @@ -3,7 +3,7 @@ package com.billpaymentservice.bbps.util; import java.text.SimpleDateFormat; import java.util.Date; -public class BBPSConstant { +public class BBPSConstant extends BBPS_RESPONSE_COD_CONSTANT { // Jwt Token Expiration Time public static final long JWT_EXPIRATION_MS = 1000 * 60 * 60 * 24; // 24 hours diff --git a/src/main/java/com/billpaymentservice/bbps/util/BBPS_RESPONSE_COD_CONSTANT.java b/src/main/java/com/billpaymentservice/bbps/util/BBPS_RESPONSE_COD_CONSTANT.java new file mode 100644 index 0000000..8bd51a9 --- /dev/null +++ b/src/main/java/com/billpaymentservice/bbps/util/BBPS_RESPONSE_COD_CONSTANT.java @@ -0,0 +1,5 @@ +package com.billpaymentservice.bbps.util; + +public class BBPS_RESPONSE_COD_CONSTANT { + +} diff --git a/src/main/java/com/billpaymentservice/bbps/util/BillerMapperHelper.java b/src/main/java/com/billpaymentservice/bbps/util/BillerMapperHelper.java index 4016a9c..62b5783 100644 --- a/src/main/java/com/billpaymentservice/bbps/util/BillerMapperHelper.java +++ b/src/main/java/com/billpaymentservice/bbps/util/BillerMapperHelper.java @@ -720,9 +720,7 @@ public class BillerMapperHelper { parent.setUserId(responseWrapper.getUserId()); // Map incoming DTO date strings into Auditable LocalDateTime fields LocalDateTime parentCreatedOn = parseToLocalDateTime(responseWrapper.getCreationDate()); - if (parentCreatedOn != null) parent.setCreatedOn(parentCreatedOn); LocalDateTime parentUpdatedOn = parseToLocalDateTime(responseWrapper.getModifiedDate()); - if (parentUpdatedOn != null) parent.setUpdatedOn(parentUpdatedOn); parent.setCreatedEpochDate(responseWrapper.getCreatedEpochDate()); parent.setModifiedEpochDate(responseWrapper.getModifiedEpochDate()); parent.setReqId(reqId); @@ -965,12 +963,10 @@ public class BillerMapperHelper { // DATES // Map incoming DTO date strings into Auditable LocalDateTime fields LocalDateTime bmCreatedOn = parseToLocalDateTime(responseWrapper.getCreationDate()); - if (bmCreatedOn != null) { - billMaster.setCreatedOn(bmCreatedOn); - } + LocalDateTime bmUpdatedOn = parseToLocalDateTime(responseWrapper.getModifiedDate()); if (bmUpdatedOn != null) { - billMaster.setUpdatedOn(bmUpdatedOn); + billMaster.setUpdatedDateTime(bmUpdatedOn); } billMaster.setCreatedEpochDate(responseWrapper.getCreatedEpochDate()); billMaster.setModifiedEpochDate(responseWrapper.getModifiedEpochDate()); @@ -1099,15 +1095,12 @@ public class BillerMapperHelper { record.setQrString(Optional.ofNullable(apiResponse.getResponseData().getQrString()).orElse("NO_QR")); // parse createdOn string into LocalDateTime and set on Auditable LocalDateTime createdOn = parseToLocalDateTime(apiResponse.getResponseData().getCreatedOn()); - if (createdOn == null) createdOn = LocalDateTime.now(); - record.setCreatedOn(createdOn); - // default status pending + record.setTxnStatus("PENDING"); } else { record.setAmount("0.00"); record.setMid(request.getMid()); record.setTxnStatus("PENDING"); - record.setCreatedOn(LocalDateTime.now()); } // store the original request body and raw response diff --git a/src/main/java/com/billpaymentservice/bbps/util/Helper.java b/src/main/java/com/billpaymentservice/bbps/util/Helper.java index 78daae4..6c62baf 100644 --- a/src/main/java/com/billpaymentservice/bbps/util/Helper.java +++ b/src/main/java/com/billpaymentservice/bbps/util/Helper.java @@ -184,8 +184,6 @@ public class Helper { user.setName(request.getName()); user.setEmail(request.getEmail()); user.setMid(request.getMid()); - // set audit createdOn timestamp - user.setCreatedOn(LocalDateTime.now()); // Generate a unique userId (UUID) String uniqueUserId = generateUniqueUserId(); user.setUserId(uniqueUserId); diff --git a/src/main/resources/application-local.properties b/src/main/resources/application-local.properties index 92c14ea..6722a87 100644 --- a/src/main/resources/application-local.properties +++ b/src/main/resources/application-local.properties @@ -18,11 +18,14 @@ swagger.server.description=Local Development Server # DataSource -spring.datasource.url=jdbc:postgresql://localhost:5432/devdbuat -spring.datasource.username=postgres -spring.datasource.password=UAT@DB_2025 +spring.datasource.url=jdbc:postgresql://localhost:5432/bbps_new_updated -#spring.datasource.password=himanshu@123 +#spring.datasource.url=jdbc:postgresql://localhost:5432/bbps_enhanced +>>>>>>> Stashed changes +spring.datasource.username=postgres +#spring.datasource.password=UAT@DB_2025 + +spring.datasource.password=himanshu@123 spring.datasource.driver-class-name=org.postgresql.Driver spring.datasource.type=com.zaxxer.hikari.HikariDataSource diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 73bb076..b64b5a8 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -21,7 +21,7 @@ spring.profiles.active=local # DataSource -spring.datasource.url=jdbc:postgresql://localhost:5432/BBPS_Enhanced +spring.datasource.url=jdbc:postgresql://localhost:5432/devdbuat spring.datasource.username=postgres #spring.datasource.password=UAT@DB_2025 From b3c507338eaea5f4822e6d6d007c5bf371d7881c Mon Sep 17 00:00:00 2001 From: Lincpay Tech Date: Thu, 11 Dec 2025 18:00:36 +0530 Subject: [PATCH 2/2] handled connection timeout exception --- .../exception/GlobalExceptionHandler.java | 76 +++++++++++++++++++ .../bbps/serviceImpl/HttpService.java | 44 ++++++++++- 2 files changed, 117 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/billpaymentservice/bbps/exception/GlobalExceptionHandler.java b/src/main/java/com/billpaymentservice/bbps/exception/GlobalExceptionHandler.java index 3b09f07..15a4eac 100644 --- a/src/main/java/com/billpaymentservice/bbps/exception/GlobalExceptionHandler.java +++ b/src/main/java/com/billpaymentservice/bbps/exception/GlobalExceptionHandler.java @@ -2,6 +2,8 @@ package com.billpaymentservice.bbps.exception; import java.util.*; import java.util.stream.Collectors; +import java.net.ConnectException; +import java.net.SocketTimeoutException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -12,6 +14,7 @@ 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 org.springframework.web.client.ResourceAccessException; import com.billpaymentservice.bbps.dto.ResponseModel; @@ -161,6 +164,79 @@ public class GlobalExceptionHandler { return ResponseEntity.status(httpStatus).body(resp); } + // ------------------------- RESOURCE ACCESS / TIMEOUT / CONNECT ------------------------ + @ExceptionHandler(ResourceAccessException.class) + public ResponseEntity> handleResourceAccess( + ResourceAccessException ex, + HttpServletRequest request, + HandlerMethod handlerMethod) { + + String context = buildErrorContext(ex, request, handlerMethod); + logger.error("ResourceAccessException → {}", context, ex); + + Throwable cause = ex.getCause(); + String message; + HttpStatus status; + + if (cause instanceof SocketTimeoutException) { + message = "Server timeout - server may be down"; + status = HttpStatus.GATEWAY_TIMEOUT; // 504 + } else if (cause instanceof ConnectException) { + message = "Server unavailable - connection refused"; + status = HttpStatus.SERVICE_UNAVAILABLE; // 503 + } else { + message = "Server unreachable: " + ex.getMessage(); + status = HttpStatus.SERVICE_UNAVAILABLE; + } + + ResponseModel resp = new ResponseModel<>( + message, + "FAILED", + status.value(), + null + ); + + return ResponseEntity.status(status).body(resp); + } + + @ExceptionHandler(SocketTimeoutException.class) + public ResponseEntity> handleSocketTimeout( + SocketTimeoutException ex, + HttpServletRequest request, + HandlerMethod handlerMethod) { + + String context = buildErrorContext(ex, request, handlerMethod); + logger.error("SocketTimeoutException → {}", context, ex); + + ResponseModel resp = new ResponseModel<>( + "Server timeout - server is down", + "FAILED", + HttpStatus.GATEWAY_TIMEOUT.value(), + null + ); + + return ResponseEntity.status(HttpStatus.GATEWAY_TIMEOUT).body(resp); + } + + @ExceptionHandler(ConnectException.class) + public ResponseEntity> handleConnectException( + ConnectException ex, + HttpServletRequest request, + HandlerMethod handlerMethod) { + + String context = buildErrorContext(ex, request, handlerMethod); + logger.error("ConnectException → {}", context, ex); + + ResponseModel resp = new ResponseModel<>( + "Server unavailable - connection refused", + "FAILED", + HttpStatus.SERVICE_UNAVAILABLE.value(), + null + ); + + return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).body(resp); + } + // ------------------------- GENERIC EXCEPTION ------------------------ @ExceptionHandler(Exception.class) public ResponseEntity> handleAll( diff --git a/src/main/java/com/billpaymentservice/bbps/serviceImpl/HttpService.java b/src/main/java/com/billpaymentservice/bbps/serviceImpl/HttpService.java index 9541c06..32aa1a7 100644 --- a/src/main/java/com/billpaymentservice/bbps/serviceImpl/HttpService.java +++ b/src/main/java/com/billpaymentservice/bbps/serviceImpl/HttpService.java @@ -2,6 +2,7 @@ package com.billpaymentservice.bbps.serviceImpl; import java.util.HashMap; +import java.net.ConnectException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -12,6 +13,7 @@ import org.springframework.stereotype.Service; import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.HttpServerErrorException; import org.springframework.web.client.RestClient; +import org.springframework.web.client.ResourceAccessException; import com.fasterxml.jackson.databind.ObjectMapper; @@ -24,6 +26,15 @@ public class HttpService { private final RestClient restClient; private final ObjectMapper objectMapper = new ObjectMapper(); + // Helper to produce server-down/timeout style responses + private HashMap buildServerDownResponse(String code, String message, Exception e) { + HashMap response = new HashMap<>(); + response.put("respCode", code); + response.put("data", message); + log.error("❌ {} - {}", code, message, e); + return response; + } + public HashMap postRequest(String url, String payload, HttpHeaders customHeaders) { @@ -60,6 +71,15 @@ public class HttpService { response.put("respCode", "403"); response.put("data", "FORBIDDEN - Check credentials in HttpService"); + } catch (ResourceAccessException e) { + // This often wraps timeouts or connection refused + Throwable cause = e.getCause(); + if (cause instanceof ConnectException) { + return buildServerDownResponse("503", "Server unavailable - connection refused", e); + } else { + return buildServerDownResponse("503", "Server unreachable: " + e.getMessage(), e); + } + } catch (Exception e) { log.error("❌ HttpService Exception: {}", e.getMessage()); response.put("respCode", "500"); @@ -97,6 +117,13 @@ public class HttpService { log.error("❌ HTTP Error: {}", httpException.getMessage(), httpException); response.put("respCode", String.valueOf(httpException.getStatusCode().value())); response.put("data", httpException.getResponseBodyAsString()); + } catch (ResourceAccessException e) { + Throwable cause = e.getCause(); + if (cause instanceof ConnectException) { + return buildServerDownResponse("503", "Server unavailable - connection refused", e); + } else { + return buildServerDownResponse("503", "Server unreachable: " + e.getMessage(), e); + } } catch (Exception e) { log.error("❌ Exception during GET call: {}", e.getMessage(), e); response.put("respCode", "500"); @@ -133,6 +160,13 @@ public class HttpService { log.error("❌ HTTP Error: {}", httpException.getMessage(), httpException); response.put("respCode", String.valueOf(httpException.getStatusCode().value())); response.put("data", httpException.getResponseBodyAsString()); + } catch (ResourceAccessException e) { + Throwable cause = e.getCause(); + if (cause instanceof ConnectException) { + return buildServerDownResponse("503", "Server unavailable - connection refused", e); + } else { + return buildServerDownResponse("503", "Server unreachable: " + e.getMessage(), e); + } } catch (Exception e) { log.error("❌ Exception during GET call: {}", e.getMessage(), e); response.put("respCode", "500"); @@ -171,6 +205,13 @@ public class HttpService { response.put("respCode", String.valueOf(e.getStatusCode().value())); response.put("data", e.getResponseBodyAsString()); // still HTML or error string + } catch (ResourceAccessException e) { + Throwable cause = e.getCause(); + if (cause instanceof ConnectException) { + return buildServerDownResponse("503", "Server unavailable - connection refused", e); + } else { + return buildServerDownResponse("503", "Server unreachable: " + e.getMessage(), e); + } } catch (Exception e) { log.error("❌ FORM POST Exception: {}", e.getMessage(), e); @@ -181,7 +222,4 @@ public class HttpService { return response; } - - - } \ No newline at end of file