# 폰페이 Widget API 레퍼런스

> Widget UI 개발자용. 서버는 이미 배포 완료 (Phase 3).
> Base URL: `https://api.cryptoments.cc`
> 인증: 모든 요청에 `Authorization: Bearer {widgetToken}` 필수

---

## 인증

Widget 토큰은 기존 Axim Widget과 동일한 `WidgetSessionData` 기반.
토큰에 `partnerId`, `partnerUserId` 포함 — 서버에서 자동 추출.

```
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...
```

---

## API 목록

| # | 메서드 | 경로 | 역할 |
|---|--------|------|------|
| W2 | `GET` | `/widgets/api/axim/ekyc/status` | eKYC 인증 여부 확인 |
| W3 | `GET` | `/widgets/api/axim/ekyc/info` | eKYC 인증 정보 (송금인 이름/전화/은행) |
| W4 | `POST` | `/widgets/api/phonepay/sessions` | 폰페이 세션 생성 |
| W5 | `GET` | `/widgets/api/phonepay/sessions/{id}` | 세션 상태 조회 (폴링용) |
| W6 | `POST` | `/widgets/api/phonepay/sessions/{id}/cancel` | 세션 취소 |

---

## W2. eKYC 인증 여부

```
GET /widgets/api/axim/ekyc/status
```

폰페이 화면 진입 시 최초 호출. eKYC 인증이 완료되지 않으면 폰페이를 사용할 수 없음.

### 응답

```json
{
  "verified": true,
  "status": "VERIFIED"
}
```

| 필드 | 타입 | 설명 |
|------|------|------|
| `verified` | Boolean | **이 값만 확인하면 됨.** `true`면 eKYC 완료 |
| `status` | String | `UNVERIFIED` · `VERIFYING` · `VERIFIED` · `REJECTED` · `NOT_CONFIGURED` · `NO_USER_ID` · `API_ERROR` |

### Widget 분기

| verified | 동작 |
|----------|------|
| `true` | → W3 호출 (eKYC 정보 조회) |
| `false` | eKYC 인증 안내 화면 표시 (Axim eKYC 연동 페이지로 이동) |

### 에러 시맨틱

이 API는 **예외를 던지지 않음**. Axim 설정 없음/API 오류 시에도 `verified: false` + 상태 코드로 응답.

---

## W3. eKYC 인증 정보

```
GET /widgets/api/axim/ekyc/info
```

인증된 사용자의 송금인 정보를 조회. 폰페이 결제 화면에 "홍길동 님 (KB국민은행)" 표시용.

### 응답 (200)

```json
{
  "name": "홍길동",
  "phone": "01012345678",
  "bankCode": "004",
  "bankName": "KB국민은행"
}
```

| 필드 | 타입 | 설명 |
|------|------|------|
| `name` | String | 인증된 실명 |
| `phone` | String | 휴대폰 번호 |
| `bankCode` | String | 금융기관 코드 (3자리) |
| `bankName` | String | 금융기관명 |

### 에러

| HTTP | 코드 | 의미 |
|------|------|------|
| 404 | `AXIM_SETTINGS_NOT_FOUND` | Axim 설정 없음 또는 비활성 |
| 404 | `EKYC_NOT_VERIFIED` | eKYC 미인증 (W2에서 먼저 확인 권장) |

---

## W4. 폰페이 세션 생성

```
POST /widgets/api/phonepay/sessions
Content-Type: application/json
```

사용자는 **입금 금액만** 입력. 송금인 정보(이름, 전화번호, 은행)는 서버가 eKYC API에서 자동 조회 — 위변조 차단.

### 요청

```json
{
  "amount": 50000
}
```

| 필드 | 타입 | 필수 | 설명 |
|------|------|------|------|
| `amount` | Long | ✅ | 입금 금액 (원, KRW). 정수만 |

### 응답 (200)

