update code
This commit is contained in:
@@ -59,22 +59,7 @@ public class PaymentController {
|
||||
}
|
||||
|
||||
|
||||
@PostMapping(value="/callback/paymentIntiate", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public ResponseEntity<ResponseModel> handleBillValidateCallback(@RequestBody TransactionStatusResponse transactionStatusResponse) {
|
||||
ResponseModel genralResp=paymentService.handlePaymentIntiateCallback(transactionStatusResponse);
|
||||
// Return a response indicating successful receipt
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,17 +1,23 @@
|
||||
package com.billpaymentservice.bbps.controller;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
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.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.billpaymentservice.bbps.dto.ResponseModel;
|
||||
import com.billpaymentservice.bbps.dto.request.PaymentStartUserRequest;
|
||||
import com.billpaymentservice.bbps.dto.request.CallbackRequest;
|
||||
import com.billpaymentservice.bbps.service.PaymentStartService;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/payment")
|
||||
public class PaymentStartController {
|
||||
private final PaymentStartService paymentStartService;
|
||||
|
||||
@@ -20,12 +26,28 @@ public class PaymentStartController {
|
||||
}
|
||||
|
||||
@PostMapping(value = "/startWebPayment", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public ResponseEntity<ResponseModel> startPayment(@Validated @RequestBody PaymentStartUserRequest paymentStartUserRequest) {
|
||||
public ResponseEntity<ResponseModel> startWebPayment(@Validated @RequestBody PaymentStartUserRequest paymentStartUserRequest) {
|
||||
|
||||
ResponseModel response = paymentStartService.startWebPayment(paymentStartUserRequest);
|
||||
return ResponseEntity.ok(response);
|
||||
}
|
||||
|
||||
@PostMapping(value = "/callback/startPayment", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public ResponseEntity<Map<String, String>> handleCallback(@RequestBody CallbackRequest callbackRequest) {
|
||||
ResponseModel resp = paymentStartService.handleStartCallback(callbackRequest);
|
||||
Map<String, String> result = new HashMap<>();
|
||||
// Use the status field from ResponseModel as merchant status code string
|
||||
result.put("status", resp.getStatus());
|
||||
return ResponseEntity.ok(result);
|
||||
}
|
||||
|
||||
@PostMapping(value = "/startIntentPayment", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public ResponseEntity<ResponseModel> startIntentPayment(@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);
|
||||
ResponseModel response = paymentStartService.startIntentPayment(paymentStartUserRequest);
|
||||
return ResponseEntity.ok(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.billpaymentservice.bbps.dto.request;
|
||||
|
||||
public class CallbackRequest {
|
||||
private String mid;
|
||||
private String data;
|
||||
private String signature;
|
||||
|
||||
public String getMid() {
|
||||
return mid;
|
||||
}
|
||||
|
||||
public void setMid(String mid) {
|
||||
this.mid = mid;
|
||||
}
|
||||
|
||||
public String getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public void setData(String data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public String getSignature() {
|
||||
return signature;
|
||||
}
|
||||
|
||||
public void setSignature(String signature) {
|
||||
this.signature = signature;
|
||||
}
|
||||
}
|
||||
@@ -52,4 +52,30 @@ public class PaymentStartRecord {
|
||||
|
||||
@Column(name = "created_on", nullable = false)
|
||||
private String createdOn;
|
||||
}
|
||||
|
||||
// New callback-related fields
|
||||
@Column(name = "callback_status")
|
||||
private String callbackStatus;
|
||||
|
||||
@Column(name = "bank_response", columnDefinition = "TEXT")
|
||||
private String bankResponse;
|
||||
|
||||
@Column(name = "signature")
|
||||
private String signature;
|
||||
|
||||
// Store original request body and full response body
|
||||
@Column(name = "request_body", columnDefinition = "TEXT")
|
||||
private String requestBody;
|
||||
|
||||
@Column(name = "response_body", columnDefinition = "TEXT")
|
||||
private String responseBody;
|
||||
|
||||
@Column(name = "response_type")
|
||||
private String responseType; // e.g., "QR", "HTML", "JSON"
|
||||
|
||||
@Column(name = "txn_status")
|
||||
private String txnStatus;
|
||||
|
||||
@Column(name = "callback_received_on")
|
||||
private String callbackReceivedOn;
|
||||
}
|
||||
+6
-1
@@ -1,5 +1,7 @@
|
||||
package com.billpaymentservice.bbps.repository;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@@ -7,4 +9,7 @@ import com.billpaymentservice.bbps.entites.PaymentStartRecord;
|
||||
|
||||
@Repository
|
||||
public interface PaymentStartRecordRepository extends JpaRepository<PaymentStartRecord, Long> {
|
||||
}
|
||||
Optional<PaymentStartRecord> findByOrderNo(String orderNo);
|
||||
Optional<PaymentStartRecord> findByTxnId(String txnId);
|
||||
Optional<PaymentStartRecord> findByRequestId(String requestId);
|
||||
}
|
||||
@@ -14,7 +14,6 @@ public interface PaymentService {
|
||||
|
||||
ResponseModel checkTrasactionStatusByClientReqId(TrasactionStatusByClientReqIdRequest transactionStatusRequest);
|
||||
|
||||
ResponseModel handlePaymentIntiateCallback(TransactionStatusResponse transactionStatusResponse);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -2,7 +2,12 @@ package com.billpaymentservice.bbps.service;
|
||||
|
||||
import com.billpaymentservice.bbps.dto.ResponseModel;
|
||||
import com.billpaymentservice.bbps.dto.request.PaymentStartUserRequest;
|
||||
import com.billpaymentservice.bbps.dto.request.CallbackRequest;
|
||||
|
||||
public interface PaymentStartService {
|
||||
ResponseModel startPayment(PaymentStartUserRequest request);
|
||||
}
|
||||
ResponseModel startWebPayment(PaymentStartUserRequest request);
|
||||
|
||||
ResponseModel handleStartCallback(CallbackRequest callbackRequest);
|
||||
|
||||
ResponseModel startIntentPayment(PaymentStartUserRequest paymentStartUserRequest);
|
||||
}
|
||||
@@ -141,6 +141,46 @@ public class HttpService {
|
||||
|
||||
return response;
|
||||
}
|
||||
public HashMap<String, String> postFormRequest(String url, String formBody) {
|
||||
HashMap<String, String> response = new HashMap<>();
|
||||
|
||||
try {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
|
||||
|
||||
log.info("🌐 Sending FORM POST to: {}", url);
|
||||
log.info("📦 Payload: {}", formBody);
|
||||
|
||||
ResponseEntity<String> restResponse = restClient.post()
|
||||
.uri(url)
|
||||
.headers(h -> h.addAll(headers))
|
||||
.body(formBody)
|
||||
.retrieve()
|
||||
.toEntity(String.class);
|
||||
|
||||
response.put("respCode", String.valueOf(restResponse.getStatusCode().value()));
|
||||
|
||||
// HTML RESPONSE directly stored
|
||||
response.put("data", restResponse.getBody());
|
||||
|
||||
log.info("✅ FORM POST Response Status: {}", restResponse.getStatusCode());
|
||||
|
||||
} catch (HttpClientErrorException e) {
|
||||
log.error("❌ FORM POST Error: {} - {}", e.getStatusCode(), e.getMessage());
|
||||
|
||||
response.put("respCode", String.valueOf(e.getStatusCode().value()));
|
||||
response.put("data", e.getResponseBodyAsString()); // still HTML or error string
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("❌ FORM POST Exception: {}", e.getMessage(), e);
|
||||
|
||||
response.put("respCode", "500");
|
||||
response.put("data", "HttpService FORM Error: " + e.getMessage());
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -393,22 +393,7 @@ public class PaymentServiceImpl implements PaymentService {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseModel handlePaymentIntiateCallback(TransactionStatusResponse transactionStatusResponse) {
|
||||
String apiName = "HANDLE_PAYMENT_INITIATE_CALLBACK";
|
||||
LOGGER.info("[{}] START | payload={} ", apiName, transactionStatusResponse);
|
||||
LOGGER.info("Received Call Back: {}", transactionStatusResponse);
|
||||
if (transactionStatusResponse.getGeneralResp().getStatusCode().equals(BBPSConstant.SUCCESS_CODE_STR)) {
|
||||
LOGGER.info("Callback indicates SUCCESS with status={}", transactionStatusResponse.getGeneralResp().getStatus());
|
||||
} else {
|
||||
LOGGER.info("Callback indicates FAILURE with status={}", transactionStatusResponse.getGeneralResp().getStatus());
|
||||
}
|
||||
|
||||
|
||||
LOGGER.info("[{}] END | callback processed", apiName);
|
||||
|
||||
return new ResponseModel(BBPSConstant.CALLBACK_RECEIVED_SUCCESS,
|
||||
BBPSConstant.SUCCESS_CODE_STR,
|
||||
BBPSConstant.HTTP_OK_INT,
|
||||
"");
|
||||
}
|
||||
|
||||
}
|
||||
+199
-19
@@ -6,6 +6,7 @@ import java.util.Map;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collections;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -17,6 +18,7 @@ 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.request.CallbackRequest;
|
||||
import com.billpaymentservice.bbps.dto.response.PaymentStartApiResponse;
|
||||
import com.billpaymentservice.bbps.dto.response.PaymentStatusResponse;
|
||||
import com.billpaymentservice.bbps.dto.sentRequest.PaymentDetailSentRequest;
|
||||
@@ -36,13 +38,15 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.billpaymentservice.bbps.entites.OutboundTraceLog;
|
||||
import com.billpaymentservice.bbps.entites.BharatConnectMaster;
|
||||
import com.billpaymentservice.bbps.entites.PaymentStartRecord;
|
||||
|
||||
@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 String uatPaymentUrlweb="https://uat.godemo.in/api/UATIntegration/uat/payin/paymentrequest";
|
||||
private String uatPaymentUrlintent="https://uat.godemo.in/api/UATIntegration/paymentrequest/seamless";
|
||||
|
||||
private final PaymentStartRecordRepository paymentStartRecordRepository;
|
||||
private final HttpService httpService;
|
||||
@@ -69,7 +73,7 @@ public class PaymentStartServiceImpl implements PaymentStartService {
|
||||
this.bbpsMapperHelper = bbpsMapperHelper;
|
||||
}
|
||||
@Override
|
||||
public ResponseModel startPayment(PaymentStartUserRequest request) {
|
||||
public ResponseModel startIntentPayment(PaymentStartUserRequest request) {
|
||||
|
||||
String requestId = request.getReqId();
|
||||
String apiName = "StartPayment";
|
||||
@@ -97,15 +101,17 @@ public class PaymentStartServiceImpl implements PaymentStartService {
|
||||
PaymentRequestPayload finalPayload = new PaymentRequestPayload();
|
||||
finalPayload.setMid(BBPSConstant.mid);
|
||||
finalPayload.setPayload(encrypted);
|
||||
|
||||
|
||||
String forwardJson = helper.convertObjectToJson(finalPayload);
|
||||
LOGGER.info("Payment Start Request Payload: " + forwardJson);
|
||||
OutboundTraceLog outboundLog =
|
||||
helper.createOutboundLog(txnId, requestId, apiName, uatPaymentUrl,
|
||||
helper.createOutboundLog(txnId, requestId, apiName, uatPaymentUrlintent,
|
||||
BBPSConstant.HTTP_POST, forwardJson);
|
||||
|
||||
// --- CALL EXTERNAL API ---
|
||||
Map<String, String> responseMap =
|
||||
httpService.postRequest(uatPaymentUrl, forwardJson, null);
|
||||
httpService.postRequest(uatPaymentUrlintent, forwardJson, null);
|
||||
|
||||
LOGGER.info("Payment Start Response: " + responseMap);
|
||||
|
||||
@@ -166,19 +172,7 @@ public class PaymentStartServiceImpl implements PaymentStartService {
|
||||
|
||||
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
|
||||
@@ -238,7 +232,15 @@ public class PaymentStartServiceImpl implements PaymentStartService {
|
||||
}
|
||||
|
||||
// Save record
|
||||
bbpsMapperHelper.savePaymentStartRecord(request, apiResponse, txnId, orderNo);
|
||||
// Determine response type: QR if qrString present, else JSON
|
||||
String responseType = "JSON";
|
||||
String rawResponse = respBody == null ? "" : respBody;
|
||||
if (apiResponse != null && apiResponse.getResponseData() != null
|
||||
&& apiResponse.getResponseData().getQrString() != null
|
||||
&& !apiResponse.getResponseData().getQrString().isEmpty()) {
|
||||
responseType = "QR";
|
||||
}
|
||||
bbpsMapperHelper.savePaymentStartRecord(request, apiResponse, txnId, orderNo, requestJson, rawResponse, responseType);
|
||||
|
||||
// Prepare DTO
|
||||
PaymentStartReturnDto responseData = new PaymentStartReturnDto();
|
||||
@@ -289,5 +291,183 @@ public class PaymentStartServiceImpl implements PaymentStartService {
|
||||
sent.setUdf14(request.getUdf14());
|
||||
return sent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseModel startWebPayment(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);
|
||||
|
||||
// ----------- BUILD x-www-form-urlencoded PAYLOAD -----------
|
||||
String formPayload =
|
||||
"mid=" + URLEncoder.encode(BBPSConstant.mid, StandardCharsets.UTF_8) +
|
||||
"&payload=" + URLEncoder.encode(encrypted, StandardCharsets.UTF_8);
|
||||
LOGGER.info("Form Payload: {}", formPayload);
|
||||
OutboundTraceLog outboundLog =
|
||||
helper.createOutboundLog(txnId, requestId, apiName, uatPaymentUrlweb,
|
||||
BBPSConstant.HTTP_POST, formPayload);
|
||||
|
||||
// ----------- CALL API (FORM URL-ENCODED) -----------
|
||||
Map<String, String> responseMap =
|
||||
httpService.postFormRequest(uatPaymentUrlweb, formPayload);
|
||||
|
||||
LOGGER.info("Payment Start Response: {}", responseMap);
|
||||
|
||||
int httpCode = Integer.parseInt(responseMap.getOrDefault("respCode", "500"));
|
||||
String respBody = responseMap.getOrDefault("data", "");
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// ---- EMPTY RESPONSE ----
|
||||
if (respBody.isEmpty()) {
|
||||
helper.updateInboundLogFailure(inboundLog, "Empty response from PG", "500");
|
||||
throw new CustomException("Empty response from PG", "FAILED", 500, null);
|
||||
}
|
||||
|
||||
// ❗❗ PG RETURNS HTML → NO JSON PARSING REQUIRED
|
||||
helper.updateInboundLogSuccess(inboundLog, respBody, "200");
|
||||
|
||||
// Save record with HTML (persist original request and HTML response)
|
||||
String responseTypeWeb = "HTML";
|
||||
String rawResponseWeb = respBody == null ? "" : respBody;
|
||||
// bbpsMapperHelper.savePaymentStartRecord(request, null, txnId, orderNo, requestJson, rawResponseWeb, responseTypeWeb);
|
||||
|
||||
// Return HTML directly in API response
|
||||
Map<String, String> result = new HashMap<>();
|
||||
result.put("html", respBody);
|
||||
|
||||
return ResponseModel.builder()
|
||||
.status("SUCCESS")
|
||||
.statusCode(200)
|
||||
.message("HTML Response Received")
|
||||
.data(result)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseModel handleStartCallback(CallbackRequest callbackRequest) {
|
||||
String apiName = "HANDLE_START_CALLBACK";
|
||||
LOGGER.info("[{}] START | payload={}", apiName, callbackRequest);
|
||||
|
||||
if (callbackRequest == null) {
|
||||
LOGGER.warn("Callback payload is null");
|
||||
return new ResponseModel("Invalid callback", "205", 200, null);
|
||||
}
|
||||
|
||||
String mid = callbackRequest.getMid();
|
||||
String encryptedData = callbackRequest.getData();
|
||||
String signature = callbackRequest.getSignature();
|
||||
|
||||
// Decrypt data
|
||||
String decrypted = encryptionUtil.decryptMData(encryptedData, BBPSConstant.enckey);
|
||||
if (decrypted == null) {
|
||||
LOGGER.error("Failed to decrypt callback payload for mid={}", mid);
|
||||
return new ResponseModel("Decryption failed", "207", 200, null);
|
||||
}
|
||||
|
||||
LOGGER.info("Decrypted callback data: {}", decrypted);
|
||||
|
||||
// Parse decrypted JSON
|
||||
Map<String, Object> payloadMap;
|
||||
try {
|
||||
payloadMap = objectMapper.readValue(decrypted, new TypeReference<Map<String, Object>>(){});
|
||||
} catch (JsonProcessingException e) {
|
||||
LOGGER.error("Failed to parse decrypted callback JSON: {}", e.getMessage(), e);
|
||||
return new ResponseModel("Invalid decrypted JSON", "207", 200, null);
|
||||
}
|
||||
|
||||
String txnId = String.valueOf(payloadMap.getOrDefault("txnId", payloadMap.getOrDefault("txn_id", "")));
|
||||
String orderNo = String.valueOf(payloadMap.getOrDefault("orderNo", payloadMap.getOrDefault("order_no", "")));
|
||||
String txnStatus = String.valueOf(payloadMap.getOrDefault("txnStatus", payloadMap.getOrDefault("txn_status", payloadMap.getOrDefault("status", ""))));
|
||||
|
||||
// Compute checksum: Mid + txnId + orderNo + txnStatus
|
||||
String checksumStr = mid + txnId + orderNo + txnStatus;
|
||||
String computedChecksum = encryptionUtil.getChecksumCRC32(checksumStr);
|
||||
|
||||
boolean signatureMatches = false;
|
||||
if (computedChecksum != null && signature != null) {
|
||||
signatureMatches = computedChecksum.equals(signature) || computedChecksum.equals(String.valueOf(Long.parseLong(signature)));
|
||||
}
|
||||
|
||||
// Find DB record
|
||||
Optional<PaymentStartRecord> recordOpt = Optional.empty();
|
||||
if (orderNo != null && !orderNo.isEmpty()) {
|
||||
recordOpt = paymentStartRecordRepository.findByOrderNo(orderNo);
|
||||
}
|
||||
if (recordOpt.isEmpty() && txnId != null && !txnId.isEmpty()) {
|
||||
recordOpt = paymentStartRecordRepository.findByTxnId(txnId);
|
||||
}
|
||||
|
||||
PaymentStartRecord record = recordOpt.orElse(null);
|
||||
if (record == null) {
|
||||
LOGGER.warn("No PaymentStartRecord found for orderNo={} txnId={}", orderNo, txnId);
|
||||
// still return 205 to acknowledge receipt
|
||||
return new ResponseModel("No matching record", "205", 200, null);
|
||||
}
|
||||
|
||||
// Update record with bank response and signature
|
||||
record.setBankResponse(decrypted);
|
||||
record.setSignature(signature);
|
||||
// update txn status and callback timestamp
|
||||
record.setTxnStatus(txnStatus);
|
||||
record.setCallbackReceivedOn(java.time.LocalDateTime.now().toString());
|
||||
|
||||
String merchantStatus;
|
||||
if (!signatureMatches) {
|
||||
LOGGER.error("Signature mismatch. computed={} provided={}", computedChecksum, signature);
|
||||
record.setCallbackStatus("SIGNATURE_MISMATCH");
|
||||
paymentStartRecordRepository.save(record);
|
||||
merchantStatus = "207"; // treat as rejected/failure
|
||||
LOGGER.info("[{}] END | returning status={}", apiName, merchantStatus);
|
||||
return new ResponseModel("Signature mismatch", merchantStatus, 200, null);
|
||||
}
|
||||
|
||||
// signature ok - decide response based on txnStatus
|
||||
if (txnStatus.equalsIgnoreCase("SUCCESS") || txnStatus.equalsIgnoreCase("SUCCESSFUL") || txnStatus.equalsIgnoreCase("SUCCESSFULL")) {
|
||||
record.setCallbackStatus("SUCCESS");
|
||||
paymentStartRecordRepository.save(record);
|
||||
merchantStatus = "206"; // accepted by merchant
|
||||
} else if (txnStatus.equalsIgnoreCase("FAILED") || txnStatus.equalsIgnoreCase("FAIL")) {
|
||||
record.setCallbackStatus("FAILED");
|
||||
paymentStartRecordRepository.save(record);
|
||||
merchantStatus = "207"; // rejected
|
||||
} else {
|
||||
record.setCallbackStatus("RECEIVED");
|
||||
paymentStartRecordRepository.save(record);
|
||||
merchantStatus = "205"; // generic ack
|
||||
}
|
||||
|
||||
LOGGER.info("Callback processed for orderNo={} txnId={} status={} returning merchantStatus={}", orderNo, txnId, txnStatus, merchantStatus);
|
||||
LOGGER.info("[{}] END", apiName);
|
||||
|
||||
return new ResponseModel("Callback processed", merchantStatus, 200, null);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1081,18 +1081,25 @@ public class BillerMapperHelper {
|
||||
}
|
||||
|
||||
public void savePaymentStartRecord(PaymentStartUserRequest request, PaymentStartApiResponse apiResponse,
|
||||
String txnId, String orderNo) {
|
||||
String txnId, String orderNo, String requestJson, String rawResponse, String responseType) {
|
||||
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());
|
||||
if (apiResponse != null && apiResponse.getResponseData() != null) {
|
||||
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());
|
||||
}
|
||||
|
||||
// store the original request body and raw response
|
||||
record.setRequestBody(requestJson);
|
||||
record.setResponseBody(rawResponse);
|
||||
record.setResponseType(responseType);
|
||||
|
||||
paymentStartRecordRepository.save(record);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
package com.billpaymentservice.bbps.util;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
import java.util.zip.CRC32;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
@@ -18,12 +22,9 @@ public class EncryptionUtil {
|
||||
/**
|
||||
* 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");
|
||||
@@ -61,4 +62,56 @@ public class EncryptionUtil {
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypts data encrypted by encryptMData: Base64(iv + ciphertext) with AES/CBC/PKCS5Padding
|
||||
*/
|
||||
public String decryptMData(String base64Payload, String encryptKey) {
|
||||
if (base64Payload == null) return null;
|
||||
try {
|
||||
byte[] combined = Base64.getDecoder().decode(base64Payload);
|
||||
// IV is first 16 bytes for AES
|
||||
if (combined.length < 16) {
|
||||
LOGGER.warn("Encrypted payload too short to contain IV");
|
||||
return null;
|
||||
}
|
||||
byte[] iv = new byte[16];
|
||||
System.arraycopy(combined, 0, iv, 0, 16);
|
||||
byte[] cipherText = new byte[combined.length - 16];
|
||||
System.arraycopy(combined, 16, cipherText, 0, cipherText.length);
|
||||
|
||||
byte[] keyBytes = normalizeKey(encryptKey);
|
||||
SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, "AES");
|
||||
|
||||
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
||||
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, new IvParameterSpec(iv));
|
||||
|
||||
byte[] plain = cipher.doFinal(cipherText);
|
||||
return new String(plain, StandardCharsets.UTF_8);
|
||||
} catch (Exception ex) {
|
||||
LOGGER.error("Error decrypting payload: {}", ex.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute CRC32 checksum value for the provided input string and return the numeric value as String
|
||||
*/
|
||||
public String getChecksumCRC32(String input) {
|
||||
if (input == null) input = "";
|
||||
try (InputStream is = new ByteArrayInputStream(input.getBytes(StandardCharsets.UTF_8))) {
|
||||
CRC32 crc = new CRC32();
|
||||
byte[] buffer = new byte[1024];
|
||||
int len;
|
||||
while ((len = is.read(buffer)) != -1) {
|
||||
crc.update(buffer, 0, len);
|
||||
}
|
||||
long value = crc.getValue();
|
||||
LOGGER.debug("Computed CRC32: {} for input: {}", value, input);
|
||||
return String.valueOf(value);
|
||||
} catch (IOException e) {
|
||||
LOGGER.error("Error computing CRC32: {}", e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,9 +20,9 @@ swagger.server.description=Local Development Server
|
||||
# DataSource
|
||||
spring.datasource.url=jdbc:postgresql://localhost:5432/BBPS
|
||||
spring.datasource.username=postgres
|
||||
spring.datasource.password=UAT@DB_2025
|
||||
#spring.datasource.password=UAT@DB_2025
|
||||
|
||||
#spring.datasource.password=himanshu@123
|
||||
spring.datasource.password=himanshu@123
|
||||
spring.datasource.driver-class-name=org.postgresql.Driver
|
||||
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
|
||||
|
||||
|
||||
Reference in New Issue
Block a user