# P2P 수동 입금확인 + 분쟁 룰 (확정 스펙)

> 작성: 2026-07-10 (Cowork) · 상태: 룰 확정, 구현 대기
> 관련: `P2P_MEMBER_SETTINGS_GUIDE.md`(§6·§6B), `P2P_MEMBER_TELEGRAM_GUIDE.md`(§7B), `P2P_MATCHING_ARCHITECTURE.md`
> 이 문서 = 수동 입금확인의 **타임 규칙 + 분쟁 처리 룰**의 단일 출처(SoT).

## 0. 범위
수동(MANUAL) 입금방식 및 CODEF 미지원 은행(인터넷은행)에서, 판매자(출금회원)가 입금을 직접
확인하는 흐름과 **시간 규칙·분쟁(반려/재요청 반복 포함) 처리**를 확정한다.

## 1. 타임 규칙 (확정)

| 시계 | 시작점 | 값 | 비고 |
|------|--------|----|------|
| 구매자 이체 기한 | 매칭 생성 | **30분** | 기존 `MATCH_EXPIRY_MINUTES`. CREATED 미이체 → FAILED(안전) |
| **판매자 확인 목표(수동)** | **입금완료 신고(BANK_PENDING 진입)** | **10분** | C1 발송 시점 기준. 목표치(초과해도 자동실패 아님) |
| 리마인더(C2) | SLA 초과 후 | 반복(운영 조정, 제안 10분 간격) | 미확인 지속 시 |
| 에스컬레이션(C3) | 미확인 지속 | 관리자 통지 | 자동 종결 아님 |
| 분쟁 라운드 재제출 기한 | 관리자 재요청 시 | **6시간** | §5 |
| 전체 분쟁 상한 | — | **관리자 강제**(고정 상한 없음) | §5 |

**확정 안전 규칙 (필수)**
- 판매자 확인 시계는 **입금완료 신고 기준**이다(매칭 기준 아님) — 판매자는 돈이 들어와야 확인 가능.
- 구매자가 늦게(예: 28분) 이체 신고하면 판매자 확인이 30분을 넘길 수 있으므로,
  **수동 매칭의 `BANK_PENDING`은 30분 매칭 만료(`P2pMatchExpiryJob`)의 자동 FAILED 대상에서 제외**한다.
  (제외 안 하면 판매자 확인 도중 매칭이 죽어 구매자 입금분 사고)

## 2. 입금확인 흐름 요약

```
매칭 생성(+30분) → 구매자 이체 → [구매자] 이체완료 신고 → BANK_PENDING
   AUTO   : CODEF 5초 폴링 자동확인 (금액+이름 일치 → 정산 / 이름불일치 → 자동분쟁)
   MANUAL : C1 텔레그램 확인요청 → 판매자 10분 내 [✅ 입금 확인] → 정산
            (미확인 → 리마인더 → 관리자 에스컬레이션 · 문제 → 페이지 신고)
```
- 수동 확인 = **2채널(회원 페이지 [입금 확인] + 텔레그램 [✅ 입금 확인] 버튼)**, 둘 다 멱등.
  **어느 채널로 처리하든 결과가 텔레그램 메시지로 발송**(중복 탭·이미처리·만료 예외 포함 — 텔레그램 §7B-8).
- `[✅ 입금 확인]`(원탭) = **약정 정확액을 받았을 때만**. 금액 상이/미입금은 페이지 신고(§3).
- 확인 전에는 **USDT 미출금** — 분쟁이 반복돼도 자금 안전.

## 3. 분쟁 케이스 2종

### 3-1. 미입금 / 미확인 (돈이 안 옴)
- 자금 이동 없음 → 취소해도 안전.
- 관리자 판정: 실제 입금 확인 → 확인·정산 / 미입금 확정 → 취소 → 구매자 재라우팅.

### 3-2. 금액 불일치 (돈은 왔으나 금액 다름)
- **판매자는 확인 누르지 말 것**(확인 시 약정 USDT 전액 지급).
- 원칙: 시스템은 **약정 정확액에서만 확인·정산**(부분정산 수학 미도입).
  - 부족: 구매자 차액 추가입금 → 재확인 / 정정 거부 → 전액 취소·반환.
  - 초과: 판매자가 초과분 반환 → 약정액 확인·정산.
