Merge pull request 'feature/payment' (#20) from feature/payment into develop

Reviewed-on: https://git.zeptic.in/himanshu/bbps/pulls/20
This commit is contained in:
2025-12-01 17:17:38 +05:30
18 changed files with 779 additions and 7 deletions
@@ -12,9 +12,10 @@ import com.billpaymentservice.bbps.dto.ResponseModel;
import com.billpaymentservice.bbps.dto.request.BillPaymentRequest; import com.billpaymentservice.bbps.dto.request.BillPaymentRequest;
import com.billpaymentservice.bbps.dto.request.TransactionStatusRequest; import com.billpaymentservice.bbps.dto.request.TransactionStatusRequest;
import com.billpaymentservice.bbps.dto.request.TrasactionStatusByClientReqIdRequest; import com.billpaymentservice.bbps.dto.request.TrasactionStatusByClientReqIdRequest;
import com.billpaymentservice.bbps.dto.response.FetchBillValidateResponse;
import com.billpaymentservice.bbps.dto.response.TransactionStatusResponse; import com.billpaymentservice.bbps.dto.response.TransactionStatusResponse;
import com.billpaymentservice.bbps.service.PaymentService; import com.billpaymentservice.bbps.service.PaymentService;
import com.billpaymentservice.bbps.service.PaymentStartService;
import com.billpaymentservice.bbps.dto.request.PaymentStartUserRequest;
@RestController @RestController
@RequestMapping("/agent/payment") @RequestMapping("/agent/payment")
@@ -25,9 +26,12 @@ public class PaymentController {
*/ */
private final PaymentService paymentService; private final PaymentService paymentService;
private final PaymentStartService paymentStartService;
public PaymentController(PaymentService paymentService) { // New constructor used by Spring to inject both services
public PaymentController(PaymentService paymentService, PaymentStartService paymentStartService) {
this.paymentService = paymentService; this.paymentService = paymentService;
this.paymentStartService = paymentStartService;
} }
@PostMapping(value = "/intiatePayment", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) @PostMapping(value = "/intiatePayment", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@@ -62,5 +66,15 @@ public class PaymentController {
return ResponseEntity.ok(genralResp); return ResponseEntity.ok(genralResp);
} }
@PostMapping(value = "/startPayment", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
} public ResponseEntity<ResponseModel> startPayment(@Validated @RequestBody PaymentStartUserRequest paymentStartUserRequest) {
if (this.paymentStartService == null) {
// Defensive: if bean not injected, return failure
return ResponseEntity.badRequest().body(new ResponseModel("PaymentStartService not available", "FAILED", 400, null));
}
ResponseModel response = paymentStartService.startPayment(paymentStartUserRequest);
return ResponseEntity.ok(response);
}
}
@@ -0,0 +1,31 @@
package com.billpaymentservice.bbps.controller;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import com.billpaymentservice.bbps.dto.ResponseModel;
import com.billpaymentservice.bbps.dto.request.PaymentStartUserRequest;
import com.billpaymentservice.bbps.service.PaymentStartService;
@RestController
public class PaymentStartController {
private final PaymentStartService paymentStartService;
public PaymentStartController(PaymentStartService paymentStartService) {
this.paymentStartService = paymentStartService;
}
@PostMapping(value = "/startWebPayment", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<ResponseModel> startPayment(@Validated @RequestBody PaymentStartUserRequest paymentStartUserRequest) {
if (this.paymentStartService == null) {
// Defensive: if bean not injected, return failure
return ResponseEntity.badRequest().body(new ResponseModel("PaymentStartService not available", "FAILED", 400, null));
}
ResponseModel response = paymentStartService.startPayment(paymentStartUserRequest);
return ResponseEntity.ok(response);
}
}
@@ -0,0 +1,15 @@
package com.billpaymentservice.bbps.dto;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class PaymentStartReturnDto {
private String amount;
private String orderNo;
private String created_on;
private String qrString;
private String txnId;
}
@@ -0,0 +1,16 @@
package com.billpaymentservice.bbps.dto;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class PaymentStatusDataResponse {
private String amount;
private String orderNo;
private String created_on;
private String qrString;
private String mid;
private String txnId;
}
@@ -53,4 +53,8 @@ public abstract class BaseClass {
private String udf9; private String udf9;
private String udf10; private String udf10;
private String udf11;
private String udf12;
private String udf13;
private String udf14;
} }
@@ -0,0 +1,26 @@
package com.billpaymentservice.bbps.dto.request;
import java.io.Serializable;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
// DTO representing the user request payload described by the user
public class PaymentStartUserRequest extends BaseClass implements Serializable {
private String amount;
private String currency;
private String emailId;
private String customerVpa;
private String mobileNo;
private String respUrl;
private String txnReqType;
private String name;
}
@@ -0,0 +1,103 @@
package com.billpaymentservice.bbps.dto.response;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.ToString;
@ToString
public class PaymentStartApiResponse {
private String message;
private String statusCode;
private ResponseData responseData;
public PaymentStartApiResponse() {
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public String getStatusCode() {
return statusCode;
}
public void setStatusCode(String statusCode) {
this.statusCode = statusCode;
}
public ResponseData getResponseData() {
return responseData;
}
public void setResponseData(ResponseData responseData) {
this.responseData = responseData;
}
@ToString
public static class ResponseData {
private String amount;
private String orderNo;
@JsonProperty("created_on")
private String createdOn;
private String qrString;
private String mid;
private String txnId;
public ResponseData() {
}
public String getAmount() {
return amount;
}
public void setAmount(String amount) {
this.amount = amount;
}
public String getOrderNo() {
return orderNo;
}
public void setOrderNo(String orderNo) {
this.orderNo = orderNo;
}
public String getCreatedOn() {
return createdOn;
}
public void setCreatedOn(String createdOn) {
this.createdOn = createdOn;
}
public String getQrString() {
return qrString;
}
public void setQrString(String qrString) {
this.qrString = qrString;
}
public String getMid() {
return mid;
}
public void setMid(String mid) {
this.mid = mid;
}
public String getTxnId() {
return txnId;
}
public void setTxnId(String txnId) {
this.txnId = txnId;
}
}
}
@@ -0,0 +1,34 @@
package com.billpaymentservice.bbps.dto.response;
import com.billpaymentservice.bbps.dto.PaymentStatusDataResponse;
public class PaymentStatusResponse {
private String message;
private String statusCode;
private PaymentStatusDataResponse data;
public PaymentStatusResponse() {}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public String getStatusCode() {
return statusCode;
}
public void setStatusCode(String statusCode) {
this.statusCode = statusCode;
}
public PaymentStatusDataResponse getData() {
return data;
}
public void setData(PaymentStatusDataResponse data) {
this.data = data;
}
}
@@ -0,0 +1,44 @@
package com.billpaymentservice.bbps.dto.sentRequest;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class PaymentDetailSentRequest {
private String amount;
private String currency;
private String emailId;
private String userId;
private String customerVpa;
private String dateOfReg;
private String enckey;
private String mid;
private String mobileNo;
private String orderNo;
private String respUrl;
private String txnReqType;
private String name;
private String udf1;
private String udf2;
private String udf3;
private String udf4;
private String udf5;
private String udf6;
private String udf7;
private String udf8;
private String udf9;
private String udf10;
private String udf11;
private String udf12;
private String udf13;
private String udf14;
}
@@ -0,0 +1,14 @@
package com.billpaymentservice.bbps.dto.sentRequest;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
public class PaymentRequestPayload {
private String mid;
private String payload;
}
@@ -0,0 +1,55 @@
package com.billpaymentservice.bbps.entites;
import java.time.LocalDateTime;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table(name = "payment_start_record")
public class PaymentStartRecord {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "request_id", unique = true, nullable = false)
private String requestId;
@Column(name = "client_mid", nullable = false)
private String clientMid;
@Column(name = "client_user_id")
private String clientUserId;
@Column(name = "internal_txn_id", unique = true, nullable = false)
private String internalTxnId;
@Column(name = "order_no", unique = true, nullable = false)
private String orderNo;
@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;
@Column(name = "qr_string", columnDefinition = "TEXT")
private String qrString;
@Column(name = "created_on", nullable = false)
private String createdOn;
}
@@ -0,0 +1,10 @@
package com.billpaymentservice.bbps.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import com.billpaymentservice.bbps.entites.PaymentStartRecord;
@Repository
public interface PaymentStartRecordRepository extends JpaRepository<PaymentStartRecord, Long> {
}
@@ -0,0 +1,8 @@
package com.billpaymentservice.bbps.service;
import com.billpaymentservice.bbps.dto.ResponseModel;
import com.billpaymentservice.bbps.dto.request.PaymentStartUserRequest;
public interface PaymentStartService {
ResponseModel startPayment(PaymentStartUserRequest request);
}
@@ -0,0 +1,293 @@
package com.billpaymentservice.bbps.serviceImpl;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import com.billpaymentservice.bbps.dto.PaymentStartReturnDto;
import com.billpaymentservice.bbps.dto.ResponseModel;
import com.billpaymentservice.bbps.dto.request.PaymentStartUserRequest;
import com.billpaymentservice.bbps.dto.response.PaymentStartApiResponse;
import com.billpaymentservice.bbps.dto.response.PaymentStatusResponse;
import com.billpaymentservice.bbps.dto.sentRequest.PaymentDetailSentRequest;
import com.billpaymentservice.bbps.dto.sentRequest.PaymentRequestPayload;
import com.billpaymentservice.bbps.entites.InboundTraceLog;
import com.billpaymentservice.bbps.exception.CustomException;
import com.billpaymentservice.bbps.repository.PaymentStartRecordRepository;
import com.billpaymentservice.bbps.repository.BharatConnectMasterRepository;
import com.billpaymentservice.bbps.repository.BBPSMasterDataRepo;
import com.billpaymentservice.bbps.service.PaymentStartService;
import com.billpaymentservice.bbps.util.EncryptionUtil;
import com.billpaymentservice.bbps.util.Helper;
import com.billpaymentservice.bbps.util.BBPSConstant;
import com.billpaymentservice.bbps.util.BillerMapperHelper;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.billpaymentservice.bbps.entites.OutboundTraceLog;
import com.billpaymentservice.bbps.entites.BharatConnectMaster;
@Service
public class PaymentStartServiceImpl implements PaymentStartService {
private static final Logger LOGGER = LoggerFactory.getLogger(PaymentStartServiceImpl.class);
private String uatPaymentUrl="https://uat.godemo.in/api/UATIntegration/paymentrequest/seamless";
private final PaymentStartRecordRepository paymentStartRecordRepository;
private final HttpService httpService;
private final EncryptionUtil encryptionUtil;
private final Helper helper;
private final BharatConnectMasterRepository bharatConnectMasterRepository;
private final BBPSMasterDataRepo bbpsMasterDataRepo;
private final BillerMapperHelper bbpsMapperHelper ;
private final ObjectMapper objectMapper = new ObjectMapper();
public PaymentStartServiceImpl(PaymentStartRecordRepository paymentStartRecordRepository,
HttpService httpService,
EncryptionUtil encryptionUtil,
Helper helper,
BharatConnectMasterRepository bharatConnectMasterRepository,
BBPSMasterDataRepo bbpsMasterDataRepo,
BillerMapperHelper bbpsMapperHelper) {
this.paymentStartRecordRepository = paymentStartRecordRepository;
this.httpService = httpService;
this.encryptionUtil = encryptionUtil;
this.helper = helper;
this.bharatConnectMasterRepository = bharatConnectMasterRepository;
this.bbpsMasterDataRepo = bbpsMasterDataRepo;
this.bbpsMapperHelper = bbpsMapperHelper;
}
@Override
public ResponseModel startPayment(PaymentStartUserRequest request) {
String requestId = request.getReqId();
String apiName = "StartPayment";
String txnId = helper.generateTxnId(15);
helper.validateMerchant(request.getMid());
// inbound log
String requestJson = helper.convertObjectToJson(request);
InboundTraceLog inboundLog =
helper.createInboundLog(requestId, txnId, apiName, request.getMid(),
BBPSConstant.HTTP_POST, requestJson);
// Prepare request payload
PaymentDetailSentRequest sent = createPaymentSentRequest(request);
String orderNo = "ORD" + helper.generateTxnId(12);
sent.setOrderNo(orderNo);
String plaintext = helper.convertObjectToJson(sent);
String encKey = BBPSConstant.enckey;
sent.setEnckey(encKey);
String encrypted = encryptionUtil.encryptMData(plaintext, encKey);
PaymentRequestPayload finalPayload = new PaymentRequestPayload();
finalPayload.setMid(BBPSConstant.mid);
finalPayload.setPayload(encrypted);
String forwardJson = helper.convertObjectToJson(finalPayload);
OutboundTraceLog outboundLog =
helper.createOutboundLog(txnId, requestId, apiName, uatPaymentUrl,
BBPSConstant.HTTP_POST, forwardJson);
// --- CALL EXTERNAL API ---
Map<String, String> responseMap =
httpService.postRequest(uatPaymentUrl, forwardJson, null);
LOGGER.info("Payment Start Response: " + responseMap);
int httpCode = Integer.parseInt(responseMap.getOrDefault("respCode", "500"));
String respBody = responseMap.get(BBPSConstant.RESPONSE_DATA_KEY);
helper.updateOutboundLogResponse(outboundLog, respBody);
// --- HTTP FAILURE ---
if (httpCode != 200) {
helper.updateInboundLogFailure(inboundLog, respBody, String.valueOf(httpCode));
throw new CustomException(
"Upstream HTTP error",
"FAILED",
httpCode,
respBody
);
}
if (respBody == null || respBody.isEmpty()) {
helper.updateInboundLogFailure(inboundLog, "Empty response from payment provider", "500");
throw new CustomException(
"Empty response from payment provider",
"FAILED",
500,
null
);
}
// --- Convert JSON to object ---
PaymentStartApiResponse apiResponse = null;
try {
Map<String, Object> top = objectMapper.readValue(respBody, new TypeReference<Map<String, Object>>(){});
// Pick inner payload if present
Map<String, Object> payload = top;
if (top.containsKey("data") && top.get("data") instanceof Map) {
//noinspection unchecked
payload = (Map<String, Object>) top.get("data");
}
// Simple failure detection: status == Failure OR statusCode != 200
String status = String.valueOf(payload.getOrDefault("status", top.getOrDefault("status", ""))).trim();
int outcomeCode = 0;
Object scObj = payload.getOrDefault("statusCode", top.get("statusCode"));
// prefer numeric status field if present
if (status.matches("\\d+")) {
try { outcomeCode = Integer.parseInt(status); } catch (NumberFormatException ignore) {}
}
// if not present, fall back to statusCode
if (outcomeCode == 0 && scObj != null) {
try { outcomeCode = Integer.parseInt(String.valueOf(scObj)); } catch (NumberFormatException ignore) {}
}
// default to 200 when nothing provided
if (outcomeCode == 0) outcomeCode = 200;
String message = String.valueOf(payload.getOrDefault("message", top.getOrDefault("message", "")));
// Map outcome codes according to provided table:
// Success: 200
// Success without warnings: 205
// Warning -> success (fraud accepted): 206 (treat as success)
// Warning -> failed (fraud rejected): 207 (treat as failure)
// Transaction processing: 198 (treat as pending/processing)
// Fail/Exception: 417 -> failure
// Failed: 99 -> failure
// Rejected by acquirer: 197 -> failure
// Declined: 194 -> failure
// Auth success but recurring error: 193 -> failure
// Warning: 1003 -> warning (non-fatal)
// Handle special cases
if (outcomeCode == 200 || outcomeCode == 205 || outcomeCode == 206) {
// success - build apiResponse from wrapper or direct payload
if (top.containsKey("message") && (top.containsKey("responseData") || top.containsKey("data"))) {
apiResponse = objectMapper.convertValue(top, PaymentStartApiResponse.class);
} else {
Map<String, Object> dataMap = payload;
PaymentStartApiResponse.ResponseData rd = new PaymentStartApiResponse.ResponseData();
rd.setAmount(String.valueOf(dataMap.getOrDefault("amount", "")));
rd.setOrderNo(String.valueOf(dataMap.getOrDefault("orderNo", dataMap.getOrDefault("order_no", ""))));
rd.setCreatedOn(String.valueOf(dataMap.getOrDefault("created_on", dataMap.getOrDefault("createdOn", ""))));
rd.setQrString(String.valueOf(dataMap.getOrDefault("qrString", dataMap.getOrDefault("qr_string", ""))));
rd.setMid(String.valueOf(dataMap.getOrDefault("mid", "")));
rd.setTxnId(String.valueOf(dataMap.getOrDefault("txnId", dataMap.getOrDefault("txn_id", ""))));
apiResponse = new PaymentStartApiResponse();
apiResponse.setMessage(String.valueOf(top.getOrDefault("message", "SUCCESS")));
apiResponse.setStatusCode(String.valueOf(outcomeCode));
apiResponse.setResponseData(rd);
}
helper.updateInboundLogSuccess(inboundLog, respBody, String.valueOf(outcomeCode));
// proceed normally
} else if (outcomeCode == 198) {
// transaction processing - treat as non-final success/pending
helper.updateInboundLogSuccess(inboundLog, respBody, String.valueOf(outcomeCode));
return ResponseModel.builder()
.status(BBPSConstant.SENT_TO_PROCESSOR_STATUS)
.statusCode(outcomeCode)
.message(message == null || message.isEmpty() ? "Transaction processing" : message)
.data(null)
.build();
} else if (outcomeCode == 1003) {
// warning - return a WARNING response; don't throw immediately
helper.updateInboundLogSuccess(inboundLog, respBody, String.valueOf(outcomeCode));
return ResponseModel.builder()
.status("WARNING")
.statusCode(outcomeCode)
.message(message == null || message.isEmpty() ? "Warning from merchant" : message)
.data(null)
.build();
} else if (outcomeCode == 207) {
// Fraud rejected - explicit failure, handle exception
helper.updateInboundLogFailure(inboundLog, respBody, String.valueOf(outcomeCode));
throw new CustomException(message == null || message.isEmpty() ? "Fraud Rejected" : message,
"FAILED", outcomeCode, respBody);
} else {
// Other failure codes (417,99,197,194,193 etc.)
helper.updateInboundLogFailure(inboundLog, respBody, String.valueOf(outcomeCode));
throw new CustomException(message == null || message.isEmpty() ? "Upstream failure" : message,
"FAILED", outcomeCode, respBody);
}
} catch (JsonProcessingException e) {
LOGGER.error("Error parsing payment start API response JSON: {}", e.getMessage(), e);
helper.updateInboundLogFailure(inboundLog, respBody, BBPSConstant.JSON_ERROR);
throw new CustomException("Invalid JSON received from payment API");
}
// Save record
bbpsMapperHelper.savePaymentStartRecord(request, apiResponse, txnId, orderNo);
// Prepare DTO
PaymentStartReturnDto responseData = new PaymentStartReturnDto();
responseData.setAmount(apiResponse.getResponseData().getAmount());
responseData.setCreated_on(apiResponse.getResponseData().getCreatedOn());
responseData.setOrderNo(apiResponse.getResponseData().getOrderNo());
responseData.setQrString(apiResponse.getResponseData().getQrString());
responseData.setTxnId(apiResponse.getResponseData().getTxnId());
helper.updateInboundLogSuccess(inboundLog, respBody,
apiResponse.getStatusCode() == null ? "200" : apiResponse.getStatusCode().toString());
return ResponseModel.builder()
.status("SUCCESS")
.statusCode(httpCode)
.message(apiResponse.getMessage())
.data(responseData)
.build();
}
private PaymentDetailSentRequest createPaymentSentRequest(PaymentStartUserRequest request) {
PaymentDetailSentRequest sent = new PaymentDetailSentRequest();
sent.setAmount(request.getAmount());
sent.setCurrency(request.getCurrency());
sent.setEmailId(request.getEmailId());
sent.setUserId(request.getMid());
sent.setCustomerVpa(request.getCustomerVpa());
sent.setDateOfReg("2025-12-31");
String effectiveMid = (request.getMid() != null && !request.getMid().isEmpty()) ? request.getMid() : BBPSConstant.mid;
sent.setMid(BBPSConstant.mid);
sent.setMobileNo(request.getMobileNo());
sent.setRespUrl(BBPSConstant.callbackurl);
sent.setTxnReqType("S");
sent.setName(request.getName());
sent.setUdf1(request.getUdf1());
sent.setUdf2(request.getUdf2());
sent.setUdf3(request.getUdf3());
sent.setUdf4(request.getUdf4());
sent.setUdf5(request.getUdf5());
sent.setUdf6(request.getUdf6());
sent.setUdf7(request.getUdf7());
sent.setUdf8(request.getUdf8());
sent.setUdf9(request.getUdf9());
sent.setUdf10(request.getUdf10());
sent.setUdf11(request.getUdf11());
sent.setUdf12(request.getUdf12());
sent.setUdf13(request.getUdf13());
sent.setUdf14(request.getUdf14());
return sent;
}
}
@@ -146,5 +146,9 @@ public class BBPSConstant {
public static final long BILL_FETCH_DELAY_MS = 5000L; // used after initiate bill fetch public static final long BILL_FETCH_DELAY_MS = 5000L; // used after initiate bill fetch
public static final long BILL_VALIDATE_DELAY_MS = 12000L; // used after initiate bill validate public static final long BILL_VALIDATE_DELAY_MS = 12000L; // used after initiate bill validate
public static final long PAYMENT_STATUS_DELAY_MS = 9000L; // used after initiate payment public static final long PAYMENT_STATUS_DELAY_MS = 9000L; // used after initiate payment
public static final String enckey="zk52olsyr9fl95h50eus8bhtyhoo5lp7";
public static final String mid="Test-By-Aditya-1001";
public static final String callbackurl= "https://checkout.solwio.in/PGPayU/payu/callback";
public static final String companyName= "techwork technology";
} }
@@ -1,5 +1,6 @@
package com.billpaymentservice.bbps.util; package com.billpaymentservice.bbps.util;
import java.time.LocalDateTime;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Date; import java.util.Date;
@@ -38,11 +39,13 @@ import com.billpaymentservice.bbps.dto.request.ComplaintRequestDTO;
import com.billpaymentservice.bbps.dto.request.FetchBillRequest; import com.billpaymentservice.bbps.dto.request.FetchBillRequest;
import com.billpaymentservice.bbps.dto.request.FetchBillValidatestRequest; import com.billpaymentservice.bbps.dto.request.FetchBillValidatestRequest;
import com.billpaymentservice.bbps.dto.request.GetTicketLatestStausRequest; import com.billpaymentservice.bbps.dto.request.GetTicketLatestStausRequest;
import com.billpaymentservice.bbps.dto.request.PaymentStartUserRequest;
import com.billpaymentservice.bbps.dto.response.AmountOptionsResponse; import com.billpaymentservice.bbps.dto.response.AmountOptionsResponse;
import com.billpaymentservice.bbps.dto.response.BillDataResponse; import com.billpaymentservice.bbps.dto.response.BillDataResponse;
import com.billpaymentservice.bbps.dto.response.ClientReqIdResponse; import com.billpaymentservice.bbps.dto.response.ClientReqIdResponse;
import com.billpaymentservice.bbps.dto.response.ComplaintHistoryStatusResponse; import com.billpaymentservice.bbps.dto.response.ComplaintHistoryStatusResponse;
import com.billpaymentservice.bbps.dto.response.FetchBillValidateResponse; import com.billpaymentservice.bbps.dto.response.FetchBillValidateResponse;
import com.billpaymentservice.bbps.dto.response.PaymentStartApiResponse;
import com.billpaymentservice.bbps.dto.response.PlanResponse; import com.billpaymentservice.bbps.dto.response.PlanResponse;
import com.billpaymentservice.bbps.dto.response.TicketCheckStatusResponse; import com.billpaymentservice.bbps.dto.response.TicketCheckStatusResponse;
import com.billpaymentservice.bbps.dto.response.ViewAllComplaintsResponse; import com.billpaymentservice.bbps.dto.response.ViewAllComplaintsResponse;
@@ -78,6 +81,7 @@ import com.billpaymentservice.bbps.entites.CustomerParamBill;
import com.billpaymentservice.bbps.entites.CustomerParamBillValidation; import com.billpaymentservice.bbps.entites.CustomerParamBillValidation;
import com.billpaymentservice.bbps.entites.CustomerParamGroupById; import com.billpaymentservice.bbps.entites.CustomerParamGroupById;
import com.billpaymentservice.bbps.entites.GroupParam; import com.billpaymentservice.bbps.entites.GroupParam;
import com.billpaymentservice.bbps.entites.PaymentStartRecord;
import com.billpaymentservice.bbps.entites.Plan; import com.billpaymentservice.bbps.entites.Plan;
import com.billpaymentservice.bbps.entites.PlanCategorySubType; import com.billpaymentservice.bbps.entites.PlanCategorySubType;
import com.billpaymentservice.bbps.entites.PlanResponses; import com.billpaymentservice.bbps.entites.PlanResponses;
@@ -101,11 +105,14 @@ import com.billpaymentservice.bbps.repository.ComplaintStatusRepository;
import com.billpaymentservice.bbps.repository.ContactMasterDataRepository; import com.billpaymentservice.bbps.repository.ContactMasterDataRepository;
import com.billpaymentservice.bbps.repository.CustomerParamGroupByBillerIdRepository; import com.billpaymentservice.bbps.repository.CustomerParamGroupByBillerIdRepository;
import com.billpaymentservice.bbps.repository.FetchBillValidateRepository; import com.billpaymentservice.bbps.repository.FetchBillValidateRepository;
import com.billpaymentservice.bbps.repository.PaymentStartRecordRepository;
import com.billpaymentservice.bbps.repository.PlanResponseRepository; import com.billpaymentservice.bbps.repository.PlanResponseRepository;
import com.billpaymentservice.bbps.repository.ReOpenComplaintRepository; import com.billpaymentservice.bbps.repository.ReOpenComplaintRepository;
import com.billpaymentservice.bbps.repository.TicketDispositionRepository; import com.billpaymentservice.bbps.repository.TicketDispositionRepository;
import com.billpaymentservice.bbps.repository.ViewAllComplainTicketDetailRepository; import com.billpaymentservice.bbps.repository.ViewAllComplainTicketDetailRepository;
import jakarta.persistence.Column;
@Component @Component
public class BillerMapperHelper { public class BillerMapperHelper {
@@ -130,7 +137,7 @@ public class BillerMapperHelper {
private final BillerDataRepository billerDataRespository; private final BillerDataRepository billerDataRespository;
private final BillMasterRepository billMasterRepository; private final BillMasterRepository billMasterRepository;
private final BillValidationRepository billValidationRepository; private final BillValidationRepository billValidationRepository;
private final PaymentStartRecordRepository paymentStartRecordRepository;
public BillerMapperHelper(BillerCategoryWiseRepository billerRepo, public BillerMapperHelper(BillerCategoryWiseRepository billerRepo,
CustomerParamGroupByBillerIdRepository customerParamGroupByBillerIdRepo, CustomerParamGroupByBillerIdRepository customerParamGroupByBillerIdRepo,
AmountOptionByBillerIdRepository amountOptionByBillerIdRepository, AmountOptionByBillerIdRepository amountOptionByBillerIdRepository,
@@ -145,7 +152,9 @@ public class BillerMapperHelper {
ViewAllComplainTicketDetailRepository viewAllComplainTicketDetailRepository, ViewAllComplainTicketDetailRepository viewAllComplainTicketDetailRepository,
ContactMasterDataRepository contactMasterDataRepository, BBPSMasterDataRepo bbpsMasterDataRepo, ContactMasterDataRepository contactMasterDataRepository, BBPSMasterDataRepo bbpsMasterDataRepo,
BillerDataRepository billerDataRespository, BillMasterRepository billMasterRepository, BillerDataRepository billerDataRespository, BillMasterRepository billMasterRepository,
BillValidationRepository billValidationRepository) { BillValidationRepository billValidationRepository,
PaymentStartRecordRepository paymentStartRecordRepository) {
this.paymentStartRecordRepository = paymentStartRecordRepository;
this.billerRepo = billerRepo; this.billerRepo = billerRepo;
this.customerParamGroupByBillerIdRepo = customerParamGroupByBillerIdRepo; this.customerParamGroupByBillerIdRepo = customerParamGroupByBillerIdRepo;
this.amountOptionByBillerIdRepository = amountOptionByBillerIdRepository; this.amountOptionByBillerIdRepository = amountOptionByBillerIdRepository;
@@ -1060,6 +1069,25 @@ public class BillerMapperHelper {
return forward; return forward;
} }
public void savePaymentStartRecord(PaymentStartUserRequest request, PaymentStartApiResponse apiResponse,
String txnId, String orderNo) {
PaymentStartRecord record = new PaymentStartRecord();
record.setOrderNo(orderNo);
record.setRequestId(request.getReqId());
record.setClientMid(request.getMid());
record.setClientUserId(request.getClientUserId());
record.setInternalTxnId(txnId);
record.setAmount(apiResponse.getResponseData().getAmount());
record.setMid(apiResponse.getResponseData().getMid());
record.setTxnId(apiResponse.getResponseData().getTxnId());
record.setQrString(apiResponse.getResponseData().getQrString());
record.setCreatedOn(apiResponse.getResponseData().getCreatedOn());
paymentStartRecordRepository.save(record);
}
public TransactionHistoryMasterDTO toDTO(TransactionMaster transactionMaster) { public TransactionHistoryMasterDTO toDTO(TransactionMaster transactionMaster) {
if (transactionMaster == null) { if (transactionMaster == null) {
return null; return null;
@@ -0,0 +1,64 @@
package com.billpaymentservice.bbps.util;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
@Component
public class EncryptionUtil {
private static final Logger LOGGER = LoggerFactory.getLogger(EncryptionUtil.class);
/**
* Encrypts plaintext using AES/CBC/PKCS5Padding.
* If the provided key length is not 16/24/32 bytes it will be adjusted to 16 bytes (padded/truncated).
* NOTE: This follows the algorithm snippet provided by the user but pads/truncates the key to avoid
* common runtime errors when key lengths are incorrect.
*/
public String encryptMData(String plaintext, String encryptKey) {
try {
// Ensure key length is 16 bytes (AES-128). If callers provide a longer key this will truncate.
byte[] keyBytes = normalizeKey(encryptKey);
SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
byte[] ivBytes = cipher.getParameters().getParameterSpec(IvParameterSpec.class).getIV();
byte[] encryptedData = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8));
byte[] combinedBytes = new byte[ivBytes.length + encryptedData.length];
System.arraycopy(ivBytes, 0, combinedBytes, 0, ivBytes.length);
System.arraycopy(encryptedData, 0, combinedBytes, ivBytes.length, encryptedData.length);
return Base64.getEncoder().encodeToString(combinedBytes);
} catch (Exception e) {
LOGGER.error("Exception in encrypt() : encryptMData- {} : Message- {}", encryptKey, e.getMessage());
}
return null;
}
private byte[] normalizeKey(String encryptKey) {
if (encryptKey == null) {
encryptKey = "";
}
byte[] bytes = encryptKey.getBytes(StandardCharsets.UTF_8);
int targetLen = 16; // AES-128
if (bytes.length == targetLen || bytes.length == 24 || bytes.length == 32) {
return bytes;
}
byte[] normalized = new byte[targetLen];
for (int i = 0; i < targetLen; i++) {
if (i < bytes.length) {
normalized[i] = bytes[i];
} else {
normalized[i] = 0; // pad with zeros
}
}
return normalized;
}
}
@@ -177,5 +177,14 @@ public class Helper {
user.setAge(request.getAge()); user.setAge(request.getAge());
return user; return user;
} }
public <T> T convertJsonToObject(String json, Class<T> clazz) {
try {
return objectMapper.readValue(json, clazz);
} catch (Exception e) {
throw new CustomException("Invalid JSON from payment API", "FAILED", 400, json);
}
}
} }