PaymentOrder.cs 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. using System.ComponentModel.DataAnnotations;
  2. using System.ComponentModel.DataAnnotations.Schema;
  3. using Domain.Entities.Members;
  4. using Domain.Entities.Payments.ValueObject;
  5. namespace Domain.Entities.Payments;
  6. public class PaymentOrder
  7. {
  8. [ForeignKey(nameof(MemberID))]
  9. public virtual Member? Member { get; private set; }
  10. [Key]
  11. public int ID { get; private set; }
  12. public int MemberID { get; private set; }
  13. /// <summary>가맹점 주문번호 (unique, 중복 불가)</summary>
  14. public string OrderID { get; private set; } = default!;
  15. /// <summary>다날 CPID (계약 완료 후 발급)</summary>
  16. public string MerchantID { get; private set; } = default!;
  17. /// <summary>상품명</summary>
  18. public string OrderName { get; private set; } = "포인트 충전";
  19. /// <summary>다날 거래번호 (승인 후 저장)</summary>
  20. public string? TransactionID { get; private set; }
  21. /// <summary>PG 결제 총액 (= PointAmount + VatAmount, 부가세 별도 가산)</summary>
  22. public int Amount { get; private set; }
  23. /// <summary>충전될 포인트 (사용자 입력값, VAT 제외)</summary>
  24. public int PointAmount { get; private set; }
  25. /// <summary>부가세 (= PointAmount × 10%)</summary>
  26. public int VatAmount { get; private set; }
  27. public PaymentMethod PaymentMethod { get; private set; }
  28. public PaymentStatus Status { get; private set; }
  29. public DateTime? PaidAt { get; private set; }
  30. public DateTime? CancelledAt { get; private set; }
  31. public string? FailReason { get; private set; }
  32. /// <summary>가상계좌: 계좌번호</summary>
  33. public string? VirtualAccountNumber { get; private set; }
  34. /// <summary>가상계좌: 은행명</summary>
  35. public string? VirtualAccountBank { get; private set; }
  36. /// <summary>가상계좌: 예금주</summary>
  37. public string? VirtualAccountHolder { get; private set; }
  38. /// <summary>가상계좌: 입금 기한</summary>
  39. public DateTime? VirtualAccountExpireAt { get; private set; }
  40. public DateTime CreatedAt { get; private set; } = DateTime.UtcNow;
  41. private PaymentOrder() { }
  42. /// <summary>
  43. /// 주문 생성. <paramref name="pointAmount"/>는 사용자가 충전하려는 포인트(VAT 제외).
  44. /// VAT 10%는 별도 가산되며 PG 결제 총액 = pointAmount + vat.
  45. /// </summary>
  46. public static PaymentOrder Create(
  47. int memberID,
  48. string orderID,
  49. string merchantID,
  50. int pointAmount,
  51. PaymentMethod method,
  52. string orderName = "포인트 충전"
  53. ) {
  54. ArgumentOutOfRangeException.ThrowIfNegativeOrZero(memberID);
  55. ArgumentOutOfRangeException.ThrowIfNegativeOrZero(pointAmount);
  56. if (string.IsNullOrWhiteSpace(orderID)) {
  57. throw new ArgumentException("orderID required", nameof(orderID));
  58. }
  59. if (string.IsNullOrWhiteSpace(merchantID)) {
  60. throw new ArgumentException("merchantID required", nameof(merchantID));
  61. }
  62. // VAT 10% 별도 가산. PG 결제 총액 = pointAmount + vat.
  63. var vat = (int)Math.Round(pointAmount * 0.1);
  64. return new PaymentOrder
  65. {
  66. MemberID = memberID,
  67. OrderID = orderID,
  68. MerchantID = merchantID,
  69. OrderName = orderName,
  70. Amount = pointAmount + vat,
  71. PointAmount = pointAmount,
  72. VatAmount = vat,
  73. PaymentMethod = method,
  74. Status = PaymentStatus.Pending
  75. };
  76. }
  77. public void MarkPaid(string transactionID)
  78. {
  79. if (Status is not PaymentStatus.Pending and not PaymentStatus.WaitingDeposit)
  80. {
  81. throw new InvalidOperationException($"Cannot mark as paid from status {Status}");
  82. }
  83. TransactionID = transactionID;
  84. Status = PaymentStatus.Paid;
  85. PaidAt = DateTime.UtcNow;
  86. }
  87. public void MarkFailed(string reason)
  88. {
  89. Status = PaymentStatus.Failed;
  90. FailReason = reason;
  91. }
  92. public void MarkCancelled()
  93. {
  94. if (Status != PaymentStatus.Paid)
  95. {
  96. throw new InvalidOperationException($"Cannot cancel from status {Status}");
  97. }
  98. Status = PaymentStatus.Cancelled;
  99. CancelledAt = DateTime.UtcNow;
  100. }
  101. public void MarkWaitingDeposit(string accountNumber, string bank, string holder, DateTime expireAt)
  102. {
  103. Status = PaymentStatus.WaitingDeposit;
  104. VirtualAccountNumber = accountNumber;
  105. VirtualAccountBank = bank;
  106. VirtualAccountHolder = holder;
  107. VirtualAccountExpireAt = expireAt;
  108. }
  109. }