```json
{
  "sessionId": 123,
  "banqpipeSessionId": "dp_a1b2c3d4e5f6",
  "status": "WAITING",
  "amount": 50000,
  "senderName": "홍길동",
  "recipientName": "김철수",
  "recipientPhone": "01098765432",
  "recipientBank": "국민은행",
  "expiresAt": "2026-04-29T16:30:00",
  "remainingSeconds": 3600
}
```

| 필드 | 타입 | 설명 |
|------|------|------|
| `sessionId` | Long | DB PK. 이후 W5/W6에서 사용 |
| `banqpipeSessionId` | String | BanqPipe 세션 ID (`dp_xxx`) |
| `status` | String | 항상 `WAITING` (생성 직후) |
| `amount` | Long | 입금 금액 (원) |
| `senderName` | String | 송금인 이름 (eKYC) |
| `recipientName` | String | **수취인 이름** — 화면에 표시 |
| `recipientPhone` | String | **수취인 전화번호** — 연락처 이체 대상 |
| `recipientBank` | String | **수취인 은행명** — 화면에 표시 |
| `expiresAt` | String | 만료 시각 (ISO 8601). BanqPipe 기준 1시간 |
| `remainingSeconds` | Long | 남은 시간 (초) |

### Widget 표시

```
수취인: 김철수 (국민은행)
연락처: 010-9876-5432
금액: ₩50,000

위 연락처로 연락처 이체를 실행해주세요.
[타이머: 59:59]  [취소]
```

### 에러

| HTTP | 코드 | 의미 |
|------|------|------|
| 404 | `AXIM_SETTINGS_NOT_FOUND` | Axim 설정 없음 |
| 404 | `EKYC_NOT_VERIFIED` | eKYC 미인증 |
| 404 | `PHONEPAY_SETTINGS_NOT_FOUND` | 폰페이 미설정 |
| 404 | `PHONEPAY_NOT_CONFIGURED` | 폰페이 API Key 미등록 |
| 500 | `PHONEPAY_API_FAILED` | BanqPipe API 호출 실패 |

---

## W5. 세션 상태 조회 (폴링)

```
GET /widgets/api/phonepay/sessions/{id}
```

W4로 생성한 세션의 상태를 조회. **3~5초 간격 폴링** 권장.

### 응답 (200)

```json
{
  "sessionId": 123,
  "banqpipeSessionId": "dp_a1b2c3d4e5f6",
  "status": "WAITING",
  "amount": 50000,
  "senderName": "홍길동",
  "recipientName": "김철수",
  "recipientPhone": "01098765432",
  "recipientBank": "국민은행",
  "expiresAt": "2026-04-29T16:30:00",
  "remainingSeconds": 1800
}
```

W4 응답과 동일 구조. `remainingSeconds`가 실시간 갱신.

### 상태별 Widget 동작

| status | 화면 | 폴링 |
|--------|------|------|
| `WAITING` | 대기 화면 (수취인 정보 + 카운트다운 타이머) | **계속** |
| `MATCHED` | "SMS 매칭됨" 표시 (입금 처리 중) | **계속** |
| `COMPLETED` | ✅ 입금 완료 화면 | **중단** |
| `FAILED` | ❌ 입금 실패 안내 | **중단** |
| `EXPIRED` | ⏰ 만료 안내 + "다시 생성" 버튼 | **중단** |
| `CANCELLED` | 취소됨 안내 + "다시 생성" 버튼 | **중단** |

### 폴링 중단 조건

`COMPLETED`, `FAILED`, `EXPIRED`, `CANCELLED` — 이 4개 상태는 종료(terminal) 상태. 폴링을 중단하고 결과 화면 표시.

### 에러

| HTTP | 코드 | 의미 |
|------|------|------|
| 404 | `PHONEPAY_SESSION_NOT_FOUND` | 세션 없음 또는 다른 파트너 소유 |

---

## W6. 세션 취소

```
POST /widgets/api/phonepay/sessions/{id}/cancel
```

사용자가 대기 중 취소. **WAITING 상태에서만** 가능.

### 요청

Body 없음.

### 응답 (200)

```json
{
  "sessionId": 123,
  "status": "CANCELLED",
  "cancelledAt": "2026-04-29T15:05:00"
}
```

