[매입] mg_recv_svc → 전문수신 @Service (ACM-MG-004)
This commit is contained in:
parent
7bc37d0d86
commit
3b247810b7
1 changed files with 191 additions and 0 deletions
|
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in a new issue