# 하나캐피탈 × GNA(다타랩) 차량금융 연동 플랜

> 원본: `하나캐피탈_차량금융상품_연동가이드.pdf` (2026-07-01, 기술 연동 가이드)
> 본 문서 + 산출물 위치: `public_html/`

---

## 1. 연동 개요

GNA 렌탈 견적 화면에서 고객이 선택한 차량에 대해 **하나캐피탈 금융상품(오토렌탈 잔가·렌탈료)** 을
실시간 조회하는 연동. 두 축으로 구성된다.

| 구분 | 내용 | 주체 |
|---|---|---|
| ① 차량코드 매칭 | 하나캐피탈 차량 카탈로그를 GNA 차량 DB에 매칭·저장 | 하나캐피탈(카탈로그) + GNA(매칭) |
| ② 금융상품 조회 | 저장된 하나 차량코드 + 차량금액으로 잔가/렌탈료 API 호출 | GNA(호출) → 하나캐피탈(응답) |

```
[하나캐피탈] --① 차량 카탈로그(TSV) 주기 제공--> [GNA] (트림별 하나코드 저장)
[GNA] --② 하나코드+금액--> [로컬 릴레이 127.0.0.1:11080] --> [하나캐피탈] --잔가/렌탈료--> [GNA]
```

---

## 2. 아키텍처 (본 산출물)

```
GNA 견적 화면
   │  POST /api/v1/vehicle-finance/quote  {vehicle, contract, options}
   ▼
┌─────────────────────────────────────────────┐
│  오케스트레이터 API (public_html/api/app.py)  │   ← 본 산출물 (Python 표준 라이브러리)
│  1) dvCd=1 잔가조회 → mxRevalRtoCtnt 파싱      │
│  2) 잔가율×차량금액 = 적용잔가금액 계산         │
│  3) dvCd=2 렌탈료계산 → rlpnRtfe(월 렌탈료)     │
└─────────────────────────────────────────────┘
   │  POST http://localhost:11080/inbound (raw JSON, UCARI00013)
   ▼
[하나캐피탈 로컬 릴레이] ── (테스트 시 mock_relay.py 로 대체)
```

**설계 의도**: 가이드의 2단계 호출(잔가→렌탈료)과 Java-Map 문자열 파싱, 고정 파라미터 20여 종,
에러코드 해석을 서버가 캡슐화한다. GNA 화면은 매칭된 하나코드와 금액만 넘기면
`priceResidualRto / priceResidual / priceMonth` 를 받는다.

---

## 3. 조회 절차 — 2단계 호출

1. **차량 선택** (하나코드 + 차량금액)
2. **① 잔가조회 (dvCd=1)** → `Message.mxRvInfo.mxRevalRtoCtnt` → 잔가율(%)
3. **② 렌탈료계산 (dvCd=2)** → 잔가율 반영 → `Message.rentalFeeInfo.rlpnRtfe` → 월 렌탈료
4. **견적** 표시

`mxRevalRtoCtnt` 는 `{계약기간={약정주행거리구간=잔가율, ...}, ...}` 형태의 Java Map 문자열이다.
1차 키=계약기간(개월), 2차 키=주행거리구간(00000=기본), 값=최대 잔가율(%).
예: `mxRevalRtoCtnt["36"]["00000"] = 61.5` (36개월/기본구간).

> ⚠️ **정책 확인 필요**: 가이드의 정식 산식은 기본구간 `"00000"` 을 사용(worked example).
> 실제로 약정주행거리(`agrTrvgDstnCd`, 예 20000) 구간을 잔가 선택에 반영해야 하는지 하나캐피탈과 확정.
> 현재 구현은 기본 `00000`, `config.RESIDUAL_DISTANCE_KEY` / 요청 `options.residualDstnCd` 로 변경 가능.

---

## 4. API 기본 정보 (하위 릴레이)

