# Guide #75 — Axim Pay 목록 개선 + 사용자 상세 액션 버튼 정리

> **대상**: partner-ui (Vue 3 + TypeScript)
> **선행**: Axim 데이터 마이그레이션 완료 (172건 v1→v2)

---

## 변경 요약

| # | 항목 | 파일 |
|---|------|------|
| A | AximPaymentsView — "결제 요청" 버튼 제거 + 목록 컬럼 개선 | AximPaymentsView.vue |
| B | UserDetailView — 액션 버튼 5개 정리 (조건부 활성화 + 입금 세션 추가) | UserDetailView.vue |
| C | 결제 링크 생성 결과에 링크 URL + 복사 기능 추가 | UserDetailView.vue |

---

## Part A. AximPaymentsView 개선

**파일**: `partner-ui/src/views/partner/deposits/AximPaymentsView.vue`

### A-1. "결제 요청" 버튼 제거

현재 `PageHeader`의 `#actions` 슬롯에 동작하지 않는 버튼이 있음. 삭제:

```vue
<!-- 변경 전 -->
<PageHeader title="Axim Pay 결제" description="Axim Pay 결제 내역">
  <template #actions>
    <Button>결제 요청</Button>    <!-- ← 삭제 -->
  </template>
</PageHeader>

<!-- 변경 후 -->
<PageHeader title="Axim Pay 결제" description="Axim Pay 결제 내역" />
```

### A-2. 목록 컬럼 개선

Guide #74와 동일 패턴 — "통화" 컬럼 삭제, "금액" → "수량" + 심볼 병합, 네트워크 추가:

```ts
// 변경 전
const columns: Column<Record<string, unknown>>[] = [
  { key: 'paymentCode', label: '결제코드', class: 'font-mono text-xs' },
  { key: 'partnerUserId', label: '사용자 ID' },
  { key: 'currencyCode', label: '통화' },          // 삭제
  { key: 'amount', label: '금액', class: 'text-right' },
  { key: 'status', label: '상태' },
  { key: 'createdAt', label: '생성일' },
]

// 변경 후
const columns: Column<Record<string, unknown>>[] = [
  { key: 'paymentCode', label: '결제코드', class: 'font-mono text-xs' },
  { key: 'partnerUserId', label: '사용자 ID' },
  { key: 'networkCode', label: '네트워크' },        // 추가
  { key: 'amount', label: '수량', class: 'text-right' },  // '금액' → '수량'
  { key: 'status', label: '상태' },
  { key: 'createdAt', label: '생성일' },
]
```

> `#amount` 슬롯은 이미 `:currency="row.currencyCode"` 포함이므로 심볼이 표시됨. 변경 불필요.

### A-3. 백엔드 Mapper에 networkCode JOIN 추가 확인

현재 partner-api의 Axim 목록 조회 쿼리가 `networkCode`를 SELECT하는지 확인 필요.
만약 안 되어 있다면 `PartnerDepositMapper` 또는 해당 Mapper에서:

```sql
LEFT JOIN blockchain_networks bn ON ap.network_id = bn.id
```

추가하고 SELECT에 `bn.chain_symbol AS networkCode` 포함.

---

## Part B. UserDetailView 액션 버튼 정리

**파일**: `partner-ui/src/views/partner/users/UserDetailView.vue`

### B-1. 액션 버튼 5개 정의

| # | 버튼 | 조건 | 동작 |
|---|------|------|------|
| 1 | 액심 결제 요청 | 연결 지갑에 AXIM + CONNECTED 있을 때만 활성화 | SlideDrawer 모달 (기존) |
| 2 | 결제 링크 생성 | 항상 활성화 | SlideDrawer 모달 (기존 + 결과 링크 개선) |
| 3 | 입금 지갑 생성 | 항상 활성화 | SlideDrawer 모달 (기존 goCreateDeposit → 모달로 변경) |
| 4 | 사용자 출금 | 항상 활성화 | SlideDrawer 모달 (기존 goCreateWithdrawal → 모달로 변경) |
| 5 | 입금 세션 생성 | 항상 활성화 | 페이지 이동 `/partner/deposit-sessions/new?partnerUserId={userId}` |

### B-2. Axim 버튼 조건부 활성화

연결 지갑 데이터를 활용하여 AXIM 연결 여부를 판단:

