From 29d54147d3cecca955cd05199c36d3839bb35c67 Mon Sep 17 00:00:00 2001 From: Lincpay Tech Date: Mon, 1 Dec 2025 16:57:48 +0530 Subject: [PATCH] added intentbased pg --- .../bbps/controller/PaymentController.java | 22 +- .../controller/PaymentStartController.java | 31 ++ .../bbps/dto/PaymentStartReturnDto.java | 15 + .../bbps/dto/PaymentStatusDataResponse.java | 16 + .../bbps/dto/request/BaseClass.java | 4 + .../dto/request/PaymentStartUserRequest.java | 26 ++ .../dto/response/PaymentStartApiResponse.java | 103 ++++++ .../dto/response/PaymentStatusResponse.java | 34 ++ .../sentRequest/PaymentDetailSentRequest.java | 44 +++ .../sentRequest/PaymentRequestPayload.java | 14 + .../bbps/entites/PaymentStartRecord.java | 55 ++++ .../PaymentStartRecordRepository.java | 10 + .../bbps/service/PaymentStartService.java | 8 + .../serviceImpl/PaymentStartServiceImpl.java | 293 ++++++++++++++++++ .../bbps/util/BBPSConstant.java | 6 +- .../bbps/util/BillerMapperHelper.java | 34 +- .../bbps/util/EncryptionUtil.java | 64 ++++ .../billpaymentservice/bbps/util/Helper.java | 9 + 18 files changed, 781 insertions(+), 7 deletions(-) create mode 100644 src/main/java/com/billpaymentservice/bbps/controller/PaymentStartController.java create mode 100644 src/main/java/com/billpaymentservice/bbps/dto/PaymentStartReturnDto.java create mode 100644 src/main/java/com/billpaymentservice/bbps/dto/PaymentStatusDataResponse.java create mode 100644 src/main/java/com/billpaymentservice/bbps/dto/request/PaymentStartUserRequest.java create mode 100644 src/main/java/com/billpaymentservice/bbps/dto/response/PaymentStartApiResponse.java create mode 100644 src/main/java/com/billpaymentservice/bbps/dto/response/PaymentStatusResponse.java create mode 100644 src/main/java/com/billpaymentservice/bbps/dto/sentRequest/PaymentDetailSentRequest.java create mode 100644 src/main/java/com/billpaymentservice/bbps/dto/sentRequest/PaymentRequestPayload.java create mode 100644 src/main/java/com/billpaymentservice/bbps/entites/PaymentStartRecord.java create mode 100644 src/main/java/com/billpaymentservice/bbps/repository/PaymentStartRecordRepository.java create mode 100644 src/main/java/com/billpaymentservice/bbps/service/PaymentStartService.java create mode 100644 src/main/java/com/billpaymentservice/bbps/serviceImpl/PaymentStartServiceImpl.java create mode 100644 src/main/java/com/billpaymentservice/bbps/util/EncryptionUtil.java diff --git a/src/main/java/com/billpaymentservice/bbps/controller/PaymentController.java b/src/main/java/com/billpaymentservice/bbps/controller/PaymentController.java index fdfaf84..cb3731f 100644 --- a/src/main/java/com/billpaymentservice/bbps/controller/PaymentController.java +++ b/src/main/java/com/billpaymentservice/bbps/controller/PaymentController.java @@ -12,9 +12,10 @@ import com.billpaymentservice.bbps.dto.ResponseModel; import com.billpaymentservice.bbps.dto.request.BillPaymentRequest; import com.billpaymentservice.bbps.dto.request.TransactionStatusRequest; 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.service.PaymentService; +import com.billpaymentservice.bbps.service.PaymentStartService; +import com.billpaymentservice.bbps.dto.request.PaymentStartUserRequest; @RestController @RequestMapping("/agent/payment") @@ -25,9 +26,12 @@ public class PaymentController { */ 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.paymentStartService = paymentStartService; } @PostMapping(value = "/intiatePayment", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) @@ -62,5 +66,15 @@ public class PaymentController { return ResponseEntity.ok(genralResp); } - -} + @PostMapping(value = "/startPayment", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity 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); + } + + +} \ No newline at end of file diff --git a/src/main/java/com/billpaymentservice/bbps/controller/PaymentStartController.java b/src/main/java/com/billpaymentservice/bbps/controller/PaymentStartController.java new file mode 100644 index 0000000..6a42659 --- /dev/null +++ b/src/main/java/com/billpaymentservice/bbps/controller/PaymentStartController.java @@ -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 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); + } +} diff --git a/src/main/java/com/billpaymentservice/bbps/dto/PaymentStartReturnDto.java b/src/main/java/com/billpaymentservice/bbps/dto/PaymentStartReturnDto.java new file mode 100644 index 0000000..ae7fb42 --- /dev/null +++ b/src/main/java/com/billpaymentservice/bbps/dto/PaymentStartReturnDto.java @@ -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; + +} diff --git a/src/main/java/com/billpaymentservice/bbps/dto/PaymentStatusDataResponse.java b/src/main/java/com/billpaymentservice/bbps/dto/PaymentStatusDataResponse.java new file mode 100644 index 0000000..bb356e8 --- /dev/null +++ b/src/main/java/com/billpaymentservice/bbps/dto/PaymentStatusDataResponse.java @@ -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; + +} diff --git a/src/main/java/com/billpaymentservice/bbps/dto/request/BaseClass.java b/src/main/java/com/billpaymentservice/bbps/dto/request/BaseClass.java index 05948e3..ebc6c50 100644 --- a/src/main/java/com/billpaymentservice/bbps/dto/request/BaseClass.java +++ b/src/main/java/com/billpaymentservice/bbps/dto/request/BaseClass.java @@ -54,4 +54,8 @@ public abstract class BaseClass { private String udf9; private String udf10; + private String udf11; + private String udf12; + private String udf13; + private String udf14; } diff --git a/src/main/java/com/billpaymentservice/bbps/dto/request/PaymentStartUserRequest.java b/src/main/java/com/billpaymentservice/bbps/dto/request/PaymentStartUserRequest.java new file mode 100644 index 0000000..646041a --- /dev/null +++ b/src/main/java/com/billpaymentservice/bbps/dto/request/PaymentStartUserRequest.java @@ -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; + + +} \ No newline at end of file diff --git a/src/main/java/com/billpaymentservice/bbps/dto/response/PaymentStartApiResponse.java b/src/main/java/com/billpaymentservice/bbps/dto/response/PaymentStartApiResponse.java new file mode 100644 index 0000000..d62b9b8 --- /dev/null +++ b/src/main/java/com/billpaymentservice/bbps/dto/response/PaymentStartApiResponse.java @@ -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; + } + } +} diff --git a/src/main/java/com/billpaymentservice/bbps/dto/response/PaymentStatusResponse.java b/src/main/java/com/billpaymentservice/bbps/dto/response/PaymentStatusResponse.java new file mode 100644 index 0000000..2bf8efe --- /dev/null +++ b/src/main/java/com/billpaymentservice/bbps/dto/response/PaymentStatusResponse.java @@ -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; + } +} \ No newline at end of file diff --git a/src/main/java/com/billpaymentservice/bbps/dto/sentRequest/PaymentDetailSentRequest.java b/src/main/java/com/billpaymentservice/bbps/dto/sentRequest/PaymentDetailSentRequest.java new file mode 100644 index 0000000..a9f56d7 --- /dev/null +++ b/src/main/java/com/billpaymentservice/bbps/dto/sentRequest/PaymentDetailSentRequest.java @@ -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; + +} diff --git a/src/main/java/com/billpaymentservice/bbps/dto/sentRequest/PaymentRequestPayload.java b/src/main/java/com/billpaymentservice/bbps/dto/sentRequest/PaymentRequestPayload.java new file mode 100644 index 0000000..bb8064a --- /dev/null +++ b/src/main/java/com/billpaymentservice/bbps/dto/sentRequest/PaymentRequestPayload.java @@ -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; +} diff --git a/src/main/java/com/billpaymentservice/bbps/entites/PaymentStartRecord.java b/src/main/java/com/billpaymentservice/bbps/entites/PaymentStartRecord.java new file mode 100644 index 0000000..fa2a17a --- /dev/null +++ b/src/main/java/com/billpaymentservice/bbps/entites/PaymentStartRecord.java @@ -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; +} diff --git a/src/main/java/com/billpaymentservice/bbps/repository/PaymentStartRecordRepository.java b/src/main/java/com/billpaymentservice/bbps/repository/PaymentStartRecordRepository.java new file mode 100644 index 0000000..80610a3 --- /dev/null +++ b/src/main/java/com/billpaymentservice/bbps/repository/PaymentStartRecordRepository.java @@ -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 { +} diff --git a/src/main/java/com/billpaymentservice/bbps/service/PaymentStartService.java b/src/main/java/com/billpaymentservice/bbps/service/PaymentStartService.java new file mode 100644 index 0000000..eb91879 --- /dev/null +++ b/src/main/java/com/billpaymentservice/bbps/service/PaymentStartService.java @@ -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); +} diff --git a/src/main/java/com/billpaymentservice/bbps/serviceImpl/PaymentStartServiceImpl.java b/src/main/java/com/billpaymentservice/bbps/serviceImpl/PaymentStartServiceImpl.java new file mode 100644 index 0000000..52c1f4c --- /dev/null +++ b/src/main/java/com/billpaymentservice/bbps/serviceImpl/PaymentStartServiceImpl.java @@ -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 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 top = objectMapper.readValue(respBody, new TypeReference>(){}); + + // Pick inner payload if present + Map payload = top; + if (top.containsKey("data") && top.get("data") instanceof Map) { + //noinspection unchecked + payload = (Map) 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 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; + } + +} \ No newline at end of file diff --git a/src/main/java/com/billpaymentservice/bbps/util/BBPSConstant.java b/src/main/java/com/billpaymentservice/bbps/util/BBPSConstant.java index 50e56a3..1a50ce3 100644 --- a/src/main/java/com/billpaymentservice/bbps/util/BBPSConstant.java +++ b/src/main/java/com/billpaymentservice/bbps/util/BBPSConstant.java @@ -144,5 +144,9 @@ public class BBPSConstant { 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 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"; } \ No newline at end of file diff --git a/src/main/java/com/billpaymentservice/bbps/util/BillerMapperHelper.java b/src/main/java/com/billpaymentservice/bbps/util/BillerMapperHelper.java index 059db76..0608f39 100644 --- a/src/main/java/com/billpaymentservice/bbps/util/BillerMapperHelper.java +++ b/src/main/java/com/billpaymentservice/bbps/util/BillerMapperHelper.java @@ -1,5 +1,6 @@ package com.billpaymentservice.bbps.util; +import java.time.LocalDateTime; import java.util.ArrayList; import java.util.Collections; import java.util.Date; @@ -37,11 +38,13 @@ import com.billpaymentservice.bbps.dto.request.ComplaintRequestDTO; import com.billpaymentservice.bbps.dto.request.FetchBillRequest; import com.billpaymentservice.bbps.dto.request.FetchBillValidatestRequest; 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.BillDataResponse; import com.billpaymentservice.bbps.dto.response.ClientReqIdResponse; import com.billpaymentservice.bbps.dto.response.ComplaintHistoryStatusResponse; 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.TicketCheckStatusResponse; import com.billpaymentservice.bbps.dto.response.ViewAllComplaintsResponse; @@ -77,6 +80,7 @@ import com.billpaymentservice.bbps.entites.CustomerParamBill; import com.billpaymentservice.bbps.entites.CustomerParamBillValidation; import com.billpaymentservice.bbps.entites.CustomerParamGroupById; import com.billpaymentservice.bbps.entites.GroupParam; +import com.billpaymentservice.bbps.entites.PaymentStartRecord; import com.billpaymentservice.bbps.entites.Plan; import com.billpaymentservice.bbps.entites.PlanCategorySubType; import com.billpaymentservice.bbps.entites.PlanResponses; @@ -99,11 +103,14 @@ import com.billpaymentservice.bbps.repository.ComplaintStatusRepository; import com.billpaymentservice.bbps.repository.ContactMasterDataRepository; import com.billpaymentservice.bbps.repository.CustomerParamGroupByBillerIdRepository; import com.billpaymentservice.bbps.repository.FetchBillValidateRepository; +import com.billpaymentservice.bbps.repository.PaymentStartRecordRepository; import com.billpaymentservice.bbps.repository.PlanResponseRepository; import com.billpaymentservice.bbps.repository.ReOpenComplaintRepository; import com.billpaymentservice.bbps.repository.TicketDispositionRepository; import com.billpaymentservice.bbps.repository.ViewAllComplainTicketDetailRepository; +import jakarta.persistence.Column; + @Component public class BillerMapperHelper { @@ -128,7 +135,7 @@ public class BillerMapperHelper { private final BillerDataRepository billerDataRespository; private final BillMasterRepository billMasterRepository; private final BillValidationRepository billValidationRepository; - + private final PaymentStartRecordRepository paymentStartRecordRepository; public BillerMapperHelper(BillerCategoryWiseRepository billerRepo, CustomerParamGroupByBillerIdRepository customerParamGroupByBillerIdRepo, AmountOptionByBillerIdRepository amountOptionByBillerIdRepository, @@ -143,7 +150,9 @@ public class BillerMapperHelper { ViewAllComplainTicketDetailRepository viewAllComplainTicketDetailRepository, ContactMasterDataRepository contactMasterDataRepository, BBPSMasterDataRepo bbpsMasterDataRepo, BillerDataRepository billerDataRespository, BillMasterRepository billMasterRepository, - BillValidationRepository billValidationRepository) { + BillValidationRepository billValidationRepository, + PaymentStartRecordRepository paymentStartRecordRepository) { + this.paymentStartRecordRepository = paymentStartRecordRepository; this.billerRepo = billerRepo; this.customerParamGroupByBillerIdRepo = customerParamGroupByBillerIdRepo; this.amountOptionByBillerIdRepository = amountOptionByBillerIdRepository; @@ -1057,4 +1066,25 @@ public class BillerMapperHelper { forward.setTxnNpciId(request.getTxnNpciId()); 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); + + + } } diff --git a/src/main/java/com/billpaymentservice/bbps/util/EncryptionUtil.java b/src/main/java/com/billpaymentservice/bbps/util/EncryptionUtil.java new file mode 100644 index 0000000..f99b428 --- /dev/null +++ b/src/main/java/com/billpaymentservice/bbps/util/EncryptionUtil.java @@ -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; + } +} diff --git a/src/main/java/com/billpaymentservice/bbps/util/Helper.java b/src/main/java/com/billpaymentservice/bbps/util/Helper.java index 74a4d3f..b69c6e2 100644 --- a/src/main/java/com/billpaymentservice/bbps/util/Helper.java +++ b/src/main/java/com/billpaymentservice/bbps/util/Helper.java @@ -177,5 +177,14 @@ public class Helper { user.setAge(request.getAge()); return user; } + + public T convertJsonToObject(String json, Class clazz) { + try { + return objectMapper.readValue(json, clazz); + } catch (Exception e) { + throw new CustomException("Invalid JSON from payment API", "FAILED", 400, json); + } + } + } \ No newline at end of file