# External Wallet Lookup 버그 수정 지침

> **심각도**: HIGH — 다중 파트너 환경에서 입금 파트너 오귀속 가능
> **영향 범위**: `ExternalWalletMapper`, `WebhookProcessingService.processDeposit()`

---

## 1. 문제

동일 외부 지갑 주소가 여러 파트너에 연결된 경우 입금 파트너/사용자가 잘못 식별된다.

### 시나리오

```
Partner A (id=1) — user "alice" — Axim 연결 지갑 0xABC
Partner B (id=2) — user "bob"   — Axim 연결 지갑 0xABC (동일 주소)
```

0xABC → Partner A의 MASTER(0xMASTER_A)로 입금 발생:

| 단계 | 코드 | 결과 |
|------|------|------|
| L183 | `partnerId = toWallet.getPartnerId()` | partnerId = **1** (A) ✅ |
| L193 | `findByNetworkIdAndAddress(networkId, "0xABC")` | LIMIT 1 → **Partner B 레코드 반환 가능** |
| L196 | `partnerId = extWallet.getPartnerId()` | partnerId = **2** (B) ❌ 덮어씀 |
| L197 | `partnerUserId = extWallet.getPartnerUserId()` | "bob" ❌ |

**결과**: Partner A의 MASTER로 들어온 입금이 Partner B의 "bob" 입금으로 잘못 기록됨.

---

## 2. 수정 내용

### 2-1. ExternalWalletMapper — partnerId 파라미터 추가

**파일**: `common/src/main/java/com/cryptoments/common/mapper/ExternalWalletMapper.java`

기존 메서드를 유지하고 **새 메서드 추가** (기존 메서드는 다른 곳에서 사용될 수 있으므로):

```java
/**
 * partnerId + networkId + address로 ExternalWallet 조회.
 * MASTER 입금 식별 시 반드시 이 메서드 사용 — partnerId 스코핑으로 다중 파트너 충돌 방지.
 */
@Select("""
        SELECT * FROM external_wallets
        WHERE status = 'CONNECTED'
          AND partner_id = #{partnerId}
          AND JSON_UNQUOTE(JSON_EXTRACT(wallet_addresses, CONCAT('$."', #{networkId}, '"'))) = #{address}
        LIMIT 1
        """)
ExternalWallet findByPartnerIdAndNetworkIdAndAddress(@Param("partnerId") Long partnerId,
                                                      @Param("networkId") Long networkId,
                                                      @Param("address") String address);
```

### 2-2. WebhookProcessingService.processDeposit() — 호출부 수정

**파일**: `open-api/src/main/java/com/cryptoments/webhook/service/WebhookProcessingService.java`

**BEFORE** (L192-207):
```java
} else if (toWallet.getWalletType() == WalletType.MASTER) {
    ExternalWallet extWallet = externalWalletMapper
            .findByNetworkIdAndAddress(networkId, dto.getFrom());
    if (extWallet != null) {
        partnerId = extWallet.getPartnerId();
        partnerUserId = extWallet.getPartnerUserId();
        depositType = DepositType.USER_DEPOSIT;
        depositMethod = DepositMethod.EXTERNAL_WALLET;
        externalWalletId = extWallet.getId();
        log.info("EXTERNAL_WALLET 입금 식별: fromAddress={}, partnerId={}, partnerUserId={}",
                dto.getFrom(), partnerId, partnerUserId);
    } else {
        depositType = DepositType.PARTNER_CHARGE;
        depositMethod = DepositMethod.DIRECT;
    }
}
```

**AFTER**:
```java
} else if (toWallet.getWalletType() == WalletType.MASTER) {
    // partnerId는 이미 L183에서 toWallet(MASTER)로부터 설정됨 — 덮어쓰지 않음
    ExternalWallet extWallet = externalWalletMapper
            .findByPartnerIdAndNetworkIdAndAddress(partnerId, networkId, dto.getFrom());
    if (extWallet != null) {
        // partnerId는 MASTER 지갑에서 이미 정확히 설정됨 — 재할당 불필요
        partnerUserId = extWallet.getPartnerUserId();
        depositType = DepositType.USER_DEPOSIT;
        depositMethod = DepositMethod.EXTERNAL_WALLET;
        externalWalletId = extWallet.getId();
        log.info("EXTERNAL_WALLET 입금 식별: fromAddress={}, partnerId={}, partnerUserId={}",
                dto.getFrom(), partnerId, partnerUserId);
    } else {
        depositType = DepositType.PARTNER_CHARGE;
        depositMethod = DepositMethod.DIRECT;
    }
}
```

**핵심 변경 2가지**:
1. `findByNetworkIdAndAddress` → `findByPartnerIdAndNetworkIdAndAddress(partnerId, ...)` — partnerId 스코핑
2. `partnerId = extWallet.getPartnerId()` 라인 **제거** — MASTER에서 이미 정확한 값 보유

---

## 3. 기존 findByNetworkIdAndAddress 메서드 처리

다른 곳에서 사용하지 않는다면 `@Deprecated` 처리 또는 삭제.
사용처가 있다면 유지하되, 입금 식별 경로에서는 반드시 `findByPartnerIdAndNetworkIdAndAddress` 사용.

---

## 4. 검증 포인트

| 케이스 | 기대 결과 |
|--------|-----------|
| 0xABC → Partner A MASTER, A에만 연결 | partnerId=A, partnerUserId=A의 사용자 |
| 0xABC → Partner A MASTER, A+B 모두 연결 | partnerId=A, partnerUserId=**A의 사용자** (B 무시) |
| 0xABC → Partner B MASTER, A+B 모두 연결 | partnerId=B, partnerUserId=**B의 사용자** (A 무시) |
| 0xXYZ → Partner A MASTER, 미등록 주소 | PARTNER_CHARGE + DIRECT |

---

## 5. WEBHOOK_PARTNER_CURRENCY_MAPPING_GUIDE.md 동기화

`v2-docs/WEBHOOK_PARTNER_CURRENCY_MAPPING_GUIDE.md` L157-172의 예시 코드도 동일하게 업데이트 필요.