```ts
import { computed } from 'vue'

// wallets가 이미 로딩되어 있거나, summary에서 판별 가능
const hasAximConnection = computed(() => {
  return wallets.value.some(
    w => (w.connectionType as string) === 'AXIM' && (w.status as string) === 'CONNECTED'
  )
})
```

> **주의**: `wallets`는 "연결 지갑" 탭 클릭 시 lazy-load 되는데, 버튼 활성화 판단을 위해 `onMounted`에서 미리 로드해야 함.

```ts
onMounted(() => {
  fetchSummary()
  fetchDeposits()
  fetchWallets()  // ← 추가: 액션 버튼 조건 판단용
})
```

### B-3. 버튼 영역 변경

```html
<!-- 변경 전 -->
<div class="flex flex-wrap gap-2">
  <Button size="sm" @click="openAximModal">액심 결제 요청</Button>
  <Button size="sm" variant="outline" @click="openPLModal">결제 링크 생성</Button>
  <Button size="sm" variant="outline" @click="goCreateDeposit">입금 지갑 생성</Button>
  <Button size="sm" variant="outline" @click="goCreateWithdrawal">사용자 출금</Button>
</div>

<!-- 변경 후 -->
<div class="flex flex-wrap gap-2">
  <Button size="sm" :disabled="!hasAximConnection"
          :title="hasAximConnection ? '' : 'Axim 연결 지갑이 없습니다'"
          @click="openAximModal">
    액심 결제 요청
  </Button>
  <Button size="sm" variant="outline" @click="openPLModal">결제 링크 생성</Button>
  <Button size="sm" variant="outline" @click="openDepositWalletModal">입금 지갑 생성</Button>
  <Button size="sm" variant="outline" @click="openWithdrawalModal">사용자 출금</Button>
  <Button size="sm" variant="outline" @click="goCreateDepositSession">입금 세션 생성</Button>
</div>
```

### B-4. 입금 세션 생성 — 페이지 이동

기존 `goCreateDeposit()`은 이미 deposit-sessions/new으로 이동하므로 이름만 변경:

```ts
// 기존 goCreateDeposit → 입금 세션 생성으로 분리
function goCreateDepositSession() {
  router.push({ path: '/partner/deposit-sessions/new', query: { partnerUserId: userId } })
}
```

### B-5. 입금 지갑 생성 모달 (신규)

HD 입금 지갑 생성 모달. 네트워크 선택 → API 호출:

```ts
// 상태
const showDepositWalletModal = ref(false)
const dwForm = ref({ networkId: null as number | null })
const dwSaving = ref(false)
const dwError = ref('')
const dwSuccess = ref('')
const dwResult = ref<{ address: string; networkCode: string } | null>(null)

function openDepositWalletModal() {
  dwForm.value = { networkId: null }
  dwError.value = ''; dwSuccess.value = ''; dwResult.value = null
  showDepositWalletModal.value = true
  if (!networks.value.length) fetchNetworks()
}

async function submitDepositWallet() {
  if (!dwForm.value.networkId) { dwError.value = '네트워크를 선택하세요.'; return }
  dwSaving.value = true; dwError.value = ''
  try {
    const result = await depositService.createDepositAddress({
      partnerUserId: userId,
      networkId: dwForm.value.networkId,
    })
    dwResult.value = result
    dwSuccess.value = '입금 지갑이 생성되었습니다.'
  } catch (e) {
    dwError.value = (e as Error).message
  } finally {
    dwSaving.value = false
  }
}
```

**모달 UI**:
```html
<SlideDrawer :open="showDepositWalletModal" title="입금 지갑 생성" @close="showDepositWalletModal = false">
  <div class="space-y-4">
    <div>
      <label class="mb-1 block text-sm font-medium">사용자 ID</label>
      <Input :model-value="userId" disabled class="bg-muted" />
    </div>
    <div>
      <label class="mb-1 block text-sm font-medium">네트워크 *</label>
      <select v-model="dwForm.networkId"
              class="h-9 w-full rounded-md border border-input bg-background px-3 text-sm">
        <option :value="null">네트워크 선택</option>
        <option v-for="n in networks" :key="n.id" :value="n.id">{{ n.name }}</option>
      </select>
    </div>
    <!-- 결과 표시 -->
    <div v-if="dwResult" class="rounded-md bg-green-50 dark:bg-green-950 border border-green-200 dark:border-green-800 p-3">
      <p class="text-xs font-medium text-green-600 dark:text-green-400 mb-1">생성된 주소</p>
      <div class="flex items-center gap-2">
        <code class="text-sm break-all">{{ dwResult.address }}</code>
        <Button size="sm" variant="ghost" @click="copyToClipboard(dwResult!.address)">복사</Button>
      </div>
    </div>
    <p v-if="dwError" class="text-sm text-red-600">{{ dwError }}</p>
    <p v-if="dwSuccess && !dwResult" class="text-sm text-green-600">{{ dwSuccess }}</p>
    <Button v-if="!dwResult" :disabled="dwSaving" class="w-full" @click="submitDepositWallet">
      {{ dwSaving ? '생성 중...' : '생성' }}
    </Button>
  </div>
</SlideDrawer>
```

