BCards/src/BCards.Web/Services/TrialExpirationService.cs
Ricardo Carneiro aea64c0b8e
All checks were successful
BCards Deployment Pipeline / Run Tests (push) Successful in 2s
BCards Deployment Pipeline / PR Validation (push) Has been skipped
BCards Deployment Pipeline / Build and Push Image (push) Successful in 8m1s
BCards Deployment Pipeline / Deploy to Production (ARM - OCI) (push) Successful in 3m48s
BCards Deployment Pipeline / Deploy to Staging (x86 - Local) (push) Has been skipped
BCards Deployment Pipeline / Cleanup Old Resources (push) Has been skipped
BCards Deployment Pipeline / Deployment Summary (push) Successful in 0s
fix: bug para excluir trial
2025-09-12 08:44:05 -03:00

221 lines
8.1 KiB
C#

using BCards.Web.Models;
using BCards.Web.Repositories;
using MongoDB.Driver;
namespace BCards.Web.Services;
public class TrialExpirationService : BackgroundService
{
private readonly IServiceProvider _serviceProvider;
private readonly ILogger<TrialExpirationService> _logger;
private readonly TimeSpan _checkInterval = TimeSpan.FromHours(1); // Check every hour
public TrialExpirationService(
IServiceProvider serviceProvider,
ILogger<TrialExpirationService> logger)
{
_serviceProvider = serviceProvider;
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
_logger.LogInformation("TrialExpirationService started");
while (!stoppingToken.IsCancellationRequested)
{
try
{
await ProcessTrialExpirationsAsync();
// Verificar cancelamento antes de fazer delay
if (stoppingToken.IsCancellationRequested)
break;
await Task.Delay(_checkInterval, stoppingToken);
}
catch (OperationCanceledException)
{
// Cancelamento normal - não é erro
_logger.LogInformation("TrialExpirationService is being cancelled");
break;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error processing trial expirations");
// Verificar cancelamento antes de fazer delay de erro
if (stoppingToken.IsCancellationRequested)
break;
try
{
await Task.Delay(TimeSpan.FromMinutes(5), stoppingToken); // Wait 5 minutes on error
}
catch (OperationCanceledException)
{
// Cancelamento durante delay de erro - também é normal
_logger.LogInformation("TrialExpirationService cancelled during error delay");
break;
}
}
}
_logger.LogInformation("TrialExpirationService stopped");
}
private async Task ProcessTrialExpirationsAsync()
{
try
{
using var scope = _serviceProvider.CreateScope();
var subscriptionRepository = scope.ServiceProvider.GetRequiredService<ISubscriptionRepository>();
var userPageRepository = scope.ServiceProvider.GetRequiredService<IUserPageRepository>();
var userRepository = scope.ServiceProvider.GetRequiredService<IUserRepository>();
_logger.LogInformation("Checking for expired trials...");
// Get all active trial subscriptions
var trialSubscriptions = await subscriptionRepository.GetTrialSubscriptionsAsync();
var now = DateTime.UtcNow;
_logger.LogInformation($"Found {trialSubscriptions.Count} trial subscriptions to process");
foreach (var subscription in trialSubscriptions)
{
try
{
var user = await userRepository.GetByIdAsync(subscription.UserId);
if (user == null)
{
_logger.LogWarning($"User not found for subscription {subscription.Id}");
continue;
}
var daysUntilExpiration = (subscription.CurrentPeriodEnd - now).TotalDays;
if (daysUntilExpiration <= 0)
{
// Trial expired - deactivate page
_logger.LogInformation($"Trial expired for user {user.Email}");
await HandleTrialExpiredAsync(user, subscription, userPageRepository);
}
else if (daysUntilExpiration <= 2 && !user.NotifiedOfExpiration)
{
// Trial expiring soon - send notification
_logger.LogInformation($"Trial expiring in {daysUntilExpiration:F1} days for user {user.Email}");
await SendExpirationWarningAsync(user, subscription, daysUntilExpiration);
// Mark as notified
user.NotifiedOfExpiration = true;
await userRepository.UpdateAsync(user);
}
}
catch (Exception ex)
{
_logger.LogError(ex, $"Error processing trial for subscription {subscription.Id}");
}
}
_logger.LogInformation("Finished checking trial expirations");
}
catch (Exception ex)
{
_logger.LogError(ex, "Critical error in ProcessTrialExpirationsAsync");
throw; // Re-throw para ser tratado pelo ExecuteAsync
}
}
private async Task HandleTrialExpiredAsync(
User user,
Subscription subscription,
IUserPageRepository userPageRepository)
{
// Deactivate user page
var userPage = await userPageRepository.GetByUserIdAsync(user.Id);
if (userPage != null)
{
userPage.IsActive = false;
userPage.UpdatedAt = DateTime.UtcNow;
await userPageRepository.UpdateAsync(userPage);
}
// Update subscription status
subscription.Status = "expired";
subscription.UpdatedAt = DateTime.UtcNow;
using var scope = _serviceProvider.CreateScope();
var subscriptionRepository = scope.ServiceProvider.GetRequiredService<ISubscriptionRepository>();
await subscriptionRepository.UpdateAsync(subscription);
// Send expiration email
await SendTrialExpiredEmailAsync(user);
_logger.LogInformation($"Deactivated trial page for user {user.Email}");
}
private async Task SendExpirationWarningAsync(
User user,
Subscription subscription,
double daysRemaining)
{
// TODO: Implement email service
// For now, just log
_logger.LogInformation($"Should send expiration warning to {user.Email} - {daysRemaining:F1} days remaining");
// Example email content:
var subject = "Seu trial do BCards expira em breve!";
var message = $@"
Olá {user.Name},
Seu trial gratuito do BCards expira em {Math.Ceiling(daysRemaining)} dia(s).
Para continuar usando sua página de links, escolha um de nossos planos:
• Básico - R$ 9,90/mês
• Profissional - R$ 24,90/mês
• Premium - R$ 29,90/mês
Acesse: {GetUpgradeUrl()}
Equipe BCards
";
// TODO: Send actual email when email service is implemented
await Task.CompletedTask;
}
private async Task SendTrialExpiredEmailAsync(User user)
{
// TODO: Implement email service
_logger.LogInformation($"Should send trial expired email to {user.Email}");
var subject = "Seu trial do BCards expirou";
var message = $@"
Olá {user.Name},
Seu trial gratuito do BCards expirou e sua página foi temporariamente desativada.
Para reativar sua página, escolha um de nossos planos:
• Básico - R$ 9,90/mês - 5 links, analytics básicos
• Profissional - R$ 24,90/mês - 15 links, todos os temas, analytics avançados
• Premium - R$ 29,90/mês - Links ilimitados, temas premium, analytics completos
Seus dados estão seguros e serão restaurados assim que você escolher um plano.
Acesse: {GetUpgradeUrl()}
Equipe BCards
";
// TODO: Send actual email when email service is implemented
await Task.CompletedTask;
}
private string GetUpgradeUrl()
{
// TODO: Get from configuration
return "https://bcards.com.br/pricing";
}
}