FakeTossPaymentService.cs 3.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. using Application.Abstractions.Payment;
  2. namespace Application.Tests;
  3. /// <summary>
  4. /// ITossPaymentService 수제 fake — PG 호출 횟수를 기록하고 설정된 결과/예외를 반환한다.
  5. /// </summary>
  6. internal sealed class FakeTossPaymentService : ITossPaymentService
  7. {
  8. public TossPaymentResult ConfirmResult { get; set; } = Done();
  9. public Exception? ConfirmException { get; set; }
  10. public TossPaymentResult CancelResult { get; set; } = Cancelled();
  11. public Exception? CancelException { get; set; }
  12. public TossPaymentResult GetPaymentResult { get; set; } = Done();
  13. public Exception? GetPaymentException { get; set; }
  14. public int ConfirmCallCount { get; private set; }
  15. public int CancelCallCount { get; private set; }
  16. public int GetPaymentCallCount { get; private set; }
  17. public Task<TossClientConfig> GetClientConfigAsync(CancellationToken ct)
  18. => Task.FromResult(new TossClientConfig("test_ck_fake"));
  19. public Task<TossPaymentResult> ConfirmAsync(string paymentKey, string orderID, int amount, CancellationToken ct)
  20. {
  21. ConfirmCallCount++;
  22. if (ConfirmException is not null)
  23. {
  24. throw ConfirmException;
  25. }
  26. return Task.FromResult(ConfirmResult);
  27. }
  28. public Task<TossPaymentResult> CancelAsync(string paymentKey, string cancelReason, int? cancelAmount, CancellationToken ct)
  29. {
  30. CancelCallCount++;
  31. if (CancelException is not null)
  32. {
  33. throw CancelException;
  34. }
  35. return Task.FromResult(CancelResult);
  36. }
  37. public Task<TossPaymentResult> GetPaymentAsync(string paymentKey, CancellationToken ct)
  38. {
  39. GetPaymentCallCount++;
  40. if (GetPaymentException is not null)
  41. {
  42. throw GetPaymentException;
  43. }
  44. return Task.FromResult(GetPaymentResult);
  45. }
  46. /// <summary>승인/조회 성공 (status=DONE)</summary>
  47. public static TossPaymentResult Done(string? paymentKey = null, string? orderID = null, int? totalAmount = null) => new(
  48. Success: true, Code: null, Message: null,
  49. PaymentKey: paymentKey, OrderID: orderID, Status: "DONE",
  50. TotalAmount: totalAmount, Method: "카드", ApprovedAt: DateTime.UtcNow, ReceiptUrl: "https://receipt.antooza.test",
  51. VirtualAccountNumber: null, VirtualAccountBankCode: null, VirtualAccountHolder: null, VirtualAccountDueDate: null,
  52. Cancels: [], RawJson: "{}");
  53. /// <summary>토스의 명시적 거절 {code, message}</summary>
  54. public static TossPaymentResult Fail(string code = "REJECT_CARD_COMPANY", string message = "승인 거절")
  55. => Done() with { Success = false, Code = code, Message = message, Status = null };
  56. /// <summary>가상계좌 발급 (status=WAITING_FOR_DEPOSIT)</summary>
  57. public static TossPaymentResult WaitingDeposit() => Done() with
  58. {
  59. Status = "WAITING_FOR_DEPOSIT",
  60. VirtualAccountNumber = "9-4444-0000-1234",
  61. VirtualAccountBankCode = "20",
  62. VirtualAccountHolder = "개미투자",
  63. VirtualAccountDueDate = DateTime.UtcNow.AddDays(7)
  64. };
  65. /// <summary>취소 성공 (status=CANCELED, cancels[] 1건)</summary>
  66. public static TossPaymentResult Cancelled(int cancelAmount = 0) => Done() with
  67. {
  68. Status = "CANCELED",
  69. Cancels = new List<TossCancelDetail> { new("CANCEL-TX", cancelAmount, "취소", DateTime.UtcNow) }
  70. };
  71. }