Handler.cs 3.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. using Application.Abstractions.Data;
  2. using Application.Abstractions.Messaging;
  3. using Application.Abstractions.Payment;
  4. using SharedKernel.Results;
  5. using Domain.Entities.Common.ValueObject;
  6. using Domain.Entities.Payments.Danal;
  7. using Domain.Entities.Payments.Danal.ValueObject;
  8. using Domain.Entities.Payments.ValueObject;
  9. using Microsoft.EntityFrameworkCore;
  10. namespace Application.Features.Api.Payment.Cancel;
  11. internal sealed class Handler(
  12. IAppDbContext db,
  13. IDanalPayService danalPay
  14. ) : ICommandHandler<Command, Result>
  15. {
  16. public async Task<Result> Handle(Command r, CancellationToken ct)
  17. {
  18. var order = await db.PaymentOrder.FirstOrDefaultAsync(o => o.OrderID == r.OrderID && o.MemberID == r.MemberID, ct);
  19. if (order is null)
  20. {
  21. return Result.Failure(Error.NotFound("Payment.OrderNotFound", "주문을 찾을 수 없습니다."));
  22. }
  23. if (order.Status != PaymentStatus.Paid)
  24. {
  25. return Result.Failure(Error.Problem("Payment.InvalidStatusForCancel", "취소할 수 없는 주문 상태입니다."));
  26. }
  27. // 다날 결제수단 코드 매핑
  28. var method = order.PaymentMethod switch
  29. {
  30. PaymentMethod.Integrated => "INTEGRATED",
  31. PaymentMethod.Card => "CARD",
  32. PaymentMethod.VirtualAccount => "VACCOUNT",
  33. PaymentMethod.Mobile => "MOBILE",
  34. PaymentMethod.Transfer => "TRANSFER",
  35. PaymentMethod.NaverPay => "NAVERPAY",
  36. PaymentMethod.KakaoPay => "KAKAOPAY",
  37. PaymentMethod.Payco => "PAYCO",
  38. _ => "CARD"
  39. };
  40. // 취소 요청 기록 저장
  41. var cancel = DanalCancel.CreateRequest(r.MemberID, order.ID, order.PaymentMethod, order.TransactionID!, order.OrderID, order.Amount, order.MerchantID, DanalCancelType.Full);
  42. await db.DanalCancel.AddAsync(cancel, ct);
  43. await db.SaveChangesAsync(ct);
  44. var result = await danalPay.CancelAsync(method, order.TransactionID!, order.Amount, "C", ct);
  45. if (!result.Success)
  46. {
  47. cancel.SetResponse(result.Code ?? "", result.Message ?? "취소 실패");
  48. await db.DanalLog.AddAsync(DanalLog.Create(r.MemberID, DanalLogType.Failed, result.Code ?? "FAIL", result.Message ?? "취소 실패", order.TransactionID, order.OrderID), ct);
  49. await db.SaveChangesAsync(ct);
  50. return Result.Failure(Error.Problem("Payment.CancelFailed", result.Message ?? "결제 취소에 실패했습니다."));
  51. }
  52. // 취소 응답 저장 (Balance, RemainedAmount는 string? → int? 변환)
  53. cancel.SetResponse(
  54. result.Code ?? "", result.Message ?? "",
  55. result.OriginalTransactionID,
  56. result.CancelledAmount,
  57. result.TransDate, result.TransTime,
  58. int.TryParse(result.Balance, out var balanceVal) ? balanceVal : null,
  59. int.TryParse(result.RemainedAmount, out var remainedVal) ? remainedVal : null,
  60. result.ApprovalDateTime
  61. );
  62. order.MarkCancelled();
  63. // 지갑에서 PgCharged 차감
  64. var wallet = await db.Wallet.FirstOrDefaultAsync(w => w.MemberID == order.MemberID, ct);
  65. if (wallet is not null)
  66. {
  67. wallet.AdjustDecrease(Money.KRW(order.PointAmount), "결제 취소 환불", order.OrderID);
  68. }
  69. await db.SaveChangesAsync(ct);
  70. return Result.Success();
  71. }
  72. }