Init Commit
This commit is contained in:
@@ -0,0 +1,145 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using MotoresArgentinosV2.Core.DTOs;
|
||||
using MotoresArgentinosV2.Core.Interfaces;
|
||||
using Microsoft.AspNetCore.RateLimiting;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace MotoresArgentinosV2.API.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
[Authorize]
|
||||
public class PaymentsController : ControllerBase
|
||||
{
|
||||
private readonly IPaymentService _paymentService;
|
||||
private readonly IConfiguration _config;
|
||||
|
||||
public PaymentsController(IPaymentService paymentService, IConfiguration config)
|
||||
{
|
||||
_paymentService = paymentService;
|
||||
_config = config;
|
||||
}
|
||||
|
||||
[HttpPost("process")]
|
||||
[EnableRateLimiting("AuthPolicy")]
|
||||
public async Task<IActionResult> ProcessPayment([FromBody] CreatePaymentRequestDto request)
|
||||
{
|
||||
try
|
||||
{
|
||||
var userId = int.Parse(User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)?.Value ?? "0");
|
||||
|
||||
var result = await _paymentService.ProcessPaymentAsync(request, userId);
|
||||
|
||||
if (result.Status == "approved")
|
||||
{
|
||||
return Ok(new { status = "approved", id = result.PaymentId });
|
||||
}
|
||||
else if (result.Status == "in_process")
|
||||
{
|
||||
return Ok(new { status = "in_process", message = "El pago está siendo revisado." });
|
||||
}
|
||||
else
|
||||
{
|
||||
return BadRequest(new { status = "rejected", detail = result.StatusDetail });
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return StatusCode(500, new { message = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
// --- MÉTODO WEBHOOK ACTUALIZADO CON VALIDACIÓN DE FIRMA ---
|
||||
[HttpPost("webhook")]
|
||||
[AllowAnonymous]
|
||||
public async Task<IActionResult> MercadoPagoWebhook([FromQuery] string? topic, [FromQuery] string? id)
|
||||
{
|
||||
// 1. EXTRACCIÓN DE DATOS DE LA PETICIÓN
|
||||
Request.Headers.TryGetValue("x-request-id", out var xRequestId);
|
||||
Request.Headers.TryGetValue("x-signature", out var xSignature);
|
||||
|
||||
var resourceId = id;
|
||||
var resourceTopic = topic;
|
||||
|
||||
if (string.IsNullOrEmpty(resourceId))
|
||||
{
|
||||
try
|
||||
{
|
||||
Request.EnableBuffering();
|
||||
using var reader = new System.IO.StreamReader(Request.Body, leaveOpen: true);
|
||||
var body = await reader.ReadToEndAsync();
|
||||
Request.Body.Position = 0;
|
||||
|
||||
var json = System.Text.Json.JsonDocument.Parse(body);
|
||||
if (json.RootElement.TryGetProperty("type", out var typeProp))
|
||||
{
|
||||
resourceTopic = typeProp.GetString();
|
||||
}
|
||||
if (json.RootElement.TryGetProperty("data", out var dataProp) && dataProp.TryGetProperty("id", out var idProp))
|
||||
{
|
||||
resourceId = idProp.GetString();
|
||||
}
|
||||
}
|
||||
catch { /* Ignorar errores de lectura */ }
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(resourceId) || resourceTopic != "payment")
|
||||
{
|
||||
return Ok();
|
||||
}
|
||||
|
||||
// 2. VALIDACIÓN DE LA FIRMA
|
||||
var secret = _config["MercadoPago:WebhookSecret"];
|
||||
if (!string.IsNullOrEmpty(secret))
|
||||
{
|
||||
var signatureParts = xSignature.ToString().Split(',')
|
||||
.Select(part => part.Trim().Split('='))
|
||||
.ToDictionary(split => split[0], split => split.Length > 1 ? split[1] : "");
|
||||
|
||||
if (!signatureParts.TryGetValue("ts", out var ts) || !signatureParts.TryGetValue("v1", out var hash))
|
||||
{
|
||||
return Unauthorized("Invalid signature header.");
|
||||
}
|
||||
|
||||
// Según la documentación de MP, el data.id debe estar en minúsculas para la firma.
|
||||
var manifest = $"id:{resourceId.ToLower()};request-id:{xRequestId};ts:{ts};";
|
||||
|
||||
using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret));
|
||||
var computedHashBytes = hmac.ComputeHash(Encoding.UTF8.GetBytes(manifest));
|
||||
var computedHash = Convert.ToHexString(computedHashBytes).ToLower();
|
||||
|
||||
if (computedHash != hash)
|
||||
{
|
||||
return Unauthorized("Signature mismatch.");
|
||||
}
|
||||
}
|
||||
|
||||
// 3. PROCESAMIENTO (SOLO SI LA FIRMA ES VÁLIDA)
|
||||
try
|
||||
{
|
||||
await _paymentService.ProcessWebhookAsync(resourceTopic, resourceId);
|
||||
return Ok();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Error procesando webhook validado: {ex.Message}");
|
||||
return StatusCode(500, "Internal server error processing webhook.");
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost("check-status/{adId}")]
|
||||
public async Task<IActionResult> CheckStatus(int adId)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await _paymentService.CheckPaymentStatusAsync(adId);
|
||||
return Ok(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(new { message = ex.Message });
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user