| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697 |
- using Application.Abstractions.Data;
- using Domain.Entities.Common.ValueObject;
- using Domain.Entities.Payments.Danal;
- using Domain.Entities.Payments.Danal.ValueObject;
- using Microsoft.EntityFrameworkCore;
- using SharedKernel.Extensions;
- namespace Web.Api.Endpoints.Payment;
- /// <summary>가상계좌 입금 통보 (다날 서버→서버)</summary>
- internal sealed class VirtualAccountNoti : IEndpoint
- {
- public void MapEndpoint(IEndpointRouteBuilder app)
- {
- /// 다날 NotiUrl 콜백 (IP: 150.242.132.116). 입금 확인 → 포인트 적립
- app.MapPost("api/payment/noti/vaccount", async (
- HttpContext httpContext,
- IAppDbContext db,
- CancellationToken ct
- ) => {
- // IP 검증
- var clientIP = httpContext.GetClientIP();
- // 요청 파싱
- var body = await httpContext.Request.ReadFromJsonAsync<VAccountNotiBody>(ct);
- if (body is null)
- {
- return Results.Json(new { code = "FAIL" }, statusCode: 400);
- }
- // 주문 조회
- var order = await db.PaymentOrder.FirstOrDefaultAsync(o => o.OrderID == body.OrderID, ct);
- if (order is null)
- {
- return Results.Json(new { code = "FAIL" });
- }
- // 중복 입금 방지
- if (order.Status == Domain.Entities.Payments.ValueObject.PaymentStatus.Paid)
- {
- return Results.Json(new { code = "SUCCESS" });
- }
- // 금액 확인
- if (!int.TryParse(body.Amount, out var amount) || amount != order.Amount)
- {
- await db.DanalLog.AddAsync(DanalLog.Create(
- order.MemberID, DanalLogType.Failed,
- "AMOUNT_MISMATCH", $"금액 불일치: 요청={body.Amount}, 주문={order.Amount}",
- body.TransactionID, body.OrderID), ct);
- await db.SaveChangesAsync(ct);
- return Results.Json(new { code = "FAIL" });
- }
- // 결제 완료 처리
- order.MarkPaid(body.TransactionID ?? "");
- // 지갑 포인트 충전
- var wallet = await db.Wallet.FirstOrDefaultAsync(w => w.MemberID == order.MemberID, ct);
- if (wallet is null)
- {
- await db.DanalLog.AddAsync(DanalLog.Create(
- order.MemberID, DanalLogType.Error,
- "WALLET_NOT_FOUND", "지갑 정보를 찾을 수 없습니다.",
- body.TransactionID, body.OrderID), ct);
- await db.SaveChangesAsync(ct);
- return Results.Json(new { code = "FAIL" });
- }
- wallet.CreditPgCharge(
- Money.KRW(order.PointAmount),
- "가상계좌 입금 완료",
- order.OrderID
- );
- await db.DanalLog.AddAsync(DanalLog.Create(
- order.MemberID, DanalLogType.Webhook,
- "SUCCESS", "가상계좌 입금 완료",
- body.TransactionID, body.OrderID), ct);
- await db.SaveChangesAsync(ct);
- return Results.Json(new { code = "SUCCESS" });
- })
- .WithTags("Payment")
- .AllowAnonymous();
- }
- private sealed record VAccountNotiBody(
- string? Code,
- string? Message,
- string? TransactionID,
- string? OrderID,
- string? Amount
- );
- }
|