feat: 외부연동 어댑터 실구현 — NHN Toast(SMS/알림톡) + KFTC 오픈뱅킹 입금이체
공개 문서화된 실제 API 스펙에 맞춰 스켈레톤을 RestClient 기반 실구현으로 교체.
모두 @ConditionalOnProperty 로 config-gated, 기본은 Mock 유지 → 운영 영향 0.
- ToastMessageAdapter: SMS v3.0(/sms/v3.0/appKeys/{k}/sender/sms) + 알림톡 v2.3
(/alimtalk/v2.3/appkeys/{k}/messages). X-Secret-Key 인증, MessageChannel 분기.
응답 header.isSuccessful/resultCode + requestId 매핑. ToastProperties 외부화.
- KftcBankTransferAdapter: 입금이체(/v2.0/transfer/deposit/acnt_num) + 이체결과조회.
Bearer 토큰, 문서화된 KFTC 필드(bank_tran_id/req_list/rsp_code) 매핑. KftcProperties 외부화.
- 키/토큰 누락 시 fail-fast(IllegalStateException) — 잘못된 운영 적용 즉시 감지.
- application.yml app.adapter.{kftc,toast}.* 설정(env placeholder, 빈 기본값) 추가.
- 의존성 추가 없음(ga-common 의 spring-web RestClient + Jackson 재사용).
- AdapterFailFastTest 3건(설정누락 차단/알림톡 templateCode 필수) 추가.
제외: FCM 푸시 — MessageAdapter 인터페이스/PWA 연동 계약 미정으로 추측구현 회피.
실거래 검증은 운영 키(계약·AppKey·약정계좌·토큰) 주입 후 테스트베드에서 수행 필요.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -61,6 +61,25 @@ app:
|
||||
adapter:
|
||||
bank: ${ADAPTER_BANK:mock}
|
||||
message: ${ADAPTER_MESSAGE:mock}
|
||||
# KFTC 오픈뱅킹 입금이체 (bank=kftc 일 때 사용). 운영 시 토큰/약정계좌 주입.
|
||||
kftc:
|
||||
base-url: ${KFTC_BASE_URL:https://openapi.openbanking.or.kr}
|
||||
access-token: ${KFTC_ACCESS_TOKEN:}
|
||||
cntr-account-type: ${KFTC_CNTR_ACCOUNT_TYPE:N}
|
||||
cntr-account-num: ${KFTC_CNTR_ACCOUNT_NUM:}
|
||||
wd-print-content: ${KFTC_WD_PRINT:수수료정산}
|
||||
name-check-option: ${KFTC_NAME_CHECK:off}
|
||||
transfer-purpose: ${KFTC_TRANSFER_PURPOSE:TR}
|
||||
# NHN Cloud(Toast) SMS/알림톡 (message=toast 일 때 사용). 운영 시 AppKey/SecretKey 주입.
|
||||
toast:
|
||||
sms-base-url: ${TOAST_SMS_BASE_URL:https://api-sms.cloud.toast.com}
|
||||
sms-app-key: ${TOAST_SMS_APP_KEY:}
|
||||
sms-secret-key: ${TOAST_SMS_SECRET_KEY:}
|
||||
sender-no: ${TOAST_SENDER_NO:}
|
||||
alimtalk-base-url: ${TOAST_ALIMTALK_BASE_URL:https://kakaotalk-bizmessage.api.nhncloudservice.com}
|
||||
alimtalk-app-key: ${TOAST_ALIMTALK_APP_KEY:}
|
||||
alimtalk-secret-key: ${TOAST_ALIMTALK_SECRET_KEY:}
|
||||
sender-key: ${TOAST_SENDER_KEY:}
|
||||
# 펌뱅킹 재시도 배치
|
||||
bank-retry:
|
||||
limit: ${BANK_RETRY_LIMIT:50}
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
package com.ga.external;
|
||||
|
||||
import com.ga.common.adapter.bank.BankTransferRequest;
|
||||
import com.ga.common.adapter.message.MessageChannel;
|
||||
import com.ga.common.adapter.message.MessageRequest;
|
||||
import com.ga.external.bank.KftcBankTransferAdapter;
|
||||
import com.ga.external.bank.KftcProperties;
|
||||
import com.ga.external.message.ToastMessageAdapter;
|
||||
import com.ga.external.message.ToastProperties;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
/**
|
||||
* 실 KFTC/Toast 어댑터의 fail-fast 동작 검증.
|
||||
* 자격증명(키/토큰)이 비어 있으면 외부 호출 전에 IllegalStateException 으로 즉시 차단되어야 한다.
|
||||
* (실 HTTP 는 운영 키 주입 후 테스트베드에서 검증)
|
||||
*/
|
||||
class AdapterFailFastTest {
|
||||
|
||||
@Test
|
||||
@DisplayName("Toast SMS: 설정 누락 시 fail-fast")
|
||||
void toastSms_failFast() {
|
||||
ToastMessageAdapter adapter = new ToastMessageAdapter(new ToastProperties());
|
||||
MessageRequest req = MessageRequest.builder()
|
||||
.channel(MessageChannel.SMS)
|
||||
.phoneNumber("010-1234-5678")
|
||||
.content("테스트")
|
||||
.build();
|
||||
assertThatThrownBy(() -> adapter.send(req))
|
||||
.isInstanceOf(IllegalStateException.class)
|
||||
.hasMessageContaining("app.adapter.toast");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Toast 알림톡: templateCode 없으면 차단")
|
||||
void toastAlimtalk_requiresTemplate() {
|
||||
ToastProperties p = new ToastProperties();
|
||||
p.setAlimtalkAppKey("k");
|
||||
p.setAlimtalkSecretKey("s");
|
||||
p.setSenderKey("sk");
|
||||
ToastMessageAdapter adapter = new ToastMessageAdapter(p);
|
||||
MessageRequest req = MessageRequest.builder()
|
||||
.channel(MessageChannel.KAKAO)
|
||||
.phoneNumber("01012345678")
|
||||
.content("내용")
|
||||
.build();
|
||||
assertThatThrownBy(() -> adapter.send(req))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessageContaining("templateCode");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("KFTC 입금이체: 토큰/약정계좌 누락 시 fail-fast")
|
||||
void kftc_failFast() {
|
||||
KftcBankTransferAdapter adapter = new KftcBankTransferAdapter(new KftcProperties());
|
||||
BankTransferRequest req = BankTransferRequest.builder()
|
||||
.withdrawRequestId(1L)
|
||||
.bankCode("097")
|
||||
.accountNo("1101230000678")
|
||||
.accountHolderName("홍길동")
|
||||
.amount(new BigDecimal("10000"))
|
||||
.memo("정산")
|
||||
.build();
|
||||
assertThatThrownBy(() -> adapter.requestTransfer(req))
|
||||
.isInstanceOf(IllegalStateException.class)
|
||||
.hasMessageContaining("app.adapter.kftc");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user