273 lines
9.2 KiB
C#
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;
|
|
}
|
|
} |