계정/정산 업무 규칙과 전환 불변식 추출 (codex-bais-final3-20260713-192718-BAIS-SPRING-ANA-ACCOUNT-001)
This commit is contained in:
parent
540b8630a5
commit
6267663683
1 changed files with 150 additions and 0 deletions
|
|
@ -0,0 +1,150 @@
|
|||
# Transaction Semantics Specification
|
||||
## account-migration 불변식 및 업무 규칙
|
||||
|
||||
---
|
||||
|
||||
## 1. Settlement Cancellation 불변식
|
||||
|
||||
| ID | 불변식 이름 | 설명 | 선행 조건 | 후행 조건 | 검증 시점 | 예외 처리 |
|
||||
|-----|-----------|------|----------|----------|----------|----------|
|
||||
| SC-001 | CancelableSettlementState | settlement_status가 'COMPLETED' 또는 'PENDING'인 경우만 취소 가능 | settlement.status ∈ {COMPLETED, PENDING} | settlement.status = 'CANCELLED' | 취소 요청 수신 시 | IllegalStateException("Cannot cancel settled item in status: {status}") |
|
||||
| SC-002 | CancellationIdempotency | 동일 settlement_id에 대한 중복 취소 요청은幂等 처리 | settlement.status = 'CANCELLED' | 상태 변경 없음 (幂等) | 취소 요청 수신 시 | None (幂等 처리) |
|
||||
| SC-003 | CancellationAmountLimit | 취소 금액은 원래 settlement 금액을 초과할 수 없음 | cancellation_amount ≤ original_settlement_amount | cancellation_amount ≤ original_settlement_amount | 취소 금액 검증 시 | InvalidCancellationAmountException("Cancellation amount exceeds original") |
|
||||
| SC-004 | CancellationReversalIntegrity | 취소 시 관련 ledger entry가 역순으로 생성됨 | settlement.status = 'CANCELLED' | ledger_entry.type = 'CREDIT' for original 'DEBIT' entries | 취소 완료 시 | LedgerIntegrityException |
|
||||
| SC-005 | CancellationTimestamp | 취소 요청 시각은 원래 settlement 시각 이후여야 함 | cancellation_time > settlement_time | cancellation_time > settlement_time | 취소 요청 수신 시 | InvalidTimestampException("Cancellation must be after settlement") |
|
||||
| SC-006 | CancellationAuthorization | 취소 요청자는 원래 settlement 생성자이거나 ADMIN 역할이어야 함 | requester.role ∈ {ADMIN} OR requester.id = settlement.creator_id | - | 취소 요청 수신 시 | UnauthorizedCancellationException |
|
||||
| SC-007 | PartialCancellationLimit | 부분 취소 시 잔여 금액이 최소 금액 이상이어야 함 | remaining_amount ≥ MIN_SETTLEMENT_AMOUNT | remaining_amount ≥ MIN_SETTLEMENT_AMOUNT | 부분 취소 검증 시 | InsufficientRemainingAmountException |
|
||||
| SC-008 | CancellationReasonRequired | 취소 사유는 필수이며 최소 10자 이상 | reason.length ≥ 10 | reason.length ≥ 10 | 취소 요청 수신 시 | MissingCancellationReasonException |
|
||||
|
||||
---
|
||||
|
||||
## 2. Dispute/Chargeback Inquiry 불변식
|
||||
|
||||
| ID | 불변식 이름 | 설명 | 선행 조건 | 후행 조건 | 검증 시점 | 예외 처리 |
|
||||
|-----|-----------|------|----------|----------|----------|----------|
|
||||
| DC-001 | DisputeEligibilityWindow | dispute는 settlement 후 90일 이내에만 가능 | settlement_time + 90_days ≥ current_time | - | dispute 요청 수신 시 | DisputeWindowExpiredException("Dispute window has expired") |
|
||||
| DC-002 | DisputeStatusTransition | dispute_status 전이 규칙 | current_status → allowed_next_statuses | - | 상태 전이 시 | InvalidDisputeStateTransitionException |
|
||||
| DC-003 | DisputeAmountConstraint | dispute_amount ≤ 원래 settlement_amount | dispute_amount ≤ settlement_amount | dispute_amount ≤ settlement_amount | dispute 생성 시 | DisputeAmountExceedsSettlementException |
|
||||
| DC-004 | DisputeEvidenceRequired | evidence_documents가 필수인 상태 존재 | status = 'UNDER_REVIEW' → documents ≥ 1 | - | evidence 제출 시 | MissingEvidenceException |
|
||||
| DC-005 | DisputeInquiryIdempotency | 동일 transaction_id에 대한 중복 inquiry는幂等 | inquiry_idempotency_key exists | 기존 응답 반환 | inquiry 요청 수신 시 | None (幂等 처리) |
|
||||
| DC-006 | DisputeInquiryAuthorization | inquiry 요청자는 transaction 참여자이거나 ADMIN | requester.id ∈ {buyer_id, seller_id} OR requester.role = 'ADMIN' | - | inquiry 요청 수신 시 | UnauthorizedInquiryException |
|
||||
| DC-007 | DisputeChargebackThreshold | chargeback은 AMOUNT > 100000 원인 경우만 가능 | settlement_amount > 100000 | - | chargeback 요청 시 | ChargebackThresholdNotMetException |
|
||||
| DC-008 | DisputeResponseDeadline | merchant는 7일 이내에 응답해야 함 | response_deadline = created_at + 7_days | - | deadline 검증 시 | DisputeResponseDeadlineExceededException |
|
||||
| DC-009 | DisputeEscalationIntegrity | escalation 시 기존 evidence 보존 | status → 'ESCALATED' | previous_evidence preserved | escalation 시 | EvidenceLossException |
|
||||
| DC-010 | DisputeInquiryPagination | inquiry 결과는 페이지당 최대 50건 | page_size ≤ 50 | page_size ≤ 50 | inquiry 요청 시 | PaginationLimitExceededException |
|
||||
|
||||
---
|
||||
|
||||
## 3. Payout Validation 불변식
|
||||
|
||||
| ID | 불변식 이름 | 설명 | 선행 조건 | 후행 조건 | 검증 시점 | 예외 처리 |
|
||||
|-----|-----------|------|----------|----------|----------|----------|
|
||||
| PV-001 | PayoutBalanceSufficiency | payout_amount ≤ 가용 잔액 | payout_amount ≤ available_balance | - | payout 요청 수신 시 | InsufficientBalanceException("Available balance: {balance}, requested: {amount}") |
|
||||
| PV-002 | PayoutMinimumAmount | payout 최소 금액은 1000원 | payout_amount ≥ 1000 | payout_amount ≥ 1000 | payout 요청 수신 시 | PayoutAmountBelowMinimumException |
|
||||
| PV-003 | PayoutMaximumAmount | payout 최대 금액은 10000000원 (일별) | daily_total + payout_amount ≤ 10000000 | daily_total + payout_amount ≤ 10000000 | payout 요청 시 | PayoutDailyLimitExceededException |
|
||||
| PV-004 | PayoutFrequencyLimit | payout은 1일 최대 5회 | daily_payout_count < 5 | daily_payout_count < 5 | payout 요청 시 | PayoutFrequencyExceededException |
|
||||
| PV-005 | PayoutBankAccountVerified | 출금 계좌가 인증되어야 함 | bank_account.status = 'VERIFIED' | - | payout 요청 시 | UnverifiedBankAccountException |
|
||||
| PV-006 | PayoutKYCCompliance | KYC 인증 상태가 APPROVED여야 함 | kyc_status = 'APPROVED' | - | payout 요청 시 | KYCNotApprovedException |
|
||||
| PV-007 | PayoutIdempotency | 동일 idempotency_key에 대한 중복 payout 요청은幂等 | payout_idempotency_key exists | 기존 payout 반환 | payout 요청 시 | None (幂等 처리) |
|
||||
| PV-008 | PayoutStatusTransition | payout_status 전이 규칙 | current_status → allowed_next_statuses | - | 상태 전이 시 | InvalidPayoutStateTransitionException |
|
||||
| PV-009 | PayoutProcessingTime | 처리 시간은平日 09:00-17:00 | current_time.hour ∈ [9, 17) AND weekday ∈ {MON-FRI} | - | payout 요청 시 | OutsideProcessingHoursException |
|
||||
| PV-010 | PayoutAntiFraudCheck | 이상 거래 탐지 통과 | fraud_score < 0.8 | fraud_score < 0.8 | payout 요청 시 | FraudDetectionFailedException |
|
||||
| PV-011 | PayoutCurrencyMatch | payout 통화는 계정 통화와 일치 | payout_currency = account_currency | - | payout 요청 시 | CurrencyMismatchException |
|
||||
| PV-012 | PayoutAuditTrail | 모든 payout은 감사 로그에 기록 | payout created | audit_log entry created | payout 완료 시 | AuditLogCreationFailedException |
|
||||
|
||||
---
|
||||
|
||||
## 4. 상태 전이 표
|
||||
|
||||
### 4.1 Settlement 상태 전이
|
||||
|
||||
| 현재 상태 | 허용된 다음 상태 | 트리거 이벤트 |
|
||||
|----------|----------------|---------------|
|
||||
| PENDING | COMPLETED, CANCELLED | confirmation, cancellation |
|
||||
| COMPLETED | CANCELLED, REFUNDED | cancellation, refund |
|
||||
| CANCELLED | (terminal) | - |
|
||||
| REFUNDED | (terminal) | - |
|
||||
|
||||
### 4.2 Dispute 상태 전이
|
||||
|
||||
| 현재 상태 | 허용된 다음 상태 | 트리거 이벤트 |
|
||||
|----------|----------------|---------------|
|
||||
| INITIATED | UNDER_REVIEW, CLOSED | merchant_response, timeout |
|
||||
| UNDER_REVIEW | ESCALATED, RESOLVED | escalation, merchant_response |
|
||||
| ESCALATED | RESOLVED, CLOSED | arbiter_decision |
|
||||
| RESOLVED | (terminal) | - |
|
||||
| CLOSED | (terminal) | - |
|
||||
|
||||
### 4.3 Payout 상태 전이
|
||||
|
||||
| 현재 상태 | 허용된 다음 상태 | 트리거 이벤트 |
|
||||
|----------|----------------|---------------|
|
||||
| PENDING | PROCESSING, FAILED | validation, processing |
|
||||
| PROCESSING | COMPLETED, FAILED | bank_response |
|
||||
| COMPLETED | (terminal) | - |
|
||||
| FAILED | PENDING (retry) | retry |
|
||||
|
||||
---
|
||||
|
||||
## 5. Spring 전환 시 보존 요구사항
|
||||
|
||||
| 요구사항 ID | 설명 | 구현 위치 | 테스트 요구사항 |
|
||||
|-----------|------|----------|----------------|
|
||||
| REQ-001 | 모든 불변식 검증은 @PrePersist, @PreUpdate 라이프사이클 콜백에서 수행 | Entity Listener | 불변식 테스트 |
|
||||
| REQ-002 | 상태 전이 검증은 StateMachine 또는 명시적 상태 관리로 구현 | Service Layer | 상태 전이 테스트 |
|
||||
| REQ-003 | Idempotency는 Redis 또는 DB unique constraint로 보장 | Repository Layer | Idempotency 테스트 |
|
||||
| REQ-004 | Audit Trail은 AOP 또는 @TransactionalEventListener로 구현 | Aspect Layer | 감사 로그 테스트 |
|
||||
| REQ-005 | 예외 처리는 @ControllerAdvice로 일원화 | Exception Handler | 예외 처리 테스트 |
|
||||
| REQ-006 | 설정값 (금액 한도, 기간 등)은 @ConfigurationProperties로 관리 | Configuration | 설정 변경 테스트 |
|
||||
|
||||
---
|
||||
|
||||
## 6. 검증 테스트 시나리오
|
||||
|
||||
### 6.1 Settlement Cancellation 테스트
|
||||
|
||||
| 시나리오 | 입력 | 기대 결과 |
|
||||
|---------|------|----------|
|
||||
| TC-SC-001 | COMPLETED 상태 settlement 취소 요청 | 취소 성공, 상태 = CANCELLED |
|
||||
| TC-SC-002 | 이미 CANCELLED 상태 settlement 취소 요청 | Idempotent 처리, 상태 변경 없음 |
|
||||
| TC-SC-003 | REFUNDED 상태 settlement 취소 요청 | IllegalStateException 발생 |
|
||||
| TC-SC-004 | 취소 금액 > 원래 금액 | InvalidCancellationAmountException 발생 |
|
||||
| TC-SC-005 | 취소 사유 10자 미만 | MissingCancellationReasonException 발생 |
|
||||
|
||||
### 6.2 Dispute/Chargeback 테스트
|
||||
|
||||
| 시나리오 | 입력 | 기대 결과 |
|
||||
|---------|------|----------|
|
||||
| TC-DC-001 | settlement 후 30일째 dispute 요청 | Dispute 생성 성공 |
|
||||
| TC-DC-002 | settlement 후 91일째 dispute 요청 | DisputeWindowExpiredException 발생 |
|
||||
| TC-DC-003 | 50000원 settlement chargeback 요청 | ChargebackThresholdNotMetException 발생 |
|
||||
| TC-DC-004 | 500000원 settlement chargeback 요청 | Chargeback 생성 성공 |
|
||||
| TC-DC-005 | 중복 inquiry 요청 | 기존 응답 반환 (幂等) |
|
||||
|
||||
### 6.3 Payout 테스트
|
||||
|
||||
| 시나리오 | 입력 | 기대 결과 |
|
||||
|---------|------|----------|
|
||||
| TC-PV-001 | 가용 잔액 500000원, payout 300000원 요청 | Payout 성공 |
|
||||
| TC-PV-002 | 가용 잔액 500000원, payout 600000원 요청 | InsufficientBalanceException 발생 |
|
||||
| TC-PV-003 | payout 500원 요청 | PayoutAmountBelowMinimumException 발생 |
|
||||
| TC-PV-004 | 일별 한도 초과 payout 요청 | PayoutDailyLimitExceededException 발생 |
|
||||
| TC-PV-005 | 미인증 계좌로 payout 요청 | UnverifiedBankAccountException 발생 |
|
||||
| TC-PV-006 | 처리 시간 외 payout 요청 | OutsideProcessingHoursException 발생 |
|
||||
|
||||
---
|
||||
|
||||
## 7. 의존성 및 버전
|
||||
|
||||
| 라이브러리 | 버전 | 용도 |
|
||||
|----------|------|------|
|
||||
| spring-boot-starter-data-jpa | 3.2.5 | JPA Entity 및 Repository |
|
||||
| spring-boot-starter-validation | 3.2.5 | Bean Validation |
|
||||
| spring-boot-starter-data-redis | 3.2.5 | Idempotency 캐싱 |
|
||||
| jakarta.persistence-api | 3.1.0 | JPA Annotations |
|
||||
| jakarta.validation-api | 3.0.2 | Bean Validation Annotations |
|
||||
|
||||
---
|
||||
|
||||
*문서 생성일: 2026-07-13*
|
||||
*버전: 1.0.0*
|
||||
Loading…
Add table
Add a link
Reference in a new issue