> ⚠️ `depositService.createDepositAddress()` API가 구현되어 있는지 확인 필요. 없으면 `deposit.service.ts`에 추가:
> ```ts
> createDepositAddress(data: { partnerUserId: string; networkId: number }) {
>   return api.post('/api/partner/deposit-addresses', data)
> }
> ```

### B-6. 사용자 출금 모달 — OTP 인증 필수

> **⚠️ 출금 API는 OTP 인증이 필요함. `useOtpVerification` + `OtpVerificationModal` 필수.**

기존 `WithdrawalNewView.vue`와 동일한 OTP 흐름을 적용해야 함:
1. `withOtp()`로 API 호출을 래핑 → OTP 없이 먼저 시도
2. 서버가 `OTP_REQUIRED` 에러 반환 → OTP 모달 자동 표시
3. 사용자가 OTP 입력 → 재시도

**필요한 import 추가**:
```ts
import { api, otpHeaders } from '@/api/client'
import { useOtpVerification } from '@/composables/useOtpVerification'
import OtpVerificationModal from '@/components/common/OtpVerificationModal.vue'

// setup 내에서:
const { showOtpModal, otpError, withOtp, onOtpSubmit, onOtpCancel } = useOtpVerification()
```

**submitWithdrawal() 수정 — `withOtp()` 래핑**:

```ts
async function submitWithdrawal() {
  if (!wdForm.value.networkId || !wdForm.value.currencyId || !wdForm.value.amount) {
    wdError.value = '필수 항목을 모두 입력하세요.'; return
  }
  if (!wdForm.value.toAddress) {
    wdError.value = '수신 주소를 입력하세요.'; return
  }
  wdSaving.value = true; wdError.value = ''
  try {
    await withOtp((otpCode?: string) =>
      api.post('/api/partner/withdrawals', {
        partnerUserId: userId,
        networkId: wdForm.value.networkId,
        currencyId: wdForm.value.currencyId,
        toAddress: wdForm.value.toAddress,
        amount: Number(wdForm.value.amount),
      }, { headers: otpHeaders(otpCode) })
    )
    wdSuccess.value = '출금 요청이 접수되었습니다.'
    setTimeout(() => { showWithdrawalModal.value = false; fetchWithdrawals() }, 1500)
  } catch (e) {
    if ((e as Error).message !== 'OTP 인증 취소') wdError.value = (e as Error).message
  } finally {
    wdSaving.value = false
  }
}
```

**템플릿에 OTP 모달 추가** (닫는 `</div>` 바로 위):
```html
<OtpVerificationModal :open="showOtpModal" :error="otpError"
    @submit="onOtpSubmit" @cancel="onOtpCancel" />
```

핵심 차이: `withdrawalService.createWithdrawal()` 대신 `api.post()`를 직접 사용하면서 `otpHeaders(otpCode)`를 헤더에 전달해야 함.

---

*참고: 아래는 기존 상태 변수 (이미 구현됨)*