| 필드 | 타입 | 설명 |
|------|------|------|
| `sessionId` | Long | 세션 ID |
| `status` | String | `CANCELLED` |
| `cancelledAt` | String | 취소 시각 (ISO 8601) |

### 에러

| HTTP | 코드 | 의미 |
|------|------|------|
| 404 | `PHONEPAY_SESSION_NOT_FOUND` | 세션 없음 |
| 409 | `PHONEPAY_SESSION_NOT_CANCELLABLE` | WAITING이 아닌 상태 (MATCHED 이후 취소 불가) |

---

## 전체 플로우

```
Widget                              open-api 서버                    BanqPipe
  │                                      │                              │
  │ ① 폰페이 화면 진입                     │                              │
  │ GET /axim/ekyc/status                │                              │
  │ ─────────────────────────────────> │                              │
  │ { verified: true }                   │                              │
  │ <───────────────────────────────── │                              │
  │                                      │                              │
  │ ② eKYC 정보 표시                      │                              │
  │ GET /axim/ekyc/info                  │                              │
  │ ─────────────────────────────────> │                              │
  │ { name, phone, bankCode, bankName }  │                              │
  │ <───────────────────────────────── │                              │
  │                                      │                              │
  │  "홍길동 님 (KB국민은행)"              │                              │
  │  금액 입력: [50,000]                  │                              │
  │                                      │                              │
  │ ③ 세션 생성                           │                              │
  │ POST /phonepay/sessions              │                              │
  │ { "amount": 50000 }                  │                              │
  │ ─────────────────────────────────> │  BanqPipe 세션 생성            │
  │                                      │ ─────────────────────────> │
  │                                      │ <───────────────────────── │
  │ { recipientName, recipientPhone,     │                              │
  │   recipientBank, expiresAt }         │                              │
  │ <───────────────────────────────── │                              │
  │                                      │                              │
  │  수취인: 김철수 (국민은행)              │                              │
  │  연락처: 010-9876-5432               │                              │
  │  [타이머: 59:59]                      │                              │
  │                                      │                              │
  │ ④ 폴링 (3초 간격)                     │                              │
  │ GET /phonepay/sessions/{id}          │                              │
  │ ─────────────────────────────────> │                              │
  │ { status: "WAITING" }                │                              │
  │ <───────────────────────────────── │                              │
  │                                      │                              │
  │  ... 사용자가 은행 앱에서 이체 ...      │                              │
  │                                      │                              │
  │                                      │  ⑤ BanqPipe Callback         │
  │                                      │  SESSION_COMPLETED           │
  │                                      │ <───────────────────────── │
  │                                      │  → Deposit + Ledger 처리     │
  │                                      │                              │
  │ ⑥ 폴링 → COMPLETED                   │                              │
  │ GET /phonepay/sessions/{id}          │                              │
  │ ─────────────────────────────────> │                              │
  │ { status: "COMPLETED" }              │                              │
  │ <───────────────────────────────── │                              │
  │                                      │                              │
  │  ✅ 입금 완료!                        │                              │
```

---

## 구현 참고사항

### 타이머

- `expiresAt`과 `remainingSeconds` 모두 제공
- `remainingSeconds`는 서버 시간 기준. 클라이언트 타이머는 `expiresAt` 기반으로 자체 카운트다운 권장
- 만료 시 폴링에서 `EXPIRED` 상태 반환

### 에러 응답 형식

모든 에러는 동일한 JSON 구조:

```json
{
  "code": "935",
  "message": "eKYC 인증이 완료되지 않았습니다."
}
```

### 금액 제한

- 현재 서버에 최소/최대 금액 제한 없음
- BanqPipe 측 제한이 있을 수 있으므로, 생성 실패 시 에러 메시지 표시 필요

### 재생성

- `EXPIRED`, `CANCELLED`, `FAILED` 상태에서 "다시 생성" 버튼 표시
- 동일한 W4 API를 다시 호출하면 새 세션 생성됨 (이전 세션과 무관)
