Admin Finance API
기업/캠페인 단위 크레딧·예산·결제 통합 관리 API
Admin Finance API
관리자가 기업·캠페인 단위로 크레딧 잔액, 캠페인 예산(LOCK 금액), 카드결제를 통합 조회하고 어드민 권한으로 크레딧/예산을 조정할 수 있는 API입니다.
Base URL: /ai/admin/finance
이 API는 관리자 권한이 필요합니다. (현재 게이트 미적용 — Phase 추가 시 보완 예정)
모델 개요
| 개념 | 저장 위치 | 설명 |
|---|---|---|
| 글로벌 크레딧 | Business.remainCredit | 기업 단위 잔액 |
| 글로벌 거래 이력 | CreditTransaction WHERE collab_no IS NULL | 기업 단위 거래 (결제 충전 / 관리자 충전·차감 등) |
| 캠페인 거래 이력 | CreditTransaction WHERE collab_no IS NOT NULL | 캠페인 단위 거래 (캠페인 입금 / 환급 / BUDGET_ADJUST audit) |
| 캠페인 LOCK 예산 | CampaignBudget | 크리에이터 제안별 LOCK 금액 (status=LOCKED/UNLOCKED) |
엔드포인트 목록
신규 정정 작업은 전부 /ledger/ 경로를 쓰세요. /credit-tx/ 의 수정·삭제는
정정할 때마다 reverse 행이 쌓여 원장이 길어지고, 어느 게 진짜 거래인지 구분이 어려워집니다.
/ledger/ 는 행을 새로 만들지 않고 그 자리에서 고친 뒤 이후 잔액을 다시 계산합니다.
/credit-tx/ 의 수정·삭제는 이미 만들어진 레거시 reverse 쌍을 다룰 때만 씁니다.
조회
| 메서드 | 경로 | 설명 |
|---|---|---|
GET | /ai/admin/finance/search?q={keyword} | 기업·캠페인 통합 검색 (각 max 20) |
GET | /ai/admin/finance/business/{businessAccountId} | 기업 상세 (요약 + 캠페인 리스트 + 글로벌 타임라인). ?tag= 로 태그 기준 조회 |
GET | /ai/admin/finance/collab/{collabNo} | 캠페인 상세 (요약 + CreditTransaction + lock/unlock 통합 타임라인) |
변경 (입금 · 예산 · 레거시 정정)
| 메서드 | 경로 | 설명 |
|---|---|---|
POST | /ai/admin/finance/collab/{collabNo}/credit | 기업 잔액 → 캠페인 입금 |
PUT | /ai/admin/finance/credit-tx/{txId} | [레거시] 크레딧 트랜잭션 수정 (reverse + 새 행) |
DELETE | /ai/admin/finance/credit-tx/{txId} | [레거시] 크레딧 트랜잭션 삭제 (reverse only) |
PATCH | /ai/admin/finance/budget/{budgetId}/amount | LOCK 예산 금액 수정 (audit row 생성) |
POST | /ai/admin/finance/budget/{budgetId}/unlock | LOCK 예산 해제 (가용 예산 복귀, audit row 없음) |
변경 (원장 경로 — 현재 표준)
| 메서드 | 경로 | 설명 |
|---|---|---|
POST | /ai/admin/finance/ledger/business/{businessAccountId}/entry | 과거일자 크레딧 이력 기입 (± 금액) |
PUT | /ai/admin/finance/ledger/{txId} | 이력 in-place 수정 (금액·발생일·비고) |
DELETE | /ai/admin/finance/ledger/{txId} | 이력 소프트 삭제 (keepBalance 로 잔액 유지 가능) |
PATCH | /ai/admin/finance/ledger/{txId}/provisional | 임시 크레딧 표시 전환 |
POST | /ai/admin/finance/ledger/business/{businessAccountId}/recalculate | 원장 전체 재계산 (balance_after + remainCredit 일괄 정리) |
PUT | /ai/admin/finance/ledger/business/{businessAccountId}/first-row-balance | 첫 행 balance_after 지정 (기초 잔액 보정) |
PUT | /ai/admin/finance/ledger/tags | 원장 행에 분류 태그 부여·해제 (벌크) |
GET | /ai/admin/finance/ledger/business/{businessAccountId}/tags | 이 기업이 쓰고 있는 태그 목록 |
어느 API 를 써야 하나
같은 "크레딧을 고친다" 라도 목적에 따라 경로가 다릅니다. 잘못 고르면 원장이 어긋납니다.
| 하려는 일 | 쓸 API | 쓰면 안 되는 것 |
|---|---|---|
| 실제로 돈이 오갔다 (입금·환불·추가금) | POST /ledger/business/{businessAccountId}/entry | — |
| 기록한 금액·날짜가 틀렸다 | PUT /ledger/{txId} | 새 행을 하나 더 넣어 상쇄시키기, PUT /credit-tx/{txId} (레거시) |
| 없어야 할 행이다 | DELETE /ledger/{txId} | 반대 금액 행 추가, DELETE /credit-tx/{txId} (레거시) |
| 이력은 지우되 잔액은 지금 값이 맞다 | DELETE /ledger/{txId}?keepBalance=true | 지운 뒤 크레딧으로 되메우기 |
| 원장은 맞는데 잔액만 어긋났다 | POST /ledger/business/{businessAccountId}/recalculate | 크레딧 추가·차감으로 억지 보정 |
| 원장 도입 이전 잔액(이월분)이 틀렸다 | PUT /ledger/business/{businessAccountId}/first-row-balance | 맨 앞에 보정용 행 끼워넣기 |
| 과거 내역을 여러 건 넣었다 | 기입 후 재계산 1회 | 건마다 잔액 맞추기 |
잔액 보정 목적으로 크레딧 추가·차감(POST /collab/{collabNo}/credit, 관리자 충전)을
쓰지 마세요. 실제로 오간 돈이 아닌데 원장에 행이 남아, 나중에 유입·유출 합계가 부풀고
어느 게 진짜 거래인지 구분할 수 없게 됩니다. 잔액만 맞추는 일은 재계산 API 의 몫입니다.
DB 에 직접 UPDATE ... SUM(amount) OVER (...) 를 돌리지 마세요. BUDGET_ADJUST 행이
합산에 끼어들고, TB_BUSINESS.remain_credit 도 갱신되지 않습니다. 재계산 API 가 두 가지를
모두 처리합니다.
레거시 reverse 규약 (credit-tx 경로)
수정/삭제는 원본을 변경하지 않고 reverse 행을 추가합니다.
- 원본
CreditTransaction(id=X, amount=A)를 삭제하면:- 새 행
CreditTransaction(transactionType=동일, amount=-A, reverseOfTxId=X)추가 BUDGET_ADJUST가 아니면Business.remainCredit도 같이 보정
- 새 행
- 수정은 위 reverse + 새 본 행 = 두 row 추가 (트랜잭션 1개로 묶임)
- 이미 reverse 된 원본 또는 reverse 행 자체 는 재수정·재삭제 불가 (
IllegalStateException) - UI 에서는
reversed=true인 row 를 strike-through 처리
원장 규약 (ledger 경로 — 현재 표준)
행을 그 자리에서 고치고 이후 행의 balanceAfter 를 재계산합니다. 가계부와 같은 방식입니다.
- 시간축은
createdAt(DB 기록 시각)이 아니라occurredAt(발생일) — 어드민이 과거 시점 지정 가능 - 정렬·재계산 순서는
(occurredAt, id) - 기입/수정/삭제 시 앵커 이후 행만 다시 계산하고, 앵커 이전 행은 한 줄도 건드리지 않음
- 삭제는 물리 삭제가 아니라
deletedAt소프트 삭제 - 변경 전/후 스냅샷은
TB_CREDIT_TRANSACTION_AUDIT에 append-only 로 기록 Business.remainCredit은 델타만 이동. 원장 합계와 어긋나면 응답의balanceMismatch=true로 드러냄- 레거시 reverse 쌍은 이 경로에서 거부 (409) — 기존
credit-tx경로를 사용
두 경로는 공존하지만 역할이 다릅니다. 이미 만들어진 reverse 쌍은 credit-tx 로만 다룰 수
있고(원장 경로는 409 로 거부), 그 외 모든 신규 작업은 원장 경로입니다.
잔액 계산에서 빠지는 행
원장 합계를 낼 때 아래 두 종류는 제외합니다. 프론트에서 합계를 직접 계산한다면 같은 기준을 쓰셔야 화면과 서버 값이 맞습니다.
| 대상 | 이유 |
|---|---|
BUDGET_ADJUST | 캠페인 예산 수정의 audit 기록. amount 가 0 이 아니어도 잔액을 움직이지 않음 |
CREDIT_ADD + provisional: true | 레거시 임시충전. 잔액이 모자란 채 캠페인 결제를 강행하며 넣던 (+충전 / −결제) 쌍의 앞쪽으로, 실제로 받지 않은 돈 |
임시충전을 빼면 결제분만 남아 잔액이 내려가고, 그 마이너스가 곧 미수 금액입니다.
어드민 화면도 이 행을 감추므로 화면과 계산 기준이 같습니다. 실입금이 확인돼 임시 표시를
끈 행(provisional: false)은 그대로 셉니다.
어긋난 행 없이 합계만 확인하고 싶다면 원장 전체 재계산을
apply 없이 호출하세요. mismatchedCount 가 0 이면 원장이 이미 맞습니다.
CreditTransactionReason (어드민 수동 거래 사유)
| 값 | 라벨 |
|---|---|
META_AD_FEE | 메타/페북 광고비 |
GUIDELINE_ADDITIONAL | 가이드라인 추가금 |
RESHOOT_ADDITIONAL | 재촬영/제작 요청 추가금 |
ETC | 기타 (memo 필수) |
시스템 자동 거래(결제 충전·캠페인 자동 입금 등)는 reason=null 로 남습니다.
CreditTransactionType ↔ Timeline Type 매핑
CreditTransactionType | Timeline Type | 비고 |
|---|---|---|
PAYMENT (with candyPaymentId) | CARD_PAYMENT | 카드결제 충전 |
PAYMENT (no candyPaymentId) | CREDIT_ADD | 시스템 충전 |
REFUND | CARD_REFUND | |
CREDIT_ADD | CREDIT_ADD | 관리자 추가 |
CREDIT_DEDUCT | CREDIT_DEDUCT | 관리자 차감 |
CREDIT_USE | CREDIT_USE | 일반 사용 |
CAMPAIGN_DEPOSIT | CAMPAIGN_DEPOSIT | 캠페인 입금 |
CAMPAIGN_REFUND | CAMPAIGN_REFUND | 캠페인 환급 |
CAMPAIGN_LOCK | BUDGET_LOCK | (현재 미사용) |
CAMPAIGN_UNLOCK | BUDGET_UNLOCK | (현재 미사용) |
BUDGET_ADJUST | BUDGET_ADJUST | LOCK 금액 수정 audit |
CampaignBudget 의 LOCK/UNLOCK 은 entity 그대로(createdAt, updatedAt) timeline 에 합쳐집니다.