```ts
// 상태
const showWithdrawalModal = ref(false)
const wdForm = ref({
  networkId: null as number | null,
  currencyId: null as number | null,
  toAddress: '',
})
const wdSaving = ref(false)
const wdError = ref('')
const wdSuccess = ref('')

function openWithdrawalModal() {
  wdForm.value = { networkId: null, currencyId: null, toAddress: '', whitelistId: null, amount: '' }
  wdError.value = ''; wdSuccess.value = ''
  showWithdrawalModal.value = true
  if (!networks.value.length) fetchNetworks()
}

async function submitWithdrawal() {
  if (!wdForm.value.networkId || !wdForm.value.currencyId || !wdForm.value.amount) {
    wdError.value = '필수 항목을 모두 입력하세요.'; return
  }
  if (!wdForm.value.toAddress && !wdForm.value.whitelistId) {
    wdError.value = '수신 주소 또는 화이트리스트를 선택하세요.'; return
  }
  wdSaving.value = true; wdError.value = ''
  try {
    await withdrawalService.createWithdrawal({
      partnerUserId: userId,
      networkId: wdForm.value.networkId,
      currencyId: wdForm.value.currencyId,
      toAddress: wdForm.value.toAddress || undefined,
      whitelistId: wdForm.value.whitelistId ?? undefined,
      amount: Number(wdForm.value.amount),
    })
    wdSuccess.value = '출금 요청이 접수되었습니다.'
    setTimeout(() => { showWithdrawalModal.value = false; fetchWithdrawals() }, 1500)
  } catch (e) {
    wdError.value = (e as Error).message
  } finally {
    wdSaving.value = false
  }
}
```

**모달 UI**:
```html
<SlideDrawer :open="showWithdrawalModal" title="사용자 출금 요청" @close="showWithdrawalModal = false">
  <div class="space-y-4">
    <div>
      <label class="mb-1 block text-sm font-medium">사용자 ID</label>
      <Input :model-value="userId" disabled class="bg-muted" />
    </div>
    <div>
      <label class="mb-1 block text-sm font-medium">네트워크 *</label>
      <select v-model="wdForm.networkId"
              class="h-9 w-full rounded-md border border-input bg-background px-3 text-sm"
              @change="wdForm.currencyId = null; if(wdForm.networkId) fetchCurrencies(wdForm.networkId)">
        <option :value="null">네트워크 선택</option>
        <option v-for="n in networks" :key="n.id" :value="n.id">{{ n.name }}</option>
      </select>
    </div>
    <div v-if="wdForm.networkId">
      <label class="mb-1 block text-sm font-medium">통화 *</label>
      <select v-model="wdForm.currencyId"
              class="h-9 w-full rounded-md border border-input bg-background px-3 text-sm">
        <option :value="null">통화 선택</option>
        <option v-for="c in currencies" :key="c.id" :value="c.id">{{ c.symbol }}</option>
      </select>
    </div>
    <div>
      <label class="mb-1 block text-sm font-medium">수신 주소 *</label>
      <Input v-model="wdForm.toAddress" placeholder="출금 수신 주소" />
    </div>
    <CryptoAmountInput v-model="wdForm.amount" label="출금 수량" :required="true" :exchange-rate="exchangeRate" />
    <p v-if="wdError" class="text-sm text-red-600">{{ wdError }}</p>
    <p v-if="wdSuccess" class="text-sm text-green-600">{{ wdSuccess }}</p>
    <Button :disabled="wdSaving" class="w-full" @click="submitWithdrawal">
      {{ wdSaving ? '처리 중...' : '출금 요청' }}
    </Button>
  </div>
</SlideDrawer>
```

> ⚠️ `withdrawalService` import 필요:
> ```ts
> import { withdrawalService } from '@/api/services/withdrawal.service'
> ```

---

## Part C. 결제 링크 생성 — 결과에 링크 URL + 복사 기능

현재 `submitPL()` 성공 시 "결제 링크가 생성되었습니다." 메시지만 표시.
생성된 링크 URL을 보여주고 복사 기능을 제공해야 함.

### C-1. 상태 변수 추가

```ts
const plResult = ref<{ linkCode: string; url: string } | null>(null)
```

### C-2. submitPL() 수정

```ts
async function submitPL() {
  if (!plForm.value.amount) { plError.value = '금액을 입력하세요.'; return }
  plSaving.value = true; plError.value = ''; plResult.value = null
  try {
    const result = await depositService.createPaymentLink({
      title: plForm.value.title || `${userId} 결제`,
      partnerUserId: userId,
      networkId: plForm.value.networkId ?? undefined,
      currencyId: plForm.value.currencyId ?? undefined,
      amount: Number(plForm.value.amount),
    })
    // result에 linkCode가 포함되어 있다고 가정
    const linkCode = result.linkCode ?? result.link_code
    plResult.value = {
      linkCode,
      url: `${window.location.origin}/pay/${linkCode}`,
    }
    plSuccess.value = '결제 링크가 생성되었습니다.'
  } catch (e) { plError.value = (e as Error).message } finally { plSaving.value = false }
}
```

### C-3. 모달에 결과 영역 추가

기존 결제 링크 모달(`<SlideDrawer :open="showPLModal">`) 내부, 성공 메시지 아래에:

