diff --git a/src/BCards.IntegrationTests/BCards.IntegrationTests.csproj b/src/BCards.IntegrationTests/BCards.IntegrationTests.csproj index 05d8f00..9421555 100644 --- a/src/BCards.IntegrationTests/BCards.IntegrationTests.csproj +++ b/src/BCards.IntegrationTests/BCards.IntegrationTests.csproj @@ -23,7 +23,7 @@ - + diff --git a/src/BCards.Web/BCards.Web.csproj b/src/BCards.Web/BCards.Web.csproj index 30f20a7..c04f18e 100644 --- a/src/BCards.Web/BCards.Web.csproj +++ b/src/BCards.Web/BCards.Web.csproj @@ -9,7 +9,7 @@ - + diff --git a/src/BCards.Web/Controllers/AdminController.cs b/src/BCards.Web/Controllers/AdminController.cs index 89dfb18..67fe232 100644 --- a/src/BCards.Web/Controllers/AdminController.cs +++ b/src/BCards.Web/Controllers/AdminController.cs @@ -278,7 +278,7 @@ public class AdminController : Controller null, previewUrl); - TempData["Success"] = "Página atualizada e enviada para moderação!"; + TempData["Success"] = "Página atualizada! Teste e envie para moderação."; } return RedirectToAction("Dashboard"); diff --git a/src/BCards.Web/Controllers/PaymentController.cs b/src/BCards.Web/Controllers/PaymentController.cs index e4f93b8..8727f87 100644 --- a/src/BCards.Web/Controllers/PaymentController.cs +++ b/src/BCards.Web/Controllers/PaymentController.cs @@ -55,20 +55,12 @@ public class PaymentController : Controller public async Task Success() { - var user = await _authService.GetCurrentUserAsync(User); - var planType = TempData[$"PlanType|{user.Id}"].ToString(); - try { - if (!string.IsNullOrEmpty(planType) && Enum.TryParse(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!"; - } + var user = await _authService.GetCurrentUserAsync(User); + var planType = TempData[$"PlanType|{user.Id}"].ToString(); + TempData["Success"] = $"Assinatura {planType} ativada com sucesso!"; return RedirectToAction("Dashboard", "Admin"); } catch (Exception ex) diff --git a/src/BCards.Web/Controllers/StripeWebhookController.cs b/src/BCards.Web/Controllers/StripeWebhookController.cs index d165c7e..db1c560 100644 --- a/src/BCards.Web/Controllers/StripeWebhookController.cs +++ b/src/BCards.Web/Controllers/StripeWebhookController.cs @@ -59,19 +59,19 @@ public class StripeWebhookController : ControllerBase switch (stripeEvent.Type) { - case Events.InvoicePaymentSucceeded: + case "invoice.payment_succeeded": await HandlePaymentSucceeded(stripeEvent); break; - case Events.InvoicePaymentFailed: + case "invoice.payment_failed": await HandlePaymentFailed(stripeEvent); break; - case Events.CustomerSubscriptionDeleted: + case "customer.subscription.deleted": await HandleSubscriptionDeleted(stripeEvent); break; - case Events.CustomerSubscriptionUpdated: + case "customer.subscription.updated": await HandleSubscriptionUpdated(stripeEvent); break; @@ -99,8 +99,9 @@ public class StripeWebhookController : ControllerBase if (stripeEvent.Data.Object is Invoice invoice) { _logger.LogInformation($"Payment succeeded for customer: {invoice.CustomerId}"); - - var subscription = await _subscriptionRepository.GetByStripeSubscriptionIdAsync(invoice.SubscriptionId); + + var subscriptionId = GetSubscriptionId(stripeEvent); + var subscription = await _subscriptionRepository.GetByStripeSubscriptionIdAsync(subscriptionId); if (subscription != null) { subscription.Status = "active"; @@ -126,8 +127,9 @@ public class StripeWebhookController : ControllerBase if (stripeEvent.Data.Object is Invoice invoice) { _logger.LogInformation($"Payment failed for customer: {invoice.CustomerId}"); - - var subscription = await _subscriptionRepository.GetByStripeSubscriptionIdAsync(invoice.SubscriptionId); + + var subscriptionId = GetSubscriptionId(stripeEvent); + var subscription = await _subscriptionRepository.GetByStripeSubscriptionIdAsync(subscriptionId); if (subscription != null) { subscription.Status = "past_due"; @@ -153,7 +155,7 @@ public class StripeWebhookController : ControllerBase if (stripeEvent.Data.Object is Subscription stripeSubscription) { _logger.LogInformation($"Subscription cancelled: {stripeSubscription.Id}"); - + var subscription = await _subscriptionRepository.GetByStripeSubscriptionIdAsync(stripeSubscription.Id); if (subscription != null) { @@ -184,9 +186,12 @@ public class StripeWebhookController : ControllerBase var subscription = await _subscriptionRepository.GetByStripeSubscriptionIdAsync(stripeSubscription.Id); if (subscription != null) { + var service = new SubscriptionItemService(); + var subItem = service.Get(stripeSubscription.Items.Data[0].Id); + subscription.Status = stripeSubscription.Status; - subscription.CurrentPeriodStart = stripeSubscription.CurrentPeriodStart; - subscription.CurrentPeriodEnd = stripeSubscription.CurrentPeriodEnd; + subscription.CurrentPeriodStart = subItem.CurrentPeriodStart; + subscription.CurrentPeriodEnd = subItem.CurrentPeriodEnd; subscription.CancelAtPeriodEnd = stripeSubscription.CancelAtPeriodEnd; subscription.UpdatedAt = DateTime.UtcNow; @@ -216,4 +221,33 @@ public class StripeWebhookController : ControllerBase _ => "trial" }; } + + private string GetSubscriptionId(Event stripeEvent) + { + if (stripeEvent.Data.Object is Invoice invoice) + { + var subscriptionLineItem = invoice.Lines?.Data + .FirstOrDefault(line => + !string.IsNullOrEmpty(line.SubscriptionId) || + line.Subscription != null + ); + + string subscriptionId = null; + + if (subscriptionLineItem != null) + { + // Tenta obter o ID da assinatura de duas formas diferentes + subscriptionId = subscriptionLineItem.SubscriptionId + ?? subscriptionLineItem.Subscription?.Id; + } + + return subscriptionId; + } + else if (stripeEvent.Data.Object is Subscription stripeSubscription) + { + return stripeSubscription.Id; + } + + return null; + } } \ No newline at end of file diff --git a/src/BCards.Web/Services/PaymentService.cs b/src/BCards.Web/Services/PaymentService.cs index 8a5061f..a3cc862 100644 --- a/src/BCards.Web/Services/PaymentService.cs +++ b/src/BCards.Web/Services/PaymentService.cs @@ -5,6 +5,7 @@ using Microsoft.Extensions.Options; using Stripe; using Stripe.Checkout; using Stripe.BillingPortal; +using System.Numerics; namespace BCards.Web.Services; @@ -115,22 +116,22 @@ public class PaymentService : IPaymentService { try { - var stripeEvent = EventUtility.ConstructEvent(requestBody, signature, _stripeSettings.WebhookSecret); + var stripeEvent = EventUtility.ConstructEvent(requestBody, signature, _stripeSettings.WebhookSecret, throwOnApiVersionMismatch: false); switch (stripeEvent.Type) { - case Events.CheckoutSessionCompleted: + case "checkout.session.completed": var session = stripeEvent.Data.Object as Stripe.Checkout.Session; await HandleCheckoutSessionCompletedAsync(session!); break; - case Events.InvoicePaymentSucceeded: + case "invoice.finalized": var invoice = stripeEvent.Data.Object as Invoice; await HandleInvoicePaymentSucceededAsync(invoice!); break; - case Events.CustomerSubscriptionUpdated: - case Events.CustomerSubscriptionDeleted: + case "customer.subscription.updated": + case "customer.subscription.deleted": var subscription = stripeEvent.Data.Object as Stripe.Subscription; await HandleSubscriptionUpdatedAsync(subscription!); break; @@ -255,6 +256,9 @@ public class PaymentService : IPaymentService var subscriptionService = new SubscriptionService(); var stripeSubscription = await subscriptionService.GetAsync(session.SubscriptionId); + var service = new SubscriptionItemService(); + var subItem = service.Get(stripeSubscription.Items.Data[0].Id); + var limitations = await GetPlanLimitationsAsync(planType); var subscription = new Models.Subscription @@ -263,8 +267,8 @@ public class PaymentService : IPaymentService StripeSubscriptionId = session.SubscriptionId, PlanType = planType, Status = stripeSubscription.Status, - CurrentPeriodStart = stripeSubscription.CurrentPeriodStart, - CurrentPeriodEnd = stripeSubscription.CurrentPeriodEnd, + CurrentPeriodStart = subItem.CurrentPeriodStart, + CurrentPeriodEnd = subItem.CurrentPeriodEnd, MaxLinks = limitations.MaxLinks, AllowCustomThemes = limitations.AllowCustomThemes, AllowAnalytics = limitations.AllowAnalytics, @@ -287,7 +291,8 @@ public class PaymentService : IPaymentService private async Task HandleInvoicePaymentSucceededAsync(Invoice invoice) { - var subscription = await _subscriptionRepository.GetByStripeSubscriptionIdAsync(invoice.SubscriptionId); + var subscriptionId = GetSubscriptionId(invoice); + var subscription = await _subscriptionRepository.GetByStripeSubscriptionIdAsync(subscriptionId); if (subscription != null) { subscription.Status = "active"; @@ -300,9 +305,12 @@ public class PaymentService : IPaymentService var subscription = await _subscriptionRepository.GetByStripeSubscriptionIdAsync(stripeSubscription.Id); if (subscription != null) { + var service = new SubscriptionItemService(); + var subItem = service.Get(stripeSubscription.Items.Data[0].Id); + subscription.Status = stripeSubscription.Status; - subscription.CurrentPeriodStart = stripeSubscription.CurrentPeriodStart; - subscription.CurrentPeriodEnd = stripeSubscription.CurrentPeriodEnd; + subscription.CurrentPeriodStart = subItem.CurrentPeriodStart; + subscription.CurrentPeriodEnd = subItem.CurrentPeriodEnd; subscription.CancelAtPeriodEnd = stripeSubscription.CancelAtPeriodEnd; await _subscriptionRepository.UpdateAsync(subscription); @@ -386,4 +394,29 @@ public class PaymentService : IPaymentService throw new InvalidOperationException($"Erro ao criar sessão do portal: {ex.Message}"); } } + + private string GetSubscriptionId(Invoice? invoice) + { + if (invoice!=null) + { + var subscriptionLineItem = invoice.Lines?.Data + .FirstOrDefault(line => + !string.IsNullOrEmpty(line.SubscriptionId) || + line.Subscription != null + ); + + string subscriptionId = null; + + if (subscriptionLineItem != null) + { + // Tenta obter o ID da assinatura de duas formas diferentes + subscriptionId = subscriptionLineItem.SubscriptionId + ?? subscriptionLineItem.Subscription?.Id; + } + + return subscriptionId; + } + + return null; + } } \ No newline at end of file diff --git a/src/BCards.Web/ViewModels/ManagePageViewModel.cs b/src/BCards.Web/ViewModels/ManagePageViewModel.cs index adcf082..731f2c9 100644 --- a/src/BCards.Web/ViewModels/ManagePageViewModel.cs +++ b/src/BCards.Web/ViewModels/ManagePageViewModel.cs @@ -126,5 +126,6 @@ public enum PageStatus Inactive, // Pausada pelo usuário PendingModeration = 4, // Aguardando moderação Rejected = 5, // Rejeitada na moderação - Creating = 6 // Em desenvolvimento/criação + Creating = 6, // Em desenvolvimento/criação + Approved = 7 // Aprovada } \ No newline at end of file diff --git a/src/BCards.Web/ViewModels/ManageSubscriptionViewModel.cs b/src/BCards.Web/ViewModels/ManageSubscriptionViewModel.cs index fc052a0..ef75549 100644 --- a/src/BCards.Web/ViewModels/ManageSubscriptionViewModel.cs +++ b/src/BCards.Web/ViewModels/ManageSubscriptionViewModel.cs @@ -19,7 +19,7 @@ public class ManageSubscriptionViewModel public bool CanDowngrade => HasActiveSubscription && User.CurrentPlan != "basic"; public bool WillCancelAtPeriodEnd => StripeSubscription?.CancelAtPeriodEnd == true; - public DateTime? CurrentPeriodEnd => StripeSubscription?.CurrentPeriodEnd; + public DateTime? CurrentPeriodEnd => null; public DateTime? NextBillingDate => !WillCancelAtPeriodEnd ? CurrentPeriodEnd : null; public decimal? MonthlyAmount => StripeSubscription?.Items?.Data?.FirstOrDefault()?.Price?.UnitAmount / 100m; diff --git a/src/BCards.Web/Views/Admin/Dashboard.cshtml b/src/BCards.Web/Views/Admin/Dashboard.cshtml index fbb4373..709ac69 100644 --- a/src/BCards.Web/Views/Admin/Dashboard.cshtml +++ b/src/BCards.Web/Views/Admin/Dashboard.cshtml @@ -111,7 +111,7 @@ onclick="openPreview('@pageItem.Id')" data-page-category="@pageItem.Category" data-page-slug="@pageItem.Slug"> - Preview + Testar } @@ -212,7 +212,7 @@
Página em criação! - Você pode editar e fazer preview quantas vezes quiser.
+ Você pode editar e testar quantas vezes quiser.
Ao terminar, clique em para enviar a página para moderação!
@@ -268,7 +268,7 @@ {
-
+
Limite atingido! diff --git a/src/BCards.Web/Views/Admin/ManagePage.cshtml b/src/BCards.Web/Views/Admin/ManagePage.cshtml index 0615aa5..f07d053 100644 --- a/src/BCards.Web/Views/Admin/ManagePage.cshtml +++ b/src/BCards.Web/Views/Admin/ManagePage.cshtml @@ -340,7 +340,7 @@ var instagram = Model.Links.Where(x => x.Icon.Contains("instagram")).FirstOrDefault(); var facebookUrl = facebook !=null ? facebook.Url : ""; var twitterUrl = twitter !=null ? twitter.Url : ""; - var whatsappUrl = whatsapp !=null ? whatsapp.Url : ""; + var whatsappUrl = whatsapp !=null ? whatsapp.Url.Replace("https://wa.me/","") : ""; var instagramUrl = instagram !=null ? instagram.Url : ""; } @@ -1423,6 +1423,12 @@ userInput.on('input', function() { let value = $(this).val().trim(); + // Se o usuário colou uma URL completa, extrair apenas a parte do usuário + if (value.startsWith(prefix)) { + value = value.replace(prefix, ''); + $(this).val(value); + } + if (isWhatsApp) { // WhatsApp: apenas números value = value.replace(/\D/g, ''); diff --git a/src/BCards.Web/appsettings.Development.json b/src/BCards.Web/appsettings.Development.json index 16c6f12..dff4f12 100644 --- a/src/BCards.Web/appsettings.Development.json +++ b/src/BCards.Web/appsettings.Development.json @@ -6,6 +6,31 @@ } }, "DetailedErrors": true, + "Stripe": { + "PublishableKey": "pk_test_51RjUmIBMIadsOxJVP4bWc54pHEOSf5km1hpOkOBSoGVoKxI46N4KSWtevpXCSq68OjFazBuXmPJGBwZ1KDN5MNJy003lj1YmAS", + "SecretKey": "sk_test_51RjUmIBMIadsOxJVeqsMFxnZ8ePR7d8IbnaF4sAwBVJv9rrfODPEQ2C9fF3beoABpITdfzEk0ZDzGTTQfvKv63xI00PeZoABGO", + "WebhookSecret": "whsec_8d189c137ff170ab5e62498003512b9d073e2db50c50ed7d8712b7ef11a37543" + }, + "Plans": { + "Basic": { + "PriceId": "price_1RjUskBMIadsOxJVgLwlVo1y", + "Price": 9.90, + "MaxLinks": 5, + "Features": [ "basic_themes", "simple_analytics" ] + }, + "Professional": { + "PriceId": "price_1RjUv9BMIadsOxJVORqlM4E9", + "Price": 24.90, + "MaxLinks": 15, + "Features": [ "all_themes", "advanced_analytics", "custom_domain" ] + }, + "Premium": { + "PriceId": "price_1RjUw0BMIadsOxJVmdouNV1g", + "Price": 29.90, + "MaxLinks": -1, + "Features": [ "custom_themes", "full_analytics", "multiple_domains", "priority_support" ] + } + }, "MongoDb": { "ConnectionString": "mongodb://localhost:27017", "DatabaseName": "BCardsDB_Dev" diff --git a/src/BCards.Web/appsettings.Release.json b/src/BCards.Web/appsettings.Release.json index f575bc7..ebaf995 100644 --- a/src/BCards.Web/appsettings.Release.json +++ b/src/BCards.Web/appsettings.Release.json @@ -73,7 +73,7 @@ "Moderation": { "PriorityTimeframes": { "Trial": "7.00:00:00", - "Basic": "7.00:00:00", + "Basic": "7.00:00:00", "Professional": "3.00:00:00", "Premium": "1.00:00:00" },