BCards/src/BCards.Web/Controllers/PaymentController.cs
2025-07-14 23:21:25 -03:00

273 lines
9.2 KiB
C#

using BCards.Web.Models;
using BCards.Web.Repositories;
using BCards.Web.Services;
using BCards.Web.ViewModels;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
namespace BCards.Web.Controllers;
[Authorize]
public class PaymentController : Controller
{
private readonly IPaymentService _paymentService;
private readonly IAuthService _authService;
private readonly IUserRepository _userService;
private readonly ISubscriptionRepository _subscriptionRepository;
public PaymentController(IPaymentService paymentService, IAuthService authService, IUserRepository userService, ISubscriptionRepository subscriptionRepository)
{
_paymentService = paymentService;
_authService = authService;
_userService = userService;
_subscriptionRepository = subscriptionRepository;
}
[HttpPost]
public async Task<IActionResult> CreateCheckoutSession(string planType)
{
var user = await _authService.GetCurrentUserAsync(User);
if (user == null)
return RedirectToAction("Login", "Auth");
var successUrl = Url.Action("Success", "Payment", null, Request.Scheme);
var cancelUrl = Url.Action("Cancel", "Payment", null, Request.Scheme);
TempData[$"PlanType|{user.Id}"] = planType;
try
{
var checkoutUrl = await _paymentService.CreateCheckoutSessionAsync(
user.Id,
planType,
successUrl!,
cancelUrl!);
return Redirect(checkoutUrl);
}
catch (Exception ex)
{
TempData["Error"] = $"Erro ao processar pagamento: {ex.Message}";
return RedirectToAction("Pricing", "Home");
}
}
public async Task<IActionResult> Success()
{
var user = await _authService.GetCurrentUserAsync(User);
var planType = TempData[$"PlanType|{user.Id}"].ToString();
try
{
if (!string.IsNullOrEmpty(planType) && Enum.TryParse<PlanType>(planType, out var plan))
{
user.CurrentPlan = plan.ToString();
user.SubscriptionStatus = "active";
await _userService.UpdateAsync(user); // ou o método equivalente
TempData["Success"] = $"Assinatura {planType} ativada com sucesso!";
}
return RedirectToAction("Dashboard", "Admin");
}
catch (Exception ex)
{
TempData["Error"] = $"Erro ao processar pagamento: {ex.Message}";
return RedirectToAction("Dashboard", "Admin");
}
}
public IActionResult Cancel()
{
TempData["Info"] = "Pagamento cancelado. Você pode tentar novamente quando quiser.";
return RedirectToAction("Pricing", "Home");
}
[HttpPost]
[Route("webhook/stripe")]
[AllowAnonymous]
public async Task<IActionResult> StripeWebhook()
{
var signature = Request.Headers["Stripe-Signature"].FirstOrDefault();
if (string.IsNullOrEmpty(signature))
return BadRequest();
string requestBody;
using (var reader = new StreamReader(Request.Body))
{
requestBody = await reader.ReadToEndAsync();
}
try
{
await _paymentService.HandleWebhookAsync(requestBody, signature);
return Ok();
}
catch (Exception ex)
{
return BadRequest($"Webhook error: {ex.Message}");
}
}
public async Task<IActionResult> ManageSubscription()
{
var user = await _authService.GetCurrentUserAsync(User);
if (user == null)
return RedirectToAction("Login", "Auth");
try
{
var viewModel = new ManageSubscriptionViewModel
{
User = user,
StripeSubscription = await _paymentService.GetSubscriptionDetailsAsync(user.Id),
PaymentHistory = await _paymentService.GetPaymentHistoryAsync(user.Id),
AvailablePlans = GetAvailablePlans(user.CurrentPlan)
};
// Pegar assinatura local se existir
if (!string.IsNullOrEmpty(user.StripeCustomerId))
{
// Aqui você poderia buscar a subscription local se necessário
// viewModel.LocalSubscription = await _subscriptionRepository.GetByUserIdAsync(user.Id);
}
return View(viewModel);
}
catch (Exception ex)
{
var errorViewModel = new ManageSubscriptionViewModel
{
User = user,
ErrorMessage = $"Erro ao carregar dados da assinatura: {ex.Message}",
AvailablePlans = GetAvailablePlans(user.CurrentPlan)
};
return View(errorViewModel);
}
}
[HttpPost]
public async Task<IActionResult> CancelSubscription(string subscriptionId)
{
try
{
await _paymentService.CancelSubscriptionAsync(subscriptionId);
TempData["Success"] = "Sua assinatura será cancelada no final do período atual.";
}
catch (Exception ex)
{
TempData["Error"] = $"Erro ao cancelar assinatura: {ex.Message}";
}
return RedirectToAction("ManageSubscription");
}
[HttpPost]
public async Task<IActionResult> ChangePlan(string newPlanType)
{
var user = await _authService.GetCurrentUserAsync(User);
if (user == null)
return RedirectToAction("Login", "Auth");
try
{
// Para mudanças de plano, vamos usar o Stripe Checkout
var returnUrl = Url.Action("ManageSubscription", "Payment", null, Request.Scheme);
var cancelUrl = Url.Action("ManageSubscription", "Payment", null, Request.Scheme);
var checkoutUrl = await _paymentService.CreateCheckoutSessionAsync(
user.Id,
newPlanType,
returnUrl!,
cancelUrl!);
return Redirect(checkoutUrl);
}
catch (Exception ex)
{
TempData["Error"] = $"Erro ao alterar plano: {ex.Message}";
return RedirectToAction("ManageSubscription");
}
}
[HttpPost]
public async Task<IActionResult> OpenStripePortal()
{
var user = await _authService.GetCurrentUserAsync(User);
if (user == null || string.IsNullOrEmpty(user.StripeCustomerId))
{
TempData["Error"] = "Erro: dados de assinatura não encontrados.";
return RedirectToAction("ManageSubscription");
}
try
{
var returnUrl = Url.Action("ManageSubscription", "Payment", null, Request.Scheme);
var portalUrl = await _paymentService.CreatePortalSessionAsync(user.StripeCustomerId, returnUrl!);
return Redirect(portalUrl);
}
catch (Exception ex)
{
TempData["Error"] = $"Erro ao abrir portal de pagamento: {ex.Message}";
return RedirectToAction("ManageSubscription");
}
}
private List<AvailablePlanViewModel> GetAvailablePlans(string currentPlan)
{
var plans = new List<AvailablePlanViewModel>
{
new()
{
PlanType = "basic",
DisplayName = "Básico",
Price = 9.90m,
PriceId = "price_basic", // Substitua pelos IDs reais do Stripe
MaxLinks = 5,
AllowAnalytics = true,
Features = new List<string> { "5 links", "Temas básicos", "Análises básicas" },
IsCurrentPlan = currentPlan == "basic"
},
new()
{
PlanType = "professional",
DisplayName = "Profissional",
Price = 24.90m,
PriceId = "price_professional", // Substitua pelos IDs reais do Stripe
MaxLinks = 15,
AllowAnalytics = true,
AllowCustomDomain = true,
Features = new List<string> { "15 links", "Todos os temas", "Domínio personalizado", "Análises avançadas" },
IsCurrentPlan = currentPlan == "professional"
},
new()
{
PlanType = "premium",
DisplayName = "Premium",
Price = 29.90m,
PriceId = "price_premium", // Substitua pelos IDs reais do Stripe
MaxLinks = -1, // Ilimitado
AllowCustomThemes = true,
AllowAnalytics = true,
AllowCustomDomain = true,
Features = new List<string> { "Links ilimitados", "Temas personalizados", "Múltiplos domínios", "Suporte prioritário" },
IsCurrentPlan = currentPlan == "premium"
}
};
// Marcar upgrades e downgrades
var currentPlanIndex = plans.FindIndex(p => p.IsCurrentPlan);
for (int i = 0; i < plans.Count; i++)
{
if (i > currentPlanIndex)
plans[i].IsUpgrade = true;
else if (i < currentPlanIndex)
plans[i].IsDowngrade = true;
}
return plans;
}
}