# P2P 프론트엔드 구현 Handoff — 파트너 콘솔 + 출금자 페이지

> **작성일**: 2026-06-05
> **범위**: 파트너 콘솔 P2P 관리 + 출금자 P2P 페이지
> **백엔드**: Phase 1~3 구현 완료 (push 대기)
> **프로젝트**: cryptoments-admin (partner-ui), 출금자 페이지 별도

---

## 1. 파트너 콘솔 (partner-ui)

### 1.1 사이드바 메뉴 추가

`partner-ui/src/utils/constants.ts` — MENU_ITEMS에 추가:

```typescript
// 출금 관리 다음, 지갑 관리 앞에 삽입
{
  label: 'P2P 관리', icon: Repeat2,  // lucide-vue-next
  children: [
    { label: '회원 관리', path: '/partner/p2p/members' },
    { label: '입금 링크', path: '/partner/p2p/deposit-links' },
    { label: '출금 주문', path: '/partner/p2p/withdraw-orders' },
    { label: '거래 내역', path: '/partner/p2p/trades' },
  ],
},
```

STATUS_COLORS에 P2P 상태 추가:
```typescript
P2P_PENDING: 'bg-purple-100 text-purple-800',
PARTIALLY_MATCHED: 'bg-blue-100 text-blue-800',
FULLY_MATCHED: 'bg-blue-100 text-blue-800',
SETTLING: 'bg-yellow-100 text-yellow-800',
MATCHED: 'bg-blue-100 text-blue-800',
MATCHING: 'bg-yellow-100 text-yellow-800',
BANK_PENDING: 'bg-yellow-100 text-yellow-800',
BANK_CONFIRMED: 'bg-green-100 text-green-800',
DISPUTED: 'bg-red-100 text-red-800',
INNER: 'bg-indigo-100 text-indigo-800',
ONCHAIN: 'bg-emerald-100 text-emerald-800',
FORCE_SETTLE: 'bg-orange-100 text-orange-800',
SUSPENDED: 'bg-gray-100 text-gray-800',
```

### 1.2 라우터 추가

`partner-ui/src/router/index.ts` — children에 추가:

```typescript
// P2P 관리
{ path: 'p2p/members', name: 'partner-p2p-members', component: () => import('@/views/partner/p2p/P2pMembersView.vue'), meta: { title: 'P2P 회원 관리' } },
{ path: 'p2p/members/:token', name: 'partner-p2p-member-detail', component: () => import('@/views/partner/p2p/P2pMemberDetailView.vue'), meta: { title: 'P2P 회원 상세' } },
{ path: 'p2p/deposit-links', name: 'partner-p2p-deposit-links', component: () => import('@/views/partner/p2p/P2pDepositLinksView.vue'), meta: { title: 'P2P 입금 링크' } },
{ path: 'p2p/withdraw-orders', name: 'partner-p2p-withdraw-orders', component: () => import('@/views/partner/p2p/P2pWithdrawOrdersView.vue'), meta: { title: 'P2P 출금 주문' } },
{ path: 'p2p/withdraw-orders/:code', name: 'partner-p2p-withdraw-order-detail', component: () => import('@/views/partner/p2p/P2pWithdrawOrderDetailView.vue'), meta: { title: 'P2P 출금 상세' } },
{ path: 'p2p/trades', name: 'partner-p2p-trades', component: () => import('@/views/partner/p2p/P2pTradesView.vue'), meta: { title: 'P2P 거래 내역' } },
```

### 1.3 API 서비스

`partner-ui/src/api/services/p2p.service.ts`:

