using System; using System.Linq; using System.Threading.Tasks; using ManagerService.Data; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; namespace ManagerService.Services { /// /// Daily Hangfire job driving the 14-day Essentiel trial: check-in (J+7), ending reminder /// (J+10), last-day warning (J+14), then automatic deactivation if no subscription was /// started. Flags on (TrialCheckInEmailSent, etc.) prevent sending /// the same email twice if the job runs more than once on the same day. /// public class TrialLifecycleService { private const int CheckInAfterDays = 7; private const int ReminderAfterDays = 10; private const int LastDayAfterDays = 13; // day before the 14-day trial ends private readonly ILogger _logger; private readonly IServiceScopeFactory _scopeFactory; public TrialLifecycleService(ILogger logger, IServiceScopeFactory scopeFactory) { _logger = logger; _scopeFactory = scopeFactory; } public async Task RunAsync() { using var scope = _scopeFactory.CreateScope(); var db = scope.ServiceProvider.GetRequiredService(); var emailService = scope.ServiceProvider.GetRequiredService(); var configuration = scope.ServiceProvider.GetRequiredService(); var managerAppUrl = configuration["AppUrls:ManagerApp"]; var now = DateTime.UtcNow; var trialInstances = db.Instances.Where(i => i.IsTrialActive).ToList(); foreach (var instance in trialInstances) { try { await ProcessInstanceAsync(instance, db, emailService, managerAppUrl, now); } catch (Exception ex) { _logger.LogError(ex, "Error processing trial lifecycle for instance {InstanceId}", instance.Id); } } } private async Task ProcessInstanceAsync(Instance instance, MyInfoMateDbContext db, IEmailService emailService, string managerAppUrl, DateTime now) { var daysSinceCreation = (now - instance.DateCreation).TotalDays; var user = db.Users.FirstOrDefault(u => u.InstanceId == instance.Id); // Trial expired without an active subscription -> deactivate if (instance.TrialEndsAt != null && instance.TrialEndsAt < now && string.IsNullOrEmpty(instance.StripeSubscriptionId)) { instance.IsTrialActive = false; instance.IsActive = false; db.SaveChanges(); return; } if (user == null) return; if (daysSinceCreation >= CheckInAfterDays && !instance.TrialCheckInEmailSent) { await emailService.SendTrialCheckInEmailAsync(user.Email, user.FirstName); instance.TrialCheckInEmailSent = true; db.SaveChanges(); } if (daysSinceCreation >= ReminderAfterDays && !instance.TrialReminderEmailSent && instance.TrialEndsAt != null) { var checkoutUrl = $"{managerAppUrl}/main/web"; await emailService.SendTrialEndingReminderEmailAsync(user.Email, user.FirstName, instance.TrialEndsAt.Value, checkoutUrl); instance.TrialReminderEmailSent = true; db.SaveChanges(); } if (daysSinceCreation >= LastDayAfterDays && !instance.TrialLastDayEmailSent) { var checkoutUrl = $"{managerAppUrl}/main/web"; await emailService.SendTrialLastDayEmailAsync(user.Email, user.FirstName, checkoutUrl); instance.TrialLastDayEmailSent = true; db.SaveChanges(); } } } }