- (선택) 관리자 조정정산은 후속 — 초기엔 정확액/전액취소로만.

### 3-3. 신고(report) 사유
`NO_DEPOSIT`(미입금) / `AMOUNT_SHORT`(부족) / `AMOUNT_OVER`(초과) / `ETC`(+ 실수령액·증빙).

**회원 UI는 원탭 신고(2026-07-15 확정)** — 수동확인 시트에 폼 없이 **[입금 안 옴 신고]** 버튼 하나.
- **10분 하드 게이트**: 확인 요청(C1) 후 10분(SLA) 경과 시에만 버튼 노출(조기 신고 노이즈 방지).
- 원탭 = `NO_DEPOSIT` 고정. **금액 상이 등도 이 신고에 흡수** — 판매자 주장은 플래그일 뿐이고
  판정 근거는 구매자 증빙 + 관리자 스레드(REQUEST_MORE)이므로, 세부 사유·실수령액은 스레드에서 정리.
- API의 4사유+실수령액은 유지(관리자·후속 확장용). UI 노출만 원탭.

## 4. 제기 주체 + 비대칭 (판정 원칙)

| 제기 | 대표 주장 | 증빙 |
|------|-----------|------|
| 구매자 | "보냈는데 확인 안 됨" | 이체 증빙(필수) |
| 판매자 | "안 왔다 / 금액 다름" | 통장 내역(필수) |
| 시스템 | 이름 불일치(자동) | CODEF |

**핵심 비대칭** — 확인 전 USDT 미출금이므로:
- 구매자 허위("안 보내고 보냈다 주장") → 판매자 **무해**(증빙 없으면 취소).
- 판매자 허위("받고도 안 왔다") → 구매자 **유해**(돈 내고 못 받음). 수동/인터넷은행은 스크래핑 증빙 없음.
  → **판정은 구매자 이체증빙을 우선 근거**로 하고, **판매자 제재 장치**(오신고 확정 시 거래중지/한도/신뢰등급) 필요.
- **증빙 첨부는 양측 모두 강제.**

## 5. 분쟁 스레드 (반려/재요청 반복) — 확정

단일 판정이 아니라 **증빙 제출 ↔ 반려·재요청**이 여러 번 도는 스레드. `p2p_matches`의 단일
분쟁 필드로는 부족 → **append-only 이벤트 로그** 도입.

### 5-1. 진행 규칙
- **중재자 = 관리자 단일.** 당사자는 제출·반박만, **반려·재요청·최종 판정은 관리자만**.
- **라운드 재제출 기한 = 6시간**(관리자 재요청 시 지정된 쪽이 6h 내 제출).
- **전체 상한 없음 → 관리자가 언제든 강제 판정**(반복 허용, 종결은 관리자 몫).
- **기한 초과 default = 미제출 쪽 불리**(정책):
  - 구매자 무응답 → 취소(판매자 승) / 판매자 무응답 + 구매자 증빙 → 확인·정산(구매자 승).
- **확인 전 USDT 미출금 유지** — 몇 라운드가 돌든 자금 안전.
- 라운드 전환마다 대상에게 텔레그램/위젯 알림(증빙 재요청·기한 임박).

### 5-2. ⚠️ 서비스 초기 정책 (Phase 1)
**초기에는 자동 default 판정을 켜지 않고, 모든 분쟁을 관리자가 검토 후 수동 진행한다.**
6시간 기한·"미제출 쪽 불리" 자동 처리는 **이후 단계(Phase 2)**에서 자동화. 초기엔 기한은
관리자 판단 참고용 표시로만 사용.

