diff --git a/RateLimitingFilter.java b/RateLimitingFilter.java deleted file mode 100644 index 0d35a5a..0000000 --- a/RateLimitingFilter.java +++ /dev/null @@ -1,176 +0,0 @@ -package com.billpaymentservice.bbps.filter; - -import java.io.IOException; -import java.time.Instant; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import jakarta.servlet.FilterChain; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.http.MediaType; -import org.springframework.web.filter.OncePerRequestFilter; -import org.springframework.data.redis.core.StringRedisTemplate; -import org.springframework.data.redis.core.script.DefaultRedisScript; -import org.springframework.data.redis.RedisConnectionFailureException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.billpaymentservice.bbps.ratelimit.LocalBucketManager; -import com.billpaymentservice.bbps.util.Helper; - -import io.github.bucket4j.Bucket; - - -public class RateLimitingFilter extends OncePerRequestFilter { - - private static final Logger LOGGER = LoggerFactory.getLogger(RateLimitingFilter.class); - - private static final long RATE_PER_WINDOW = 1L; - private static final long BURST = 0L; - private static final long WINDOW_MILLIS = 60_000L; - - private final StringRedisTemplate redisTemplate; - private final LocalBucketManager localBucketManager; - private final ObjectMapper mapper = new ObjectMapper(); - - private final DefaultRedisScript incrWithExpireScript; - - public RateLimitingFilter(StringRedisTemplate redisTemplate, LocalBucketManager localBucketManager) { - this.redisTemplate = redisTemplate; - this.localBucketManager = localBucketManager; - String script = - "local v = redis.call('INCR', KEYS[1]);" + - "if v == 1 then redis.call('EXPIRE', KEYS[1], ARGV[1]) end;" + - "return v"; - - this.incrWithExpireScript = new DefaultRedisScript<>(script, Long.class); - } - - @Override - protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, - FilterChain filterChain) throws ServletException, IOException { - - String mid = validateAndExtractMid(request, response); - if (mid == null) return; - - LOGGER.info("RateLimiter activated → path={} mid={}", request.getRequestURI(), mid); - response.setHeader("X-RateLimit-Filter", "invoked"); - - long limit = RATE_PER_WINDOW + BURST; - String redisKey = buildRedisKey(mid); - - // 1️⃣ Local Bucket Check - if (!consumeLocalToken(mid, response)) { - long resetAt = calculateResetTime(); - setRateHeaders(response, limit, 0, resetAt); - writeErrorResponse(request, response, 429, "Too Many Requests", "Rate limit exceeded (local)", mid); - return; - } - - // 2️⃣ Redis Counter - Long count = incrementRedisCounter(redisKey); - long resetAt = calculateResetTime(); - long remaining = Math.max(0, limit - count); - - setRateHeaders(response, limit, remaining, resetAt); - response.setHeader("X-RateLimit-Count", String.valueOf(count)); - - if (count > limit) { - handleGlobalLimitExceeded(mid, response, request, resetAt); - return; - } - - filterChain.doFilter(request, response); - - } - - // -------------------------HELPERS------------------------- - - - private String validateAndExtractMid(HttpServletRequest request, HttpServletResponse response) throws IOException - { String mid = request.getHeader("mid"); - if (mid == null || mid.trim().isEmpty()) - { writeErrorResponse(request, response, 400, "Bad Request", "Required header 'mid' is missing or empty", null); - return null; } - return mid.trim(); } - - private boolean consumeLocalToken(String mid, HttpServletResponse response) { - Bucket bucket = localBucketManager.getBucket("local:mid:" + mid); - boolean consumed = bucket.tryConsume(1); - response.setHeader("X-RateLimit-Local-Remaining", String.valueOf(bucket.getAvailableTokens())); - return consumed; - } - - private Long incrementRedisCounter(String key) { - long ttlSeconds = (WINDOW_MILLIS / 1000L) + 2; - - try { - Long result = redisTemplate.execute( - incrWithExpireScript, - Collections.singletonList(key), - String.valueOf(ttlSeconds) - ); - return (result == null ? 1 : result); - } catch (Exception e) { - LOGGER.warn("Redis unavailable → fallback mode"); - return 1L; - } - } - - private long calculateResetTime() { - long nowSec = System.currentTimeMillis() / 1000L; - long win = WINDOW_MILLIS / 1000L; - return ((nowSec / win) * win) + win; - } - - private String buildRedisKey(String mid) { - long index = System.currentTimeMillis() / WINDOW_MILLIS; - return "ratelimit:mid:" + mid + ":ts:" + index; - } - - private void setRateHeaders(HttpServletResponse response, long limit, long remaining, long resetAt) { - response.setHeader("X-RateLimit-Limit", String.valueOf(limit)); - response.setHeader("X-RateLimit-Remaining", String.valueOf(remaining)); - response.setHeader("X-RateLimit-Reset", String.valueOf(resetAt)); - } - - private void handleGlobalLimitExceeded(String mid, - HttpServletResponse res, - HttpServletRequest req, - long resetAt) throws IOException { - - long now = System.currentTimeMillis() / 1000L; - res.setHeader("Retry-After", String.valueOf(Math.max(1, resetAt - now))); - - writeErrorResponse(req, res, 429, - "Too Many Requests", "Rate limit exceeded (global)", mid); - } - - private void writeErrorResponse(HttpServletRequest request, - HttpServletResponse response, - int status, String error, - String message, String mid) throws IOException { - - if (response.isCommitted()) return; - - Map body = new HashMap<>(); - body.put("timestamp", Instant.now().toString()); - body.put("status", status); - body.put("error", error); - body.put("message", message); - body.put("path", request.getRequestURI()); - - if (mid != null) body.put("mid", mid); - - response.setStatus(status); - response.setContentType(MediaType.APPLICATION_JSON_VALUE); - mapper.writeValue(response.getWriter(), body); - } -} - - - diff --git a/pom.xml b/pom.xml index a50ad5a..6f11b75 100644 --- a/pom.xml +++ b/pom.xml @@ -44,6 +44,12 @@ + + + io.github.resilience4j + resilience4j-spring-boot3 + 2.2.0 + org.springframework.boot @@ -179,6 +185,8 @@ spring-boot-configuration-processor true + + diff --git a/src/main/java/com/billpaymentservice/bbps/config/RateLimitingFilter.java b/src/main/java/com/billpaymentservice/bbps/config/RateLimitingFilter.java new file mode 100644 index 0000000..14ff370 --- /dev/null +++ b/src/main/java/com/billpaymentservice/bbps/config/RateLimitingFilter.java @@ -0,0 +1,116 @@ +package com.billpaymentservice.bbps.config; + +import java.io.IOException; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; +import com.billpaymentservice.bbps.util.BBPSConstant; +import com.billpaymentservice.bbps.dto.ResponseModel; + +import io.github.resilience4j.ratelimiter.RateLimiter; +import io.github.resilience4j.ratelimiter.RateLimiterRegistry; +import com.fasterxml.jackson.databind.ObjectMapper; + +@Component +@Order(1) +public class RateLimitingFilter extends OncePerRequestFilter { + + private final RateLimiterRegistry rateLimiterRegistry; + private static final Logger LOGGER = LoggerFactory.getLogger(RateLimitingFilter.class); + + // Har MID ke liye alag limiter + private final Map limiterCache = new ConcurrentHashMap<>(); + + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + + public RateLimitingFilter(RateLimiterRegistry rateLimiterRegistry) { + this.rateLimiterRegistry = rateLimiterRegistry; + } + + @Override + protected void doFilterInternal(HttpServletRequest request, + HttpServletResponse response, + FilterChain filterChain) + throws ServletException, IOException { + String mid = request.getHeader("mid"); + if(mid == null) { + mid = "GLOBAL"; + } + + // Validate MID + if (mid == null || mid.isBlank()) { + response.setStatus(HttpServletResponse.SC_BAD_REQUEST); + response.setContentType("application/json"); + + ResponseModel apiResponse = new ResponseModel<>("mid cannot be null or blank", "FAILURE", HttpServletResponse.SC_BAD_REQUEST, null); + response.getWriter().write(OBJECT_MAPPER.writeValueAsString(apiResponse)); + return; + } + + try { + RateLimiter rateLimiter = limiterCache.computeIfAbsent( + mid, + key -> rateLimiterRegistry.rateLimiter("default") + ); + + // Rate limit check + if (!rateLimiter.acquirePermission()) { + + // Prefer existing constant if it maps to 429, otherwise fallback to literal 429 + int tooManyRequestsStatus =429; + + response.setStatus(tooManyRequestsStatus); + response.setContentType("application/json"); + + ResponseModel apiResponse = new ResponseModel<>("Rate limit exceeded for MID: " + mid, "FAILURE", tooManyRequestsStatus, null); + response.getWriter().write(OBJECT_MAPPER.writeValueAsString(apiResponse)); + return; + } + + // Proceed with the chain and catch downstream exceptions to return consistent model + try { + filterChain.doFilter(request, response); + } catch (Exception ex) { + LOGGER.error("Unhandled exception in filter chain for MID: {}", mid, ex); + if (!response.isCommitted()) { + response.reset(); + response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + response.setContentType("application/json"); + ResponseModel apiResponse = new ResponseModel<>("Internal server error", "FAILURE", HttpServletResponse.SC_INTERNAL_SERVER_ERROR, null); + response.getWriter().write(OBJECT_MAPPER.writeValueAsString(apiResponse)); + } + } + + } catch (Exception e) { + LOGGER.error("Error while rate limiting request", e); + if (!response.isCommitted()) { + response.reset(); + response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + response.setContentType("application/json"); + ResponseModel apiResponse = new ResponseModel<>("Internal server error", "FAILURE", HttpServletResponse.SC_INTERNAL_SERVER_ERROR, null); + response.getWriter().write(OBJECT_MAPPER.writeValueAsString(apiResponse)); + } + return; + } + } + + @Override + protected boolean shouldNotFilter(HttpServletRequest request) { + + String path = request.getRequestURI(); + + return path.startsWith("/swagger") + || path.startsWith("/v3/api-docs") + || path.startsWith("/actuator"); + } +} \ No newline at end of file diff --git a/src/main/java/com/billpaymentservice/bbps/controller/PaymentController.java b/src/main/java/com/billpaymentservice/bbps/controller/PaymentController.java index 4bcbd62..b2a9a2f 100644 --- a/src/main/java/com/billpaymentservice/bbps/controller/PaymentController.java +++ b/src/main/java/com/billpaymentservice/bbps/controller/PaymentController.java @@ -46,7 +46,6 @@ public class PaymentController { @ApiDocCommonResponses public ResponseEntity requestBillFetch( @RequestBody BillPaymentRequest billPaymentRequest) { - ResponseModel response = paymentService.initiatePayment(billPaymentRequest); return ResponseEntity.ok(response); } diff --git a/src/main/java/com/billpaymentservice/bbps/dto/TransactionDTO.java b/src/main/java/com/billpaymentservice/bbps/dto/TransactionDTO.java index d6bd669..0b3db0c 100644 --- a/src/main/java/com/billpaymentservice/bbps/dto/TransactionDTO.java +++ b/src/main/java/com/billpaymentservice/bbps/dto/TransactionDTO.java @@ -66,9 +66,12 @@ public class TransactionDTO { private String planType; // ✅ Date fields as String (for JSON compatibility) - private String creationDate; - private String modifiedDate; - - private Long createdEpochDate; - private Long modifiedEpochDate; + @JsonProperty("creationDate") + private String creationDateBank; + @JsonProperty("modifiedDate") + private String modifiedDateBank; + @JsonProperty("createdEpochDate") + private Long createdEpochDateBank; + @JsonProperty("modifiedEpochDate") + private Long modifiedEpochDateBank; } diff --git a/src/main/java/com/billpaymentservice/bbps/entites/PaymentStartRecord.java b/src/main/java/com/billpaymentservice/bbps/entites/PaymentStartRecord.java index 3f45dba..a809a87 100644 --- a/src/main/java/com/billpaymentservice/bbps/entites/PaymentStartRecord.java +++ b/src/main/java/com/billpaymentservice/bbps/entites/PaymentStartRecord.java @@ -47,9 +47,6 @@ public class PaymentStartRecord extends Auditable { @Column(name = "amount", nullable = false) private String amount; - @Column(name = "mid", nullable = false) - private String mid; - @Column(name = "txn_id", unique = true, nullable = false) private String txnId; diff --git a/src/main/java/com/billpaymentservice/bbps/entites/TransactionMaster.java b/src/main/java/com/billpaymentservice/bbps/entites/TransactionMaster.java index 3033829..d563f8d 100644 --- a/src/main/java/com/billpaymentservice/bbps/entites/TransactionMaster.java +++ b/src/main/java/com/billpaymentservice/bbps/entites/TransactionMaster.java @@ -164,15 +164,15 @@ public class TransactionMaster extends Auditable { @Column(name = "plan_type") private String planType; - @Column(name = "creation_date") - private String creationDate; + @Column(name = "creation_date_bank") + private String creationDateBank; - @Column(name = "modified_date") - private String modifiedDate; + @Column(name = "modified_date_bank") + private String modifiedDateBank; - @Column(name = "created_epoch_date") - private Long createdEpochDate; + @Column(name = "created_epoch_date_bank") + private Long createdEpochDateBank; - @Column(name = "modified_epoch_date") - private Long modifiedEpochDate; + @Column(name = "modified_epoch_date_bank") + private Long modifiedEpochDateBank; } \ No newline at end of file diff --git a/src/main/java/com/billpaymentservice/bbps/serviceImpl/AgentServiceImpl.java b/src/main/java/com/billpaymentservice/bbps/serviceImpl/AgentServiceImpl.java index 692bcc7..ee8e573 100644 --- a/src/main/java/com/billpaymentservice/bbps/serviceImpl/AgentServiceImpl.java +++ b/src/main/java/com/billpaymentservice/bbps/serviceImpl/AgentServiceImpl.java @@ -153,7 +153,7 @@ public class AgentServiceImpl implements AgentService { LOGGER.error("JSON Parsing Error: {}", e.getMessage(), e); helper.updateInboundLogFailure(inboundLog, e.getMessage(), BBPSConstant.JSON_ERROR); // Use standardized user-facing message and status, keep internal details in remarks - throw new CustomException(BBPSConstant.MSG_INVALID_JSON_FROM_UPSTREAM, "FAILED", BBPSConstant.INVALID_JSON_STATUS_CODE, e.getMessage()); + throw new CustomException(BBPSConstant.MSG_INVALID_JSON_FROM_UPSTREAM, BBPSConstant.FAILED_STATUS, BBPSConstant.INVALID_JSON_STATUS_CODE, e.getMessage()); } } @@ -206,7 +206,7 @@ public class AgentServiceImpl implements AgentService { } catch (JsonProcessingException e) { LOGGER.error("Error parsing BillById JSON: {}", e.getMessage(), e); helper.updateInboundLogFailure(inboundLog, e.getMessage(), BBPSConstant.JSON_ERROR); - throw new CustomException(BBPSConstant.MSG_INVALID_JSON_FROM_UPSTREAM, "FAILED", BBPSConstant.INVALID_JSON_STATUS_CODE, e.getMessage()); + throw new CustomException(BBPSConstant.MSG_INVALID_JSON_FROM_UPSTREAM, BBPSConstant.FAILED_STATUS, BBPSConstant.INVALID_JSON_STATUS_CODE, e.getMessage()); } } @@ -265,7 +265,7 @@ public class AgentServiceImpl implements AgentService { catch (JsonProcessingException e) { LOGGER.error("Error parsing responseData JSON for billId={} : {}", billId, e.getMessage(), e); helper.updateInboundLogFailure(inboundLog, e.getMessage(), BBPSConstant.JSON_ERROR); - throw new CustomException(BBPSConstant.MSG_INVALID_JSON_FROM_UPSTREAM, "FAILED", BBPSConstant.INVALID_JSON_STATUS_CODE, e.getMessage()); + throw new CustomException(BBPSConstant.MSG_INVALID_JSON_FROM_UPSTREAM, BBPSConstant.FAILED_STATUS, BBPSConstant.INVALID_JSON_STATUS_CODE, e.getMessage()); } } @@ -349,7 +349,7 @@ public class AgentServiceImpl implements AgentService { } catch (JsonProcessingException e) { LOGGER.error("JSON Parsing Error: {}", e.getMessage(), e); helper.updateInboundLogFailure(inboundLog, e.getMessage(), BBPSConstant.JSON_ERROR); - throw new CustomException(BBPSConstant.MSG_INVALID_JSON_FROM_UPSTREAM, "FAILED", BBPSConstant.INVALID_JSON_STATUS_CODE, e.getMessage()); + throw new CustomException(BBPSConstant.MSG_INVALID_JSON_FROM_UPSTREAM, BBPSConstant.FAILED_STATUS ,BBPSConstant.INVALID_JSON_STATUS_CODE, e.getMessage()); } } @@ -404,7 +404,7 @@ public class AgentServiceImpl implements AgentService { } catch (JsonProcessingException e) { LOGGER.error("Error parsing BillById JSON: {}", e.getMessage(), e); helper.updateInboundLogFailure(inboundLog, e.getMessage(), BBPSConstant.JSON_ERROR); - throw new CustomException(BBPSConstant.MSG_INVALID_JSON_FROM_UPSTREAM, "FAILED", BBPSConstant.INVALID_JSON_STATUS_CODE, e.getMessage()); + throw new CustomException(BBPSConstant.MSG_INVALID_JSON_FROM_UPSTREAM, BBPSConstant.FAILED_STATUS, BBPSConstant.INVALID_JSON_STATUS_CODE, e.getMessage()); } } @@ -465,7 +465,7 @@ public class AgentServiceImpl implements AgentService { } catch (com.fasterxml.jackson.core.JsonProcessingException e) { LOGGER.error("Error parsing ValidateByBillId JSON: {}", e.getMessage(), e); helper.updateInboundLogFailure(inboundLog, e.getMessage(), BBPSConstant.JSON_ERROR); - throw new CustomException(BBPSConstant.MSG_INVALID_JSON_FROM_UPSTREAM, "FAILED", BBPSConstant.INVALID_JSON_STATUS_CODE, e.getMessage()); + throw new CustomException(BBPSConstant.MSG_INVALID_JSON_FROM_UPSTREAM,BBPSConstant.FAILED_STATUS, BBPSConstant.INVALID_JSON_STATUS_CODE, e.getMessage()); } } // ================================== Bill fetch and Bill Validate by Client Req @@ -536,7 +536,7 @@ public class AgentServiceImpl implements AgentService { } catch (JsonProcessingException e) { helper.updateInboundLogFailure(inboundLog, e.getMessage(), BBPSConstant.JSON_ERROR); - throw new CustomException(BBPSConstant.MSG_INVALID_JSON_FROM_UPSTREAM, "FAILED", BBPSConstant.INVALID_JSON_STATUS_CODE, e.getMessage()); + throw new CustomException(BBPSConstant.MSG_INVALID_JSON_FROM_UPSTREAM, BBPSConstant.FAILED_STATUS, BBPSConstant.INVALID_JSON_STATUS_CODE, e.getMessage()); } } @@ -598,7 +598,7 @@ public class AgentServiceImpl implements AgentService { } catch (JsonProcessingException e) { helper.updateInboundLogFailure(inboundLog, e.getMessage(), BBPSConstant.JSON_ERROR); - throw new CustomException(BBPSConstant.MSG_INVALID_JSON_FROM_UPSTREAM, "FAILED", BBPSConstant.INVALID_JSON_STATUS_CODE, e.getMessage()); + throw new CustomException(BBPSConstant.MSG_INVALID_JSON_FROM_UPSTREAM, BBPSConstant.FAILED_STATUS, BBPSConstant.INVALID_JSON_STATUS_CODE, e.getMessage()); } } diff --git a/src/main/java/com/billpaymentservice/bbps/serviceImpl/ComplainServiceImpl.java b/src/main/java/com/billpaymentservice/bbps/serviceImpl/ComplainServiceImpl.java index 16b78fc..b5d1926 100644 --- a/src/main/java/com/billpaymentservice/bbps/serviceImpl/ComplainServiceImpl.java +++ b/src/main/java/com/billpaymentservice/bbps/serviceImpl/ComplainServiceImpl.java @@ -134,7 +134,7 @@ public class ComplainServiceImpl implements ComplainService { TransactionMaster transData = dataById.get(); // Inject tenantId + agentId into request JSON - String transactionCreatedDateString = transData.getCreationDate(); + String transactionCreatedDateString = transData.getCreationDateBank(); LOGGER.info("Date is coming -:" + transactionCreatedDateString); LOGGER.info("Return Date Time After 24 Hours" + BillerMapperHelper.isComplaintAllowed(transactionCreatedDateString)); diff --git a/src/main/java/com/billpaymentservice/bbps/serviceImpl/MasterDataServiceImpl.java b/src/main/java/com/billpaymentservice/bbps/serviceImpl/MasterDataServiceImpl.java index 5c6f38a..360b425 100644 --- a/src/main/java/com/billpaymentservice/bbps/serviceImpl/MasterDataServiceImpl.java +++ b/src/main/java/com/billpaymentservice/bbps/serviceImpl/MasterDataServiceImpl.java @@ -165,7 +165,7 @@ public class MasterDataServiceImpl implements MasterDataService { LOGGER.error("Error parsing responseData JSON: {}", e.getMessage()); helper.updateInboundLogFailure(inboundLog, e.getMessage(), BBPSConstant.JSON_ERROR); // Use user-friendly message constant and keep internal details in remarks - throw new CustomException(BBPSConstant.MSG_INVALID_JSON_FROM_UPSTREAM, "FAILED", BBPSConstant.INVALID_JSON_STATUS_CODE, e.getMessage()); + throw new CustomException(BBPSConstant.MSG_INVALID_JSON_FROM_UPSTREAM, BBPSConstant.FAILED_STATUS, BBPSConstant.INVALID_JSON_STATUS_CODE, e.getMessage()); } @@ -229,7 +229,7 @@ public class MasterDataServiceImpl implements MasterDataService { LOGGER.error("Error parsing responseData JSON: {}", e.getMessage()); helper.updateInboundLogFailure(inboundLog, e.getMessage(), BBPSConstant.JSON_ERROR); // Unified constant for invalid JSON - throw new CustomException(BBPSConstant.MSG_INVALID_JSON_FROM_UPSTREAM, "FAILED", BBPSConstant.INVALID_JSON_STATUS_CODE, e.getMessage()); + throw new CustomException(BBPSConstant.MSG_INVALID_JSON_FROM_UPSTREAM, BBPSConstant.FAILED_STATUS, BBPSConstant.INVALID_JSON_STATUS_CODE, e.getMessage()); } } @@ -300,7 +300,7 @@ public class MasterDataServiceImpl implements MasterDataService { } catch (JsonProcessingException e) { LOGGER.error("Error parsing responseData JSON: {}", e.getMessage()); helper.updateInboundLogFailure(inboundLog, e.getMessage(), BBPSConstant.JSON_ERROR); - throw new CustomException(BBPSConstant.MSG_INVALID_JSON_FROM_UPSTREAM, "FAILED", BBPSConstant.INVALID_JSON_STATUS_CODE, e.getMessage()); + throw new CustomException(BBPSConstant.MSG_INVALID_JSON_FROM_UPSTREAM,BBPSConstant.FAILED_STATUS, BBPSConstant.INVALID_JSON_STATUS_CODE, e.getMessage()); } } @@ -366,7 +366,7 @@ public class MasterDataServiceImpl implements MasterDataService { } catch (JsonProcessingException e) { LOGGER.error("Error parsing responseData JSON: {}", e.getMessage()); helper.updateInboundLogFailure(inboundLog, e.getMessage(), BBPSConstant.JSON_ERROR); - throw new CustomException(BBPSConstant.MSG_INVALID_JSON_FROM_UPSTREAM, "FAILED", BBPSConstant.INVALID_JSON_STATUS_CODE, e.getMessage()); + throw new CustomException(BBPSConstant.MSG_INVALID_JSON_FROM_UPSTREAM, BBPSConstant.FAILED_STATUS, BBPSConstant.INVALID_JSON_STATUS_CODE, e.getMessage()); } } @@ -433,7 +433,7 @@ public class MasterDataServiceImpl implements MasterDataService { } catch (JsonProcessingException e) { LOGGER.error("Error parsing responseData JSON: {}", e.getMessage()); helper.updateInboundLogFailure(inboundLog, e.getMessage(), BBPSConstant.JSON_ERROR); - throw new CustomException(BBPSConstant.MSG_INVALID_JSON_FROM_UPSTREAM, "FAILED", BBPSConstant.INVALID_JSON_STATUS_CODE, e.getMessage()); + throw new CustomException(BBPSConstant.MSG_INVALID_JSON_FROM_UPSTREAM,BBPSConstant.FAILED_STATUS, BBPSConstant.INVALID_JSON_STATUS_CODE, e.getMessage()); } } @@ -572,7 +572,7 @@ public class MasterDataServiceImpl implements MasterDataService { } catch (JsonProcessingException e) { LOGGER.error("Error parsing responseData JSON: {}", e.getMessage()); helper.updateInboundLogFailure(inboundLog, e.getMessage(), BBPSConstant.JSON_ERROR); - throw new CustomException(BBPSConstant.MSG_INVALID_JSON_FROM_UPSTREAM, "FAILED", BBPSConstant.INVALID_JSON_STATUS_CODE, e.getMessage()); + throw new CustomException(BBPSConstant.MSG_INVALID_JSON_FROM_UPSTREAM, BBPSConstant.FAILED_STATUS, BBPSConstant.INVALID_JSON_STATUS_CODE, e.getMessage()); } } @@ -638,7 +638,7 @@ public class MasterDataServiceImpl implements MasterDataService { } catch (JsonProcessingException e) { LOGGER.error("Error parsing responseData JSON: {}", e.getMessage()); helper.updateInboundLogFailure(inboundLog, e.getMessage(), BBPSConstant.JSON_ERROR); - throw new CustomException(BBPSConstant.MSG_INVALID_JSON_FROM_UPSTREAM, "FAILED", BBPSConstant.INVALID_JSON_STATUS_CODE, e.getMessage()); + throw new CustomException(BBPSConstant.MSG_INVALID_JSON_FROM_UPSTREAM, BBPSConstant.FAILED_STATUS, BBPSConstant.INVALID_JSON_STATUS_CODE, e.getMessage()); } } @@ -702,7 +702,7 @@ public class MasterDataServiceImpl implements MasterDataService { } catch (JsonProcessingException e) { LOGGER.error("Error parsing responseData JSON: {}", e.getMessage()); helper.updateInboundLogFailure(inboundLog, e.getMessage(), BBPSConstant.JSON_ERROR); - throw new CustomException(BBPSConstant.MSG_INVALID_JSON_FROM_UPSTREAM, "FAILED", BBPSConstant.INVALID_JSON_STATUS_CODE, e.getMessage()); + throw new CustomException(BBPSConstant.MSG_INVALID_JSON_FROM_UPSTREAM, BBPSConstant.FAILED_STATUS, BBPSConstant.INVALID_JSON_STATUS_CODE, e.getMessage()); } } @@ -759,7 +759,7 @@ public class MasterDataServiceImpl implements MasterDataService { } catch (JsonProcessingException e) { LOGGER.error("Error parsing responseData JSON: {}", e.getMessage()); helper.updateInboundLogFailure(inboundLog, e.getMessage(), BBPSConstant.JSON_ERROR); - throw new CustomException(BBPSConstant.MSG_INVALID_JSON_FROM_UPSTREAM, "FAILED", BBPSConstant.INVALID_JSON_STATUS_CODE, e.getMessage()); + throw new CustomException(BBPSConstant.MSG_INVALID_JSON_FROM_UPSTREAM,BBPSConstant.FAILED_STATUS , BBPSConstant.INVALID_JSON_STATUS_CODE, e.getMessage()); } } @@ -831,7 +831,7 @@ public class MasterDataServiceImpl implements MasterDataService { } catch (JsonProcessingException e) { LOGGER.error("Error parsing responseData JSON: {}", e.getMessage()); helper.updateInboundLogFailure(inboundLog, e.getMessage(), BBPSConstant.JSON_ERROR); - throw new CustomException(BBPSConstant.MSG_INVALID_JSON_FROM_UPSTREAM, "FAILED", BBPSConstant.INVALID_JSON_STATUS_CODE, e.getMessage()); + throw new CustomException(BBPSConstant.MSG_INVALID_JSON_FROM_UPSTREAM, BBPSConstant.FAILED_STATUS, BBPSConstant.INVALID_JSON_STATUS_CODE, e.getMessage()); } } @@ -889,7 +889,7 @@ public class MasterDataServiceImpl implements MasterDataService { } catch (JsonProcessingException e) { LOGGER.error("Error parsing responseData JSON: {} :: class :: method ", e.getMessage()); helper.updateInboundLogFailure(inboundLog, e.getMessage(), BBPSConstant.JSON_ERROR); - throw new CustomException(BBPSConstant.MSG_INVALID_JSON_FROM_UPSTREAM, "FAILED", BBPSConstant.INVALID_JSON_STATUS_CODE, e.getMessage()); + throw new CustomException(BBPSConstant.MSG_INVALID_JSON_FROM_UPSTREAM,BBPSConstant.FAILED_STATUS, BBPSConstant.INVALID_JSON_STATUS_CODE, e.getMessage()); } } diff --git a/src/main/java/com/billpaymentservice/bbps/serviceImpl/PaymentServiceImpl.java b/src/main/java/com/billpaymentservice/bbps/serviceImpl/PaymentServiceImpl.java index c9e560f..dee3150 100644 --- a/src/main/java/com/billpaymentservice/bbps/serviceImpl/PaymentServiceImpl.java +++ b/src/main/java/com/billpaymentservice/bbps/serviceImpl/PaymentServiceImpl.java @@ -188,7 +188,7 @@ public class PaymentServiceImpl implements PaymentService { } catch (JsonProcessingException e) { LOGGER.error("JSON Parsing Error in initiatePayment: {}", e.getMessage(), e); helper.updateInboundLogFailure(inboundLog, e.getMessage(), BBPSConstant.JSON_ERROR); - throw new CustomException(BBPSConstant.MSG_INVALID_JSON_FROM_UPSTREAM, "FAILED", BBPSConstant.INVALID_JSON_STATUS_CODE, e.getMessage()); + throw new CustomException(BBPSConstant.MSG_INVALID_JSON_FROM_UPSTREAM, BBPSConstant.FAILED_STATUS, BBPSConstant.INVALID_JSON_STATUS_CODE, e.getMessage()); } } @@ -266,7 +266,7 @@ public class PaymentServiceImpl implements PaymentService { } catch (JsonProcessingException e) { LOGGER.error("Error parsing transaction status JSON: {}", e.getMessage(), e); helper.updateInboundLogFailure(inboundLog, e.getMessage(), BBPSConstant.JSON_ERROR); - throw new CustomException(BBPSConstant.MSG_INVALID_JSON_FROM_UPSTREAM, "FAILED", BBPSConstant.INVALID_JSON_STATUS_CODE, e.getMessage()); + throw new CustomException(BBPSConstant.MSG_INVALID_JSON_FROM_UPSTREAM, BBPSConstant.FAILED_STATUS, BBPSConstant.INVALID_JSON_STATUS_CODE, e.getMessage()); } } @@ -332,7 +332,7 @@ public class PaymentServiceImpl implements PaymentService { } catch (JsonProcessingException e) { LOGGER.error("Error parsing responseData JSON: {}", e.getMessage(), e); helper.updateInboundLogFailure(inboundLog, e.getMessage(), BBPSConstant.JSON_ERROR); - throw new CustomException(BBPSConstant.MSG_INVALID_JSON_FROM_UPSTREAM, "FAILED", BBPSConstant.INVALID_JSON_STATUS_CODE, e.getMessage()); + throw new CustomException(BBPSConstant.MSG_INVALID_JSON_FROM_UPSTREAM,BBPSConstant.FAILED_STATUS, BBPSConstant.INVALID_JSON_STATUS_CODE, e.getMessage()); } } @@ -415,7 +415,7 @@ public class PaymentServiceImpl implements PaymentService { } catch (JsonProcessingException e) { LOGGER.error("Error parsing JSON in transaction status API: {}", e.getMessage(), e); helper.updateInboundLogFailure(inboundLog, e.getMessage(), BBPSConstant.JSON_ERROR); - throw new CustomException(BBPSConstant.MSG_INVALID_JSON_FROM_UPSTREAM, "FAILED", BBPSConstant.INVALID_JSON_STATUS_CODE, e.getMessage()); + throw new CustomException(BBPSConstant.MSG_INVALID_JSON_FROM_UPSTREAM,BBPSConstant.FAILED_STATUS, BBPSConstant.INVALID_JSON_STATUS_CODE, e.getMessage()); } } diff --git a/src/main/java/com/billpaymentservice/bbps/util/BBPSConstant.java b/src/main/java/com/billpaymentservice/bbps/util/BBPSConstant.java index 763d2f4..d8b5e09 100644 --- a/src/main/java/com/billpaymentservice/bbps/util/BBPSConstant.java +++ b/src/main/java/com/billpaymentservice/bbps/util/BBPSConstant.java @@ -19,9 +19,9 @@ public class BBPSConstant extends BBPS_RESPONSE_COD_CONSTANT { public static final Long FRAUD_CODE = 1001L; public static final int BADREQUEST = 400; - public static final String SUCCESS_STATUS = "success"; - public static final String FAILED_STATUS = "failed"; - public static final String PENDING_STATUS = "pending"; + public static final String SUCCESS_STATUS = "SUCCESS"; + public static final String FAILED_STATUS = "FAILED"; + public static final String PENDING_STATUS = "PENDING"; public static final String SENT_TO_PROCESSOR_STATUS = "txn sent to processor"; public static final String SUCCESS_REMARK = "200-Transaction process successfully"; @@ -130,6 +130,7 @@ public class BBPSConstant extends BBPS_RESPONSE_COD_CONSTANT { public static final int HTTP_OK_INT = 200; public static final int HTTP_NOT_FOUND = 404; public static final int HTTP_INTERNAL_SERVER_ERROR = 500; + public static final int HTTP_TO_MANY_REQUEST=429; // Response and helper keys public static final String HTTP_SERVICE_ERROR_PREFIX = "HttpService Error"; diff --git a/src/main/java/com/billpaymentservice/bbps/util/BillerMapperHelper.java b/src/main/java/com/billpaymentservice/bbps/util/BillerMapperHelper.java index c889a2a..04d1af6 100644 --- a/src/main/java/com/billpaymentservice/bbps/util/BillerMapperHelper.java +++ b/src/main/java/com/billpaymentservice/bbps/util/BillerMapperHelper.java @@ -1037,9 +1037,9 @@ public class BillerMapperHelper { dto.setPaymentInformation(transactionMaster.getPaymentInformation()); dto.setPlanId(transactionMaster.getPlanId()); dto.setPlanType(transactionMaster.getPlanType()); - dto.setCreationDate(transactionMaster.getCreationDate()); - dto.setModifiedDate(transactionMaster.getModifiedDate()); - dto.setCreatedEpochDate(transactionMaster.getCreatedEpochDate()); + dto.setCreationDate(transactionMaster.getCreationDateBank()); + dto.setModifiedDate(transactionMaster.getModifiedDateBank()); + dto.setCreatedEpochDate(transactionMaster.getCreatedEpochDateBank()); dto.setModifiedEpochDate(transactionMaster.getModifiedEpochDate()); return dto; } @@ -1052,7 +1052,7 @@ public class BillerMapperHelper { TransactionHistoryParticularDetailsDTO dto = new TransactionHistoryParticularDetailsDTO(); dto.setBankTxnId(transactionMaster.getBankTxnId()); dto.setTxnId(transactionMaster.getTxnId()); - dto.setCreationDate(transactionMaster.getCreationDate()); + dto.setCreationDate(transactionMaster.getCreationDateBank()); dto.setBillerCategory(transactionMaster.getBillerCategory()); dto.setBillerName(transactionMaster.getBillerName()); dto.setStatus(transactionMaster.getStatus()); @@ -1095,21 +1095,21 @@ public class BillerMapperHelper { record.setClientUserId(request.getClientUserId()); record.setInternalTxnId(txnId); - if (apiResponse != null && apiResponse.getResponseData() != null) { - record.setAmount(Optional.ofNullable(apiResponse.getResponseData().getAmount()).orElse("0.00")); - record.setMid(Optional.ofNullable(apiResponse.getResponseData().getMid()).orElse("DUMMY_MID")); - record.setTxnId(Optional.ofNullable(apiResponse.getResponseData().getTxnId()).orElse("DUMMY_TXN")); - 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()); - - record.setTxnStatus("PENDING"); - } else { - record.setAmount("0.00"); - record.setMid(request.getMid()); - record.setTxnStatus("PENDING"); - } - + if (apiResponse != null && + apiResponse.getResponseData() != null) { + record.setAmount(Optional.ofNullable(apiResponse.getResponseData().getAmount( + )).orElse("0.00")); + + record.setTxnId(Optional.ofNullable(apiResponse.getResponseData().getTxnId()) + .orElse("DUMMY_TXN")); + 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()); + + record.setTxnStatus("PENDING"); } else { record.setAmount("0.00"); + record.setTxnStatus("PENDING"); } + + // store the original request body and raw response record.setRequestBody(requestJson); record.setResponseBody(rawResponse); diff --git a/src/main/resources/application-local.properties b/src/main/resources/application-local.properties index d9475fd..79e2fdd 100644 --- a/src/main/resources/application-local.properties +++ b/src/main/resources/application-local.properties @@ -55,6 +55,9 @@ master.data.url = https://bbps-staging.digiledge.in/agent/cou-master/masters agent.data.url=https://bbps-staging.digiledge.in/agent/bill-payment-new payment.data.url=https://bbps-staging.digiledge.in/couapp +resilience4j.ratelimiter.configs.default.limit-for-period=1000 +resilience4j.ratelimiter.configs.default.limit-refresh-period=1m +resilience4j.ratelimiter.configs.default.timeout-duration=0 # Resilience4j Retry resilience4j.retry.instances.callbackService.max-attempts=1 diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index b64b5a8..7efe143 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -72,7 +72,13 @@ spring.mvc.pathmatch.matching-strategy=ANT_PATH_MATCHER jwt.secret=6C756148487475787A414646736D4B6E444E585A766145396E6A4555594A595566657A6E434D62536856674A704C494F6C6E666B366A4A6A6D5630323564716C614F +# =============================== +# Resilience4j Rate Limiter +# =============================== +resilience4j.ratelimiter.configs.default.limit-for-period=5 +resilience4j.ratelimiter.configs.default.limit-refresh-period=1m +resilience4j.ratelimiter.configs.default.timeout-duration=0 # Resilience4j Retry resilience4j.retry.instances.callbackService.max-attempts=1 resilience4j.retry.instances.callbackService.wait-duration=2s