feat: ajustes de callback do stripe e atualização do stripe.net
This commit is contained in:
parent
c6129a1c63
commit
5fc7eb5ad3
@ -23,7 +23,7 @@
|
||||
<PackageReference Include="PuppeteerSharp" Version="13.0.2" />
|
||||
<PackageReference Include="MongoDB.Driver" Version="2.25.0" />
|
||||
<PackageReference Include="Testcontainers.MongoDb" Version="3.6.0" />
|
||||
<PackageReference Include="Stripe.net" Version="44.7.0" />
|
||||
<PackageReference Include="Stripe.net" Version="48.4.0" />
|
||||
<PackageReference Include="FluentAssertions" Version="6.12.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
|
||||
|
||||
@ -9,7 +9,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MongoDB.Driver" Version="2.25.0" />
|
||||
<PackageReference Include="Stripe.net" Version="44.7.0" />
|
||||
<PackageReference Include="Stripe.net" Version="48.4.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.Google" Version="8.0.4" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.MicrosoftAccount" Version="8.0.4" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Localization" Version="2.2.0" />
|
||||
|
||||
@ -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");
|
||||
|
||||
@ -54,21 +54,13 @@ public class PaymentController : Controller
|
||||
}
|
||||
|
||||
public async Task<IActionResult> Success()
|
||||
{
|
||||
try
|
||||
{
|
||||
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)
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -100,7 +100,8 @@ public class StripeWebhookController : ControllerBase
|
||||
{
|
||||
_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";
|
||||
@ -127,7 +128,8 @@ public class StripeWebhookController : ControllerBase
|
||||
{
|
||||
_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";
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
@ -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;
|
||||
|
||||
@ -111,7 +111,7 @@
|
||||
onclick="openPreview('@pageItem.Id')"
|
||||
data-page-category="@pageItem.Category"
|
||||
data-page-slug="@pageItem.Slug">
|
||||
<i class="fas fa-eye me-1"></i>Preview
|
||||
<i class="fas fa-eye me-1"></i>Testar
|
||||
</button>
|
||||
}
|
||||
|
||||
@ -212,7 +212,7 @@
|
||||
<i class="fas fa-exclamation-triangle me-3"></i>
|
||||
<div>
|
||||
<strong>Página em criação!</strong>
|
||||
Você pode editar e fazer preview quantas vezes quiser. <br />
|
||||
Você pode editar e testar quantas vezes quiser. <br />
|
||||
Ao terminar, clique em <i class="fas fa-ellipsis-v"></i> para enviar a página <b><span id="pageNameDisplay"></span></b> para moderação!
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
@ -268,7 +268,7 @@
|
||||
{
|
||||
<!-- Limite atingido -->
|
||||
<div class="col-12">
|
||||
<div class="alert alert-warning d-flex align-items-center">
|
||||
<div class="alert alert-warning d-flex align-items-center alert-permanent">
|
||||
<i class="fas fa-exclamation-triangle me-3"></i>
|
||||
<div>
|
||||
<strong>Limite atingido!</strong>
|
||||
|
||||
@ -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 : "";
|
||||
}
|
||||
<!-- Passo 4: Redes Sociais (Opcional) -->
|
||||
@ -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, '');
|
||||
|
||||
@ -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"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user