feat: complete Spring Boot 카드 매입·정산 system (acquire-core migration target, mvn verify green, 24 tests)

This commit is contained in:
forge-bot 2026-07-18 15:27:41 +00:00
commit 0f6e3acfe6
92 changed files with 4320 additions and 0 deletions

38
modules/gateway/pom.xml Normal file
View file

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.klaro.acquiring</groupId>
<artifactId>card-acquiring-boot</artifactId>
<version>1.0.0</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<artifactId>gateway</artifactId>
<name>gateway</name>
<dependencies>
<dependency>
<groupId>com.klaro.acquiring</groupId>
<artifactId>common-framework</artifactId>
</dependency>
<dependency>
<groupId>com.klaro.acquiring</groupId>
<artifactId>domain</artifactId>
</dependency>
<dependency>
<groupId>com.klaro.acquiring</groupId>
<artifactId>persistence</artifactId>
</dependency>
<dependency>
<groupId>com.klaro.acquiring</groupId>
<artifactId>acquiring</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View file

@ -0,0 +1,93 @@
package com.klaro.acquiring.gateway;
import com.klaro.acquiring.acquiring.AcquiringService;
import com.klaro.acquiring.acquiring.dto.PurchaseCommand;
import com.klaro.acquiring.common.codec.FieldSpec;
import com.klaro.acquiring.common.codec.MessageCodec;
import com.klaro.acquiring.common.codec.MessageSpec;
import com.klaro.acquiring.common.error.ErrorCode;
import com.klaro.acquiring.common.error.AcquiringException;
import com.klaro.acquiring.domain.entity.Purchase;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* 전문 게이트웨이(mg). 고정길이 전문을 수신·디코드하여 매입 서비스로 위임하고,
* 처리 결과를 응답 전문으로 인코드한다. 레거시 mg_ol_* 전문 송수신 대체.
*/
@Service
public class GatewayService {
private static final Logger log = LoggerFactory.getLogger(GatewayService.class);
/** 매입 요청 전문. */
public static final MessageSpec REQUEST_SPEC = MessageSpec.of("MG_REQ",
FieldSpec.an("TXNCODE", 4),
FieldSpec.an("APPRNO", 12),
FieldSpec.num("AMOUNT", 15));
/** 매입 응답 전문. */
public static final MessageSpec RESPONSE_SPEC = MessageSpec.of("MG_RES",
FieldSpec.an("RESPCODE", 4),
FieldSpec.an("APPRNO", 12),
FieldSpec.num("NETAMOUNT", 15),
FieldSpec.an("MESSAGE", 40));
/** 매입 접수 거래코드. */
public static final String TXN_ACQUIRE = "0210";
private final MessageCodec requestCodec = new MessageCodec(REQUEST_SPEC);
private final MessageCodec responseCodec = new MessageCodec(RESPONSE_SPEC);
private final AcquiringService acquiringService;
public GatewayService(AcquiringService acquiringService) {
this.acquiringService = acquiringService;
}
/** 요청 전문 처리 → 응답 전문. */
public String handle(String requestMessage) {
Map<String, String> req;
try {
req = requestCodec.decode(requestMessage);
} catch (RuntimeException e) {
log.warn("[mg] 전문 디코드 실패: {}", e.getMessage());
return respond(ErrorCode.INVALID_REQUEST, "", BigDecimal.ZERO, "전문 형식 오류");
}
String txn = req.get("TXNCODE");
String apprNo = req.get("APPRNO");
if (!TXN_ACQUIRE.equals(txn)) {
return respond(ErrorCode.INVALID_REQUEST, apprNo, BigDecimal.ZERO,
"미지원 거래코드: " + txn);
}
try {
BigDecimal amount = new BigDecimal(req.get("AMOUNT"));
Purchase p = acquiringService.acceptPurchase(new PurchaseCommand(apprNo, amount));
log.info("[mg] 매입 승인 apprNo={} net={}", apprNo, p.getNetAmount());
return respond(ErrorCode.OK, apprNo, p.getNetAmount(), "정상 처리");
} catch (AcquiringException e) {
log.warn("[mg] 매입 거절 apprNo={} code={} msg={}",
apprNo, e.getErrorCode().code(), e.getMessage());
return respond(e.getErrorCode(), apprNo, BigDecimal.ZERO, e.getMessage());
}
}
private String respond(ErrorCode code, String apprNo, BigDecimal netAmount, String message) {
Map<String, String> res = new LinkedHashMap<>();
res.put("RESPCODE", code.code());
res.put("APPRNO", apprNo == null ? "" : apprNo);
res.put("NETAMOUNT", netAmount.max(BigDecimal.ZERO).toBigInteger().toString());
res.put("MESSAGE", message == null ? "" : message);
return responseCodec.encode(res);
}
public MessageCodec responseCodec() {
return responseCodec;
}
}