[매입] mg_recv_svc → 전문수신 @Service #8

Open
forge-bot wants to merge 9 commits from forge/ACM-MG-004-attempt-3-run-56504178208a into main
Showing only changes of commit 3b247810b7 - Show all commits

View file

@ -0,0 +1,191 @@
package com.klaro.acquirecore.acquiring.service;
import com.klaro.acquirecore.acquiring.dto.TxMessage;
import com.klaro.acquirecore.acquiring.dto.TxResult;
import com.klaro.acquirecore.acquiring.repository.TxRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.UUID;
/**
* TX_SERVICE: Main service for receiving and processing acquiring messages.
* Migrated from legacy/app/online/mg_recv_svc.pgc
*/
@Service
public class TxMessageReceiveService {
private static final Logger log = LoggerFactory.getLogger(TxMessageReceiveService.class);
private static final DateTimeFormatter DT_FMT = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
private final TxMessageCodec codec;
private final TxRepository txRepository;
public TxMessageReceiveService(TxMessageCodec codec, TxRepository txRepository) {
this.codec = codec;
this.txRepository = txRepository;
}
/**
* Receive and process raw binary message.
* @param raw raw message bytes
* @return processing result
*/
public TxResult receive(byte[] raw) {
String traceId = UUID.randomUUID().toString().substring(0, 8);
log.info("[{}] Receiving message, size={}", traceId, raw != null ? raw.length : 0);
try {
// 1. Unpack message
TxMessage msg = codec.unpack(raw);
log.debug("[{}] Unpacked: {}", traceId, msg);
// 2. Validate required fields
TxResult validation = validate(msg, traceId);
if (validation != null) {
return validation;
}
// 3. Process transaction
return processTransaction(msg, traceId);
} catch (Exception e) {
log.error("[{}] Processing error: {}", traceId, e.getMessage(), e);
return TxResult.error(traceId, "9999", "Processing error: " + e.getMessage());
}
}
/**
* Validate message fields.
*/
private TxResult validate(TxMessage msg, String traceId) {
if (msg.getMerchantId() == null || msg.getMerchantId().isBlank()) {
log.warn("[{}] Missing merchantId", traceId);
return TxResult.error(traceId, "1001", "Missing merchantId");
}
if (msg.getTerminalId() == null || msg.getTerminalId().isBlank()) {
log.warn("[{}] Missing terminalId", traceId);
return TxResult.error(traceId, "1002", "Missing terminalId");
}
if (msg.getAmount() == null || msg.getAmount().isBlank()) {
log.warn("[{}] Missing amount", traceId);
return TxResult.error(traceId, "1003", "Missing amount");
}
// Validate amount is numeric
try {
Long.parseLong(msg.getAmount());
} catch (NumberFormatException e) {
log.warn("[{}] Invalid amount format: {}", traceId, msg.getAmount());
return TxResult.error(traceId, "1004", "Invalid amount format");
}
return null;
}
/**
* Process the transaction.
*/
private TxResult processTransaction(TxMessage msg, String traceId) {
try {
// 1. Check merchant status via repository stub
if (!txRepository.isMerchantActive(msg.getMerchantId())) {
log.warn("[{}] Merchant not active: {}", traceId, msg.getMerchantId());
return TxResult.error(traceId, "2001", "Merchant not active");
}
// 2. Check terminal status via repository stub
if (!txRepository.isTerminalActive(msg.getMerchantId(), msg.getTerminalId())) {
log.warn("[{}] Terminal not active: {}/{}", traceId, msg.getMerchantId(), msg.getTerminalId());
return TxResult.error(traceId, "2002", "Terminal not active");
}
// 3. Generate transaction ID if not present
if (msg.getTxId() == null || msg.getTxId().isBlank()) {
msg.setTxId(generateTxId());
}
// 4. Set transaction timestamp
String now = LocalDateTime.now().format(DT_FMT);
msg.setTranDate(now.substring(0, 8));
msg.setTranTime(now.substring(8));
// 5. Save transaction via repository stub
txRepository.saveTransaction(msg);
// 6. Process based on status
String status = msg.getStatus() != null ? msg.getStatus().toUpperCase() : "REQ";
switch (status) {
case "REQ":
case "APPROVE":
return handleApproval(msg, traceId);
case "CANCEL":
case "REFUND":
return handleCancellation(msg, traceId);
default:
log.info("[{}] Unknown status, defaulting to approval", traceId);
return handleApproval(msg, traceId);
}
} catch (Exception e) {
log.error("[{}] Transaction processing error: {}", traceId, e.getMessage(), e);
return TxResult.error(traceId, "9999", "Transaction processing failed");
}
}
/**
* Handle approval transaction.
*/
private TxResult handleApproval(TxMessage msg, String traceId) {
log.info("[{}] Processing approval for merchant={}, amount={}",
traceId, msg.getMerchantId(), msg.getAmount());
// Generate auth number
String authNo = generateAuthNo();
msg.setAuthNo(authNo);
msg.setStatus("APPROVED");
// Update via repository
txRepository.updateTransactionStatus(msg.getTxId(), "APPROVED");
log.info("[{}] Approval complete, authNo={}", traceId, authNo);
return TxResult.ok(msg.getTxId(), "Approval complete");
}
/**
* Handle cancellation/refund transaction.
*/
private TxResult handleCancellation(TxMessage msg, String traceId) {
log.info("[{}] Processing cancellation for txId={}", traceId, msg.getTxId());
// Check if original transaction exists
if (!txRepository.transactionExists(msg.getTxId())) {
log.warn("[{}] Original transaction not found: {}", traceId, msg.getTxId());
return TxResult.error(traceId, "3001", "Original transaction not found");
}
// Check if already cancelled
String currentStatus = txRepository.getTransactionStatus(msg.getTxId());
if ("CANCELLED".equals(currentStatus)) {
log.warn("[{}] Already cancelled: {}", traceId, msg.getTxId());
return TxResult.error(traceId, "3002", "Already cancelled");
}
// Process cancellation
msg.setStatus("CANCELLED");
txRepository.updateTransactionStatus(msg.getTxId(), "CANCELLED");
log.info("[{}] Cancellation complete", traceId);
return TxResult.ok(msg.getTxId(), "Cancellation complete");
}
private String generateTxId() {
return "TX" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"))
+ String.format("%04d", (int)(Math.random() * 10000));
}
private String generateAuthNo() {
return String.format("%06d", (int)(Math.random() * 1000000));
}
}