```typescript
// 회원 관리
getMembers(params?)                    // GET /api/partner/p2p/members
createMember(data)                     // POST /api/partner/p2p/members
getMemberDetail(token)                 // GET /api/partner/p2p/members/{token}
resetMemberPin(token)                  // POST /api/partner/p2p/members/{token}/reset-pin
suspendMember(token)                   // POST /api/partner/p2p/members/{token}/suspend
activateMember(token)                  // POST /api/partner/p2p/members/{token}/activate

// 입금 링크
getDepositLinks(params?)               // GET /api/partner/p2p/deposit-links
createDepositLink(data)                // POST /api/partner/p2p/deposit-links

// 출금 주문
getWithdrawOrders(params?)             // GET /api/partner/p2p/withdraw-orders
getWithdrawOrderDetail(code)           // GET /api/partner/p2p/withdraw-orders/{code}
forceSettle(code, data)                // POST /api/partner/p2p/withdraw-orders/{code}/force-settle
cancelRemaining(code, data)            // POST /api/partner/p2p/withdraw-orders/{code}/cancel-remaining
convertDirect(code, data)              // POST /api/partner/p2p/withdraw-orders/{code}/convert-direct
approveUsdtConvert(code)               // POST /api/partner/p2p/withdraw-orders/{code}/approve-usdt

// 출금 P2P 전환 (기존 withdrawal API 확장)
convertToP2p(withdrawalId, data)       // POST /api/partner/withdrawals/{id}/p2p-convert

// 서비스 계좌
getServiceAccount()                    // GET /api/partner/p2p/service-account
createServiceAccount(data)             // POST /api/partner/p2p/service-account
updateServiceAccount(data)             // PUT /api/partner/p2p/service-account
deleteServiceAccount()                 // DELETE /api/partner/p2p/service-account
```

### 1.4 Vue 파일 목록

```
partner-ui/src/views/partner/p2p/
├── P2pMembersView.vue              — 회원 목록 + 등록 다이얼로그
├── P2pMemberDetailView.vue         — 회원 상세 (링크 복사, 핀리셋, 차단)
├── P2pDepositLinksView.vue         — 입금 링크 목록 + 생성 다이얼로그
├── P2pWithdrawOrdersView.vue       — 출금 주문 목록
├── P2pWithdrawOrderDetailView.vue  — 출금 주문 상세 + 잔여분 처리
└── P2pTradesView.vue               — 거래(매칭) 내역
```

### 1.5 화면별 상세

#### P2pMembersView.vue — 회원 목록

```
[회원 등록]

────────────────────────────────────────────────────
사용자ID    계좌        스크래핑   활성출금   상태   
────────────────────────────────────────────────────
user_hong  KB 123-xxx  활성       2건       활성  [상세]
user_kim   미등록       —         0건       활성  [상세]
user_lee   신한 456-xxx 만료       1건       차단  [상세]
```

등록 다이얼로그:
```
P2P 회원 등록
사용자 ID: [              ]
[등록] → 고정 링크 자동 발급 → 링크 복사 버튼
```

#### P2pMemberDetailView.vue — 회원 상세

```
user_hong

출금 페이지 링크:
https://p2p.cryptoments.cc/m/abc123xyz  [복사]

계좌: KB국민 123-456-789 홍길동
스크래핑: 활성 (만료: 2026-07-01)

핀코드: 설정됨
[핀코드 리셋]

상태: 활성
[접근 차단]

최근 출금 주문:
  500,000원 / 339 USDT — 매칭 대기 중
  300,000원 / 203 USDT — 완료 (05-28)
```

#### P2pDepositLinksView.vue — 입금 링크

```
[링크 생성]

────────────────────────────────────────────────
링크코드       금액          모드    만료   상태
────────────────────────────────────────────────
plnk_abc123   500,000 / 339  AUTO   30분  ACTIVE  [복사]
plnk_def456   300,000 / 203  MANUAL 10분  USED
```

생성 다이얼로그:
```
P2P 입금 링크 생성

금액 *: [          ] 원  ≈ 339 USDT
네트워크 *: [TRON ▼]
매칭 모드 *: [자동 ▼]
  MANUAL 선택 시 → 대기 중 출금자 목록 표시, 체크박스 선택
만료 *: [30분 ▼] (5/10/30/60분)
입금자 ID: [        ] (선택)

[생성] → 링크 표시 + [복사] [QR]
```

#### P2pWithdrawOrderDetailView.vue — 출금 주문 상세 (핵심)

```
출금 주문: pwo_abc123
금액: 1,000,000원 / 679 USDT
사용자: user_hong
원본 출금: wdr_2606_xxx
계좌: KB국민 123-456-789 홍길동

매칭 내역:
┌────────────────────────────────────┐
│ ① 300,000원 / 203.5 USDT          │
│ 입금자: B사이트 user456            │
│ 이체: ✓ 확인 (06-01 15:20)        │
│ 정산: ✓ 완료 (TX: 0xa3b...)       │
└────────────────────────────────────┘
┌────────────────────────────────────┐
│ ② 600,000원 / 407.0 USDT          │
│ 이체: ✓ 확인                       │
│ 정산: ✓ 완료                       │
└────────────────────────────────────┘

잔여: 100,000원 / 67.9 USDT

  USDT 전환 요청: 있음 (TRwr...abc, 06-02 요청)
  [USDT 전환 승인]  [거부]

  [출금 전환]  [강제 정산]  [취소]
```