```html
<!-- 기존 성공 메시지 대신 아래로 교체 -->
<div v-if="plResult" class="rounded-md bg-green-50 dark:bg-green-950 border border-green-200 dark:border-green-800 p-3 space-y-2">
  <p class="text-xs font-medium text-green-600 dark:text-green-400">결제 링크가 생성되었습니다</p>
  <div class="flex items-center gap-2">
    <Input :model-value="plResult.url" readonly class="bg-white dark:bg-gray-900 text-xs font-mono flex-1" />
    <Button size="sm" variant="outline" @click="copyToClipboard(plResult!.url)">복사</Button>
  </div>
  <p class="text-xs text-muted-foreground">링크 코드: {{ plResult.linkCode }}</p>
</div>
```

### C-4. 클립보드 복사 유틸

```ts
async function copyToClipboard(text: string) {
  try {
    await navigator.clipboard.writeText(text)
    // 선택적: 토스트 알림 "복사되었습니다"
  } catch {
    // fallback
    const el = document.createElement('textarea')
    el.value = text
    document.body.appendChild(el)
    el.select()
    document.execCommand('copy')
    document.body.removeChild(el)
  }
}
```

> 이미 프로젝트에 `useClipboard` 같은 composable이 있으면 그걸 사용.

---

## Part D. UserDetailView 탭 내 컬럼도 통일 (Guide #74 연동)

Guide #74의 "금액" → "수량" + 통화 컬럼 삭제 패턴을 UserDetailView의 탭 내 테이블에도 적용:

```ts
// 변경 전
const depositColumns: Column<R>[] = [
  { key: 'depositCode', label: '코드', class: 'font-mono text-xs' },
  { key: 'currencyCode', label: '통화' },         // 삭제
  { key: 'amount', label: '금액', class: 'text-right' },
  { key: 'status', label: '상태' },
  { key: 'txHash', label: 'TX Hash' },
  { key: 'createdAt', label: '날짜' },
]

// 변경 후
const depositColumns: Column<R>[] = [
  { key: 'depositCode', label: '코드', class: 'font-mono text-xs' },
  { key: 'networkCode', label: '네트워크' },       // 추가 (선택)
  { key: 'amount', label: '수량', class: 'text-right' },  // '금액' → '수량'
  { key: 'status', label: '상태' },
  { key: 'txHash', label: 'TX Hash' },
  { key: 'createdAt', label: '날짜' },
]

// 출금도 동일
const withdrawalColumns: Column<R>[] = [
  { key: 'withdrawalCode', label: '코드', class: 'font-mono text-xs' },
  { key: 'networkCode', label: '네트워크' },       // 추가 (선택)
  { key: 'amount', label: '수량', class: 'text-right' },
  { key: 'toAddress', label: '수신 주소' },
  { key: 'status', label: '상태' },
  { key: 'createdAt', label: '날짜' },
]
```

> `#amount` 슬롯은 이미 `:currency="row.currencyCode"` 포함이므로 심볼이 자동 표시됨.

---

## 체크리스트

