using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; namespace Domain.Entities.Developers; /// /// API 결제 취소 감사 기록. ApiPurchase 1:1 (PurchaseID UNIQUE — 중복 취소 방지). /// WasConfirmed=false → 보류 중 취소 (지갑 무관, Recovered/Shortfall 0). /// WasConfirmed=true → 확정 후 취소 (StoreRevenue 회수, 잔액 부족분은 ShortfallAmount 에 기록). /// public class ApiPurchaseCancel { [ForeignKey(nameof(PurchaseID))] public virtual ApiPurchase? Purchase { get; private set; } [Key] public int ID { get; private set; } public int PurchaseID { get; private set; } public int ApplicationID { get; private set; } public int ChannelID { get; private set; } public int CreditedMemberID { get; private set; } /// 확정(지갑 적립) 후의 취소였는지 public bool WasConfirmed { get; private set; } /// 실제 회수액 (KRW). 보류 중 취소면 0. public int RecoveredAmount { get; private set; } /// 잔액 부족으로 회수하지 못한 금액 (KRW). 0 이면 완전 회수. public int ShortfallAmount { get; private set; } public DateTime CreatedAt { get; private set; } = DateTime.UtcNow; private ApiPurchaseCancel() { } public static ApiPurchaseCancel Create(ApiPurchase purchase, bool wasConfirmed, int recoveredAmount, int shortfallAmount) { if (purchase.ID <= 0) { throw new ArgumentException("Purchase must be persisted.", nameof(purchase)); } if (recoveredAmount < 0) { throw new ArgumentOutOfRangeException(nameof(recoveredAmount)); } if (shortfallAmount < 0) { throw new ArgumentOutOfRangeException(nameof(shortfallAmount)); } return new ApiPurchaseCancel { PurchaseID = purchase.ID, ApplicationID = purchase.ApplicationID, ChannelID = purchase.ChannelID, CreditedMemberID = purchase.CreditedMemberID, WasConfirmed = wasConfirmed, RecoveredAmount = recoveredAmount, ShortfallAmount = shortfallAmount }; } }