using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Domain.Entities.Members;
using Domain.Entities.Payments.ValueObject;
namespace Domain.Entities.Payments;
public class PaymentOrder
{
[ForeignKey(nameof(MemberID))]
public virtual Member? Member { get; private set; }
[Key]
public int ID { get; private set; }
public int MemberID { get; private set; }
/// 가맹점 주문번호 (unique, 중복 불가)
public string OrderID { get; private set; } = default!;
/// 다날 CPID (계약 완료 후 발급)
public string MerchantID { get; private set; } = default!;
/// 상품명
public string OrderName { get; private set; } = "포인트 충전";
/// 다날 거래번호 (승인 후 저장)
public string? TransactionID { get; private set; }
/// 결제 금액 (부가세 포함 총액)
public int Amount { get; private set; }
/// 충전될 포인트 (= 결제금액, 1:1)
public int PointAmount { get; private set; }
/// 부가세 (표시용)
public int VatAmount { get; private set; }
public PaymentMethod PaymentMethod { get; private set; }
public PaymentStatus Status { get; private set; }
public DateTime? PaidAt { get; private set; }
public DateTime? CancelledAt { get; private set; }
public string? FailReason { get; private set; }
/// 가상계좌: 계좌번호
public string? VirtualAccountNumber { get; private set; }
/// 가상계좌: 은행명
public string? VirtualAccountBank { get; private set; }
/// 가상계좌: 예금주
public string? VirtualAccountHolder { get; private set; }
/// 가상계좌: 입금 기한
public DateTime? VirtualAccountExpireAt { get; private set; }
public DateTime CreatedAt { get; private set; } = DateTime.UtcNow;
private PaymentOrder() { }
public static PaymentOrder Create(
int memberID,
string orderID,
string merchantID,
int amount,
PaymentMethod method,
string orderName = "포인트 충전"
) {
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(memberID);
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(amount);
if (string.IsNullOrWhiteSpace(orderID)) {
throw new ArgumentException("orderID required", nameof(orderID));
}
if (string.IsNullOrWhiteSpace(merchantID)) {
throw new ArgumentException("merchantID required", nameof(merchantID));
}
var vat = (int)Math.Round(amount / 11.0);
return new PaymentOrder
{
MemberID = memberID,
OrderID = orderID,
MerchantID = merchantID,
OrderName = orderName,
Amount = amount,
PointAmount = amount,
VatAmount = vat,
PaymentMethod = method,
Status = PaymentStatus.Pending
};
}
public void MarkPaid(string transactionID)
{
if (Status is not PaymentStatus.Pending and not PaymentStatus.WaitingDeposit)
{
throw new InvalidOperationException($"Cannot mark as paid from status {Status}");
}
TransactionID = transactionID;
Status = PaymentStatus.Paid;
PaidAt = DateTime.UtcNow;
}
public void MarkFailed(string reason)
{
Status = PaymentStatus.Failed;
FailReason = reason;
}
public void MarkCancelled()
{
if (Status != PaymentStatus.Paid)
{
throw new InvalidOperationException($"Cannot cancel from status {Status}");
}
Status = PaymentStatus.Cancelled;
CancelledAt = DateTime.UtcNow;
}
public void MarkWaitingDeposit(string accountNumber, string bank, string holder, DateTime expireAt)
{
Status = PaymentStatus.WaitingDeposit;
VirtualAccountNumber = accountNumber;
VirtualAccountBank = bank;
VirtualAccountHolder = holder;
VirtualAccountExpireAt = expireAt;
}
}