From 38979f0f94f6f774cb0e5ce222875c03d8b57008 Mon Sep 17 00:00:00 2001 From: Lincpay Tech Date: Tue, 23 Dec 2025 10:38:59 +0530 Subject: [PATCH] transaction master and payment start record new entity with update --- .../bbps/entites/BillMaster.java | 1 + .../bbps/entites/PaymentStartRecord.java | 12 +- .../bbps/entites/TransactionMaster.java | 11 +- .../TransactionMasterRepository.java | 11 +- .../bbps/scheduler/PaymentRetryScheduler.java | 162 ------------------ .../bbps/serviceImpl/PaymentServiceImpl.java | 3 +- .../serviceImpl/TxnHistoryServiceImpl.java | 2 +- .../bbps/util/BillerMapperHelper.java | 6 +- .../billpaymentservice/bbps/util/Helper.java | 2 +- 9 files changed, 26 insertions(+), 184 deletions(-) delete mode 100644 src/main/java/com/billpaymentservice/bbps/scheduler/PaymentRetryScheduler.java diff --git a/src/main/java/com/billpaymentservice/bbps/entites/BillMaster.java b/src/main/java/com/billpaymentservice/bbps/entites/BillMaster.java index d39fd82..3b59f1f 100644 --- a/src/main/java/com/billpaymentservice/bbps/entites/BillMaster.java +++ b/src/main/java/com/billpaymentservice/bbps/entites/BillMaster.java @@ -30,6 +30,7 @@ public class BillMaster extends Auditable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + @Column(name = "bill_id", unique = true) private String billId; // primary key private String reqId; // client se aaya hua diff --git a/src/main/java/com/billpaymentservice/bbps/entites/PaymentStartRecord.java b/src/main/java/com/billpaymentservice/bbps/entites/PaymentStartRecord.java index 3da7591..c992a8e 100644 --- a/src/main/java/com/billpaymentservice/bbps/entites/PaymentStartRecord.java +++ b/src/main/java/com/billpaymentservice/bbps/entites/PaymentStartRecord.java @@ -35,11 +35,11 @@ public class PaymentStartRecord extends Auditable { @Column(name = "mid", nullable = false) private String mid; - @Column(name = "client_user_id") - private String clientUserId; + @Column(name = "user_id") + private String userId; - @Column(name = "internal_txn_id", unique = true, nullable = false) - private String internalTxnId; + @Column(name = "txn_id", unique = true, nullable = false) + private String txnId; @Column(name = "order_no", unique = true, nullable = false) private String orderNo; @@ -47,8 +47,8 @@ public class PaymentStartRecord extends Auditable { @Column(name = "amount", nullable = false) private String amount; - @Column(name = "txn_id",unique = true) - private String txnId; + @Column(name = "payment_ref_id",unique = true) + private String paymentRefId; @Column(name = "qr_string", columnDefinition = "TEXT") private String qrString; diff --git a/src/main/java/com/billpaymentservice/bbps/entites/TransactionMaster.java b/src/main/java/com/billpaymentservice/bbps/entites/TransactionMaster.java index 8fe66de..381f4c2 100644 --- a/src/main/java/com/billpaymentservice/bbps/entites/TransactionMaster.java +++ b/src/main/java/com/billpaymentservice/bbps/entites/TransactionMaster.java @@ -30,6 +30,7 @@ import lombok.ToString; @NoArgsConstructor @AllArgsConstructor public class TransactionMaster extends Auditable { + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") @@ -48,8 +49,8 @@ public class TransactionMaster extends Auditable { @Column(name = "checker_txn_id") private String checkerTxnId; - @Column(name = "client_user_id") - private String clientUserId; + @Column(name = "user_id") + private String userId; @Column(name = "mid") private String mid; @@ -78,8 +79,8 @@ public class TransactionMaster extends Auditable { @Column(name = "user_type") private String userType; - @Column(name = "user_id") - private String userId; + @Column(name = "bank_user_id") + private String bankUserId; @Column(name = "txn_amount") private String txnAmount; @@ -163,7 +164,7 @@ public class TransactionMaster extends Auditable { // ✅ Relation with PaymentStartRecord (one-to-one) @OneToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "payment_ref_id", referencedColumnName = "txn_id", insertable = false, updatable = false) + @JoinColumn(name = "payment_ref_id", referencedColumnName = "payment_ref_id", insertable = false, updatable = false) @JsonIgnore private PaymentStartRecord paymentStartRecord; diff --git a/src/main/java/com/billpaymentservice/bbps/repository/TransactionMasterRepository.java b/src/main/java/com/billpaymentservice/bbps/repository/TransactionMasterRepository.java index cc7f68d..4297436 100644 --- a/src/main/java/com/billpaymentservice/bbps/repository/TransactionMasterRepository.java +++ b/src/main/java/com/billpaymentservice/bbps/repository/TransactionMasterRepository.java @@ -21,10 +21,13 @@ public interface TransactionMasterRepository extends JpaRepository findByMid(@Param("mid") String mid, Pageable pageable); - @Query("SELECT t FROM TransactionMaster t WHERE t.mid = :mid AND t.clientUserId = :clientUserId") - Page findByMidOrClientUserId(@Param("mid") String mid, - @Param("clientUserId") String clientUserId, Pageable pageable); - + @Query("SELECT t FROM TransactionMaster t WHERE t.mid = :mid AND t.userId = :userId") + Page findByMidAndUserId( + @Param("mid") String mid, + @Param("userId") String userId, + Pageable pageable + ); + @Query("SELECT t FROM TransactionMaster t WHERE t.txnId = :txnId") List findByTxnId(@Param("txnId") String txnId); diff --git a/src/main/java/com/billpaymentservice/bbps/scheduler/PaymentRetryScheduler.java b/src/main/java/com/billpaymentservice/bbps/scheduler/PaymentRetryScheduler.java deleted file mode 100644 index 045f472..0000000 --- a/src/main/java/com/billpaymentservice/bbps/scheduler/PaymentRetryScheduler.java +++ /dev/null @@ -1,162 +0,0 @@ -package com.billpaymentservice.bbps.scheduler; - -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.Optional; - -import org.modelmapper.ModelMapper; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpHeaders; -import org.springframework.scheduling.annotation.Scheduled; -import org.springframework.stereotype.Component; - -import com.billpaymentservice.bbps.dto.GeneralResp; -import com.billpaymentservice.bbps.dto.response.BillPaymentResponse; -import com.billpaymentservice.bbps.dto.request.TransactionStatusRequest; -import com.billpaymentservice.bbps.entites.BillPayment; -import com.billpaymentservice.bbps.entites.BharatConnectMaster; -import com.billpaymentservice.bbps.entites.OutboundTraceLog; -import com.billpaymentservice.bbps.entites.TransactionMaster; -import com.billpaymentservice.bbps.repository.BharatConnectMasterRepository; -import com.billpaymentservice.bbps.repository.OutboundTraceLogRepository; -import com.billpaymentservice.bbps.repository.TransactionMasterRepository; -import com.billpaymentservice.bbps.repository.PaymentRepository; -import com.billpaymentservice.bbps.service.PaymentService; -import com.billpaymentservice.bbps.serviceImpl.HttpService; -import com.billpaymentservice.bbps.util.BBPSConstant; -import com.billpaymentservice.bbps.util.Helper; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; - -@Component -public class PaymentRetryScheduler { - - private static final Logger LOGGER = LoggerFactory.getLogger(PaymentRetryScheduler.class); - - private final TransactionMasterRepository transactionMasterRepository; - private final OutboundTraceLogRepository outboundTraceLogRepository; - private final BharatConnectMasterRepository beneficiaryDetailsRepository; - private final Helper helper; - private final HttpService httpService; - private final PaymentRepository paymentRepository; - private final PaymentService paymentService; - private final ObjectMapper objectMapper; - private final ModelMapper modelMapper; - - @Autowired - public PaymentRetryScheduler(TransactionMasterRepository transactionMasterRepository, - OutboundTraceLogRepository outboundTraceLogRepository, - BharatConnectMasterRepository beneficiaryDetailsRepository, Helper helper, HttpService httpService, - PaymentRepository paymentRepository, PaymentService paymentService) { - this.transactionMasterRepository = transactionMasterRepository; - this.outboundTraceLogRepository = outboundTraceLogRepository; - this.beneficiaryDetailsRepository = beneficiaryDetailsRepository; - this.helper = helper; - this.httpService = httpService; - this.paymentRepository = paymentRepository; - this.paymentService = paymentService; - this.objectMapper = new ObjectMapper(); - this.modelMapper = new ModelMapper(); - } - - // runs every 60 seconds by default; configurable via property scheduler.retry.delay.ms - @Scheduled(fixedDelayString = "${scheduler.retry.delay.ms:60000}") - public void retryPendingPayments() { - LOGGER.info("PaymentRetryScheduler triggered"); - List statuses = Arrays.asList("PENDING_UPSTREAM", "INVALID_RESPONSE_FROM_BBPS", - "INVALID_JSON_FROM_UPSTREAM", "Failed", "FAILED"); - - List pendingList = transactionMasterRepository.findByStatusIn(statuses); - if (pendingList == null || pendingList.isEmpty()) { - LOGGER.debug("No pending TransactionMaster entries found"); - return; - } - - for (TransactionMaster tm : pendingList) { - try { - LOGGER.info("Retrying transaction txnId={} status={}", tm.getTxnId(), tm.getStatus()); - - Optional outOpt = outboundTraceLogRepository - .findTopByTxnIdAndApiNameOrderByIdDesc(tm.getTxnId(), BBPSConstant.API_INITIATE_PAYMENT); - if (outOpt.isEmpty()) { - LOGGER.warn("No outbound trace log found for txnId={}", tm.getTxnId()); - continue; - } - - OutboundTraceLog outbound = outOpt.get(); - String payload = outbound.getRequestPayload(); - if (payload == null || payload.isEmpty()) { - LOGGER.warn("Outbound request payload empty for txnId={}", tm.getTxnId()); - continue; - } - - BharatConnectMaster headersData = beneficiaryDetailsRepository.findByOrgId(BBPSConstant.ORG_ID); - HttpHeaders headers = helper.buildHeaders(headersData); - - LOGGER.info("Resending request to BBPS for txnId={} url={}", tm.getTxnId(), outbound.getEndpointUrl()); - Map responseMap = httpService.postRequest(outbound.getEndpointUrl(), payload, headers); - String responseData = responseMap.get(BBPSConstant.RESPONSE_DATA_KEY); - LOGGER.info("Retry response for txnId={} => {}", tm.getTxnId(), responseData); - - helper.updateOutboundLogResponse(outbound, responseData); - outboundTraceLogRepository.save(outbound); - - if (responseData == null || responseData.isEmpty()) { - LOGGER.warn("Empty response on retry for txnId={}", tm.getTxnId()); - // leave as pending for next run - continue; - } - - Map dataMap = objectMapper.readValue(responseData, new TypeReference>() {}); - if (!dataMap.containsKey(BBPSConstant.RESPONSE_GENERIC_KEY)) { - LOGGER.warn("Retry response invalid structure for txnId={}", tm.getTxnId()); - // update TransactionMaster status for visibility - tm.setStatus("INVALID_RESPONSE_FROM_BBPS"); - transactionMasterRepository.save(tm); - continue; - } - - BillPaymentResponse billResp = objectMapper.convertValue(dataMap, BillPaymentResponse.class); - GeneralResp gen = billResp.getGenericResponse(); - - if (gen == null) { - LOGGER.warn("Generic response null after retry for txnId={}", tm.getTxnId()); - continue; - } - - if (!"Failed".equalsIgnoreCase(gen.getStatus())) { - // success path - BillPayment entity = modelMapper.map(billResp, BillPayment.class); - paymentRepository.save(entity); - - tm.setBankTxnId(billResp.getTxnId()); - tm.setStatus(billResp.getStatus()); - transactionMasterRepository.save(tm); - - // trigger transaction status check (best-effort) - try { - TransactionStatusRequest tsReq = new TransactionStatusRequest(); - tsReq.setTxnId(billResp.getTxnId()); - tsReq.setReqId(tm.getReqId()); - tsReq.setMid(tm.getMid()); - tsReq.setClientUserId(tm.getClientUserId()); - paymentService.checkTrasactionStatus(tsReq); - } catch (Exception e) { - LOGGER.warn("Transaction status check failed after retry for txnId={} : {}", tm.getTxnId(), e.getMessage()); - } - } else { - // upstream returned failed status - tm.setBankTxnId(billResp.getTxnId()); - tm.setStatus(gen.getStatus()); - transactionMasterRepository.save(tm); - } - - } catch (Exception ex) { - LOGGER.error("Error while retrying transaction txnId={}: {}", tm.getTxnId(), ex.getMessage(), ex); - } - } - } -} \ No newline at end of file diff --git a/src/main/java/com/billpaymentservice/bbps/serviceImpl/PaymentServiceImpl.java b/src/main/java/com/billpaymentservice/bbps/serviceImpl/PaymentServiceImpl.java index aec396e..5b5e0aa 100644 --- a/src/main/java/com/billpaymentservice/bbps/serviceImpl/PaymentServiceImpl.java +++ b/src/main/java/com/billpaymentservice/bbps/serviceImpl/PaymentServiceImpl.java @@ -117,9 +117,8 @@ public class PaymentServiceImpl implements PaymentService { preTransaction.setInitiatorTxnId(txnId); preTransaction.setReqId(requestId); preTransaction.setMid(billPaymentRequest.getMid()); - preTransaction.setClientUserId(billPaymentRequest.getClientUserId()); + preTransaction.setUserId(billPaymentRequest.getClientUserId()); preTransaction.setClientReqId(billPaymentRequest.getClientReqId()); - preTransaction.setUserId(userId); preTransaction.setStatus("PENDING"); EntityRelationshipUtil.linkTransactionMasterRelationships(preTransaction); // ensure txnId exists (no uniqueness suffixing) diff --git a/src/main/java/com/billpaymentservice/bbps/serviceImpl/TxnHistoryServiceImpl.java b/src/main/java/com/billpaymentservice/bbps/serviceImpl/TxnHistoryServiceImpl.java index c1b3714..5a18bdd 100644 --- a/src/main/java/com/billpaymentservice/bbps/serviceImpl/TxnHistoryServiceImpl.java +++ b/src/main/java/com/billpaymentservice/bbps/serviceImpl/TxnHistoryServiceImpl.java @@ -67,7 +67,7 @@ public class TxnHistoryServiceImpl implements TxnHistoryService { } else { Pageable pageable = PageRequest.of(paymentHistoryRequest.getPageNo(), paymentHistoryRequest.getPageSize()); - dataResult = transactionMasterRepository.findByMidOrClientUserId(mid, clientUserId, pageable); + dataResult = transactionMasterRepository.findByMidAndUserId(mid, clientUserId, pageable); } // List dataResult = diff --git a/src/main/java/com/billpaymentservice/bbps/util/BillerMapperHelper.java b/src/main/java/com/billpaymentservice/bbps/util/BillerMapperHelper.java index f28d430..38fa77b 100644 --- a/src/main/java/com/billpaymentservice/bbps/util/BillerMapperHelper.java +++ b/src/main/java/com/billpaymentservice/bbps/util/BillerMapperHelper.java @@ -1034,15 +1034,15 @@ public class BillerMapperHelper { record.setOrderNo(orderNo); record.setRequestId(request.getReqId()); record.setMid(request.getMid()); - record.setClientUserId(request.getClientUserId()); - record.setInternalTxnId(txnId); + record.setUserId(request.getClientUserId()); + record.setTxnId(txnId); } if (apiResponse != null && apiResponse.getResponseData() != null) { record.setAmount(Optional.ofNullable( apiResponse.getResponseData().getAmount()).orElse("0.00")); - record.setTxnId(Optional.ofNullable( + record.setPaymentRefId(Optional.ofNullable( apiResponse.getResponseData().getTxnId()).orElse("DUMMY_TXN")); Date createdOn = parseToDate(apiResponse.getResponseData().getCreatedOn()); diff --git a/src/main/java/com/billpaymentservice/bbps/util/Helper.java b/src/main/java/com/billpaymentservice/bbps/util/Helper.java index a8d7876..dff3042 100644 --- a/src/main/java/com/billpaymentservice/bbps/util/Helper.java +++ b/src/main/java/com/billpaymentservice/bbps/util/Helper.java @@ -238,7 +238,7 @@ public class Helper { if (src.getCheckerTxnId() != null && !src.getCheckerTxnId().isBlank()) dest.setCheckerTxnId(src.getCheckerTxnId()); if (src.getReqId() != null && !src.getReqId().isBlank()) dest.setReqId(src.getReqId()); if (src.getMid() != null && !src.getMid().isBlank()) dest.setMid(src.getMid()); - if (src.getClientUserId() != null && !src.getClientUserId().isBlank()) dest.setClientUserId(src.getClientUserId()); + if (src.getBankUserId()!= null && !src.getBankUserId().isBlank()) dest.setBankUserId(src.getBankUserId()); if (src.getClientReqId() != null && !src.getClientReqId().isBlank()) dest.setClientReqId(src.getClientReqId()); if (src.getUserId() != null && !src.getUserId().isBlank()) dest.setUserId(src.getUserId()); if (src.getBillerId() != null && !src.getBillerId().isBlank()) dest.setBillerId(src.getBillerId());