| # | 항목 | 파일 |
|---|------|------|
| A-1 | AximPaymentsView "결제 요청" 버튼 삭제 | AximPaymentsView.vue |
| A-2 | AximPaymentsView 컬럼 개선 (통화 삭제, 수량, 네트워크 추가) | AximPaymentsView.vue |
| A-3 | 백엔드 Axim 목록 쿼리에 networkCode JOIN 확인 | PartnerDepositMapper 또는 해당 Mapper |
| B-2 | Axim 버튼 조건부 활성화 (hasAximConnection) | UserDetailView.vue |
| B-3 | 버튼 5개 정리 (입금 세션 생성 추가) | UserDetailView.vue |
| B-4 | goCreateDepositSession() 함수 추가 | UserDetailView.vue |
| B-5 | 입금 지갑 생성 모달 (신규) | UserDetailView.vue |
| B-6 | 사용자 출금 모달 (신규) | UserDetailView.vue |
| C-1~4 | 결제 링크 결과 URL + 복사 기능 | UserDetailView.vue |
| D | 탭 내 테이블 컬럼 통화 삭제 + 수량 변경 | UserDetailView.vue |
| **E-1** | **CreateDepositAddressRequest DTO 추가** | **partner-api dto/request/** |
| **E-2** | **PartnerDepositService.createDepositAddress() 추가** | **partner-api service/** |
| **E-3** | **POST /deposit-addresses 엔드포인트 추가** | **PartnerDepositController.java** |

## 구현 순서

1. **E-1~E-3** — **백엔드 POST /deposit-addresses 추가 (현재 에러 원인)**
2. **A-1, A-2** — AximPaymentsView 정리 (가장 간단)
3. **A-3** — 백엔드 networkCode JOIN 확인/추가
4. **B-2, B-3** — 버튼 조건 + 레이아웃
5. **B-4** — 입금 세션 이동 함수
6. **C-1~4** — 결제 링크 결과 개선
7. **B-5** — 입금 지갑 생성 모달 (프론트 이미 구현됨)
8. **B-6** — 사용자 출금 모달 (프론트 이미 구현됨)
9. **D** — 탭 내 컬럼 통일 (프론트 이미 구현됨)

## Part E. 백엔드 — POST /deposit-addresses 엔드포인트 추가 (필수)

> **⚠️ 현재 이 엔드포인트가 없어서 "지원하지 않는 HTTP 메소드입니다" 에러 발생 중**

`PartnerDepositController`에 GET(목록)과 GET(상세)만 있고, **POST(생성)가 없음**.
core 모듈의 `WalletService.createHotWallet()`은 구현되어 있으므로 연결만 하면 됨.

### E-1. DTO 추가

**파일**: `partner-api/.../dto/request/CreateDepositAddressRequest.java`

```java
package com.cryptoments.partnerapi.dto.request;

import jakarta.validation.constraints.NotNull;
import lombok.*;

/**
 * 입금 지갑(HOT) 생성 요청 DTO.
 */
@Getter @Setter @Builder
@NoArgsConstructor @AllArgsConstructor
public class CreateDepositAddressRequest {

    /** 파트너 사용자 ID */
    @NotNull
    private String partnerUserId;

    /** 네트워크 ID */
    @NotNull
    private Long networkId;
}
```

### E-2. Service 메서드 추가

**파일**: `partner-api/.../service/PartnerDepositService.java`

```java
/**
 * 사용자에게 HD 입금 지갑을 생성/할당한다.
 * 이미 존재하면 기존 주소를 반환 (멱등).
 */
public WalletAddress createDepositAddress(Long partnerId, CreateDepositAddressRequest request) {
    return walletService.createHotWallet(partnerId, request.getNetworkId(), request.getPartnerUserId());
}
```

> `WalletService`가 이미 inject 되어 있는지 확인. 없으면 추가:
> ```java
> private final WalletService walletService;
> ```

### E-3. Controller 엔드포인트 추가

**파일**: `partner-api/.../controller/PartnerDepositController.java`

기존 `getDepositAddresses()` 메서드 위에 추가:

```java
/**
 * 사용자에게 HD 입금 지갑(HOT)을 생성한다.
 * 이미 같은 네트워크에 지갑이 있으면 기존 주소를 반환한다.
 *
 * @param request 생성 요청 (partnerUserId, networkId)
 * @return 생성된 지갑 주소
 * @response 201 생성 성공
 * @group 입금 관리
 * @auth true
 */
@PostMapping(name = "입금 지갑 생성", value = "/deposit-addresses")
@ResponseStatus(HttpStatus.CREATED)
public WalletAddress createDepositAddress(@Valid @RequestBody CreateDepositAddressRequest request) {
    Long partnerId = getSession().getPartnerId();
    return partnerDepositService.createDepositAddress(partnerId, request);
}
```

### E-4. 프론트엔드 API 확인

`deposit.service.ts`에 이미 추가되어 있음:
```ts
createDepositAddress: (data: { partnerUserId: string; networkId: number }) =>
    api.post<Record<string, unknown>>('/api/partner/deposit-addresses', data),
```

---

## 마이그레이션 참고

v1→v2 axim_payments 데이터 172건이 마이그레이션 완료됨:
- CONFIRMED: 50건 (v1 COMPLETED → v2 CONFIRMED)
- FAILED: 51건
- EXPIRED: 70건
- PENDING: 1건

마이그레이션 SQL: `v2-docs/sql/AXIM_PAYMENTS_MIGRATION.sql`

v1→v2 status 매핑:
| v1 | v2 |
|----|-----|
| COMPLETED | CONFIRMED |
| CONFIRMING | PENDING |
| PENDING | PENDING |
| FAILED | FAILED |
| EXPIRED | EXPIRED |