#### 기존 출금 상세에 P2P 전환 추가

`WithdrawalDetailView.vue` (또는 `WithdrawalListView.vue`의 상세 다이얼로그) 수정:

```
출금 상세 (PENDING_APPROVAL)

[직접 출금 승인]  [P2P 전환]  [거부]

── P2P 전환 클릭 시 다이얼로그 ──

P2P 전환

이 출금을 P2P로 전환합니다.
사용자에게 출금 페이지 링크가 전달됩니다.

P2P 회원: user_hong (등록됨)
  또는
P2P 회원: 미등록 → 자동 등록됩니다

[전환] → 링크 표시 + [복사]
```

#### 비즈니스 설정에 P2P 섹션 추가

`BusinessSettingsView.vue` 수정 — P2P 탭 또는 섹션:

```
P2P 서비스 설정

P2P 서비스: [■ 활성화]
P2P 수수료율: [2.0] % (구매자 부담)

서비스 계좌:
  KB국민 987-654-321 (주)크립토서비스
  스크래핑: 활성 (만료: 2026-07-15)
  [수정]  [삭제]
  또는
  [계좌 등록]
```

---

## 2. 출금자 P2P 페이지 (별도)

### 2.1 프로젝트 구조

widget-ui와 별도이거나 같은 프로젝트 내 별도 라우트. 모바일핏.
URL: `https://p2p.cryptoments.cc/m/{memberToken}`

### 2.2 API

백엔드: open-api의 `P2pPageAuthController` + `P2pWithdrawPageController`

```typescript
// 인증
setPin(token, { pin })           // POST /p2p/page/auth (최초 설정)
verifyPin(token, { pin })        // POST /p2p/page/auth (인증 → 세션 토큰)

// 메인
getDashboard()                   // GET /p2p/page/dashboard
getBanks()                       // GET /p2p/page/banks (은행 목록 + 패턴)

// 계좌
registerAccount(data)            // POST /p2p/page/account (계좌 + 스크래핑)
deleteAccount()                  // DELETE /p2p/page/account
reAuth()                         // POST /p2p/page/account/re-auth (만료 시)

// 거래
getOrders(params?)               // GET /p2p/page/orders
getOrderDetail(code)             // GET /p2p/page/orders/{code}
getReceipt(code)                 // GET /p2p/page/orders/{code}/receipt
confirmDeposit(code, matchId)    // POST /p2p/page/orders/{code}/confirm-deposit/{matchId}
getDisputes(code)                // GET /p2p/page/orders/{code}/disputes

// USDT 전환
requestConvert(data)             // POST /p2p/page/convert-request
```

### 2.3 화면 구성

```
views/
├── PinView.vue              — 핀코드 입력/설정
├── DashboardView.vue        — 메인 (상태, 잔액, 최근 거래)
├── AccountRegisterView.vue  — 계좌 등록 (은행선택 → 패턴별 폼 → 스크래핑)
├── AccountReAuthView.vue    — 스크래핑 재인증
├── OrdersView.vue           — 거래 내역 목록
├── OrderDetailView.vue      — 거래 상세 (매칭/분쟁)
├── ReceiptView.vue          — 거래 확인증 (인쇄/저장)
├── DisputeView.vue          — 분쟁 상세
└── ConvertRequestView.vue   — USDT 전환 요청
```

### 2.4 화면별 상세

#### PinView.vue

```
Cryptoments P2P

[최초] 핀코드를 설정해주세요
      [● ● ● ● ● ●]
      확인: [● ● ● ● ● ●]
      [설정]

[재방문] 핀코드를 입력해주세요
        [● ● ● ● ● ●]
        자동 인증 → DashboardView
```

#### DashboardView.vue

