feat: P6 외부연동 어댑터 모듈 PoC (펌뱅킹/SMS·카카오)

신규 모듈 ga-external-adapter — 외부 SDK 의존성을 격리.
실제 SDK 통합 시 이 모듈의 구현체만 교체하면 ga-api/ga-common 무변경.

- ga-common.adapter.bank: BankTransferAdapter 인터페이스 + Request/Response/Status
- ga-common.adapter.message: MessageAdapter 인터페이스 + Channel(SMS/KAKAO)/Request/Response
- ga-external-adapter: MockBankTransferAdapter / MockMessageAdapter (로그 + 마스킹)
- V82: withdraw_request.transfer_tx_id/transfer_status/transfer_at + 부분 인덱스
- ApprovalService.handleApprovalCallback(WITHDRAW) — 최종 승인 시 펌뱅킹 어댑터 호출
  - 성공: status APPROVED → SENT 전이, transfer_tx_id 영속화
  - 실패: APPROVED 유지 + fail_reason 기록 (재시도 배치는 추후)
- ApprovalService.sendStepNotification — DB 알림 외 SMS 어댑터 호출 (non-critical)
- WithdrawRequestMapper.updateTransferResult 추가
This commit is contained in:
GA Pro
2026-05-24 02:10:06 +09:00
parent c005ad705a
commit b04fa79840
17 changed files with 332 additions and 3 deletions
+6
View File
@@ -0,0 +1,6 @@
// ga-external-adapter: 외부 시스템 연동 어댑터 구현체 모듈 (라이브러리)
// - 펌뱅킹/SMS/카카오 등 외부 SDK 의존성을 이 모듈에 격리한다.
// - PoC 단계: Mock 구현만 제공. 실제 SDK 통합 시 이 모듈에서만 의존성/구현 교체.
dependencies {
api project(':ga-common')
}
@@ -0,0 +1,57 @@
package com.ga.external.bank;
import com.ga.common.adapter.bank.BankTransferAdapter;
import com.ga.common.adapter.bank.BankTransferRequest;
import com.ga.common.adapter.bank.BankTransferResponse;
import com.ga.common.adapter.bank.BankTransferStatus;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.util.UUID;
/**
* 펌뱅킹 Mock 구현.
* <p>
* 실제 KFTC/은행 SDK 가 들어오기 전까지 사용. 결과는 항상 ACCEPTED 반환 + 로그.
* 계좌번호는 뒷 4자리 외 마스킹해 로그에 남긴다.
*/
@Slf4j
@Component
public class MockBankTransferAdapter implements BankTransferAdapter {
@Override
public BankTransferResponse requestTransfer(BankTransferRequest request) {
String masked = maskAccount(request.getAccountNo());
String txId = "MOCK-" + UUID.randomUUID();
log.info("[MOCK BANK] requestTransfer withdrawId={} bank={} acc={} amount={} txId={}",
request.getWithdrawRequestId(), request.getBankCode(), masked, request.getAmount(), txId);
return BankTransferResponse.builder()
.txId(txId)
.status(BankTransferStatus.ACCEPTED)
.resultCode("0000")
.resultMessage("MOCK accepted")
.processedAt(LocalDateTime.now())
.build();
}
@Override
public BankTransferResponse queryStatus(String txId) {
log.info("[MOCK BANK] queryStatus txId={}", txId);
return BankTransferResponse.builder()
.txId(txId)
.status(BankTransferStatus.SETTLED)
.resultCode("0000")
.resultMessage("MOCK settled")
.processedAt(LocalDateTime.now())
.build();
}
private String maskAccount(String accountNo) {
if (accountNo == null) return null;
String digits = accountNo.replaceAll("[^0-9]", "");
if (digits.length() <= 4) return "****";
return "****" + digits.substring(digits.length() - 4);
}
}
@@ -0,0 +1,45 @@
package com.ga.external.message;
import com.ga.common.adapter.message.MessageAdapter;
import com.ga.common.adapter.message.MessageRequest;
import com.ga.common.adapter.message.MessageResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.util.UUID;
/**
* SMS / 카카오 알림톡 Mock 구현.
* <p>
* 실제 NHN Toast/알리고/카카오 비즈메시지 SDK 가 들어오기 전까지 사용. 항상 성공 응답.
* phoneNumber 는 뒷 4자리 외 마스킹.
*/
@Slf4j
@Component
public class MockMessageAdapter implements MessageAdapter {
@Override
public MessageResponse send(MessageRequest request) {
String masked = maskPhone(request.getPhoneNumber());
String msgId = "MOCK-MSG-" + UUID.randomUUID();
log.info("[MOCK MSG] send channel={} to={} title={} refType={} refId={} msgId={}",
request.getChannel(), masked, request.getTitle(),
request.getRefType(), request.getRefId(), msgId);
return MessageResponse.builder()
.messageId(msgId)
.sent(true)
.resultCode("0000")
.resultMessage("MOCK sent")
.sentAt(LocalDateTime.now())
.build();
}
private String maskPhone(String phone) {
if (phone == null) return null;
String digits = phone.replaceAll("[^0-9]", "");
if (digits.length() <= 4) return "****";
return "***-****-" + digits.substring(digits.length() - 4);
}
}