update code security otp

This commit is contained in:
Mohd-Arham
2025-11-26 18:52:00 +05:30
23 changed files with 700 additions and 65 deletions
@@ -8,9 +8,11 @@ import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import com.billpaymentservice.bbps.dto.auth.RegisterRequest;
import com.billpaymentservice.bbps.entites.BBPSMastersData;
import com.billpaymentservice.bbps.entites.InboundTraceLog;
import com.billpaymentservice.bbps.entites.OutboundTraceLog;
import com.billpaymentservice.bbps.entites.UserEntity;
import com.billpaymentservice.bbps.entites.BharatConnectMaster;
import com.billpaymentservice.bbps.exception.CustomException;
import com.billpaymentservice.bbps.repository.BBPSMasterDataRepo;
@@ -166,4 +168,14 @@ public class Helper {
return headers;
}
public UserEntity createNewUser(RegisterRequest request) {
UserEntity user = new UserEntity();
user.setMobile(request.getMobile());
user.setName(request.getName());
user.setEmail(request.getEmail());
user.setAge(request.getAge());
return user;
}
}
@@ -0,0 +1,186 @@
package com.billpaymentservice.bbps.util;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import com.billpaymentservice.bbps.dto.auth.OtpSendResponse;
import java.util.HashMap;
import java.util.Map;
import java.util.Collections;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.databind.ObjectMapper;
@Component
public class OtpClient {
private static final Logger logger = LoggerFactory.getLogger(OtpClient.class);
private final RestTemplate restTemplate = new RestTemplate();
@Value("${ekyc.mobile}")
private String sendUrl;
@Value("${ekyc.mobile.verify.otp}")
private String verifyUrl;
// New config properties for eKYC-style OTP API
@Value("${ekyc.mobile}")
private String ekycMobileUrl;
@Value("${ekyc.apikey}")
private String apiKey;
@Value("${ekyc.secreteKey}")
private String secretKey;
/**
* Calls external API to send OTP. The implementation assumes a POST with JSON {"mobile":"..."}
* Modify headers/body as per your external API. Updated to support eKYC OTP generation logic.
*/
public OtpSendResponse sendOtp(String mobile) {
try {
logger.info("Initiating OTP send process for mobile: {}", mobile);
logger.info("eKYC Mobile URL: {}", ekycMobileUrl);
logger.info("API Key: {}", apiKey != null && !apiKey.isEmpty());
logger.info("Secret Key: {}", secretKey != null && !secretKey.isEmpty());
logger.info("Send URL: {}", sendUrl);
logger.info("Verify URL: {}", verifyUrl);
logger.info("Mobile Number: {}", mobile);
// If explicit eKYC config present, use that flow
if (ekycMobileUrl != null && !ekycMobileUrl.isEmpty()) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
// Build JSON body string as in user's snippet
String jsonBody = "{\"mobileNumber\":\"" + mobile + "\", \"apiKey\":\"" + apiKey
+ "\", \"secretKey\":\"" + secretKey + "\"}";
HttpEntity<String> request = new HttpEntity<>(jsonBody, headers);
try {
ResponseEntity<String> response = restTemplate.exchange(ekycMobileUrl, HttpMethod.POST, request,
String.class);
logger.info("eKYC OTP generation API response status: {}", response.getStatusCode());
if (response.getStatusCode() == HttpStatus.OK) {
logger.info("OTP generated successfully for mobile: {}", mobile);
return new OtpSendResponse(true, "OTP generated successfully");
} else {
logger.error("Failed to call eKYC OTP generation API: {}", response.getStatusCode());
return new OtpSendResponse(false, "Failed to call OTP generation service");
}
} catch (Exception e) {
logger.error("Error calling eKYC OTP generation API: {}", e.getMessage(), e);
return new OtpSendResponse(false, "Error while generating OTP: " + e.getMessage());
}
}
// Fallback to previously configured generic sendUrl behavior
if (sendUrl == null || sendUrl.isEmpty()) {
return new OtpSendResponse(true, "(stub) OTP sent to " + mobile);
}
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
Map<String, String> body = new HashMap<>();
body.put("mobile", mobile);
HttpEntity<Map<String, String>> req = new HttpEntity<>(body, headers);
// expecting external API returns {"success":true,"message":"..."}
Map resp = restTemplate.postForObject(sendUrl, req, Map.class);
boolean success = resp != null && Boolean.TRUE.equals(resp.get("success"));
String message = resp == null ? "no response" : String.valueOf(resp.get("message"));
return new OtpSendResponse(success, message);
} catch (Exception e) {
logger.error("Unexpected error in sendOtp: {}", e.getMessage(), e);
return new OtpSendResponse(false, "Failed to call OTP send: " + e.getMessage());
}
}
/**
* Calls external API to verify OTP. Returns true if OTP valid.
*/
public boolean verifyOtp(String mobile, String otp) {
try {
// basic input validation
if (mobile == null || mobile.isEmpty() || otp == null || otp.isEmpty()) {
logger.debug("verifyOtp called with empty mobile or otp");
return false;
}
logger.info("Verifying Mobile OTP for mobile: {}", mobile);
// If an eKYC verify endpoint is configured, use it
if (verifyUrl != null && !verifyUrl.isEmpty()) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
Map<String, String> requestBody = new HashMap<>();
requestBody.put("mobileNumber", mobile);
requestBody.put("apiKey", apiKey);
requestBody.put("secretKey", secretKey);
requestBody.put("otp", otp);
HttpEntity<Map<String, String>> requestEntity = new HttpEntity<>(requestBody, headers);
try {
ResponseEntity<String> response = restTemplate.exchange(verifyUrl, HttpMethod.POST,
requestEntity, String.class);
logger.info("Mobile OTP verification API response: status={}, body={}", response.getStatusCode(),
response.getBody());
if (response.getStatusCode() == HttpStatus.OK) {
String respBody = response.getBody();
if (respBody != null) {
// try to parse JSON and look for common verification keys
ObjectMapper mapper = new ObjectMapper();
try {
Map respMap = mapper.readValue(respBody, Map.class);
if (respMap.containsKey("verified")) {
return Boolean.TRUE.equals(respMap.get("verified"));
}
if (respMap.containsKey("success")) {
return Boolean.TRUE.equals(respMap.get("success"));
}
if (respMap.containsKey("status") && "OK".equalsIgnoreCase(String.valueOf(respMap.get("status")))) {
return true;
}
} catch (Exception e) {
logger.debug("Unable to parse verification response body to JSON: {}", e.getMessage());
// fall through to treat 200 as success
}
}
// fallback: treat HTTP 200 as successful verification
return true;
} else {
logger.warn("OTP verification endpoint returned non-OK status: {}", response.getStatusCode());
return false;
}
} catch (Exception e) {
logger.error("Error calling eKYC OTP verify API: {}", e.getMessage(), e);
return false;
}
}
// stub behaviour when no verify URL configured: accept OTP "123456"
return false;
} catch (Exception e) {
logger.error("Unexpected error in verifyOtp: {}", e.getMessage(), e);
return false;
}
}
}