```
홍길동님

┌─────────────────────┐
│ 출금 대기 금액        │
│ 500,000원 / 339 USDT│
│ 진행 중 1건          │
└─────────────────────┘

● 계좌: KB국민 123-xxx ✓
● 스크래핑: 활성 (D-25)

[출금 현황]  [계좌 변경]

최근 거래:
  300,000원 / 203 USDT — 완료 (05-28) [확인증]
  200,000원 / 135 USDT — 완료 (05-25) [확인증]
```

계좌 미등록 시:
```
계좌를 등록해야 출금을 이용할 수 있습니다.
[계좌 등록하기]
```

스크래핑 만료 시:
```
⚠ 빠른조회 인증이 만료되었습니다
[재인증하기]
```

#### AccountRegisterView.vue

```
계좌 등록

은행 선택:
[KB국민은행              ▼]

  ℹ KB국민은행은 빠른조회 서비스 등록이 필요합니다.
    KB스타뱅킹 앱 → 빠른조회 → 서비스 등록

계좌번호: [                    ]
예금주:   [                    ]

── 인증 정보 ──
계좌 비밀번호 (4자리): [● ● ● ●]
주민번호 앞 6자리:     [● ● ● ● ● ●]

── KB/신한 추가 ──
빠른조회 ID:  [                    ]
빠른조회 PW:  [● ● ● ● ● ●]

☑ 빠른조회 서비스를 은행 앱에서 사전 등록했습니다

[등록]
```

은행 패턴에 따라 폼 필드 동적 변경:
- A_TYPE0 (14개): 계좌비밀번호 + 주민번호
- B_NO_IDENTITY (iM뱅크): 계좌비밀번호만
- D_FAST_PLUS_ACCT (KB/신한): 계좌비밀번호 + 빠른조회 ID/PW

`GET /p2p/page/banks` 응답의 `fast_inquiry_pattern`으로 판단.

#### OrderDetailView.vue

```
거래 상세

500,000원 / 339 USDT
상태: 이체 확인 대기

매칭:
┌─────────────────────────┐
│ ① 300,000원 / 203 USDT  │
│ ✓ 이체 확인됨            │
│ 정산: 완료               │
└─────────────────────────┘
┌─────────────────────────┐
│ ② 200,000원 / 135 USDT  │
│ ⚠ 이체 미확인            │
│ 입금자가 이체했다고 합니다│
│                          │
│ [입금 확인]  [입금 미확인] │
└─────────────────────────┘

잔여 USDT 출금 요청:
  전환 요청 없음
  [USDT로 전환 요청]
```

#### ReceiptView.vue

```
P2P 거래 확인증
────────────────────────
거래 번호: PM-2026060100042
거래일: 2026-06-01

출금자: 홍길동
계좌: KB국민 123-***-789

입금 금액: 300,000원
정산 USDT: 203.5 USDT
환율: 1 USDT = 1,474원

정산 TX: 0xa3b4c5...
정산일: 2026-06-01 15:25
────────────────────────
Cryptoments P2P

[저장]  [인쇄]
```

---

## 3. 기존 화면 수정

### 3.1 출금 내역 (WithdrawalListView.vue)

출금 상세 다이얼로그 또는 페이지에 P2P 전환 버튼 추가:
- PENDING_APPROVAL 또는 REQUESTED 상태에서 [P2P 전환] 버튼
- P2P_PENDING 상태: P2P 출금 주문 링크 표시

### 3.2 출금 생성 다이얼로그

출금 요청 생성 시 "출금 방식" 선택 추가:
- 직접 출금: 수신 주소 필수
- P2P 전환: 수신 주소 불필요, 즉시 P2P 전환

### 3.3 비즈니스 설정 (BusinessSettingsView.vue)

P2P 섹션 추가:
- P2P 활성화 토글
- P2P 수수료율 입력
- 파트너 서비스 계좌 관리

### 3.4 대시보드 (DashboardView.vue)

P2P 요약 카드 추가:
- 대기 중 출금, 진행 중 매칭, 오늘 완료

---

## 4. 인쇄 지원

출금자 페이지의 거래 확인증(ReceiptView)은 인쇄 가능해야 함.
기존 PartnerLayout에 `@media print` CSS 추가 완료되어 있으므로 출금자 페이지 레이아웃에도 동일 적용.

## 5. 모바일핏

출금자 P2P 페이지는 모바일 우선 설계:
- 최대 폭 제한 (max-width: 480px, mx-auto)
- 터치 친화 버튼 크기
- 핀코드 입력 패드 스타일
