added intentbased pg

This commit is contained in:
Lincpay Tech
2025-12-01 16:57:48 +05:30
parent dd30885b34
commit 29d54147d3
18 changed files with 781 additions and 7 deletions
@@ -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;
}
}