| 항목 | 값 |
|---|---|
| API 서버 | 하나캐피탈이 설치·운영하는 로컬 릴레이(게이트웨이) |
| 엔드포인트 | `http://localhost:11080/inbound` |
| 요청 방식 | HTTP POST, `Content-Type: application/json`, body=요청 JSON(raw) |
| 서비스 ID | `UCARI00013` (version 1.0) |
| 인증 | `ccoCustNo`(6502189659) / `ccoCustSno`(0) — 하나캐피탈 발급 |
| 처리구분 `dvCd` | 1:잔가 최대잔가율 조회 / 2:렌탈료 계산 |

---

## 5. 응답 판정 규칙

- **연계 정상**: `Header.code` ∈ {0000, Z000, 1200, H000, F000}
- **업무 정상**: `Message.resInfo.rspnsCd` = `PS000001`
  - 성공 시 `mxRvInfo`(dvCd=1) 또는 `rentalFeeInfo`(dvCd=2) 추가
- **취급제한(업무거절)**: `rspnsCd` = `PS000100` → `mxRvInfo/rentalFeeInfo` 없음
  (예: 쏘나타 LPi·수동트림은 제한, 가솔린 자동트림은 정상)
- **오류**: 그 외 코드는 4장 에러표로 원인 확인 (Z*** / 1xxx~2xxx / H*** / F***)

---

## 6. 산출물 목록 (`public_html/`)

| 파일 | 내용 |
|---|---|
| `index.html` | 산출물 인덱스 (랜딩) |
| `plan.md` | 본 연동 플랜/분석 |
| `required-values.md` | **필요한 값 전체 정리** (발급값·매칭값·입력값·고정값·응답값) |
| `api-docs.html` | 사람이 읽는 API 문서 (요청/응답/에러코드/예시) |
| `openapi.yaml` | 오케스트레이터 API OpenAPI 3.0 스펙 |
| `api/app.py` | 오케스트레이터 API 서버 (표준 라이브러리) |
| `api/hana_relay.py` | 릴레이 클라이언트 + `mxRevalRtoCtnt` 파서 |
| `api/config.py` | 엔드포인트·인증·고정 파라미터·에러코드표 |
| `api/mock_relay.py` | 로컬 테스트용 목 릴레이 (가이드 예시 응답) |
| `api/test_quote.py` | 통합 테스트 (실측 예시 재현 검증) |
| `api/run_demo.sh` | 목릴레이+API 기동 → 테스트 원클릭 |
| `api/README.md` | 실행 방법 |

---

## 7. 실행 (검증됨)

```bash
cd public_html/api
bash run_demo.sh
```

목 릴레이(11080) + 오케스트레이터(18080) 기동 후 통합 테스트가 실행되며,
가이드 실측 예시(쏘나타 디 엣지 가솔린 1.6터보, 3,400만원/36개월/기본구간)를 재현한다:

- `priceResidualRto = 61.5`
- `priceResidual = 20,910,000`  (= 34,000,000 × 61.5%)
- `priceMonth = 1,174,910`
- 취급제한 케이스(trimNo=RESTRICTED) → HTTP 422 `RESTRICTED` ✅

실 연동 시:
```bash
HANA_RELAY_URL="http://localhost:11080/inbound" \
HANA_CCO_CUST_NO="<발급값>" HANA_CCO_CUST_SNO="<발급값>" \
PORT=8080 python3 app.py
```

---

## 8. 진행 체크리스트

- ☐ `ccoCustNo`/`ccoCustSno` 발급 및 접속 승인
- ☐ 릴레이 엔드포인트/포트(11080)/방화벽 IP 화이트리스트 협의
- ☐ 차량 카탈로그(TSV) 정기 제공 주기·경로 확정 (권장 월 1회 + 수시)
- ☐ 잔가 선택 시 약정주행거리 구간 반영 여부 확정(3장 ⚠️)
- ☐ 고정 파라미터(`config.FIXED_INFO`) 상품정책값 최종 확정
- ☐ 코드표(지역·정비·보장)·응답 규격서·에러코드 최신본 공유
- ☐ 대표 차종별 잔가·렌탈료 테스트 케이스 상호 검증
