POST /ai/admin/campaigns/{campaignNo}/terminate
캠페인 강제 종료 (관리자 전용)
캠페인 강제 종료
진행 중인 캠페인을 관리자 권한으로 강제 종료합니다. 계약 조항에 따라 최초 입금액의 20%를 착수금으로 귀속시키고, 잔여 금액을 기업 글로벌 크레딧으로 환급합니다.
본 API는 관리자 전용입니다. 진행 중인 신청/계약/콘텐츠 데이터는 그대로 유지되며, 캠페인 단계만 CAMPAIGN_COMPLETED로 전환됩니다. 별도의 종료 사유 필드는 기록되지 않습니다.
HTTP 요청
POST /ai/admin/campaigns/{campaignNo}/terminate?waiveForfeit=false
Authorization: Bearer {access_token}요청 본문은 없습니다. 처리자(adminId)는 JWT의 인증 컨텍스트에서 자동으로 추출됩니다.
Path / Query Parameters
| 파라미터 | 위치 | 타입 | 필수 | 설명 |
|---|---|---|---|---|
campaignNo | path | Integer | 예 | 캠페인 번호 (SaaS 이후 캠페인만 지원) |
waiveForfeit | query | boolean | 아니오 | true 면 착수금 귀속 없이 전액을 기업 크레딧으로 반환 (기본 false). 결제 자체를 취소해 줘야 해서 카드 환불(/ai/admin/finance/card-refund)로 이어갈 때 사용 |
처리 흐름
- 유효성 검증
- 캠페인 존재 확인
- 레거시 캠페인(SaaS 이전) 거부
- 이미
CAMPAIGN_COMPLETED상태인 캠페인 거부
- 착수금 귀속 계산 —
최초 CAMPAIGN_DEPOSIT 입금액 × 20% - 환급금 계산 —
max(0, 총 입금액 − 착수금) - 회계 기록
CAMPAIGN_FORFEIT트랜잭션 (회계 마커, 잔액 변동 없음)- 최초 유효
CAMPAIGN_DEPOSIT부터 반환액을 반영해 각 행을 0원까지 조정하고, 남은 반환액은 다음 입금에 반영합니다.CAMPAIGN_REFUND행은 생성하지 않습니다. - 최초 변경 행부터 이후
balanceAfter를 재계산하고 마지막 잔액으로 기업 크레딧을 설정합니다. 잔액 합산 규칙은 어드민 원장 재계산과 같아BUDGET_ADJUST만 제외합니다. - 재계산 구간은 최초 변경 행 이후뿐입니다. 그 앞 구간의 기존 잔액 오차는 건드리지 않고, 구간 안의 오차는 어드민 전체 재계산과 같은 방식으로 함께 교정됩니다. 이 경우 기업 크레딧 변동이 환급액보다 크거나 작을 수 있으며, 그 차이는 서버 로그에 교정분으로 남습니다.
- 정산 전 예산과 반환액은 종료 스냅샷에, 행 변경 전후는 감사 로그에 보존합니다.
- LOCKED 예산 전부 해제 — 정산 전 LOCKED 금액·개수를 스냅샷에 보존한 뒤 LOCKED 행을 UNLOCKED로 변경합니다.
- 상태 전환 —
campaign_sub_step = CAMPAIGN_COMPLETED,campaign_completed_at = 현재 시각(최초 전환 시에만) - 슬랙 알림 발송 — DB 커밋 이후 결제-정산 알리미 채널(
slack.payment-alert.channel-id)로 전송
응답
성공 응답 (200 OK)
{
"status": 200,
"code": null,
"message": "캠페인이 강제 종료되었습니다.",
"data": {
"collabNo": 1234,
"initialDeposit": 5000000,
"totalBudget": 5500000,
"forfeitAmount": 1000000,
"refundAmount": 4500000,
"unlockedBudgetCount": 3,
"remainCreditAfter": 4800000
}
}응답 필드
| 필드 | 타입 | 설명 |
|---|---|---|
collabNo | Integer | 캠페인 번호 |
initialDeposit | Integer | 정산 전 최초 유효 입금액 (정산 스냅샷 보존) |
totalBudget | Integer | 정산 전 총 입금액 (추가입금 포함, 정산 스냅샷 보존) |
forfeitAmount | Integer | 착수금 귀속 금액 (initialDeposit × 20%, waiveForfeit=true 면 0) |
refundAmount | Integer | 기업 크레딧으로 환급된 금액 |
unlockedBudgetCount | Integer | 해제된 LOCKED 예산 행 수 |
remainCreditAfter | Integer | 환급 후 기업 잔여 크레딧 |
에러 응답
| 상태 코드 | code | 사례 |
|---|---|---|
404 | INVALID_DATA | SaaS 이전 캠페인 (기존 오류 응답 유지) |
404 | INVALID_DATA | 이미 완료·정산된 캠페인 (추가 반환 없음) |
404 | INVALID_DATA | 기업 정보 누락 등 |
404 | INVALID_COLLAB | 캠페인 없음 |
409 | SETTLEMENT_LEDGER_MISMATCH | 정산 스냅샷·정산 표시가 서로 어긋나거나 원장 행에 필수값 누락 |
409 | SETTLEMENT_DEPOSIT_INVALID | 입금·취소 내역 또는 예산 데이터 불일치 |
422 | SETTLEMENT_AMOUNT_OVERFLOW | 정산 금액이 저장 범위 초과 |
401 | — | 인증 실패 |
정책 배경
기업과의 계약서 본문에는 다음 조항이 포함됩니다 (BusinessContractPdfService 참고):
영상 제작 캠페인 신청 시, 총예산의 20%를 착수금으로 선차감합니다. 캠페인이 정상 진행될 경우 해당 금액은 크레딧 사용분으로 자동 전환되나, "갑"의 사정으로 캠페인이 미진행 또는 최초 캠페인 설정 예산의 80% 이하가 사용될 경우 해당 착수금은 "을"의 행정 및 준비 비용으로 귀속되어 환불되지 않습니다.
본 API는 위 조항을 시스템화한 것으로, 모집 시작 이전의 단순 결제 취소(POST /campaign/{collabNo}/cancel, 100% 환불)와는 구분됩니다.
waiveForfeit=true 는 위 조항을 적용하지 않고 전액을 반환하는 관리자 재량 경로입니다. 정산 스냅샷의 settlementType 은 FORCED 대신 FORCED_FULL 로 남고, CAMPAIGN_FORFEIT 마커 행은 기록하지 않습니다. 반환된 크레딧을 카드까지 돌려주려면 이어서 카드 결제 취소를 호출합니다.
진행 중인 인플루언서 대금 정산은 본 흐름의 범위 밖이며 별도 운영 절차로 처리합니다.
API 테스트
최초 100만 원과 추가 50만 원 중 130만 원을 반환하면 두 예산 행의 금액은 각각 0원과 -20만 원으로 남습니다. 종료 재호출은 추가 반환하지 않으며, 기존 환급 내역은 소급 변환하지 않습니다.