### 5-3. 상태/전이
- `p2p_matches.status = DISPUTED` 유지(스레드 진행 중 불변).
- 매칭 진행 포인터: `dispute_round`, `dispute_waiting_on`(제출 대기 대상), `dispute_due_at`(이번 라운드 6h 기한).
- 종결: 관리자 `RESOLVED_CONFIRM`(→ confirmBankTransfer → 정산) / `RESOLVED_CANCEL`(→ 취소·환불·재라우팅).
  (dispute_events.event_type 기존 값과 동일 표기)

## 6. DDL

**스레드 로그는 기존 `dispute_events`(2026-06-27, append-only) 재사용** — 신규 테이블 불필요.
`event_type`에 **`REQUEST_MORE`(반려·재요청)** 값 추가 + 금액분쟁용 `amount_krw` 컬럼만 확장.

```sql
-- 기존 dispute_events 확장 (운영 반영: ALTER + 코드 enum 추가)
ALTER TABLE dispute_events
    ADD COLUMN amount_krw BIGINT NULL COMMENT '실수령액 KRW(금액 불일치 신고 시)' AFTER reason;
-- event_type 값 REQUEST_MORE 추가(VARCHAR → 스키마 무변경, 코드/주석만).
-- reason: 수동 신고 코드 NO_DEPOSIT/AMOUNT_SHORT/AMOUNT_OVER/ETC 사용.
-- 매핑: OPEN=RAISED, 증빙재제출=EVIDENCE_SUBMITTED, 반려=REQUEST_MORE,
--       판정=RESOLVED_CONFIRM/RESOLVED_CANCEL. actor/source=DEPOSITOR/WITHDRAWER/ADMIN/SYSTEM.

-- p2p_matches 분쟁 진행 포인터 (신규)
ALTER TABLE p2p_matches
    ADD COLUMN dispute_round     INT NOT NULL DEFAULT 0 COMMENT '분쟁 라운드 수' AFTER dispute_source,
    ADD COLUMN dispute_waiting_on VARCHAR(20) NULL      COMMENT '재제출 대기 대상 DEPOSITOR/WITHDRAWER' AFTER dispute_round,
    ADD COLUMN dispute_due_at    DATETIME(6) NULL       COMMENT '이번 라운드 재제출 기한(+6h)' AFTER dispute_waiting_on;
```
- 스레드 상세 이력 = `dispute_events`(match_id별 append-only, `created_at` 순).
- `p2p_matches`의 분쟁 요약필드(`disputed_at`·`dispute_*`·`resolved_*`·`resolution`)는 **최신/종결 캐시**로 유지.
- 진행 포인터 3컬럼(round/waiting_on/due_at)은 canonical DDL `p2p_matches`에 반영 완료.

## 7. 신규/변경 엔드포인트

| 주체 | 메서드·경로 | 설명 |
|------|-------------|------|
| 판매자 | POST `/p2p/page/orders/{code}/matches/{matchId}/report` | 문제 신고(사유·실수령액·증빙) → 스레드 OPEN(WITHDRAWER) |
| 구매자 | (기존) 위젯 분쟁 제기 `submitDispute` | 스레드 OPEN(DEPOSITOR) |
| 양측 | POST `/…/matches/{matchId}/dispute/evidence` | 라운드 증빙 재제출 |
| 관리자 | POST admin `/p2p/matches/{id}/dispute/request-more` | 반려·재요청(대상·기한 6h) |
| 관리자 | POST admin `/p2p/matches/{id}/dispute/resolve` | 확인/취소 최종 판정 |

## 8. 관리자 콘솔 요구
- 분쟁 큐(대기·라운드·기한·제기주체·사유·금액).
- 라운드 히스토리 타임라인 + 증빙 뷰어.
- 액션: 증빙 재요청(대상 지정), 확인 판정(정산), 취소 판정(환불·재라우팅).
- 판매자 오신고 카운터(제재 연동, §4).

## 9. 미결(운영 조정값) / 후속
- C2 리마인더 주기/횟수(제안 10분 간격) — 운영 조정.
- 판매자 제재 세부(오신고 N회 → 거래중지/한도) — §4, 별도 확정.
- Phase 2: 6h 기한 자동 처리(미제출 불리 자동 판정) 활성화.
- (선택) 관리자 조정정산(부분/초과) 툴.
