TossConfirm.cs 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. using System.ComponentModel.DataAnnotations;
  2. using System.ComponentModel.DataAnnotations.Schema;
  3. using Domain.Entities.Members;
  4. namespace Domain.Entities.Payments.Toss;
  5. /// <summary>
  6. /// 토스페이먼츠 결제 승인 요청 + 응답 통합
  7. /// confirm API 호출 시 요청 데이터를 먼저 저장하고, 응답 수신 시 업데이트
  8. /// </summary>
  9. public class TossConfirm
  10. {
  11. [ForeignKey(nameof(MemberID))]
  12. public virtual Member? Member { get; private set; }
  13. [ForeignKey(nameof(PaymentOrderID))]
  14. public virtual PaymentOrder? PaymentOrder { get; private set; }
  15. [Key]
  16. public int ID { get; private set; }
  17. public int MemberID { get; private set; }
  18. public int PaymentOrderID { get; private set; }
  19. /// <summary>토스 결제 키 (unique — 취소·조회 API 호출에 필요)</summary>
  20. public string PaymentKey { get; private set; } = default!;
  21. /// <summary>가맹점 주문번호</summary>
  22. public string OrderID { get; private set; } = default!;
  23. /// <summary>승인 요청 금액 (서버 저장 주문 금액)</summary>
  24. public int Amount { get; private set; }
  25. // ── 응답 필드 (승인 완료/실패 시 업데이트) ──────────────────────
  26. /// <summary>응답 코드 (SUCCESS = 성공, 그 외 = Toss 에러 코드/EXCEPTION)</summary>
  27. public string? ResponseCode { get; private set; }
  28. /// <summary>응답 메시지 (실패 사유)</summary>
  29. public string? ResponseMessage { get; private set; }
  30. /// <summary>Toss Payment.status (DONE / WAITING_FOR_DEPOSIT / CANCELED / ABORTED / EXPIRED ...)</summary>
  31. public string? Status { get; private set; }
  32. /// <summary>결제수단 (Toss method 문자열 — 카드/가상계좌/간편결제 등)</summary>
  33. public string? Method { get; private set; }
  34. /// <summary>최종 승인 금액 (Toss totalAmount)</summary>
  35. public int? TotalAmount { get; private set; }
  36. /// <summary>승인 일시 (UTC)</summary>
  37. public DateTime? ApprovedAt { get; private set; }
  38. /// <summary>영수증 URL (Toss receipt.url)</summary>
  39. public string? ReceiptUrl { get; private set; }
  40. /// <summary>Toss Payment 객체 원문 JSON — 대사/CS 용</summary>
  41. public string? RawResponse { get; private set; }
  42. public DateTime CreatedAt { get; private set; } = DateTime.UtcNow;
  43. public DateTime? UpdatedAt { get; private set; }
  44. private TossConfirm() { }
  45. /// <summary>승인 요청 시 생성 (응답 전)</summary>
  46. public static TossConfirm CreateRequest(
  47. int memberID,
  48. int paymentOrderID,
  49. string paymentKey,
  50. string orderID,
  51. int amount
  52. ) {
  53. return new TossConfirm
  54. {
  55. MemberID = memberID,
  56. PaymentOrderID = paymentOrderID,
  57. PaymentKey = paymentKey,
  58. OrderID = orderID,
  59. Amount = amount
  60. };
  61. }
  62. /// <summary>승인 성공 응답 수신 시 업데이트</summary>
  63. public void SetResponse(
  64. string status,
  65. string? method = null,
  66. int? totalAmount = null,
  67. DateTime? approvedAt = null,
  68. string? receiptUrl = null,
  69. string? rawResponse = null
  70. ) {
  71. ResponseCode = "SUCCESS";
  72. ResponseMessage = null;
  73. Status = status;
  74. Method = method;
  75. TotalAmount = totalAmount;
  76. ApprovedAt = approvedAt;
  77. ReceiptUrl = receiptUrl;
  78. RawResponse = rawResponse;
  79. UpdatedAt = DateTime.UtcNow;
  80. }
  81. /// <summary>승인 실패/예외 시 업데이트</summary>
  82. public void SetError(string code, string message)
  83. {
  84. ResponseCode = code;
  85. ResponseMessage = message.Length > 500 ? message[..500] : message;
  86. UpdatedAt = DateTime.UtcNow;
  87. }
  88. /// <summary>
  89. /// 재승인(대사 재확인) 시 기존 요청 행을 재사용한다. PaymentKey/OrderID unique 인덱스 충돌을 피하려
  90. /// 새 행을 만들지 않고, 결제 키를 갱신하고 이전 응답을 초기화해 "요청 후 응답 미수신" 상태로 되돌린다. (Phase 1.5 승계)
  91. /// </summary>
  92. public void PrepareRetry(string paymentKey)
  93. {
  94. PaymentKey = paymentKey;
  95. ResponseCode = null;
  96. ResponseMessage = null;
  97. UpdatedAt = DateTime.UtcNow;
  98. }
  99. public bool IsSuccess => ResponseCode == "SUCCESS";
  100. }