using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using NexusReader.Application.Abstractions.Services; using NexusReader.Domain.Entities; using NexusReader.Infrastructure.Configuration; using NexusReader.Data.Persistence; namespace NexusReader.Infrastructure.Services; public class BillingService : IBillingService { private readonly IDbContextFactory _dbContextFactory; private readonly UserManager _userManager; private readonly StripeSettings _stripeSettings; private readonly ILogger _logger; public BillingService( IDbContextFactory dbContextFactory, UserManager userManager, IOptions stripeSettings, ILogger logger) { _dbContextFactory = dbContextFactory; _userManager = userManager; _stripeSettings = stripeSettings.Value; _logger = logger; } public async Task HandleSubscriptionUpdatedAsync(string customerEmail, string stripeProductId) { var user = await _userManager.FindByEmailAsync(customerEmail); if (user == null) { _logger.LogWarning("Attempted to update subscription for non-existent user: {Email}", customerEmail); return false; } string targetPlanName = SubscriptionPlan.FreeName; int tokenLimit = 1000; if (stripeProductId == _stripeSettings.ProProductId) { targetPlanName = SubscriptionPlan.ProName; tokenLimit = 50000; } else if (stripeProductId == _stripeSettings.BasicProductId) { targetPlanName = SubscriptionPlan.BasicName; tokenLimit = 10000; } else if (!string.IsNullOrEmpty(stripeProductId) && stripeProductId != _stripeSettings.FreeProductId) { _logger.LogWarning("Unrecognized Stripe Product ID: {ProductId} for user {Email}. Falling back to Free tier.", stripeProductId, customerEmail); } using var dbContext = await _dbContextFactory.CreateDbContextAsync(); var plan = await dbContext.SubscriptionPlans.FirstOrDefaultAsync(p => p.PlanName == targetPlanName); if (plan != null) { user.SubscriptionPlanId = plan.Id; user.AITokenLimit = tokenLimit; } var result = await _userManager.UpdateAsync(user); if (!result.Succeeded) { _logger.LogError("Failed to update user {Email} after subscription change: {Errors}", customerEmail, string.Join(", ", result.Errors.Select(e => e.Description))); return false; } return true; } public async Task HandleSubscriptionDeletedAsync(string customerEmail) { var user = await _userManager.FindByEmailAsync(customerEmail); if (user == null) { _logger.LogWarning("Attempted to delete subscription for non-existent user: {Email}", customerEmail); return false; } using var dbContext = await _dbContextFactory.CreateDbContextAsync(); var freePlan = await dbContext.SubscriptionPlans.FirstOrDefaultAsync(p => p.PlanName == SubscriptionPlan.FreeName); if (freePlan != null) { user.SubscriptionPlanId = freePlan.Id; user.AITokenLimit = freePlan.AITokenLimit; } var result = await _userManager.UpdateAsync(user); if (!result.Succeeded) { _logger.LogError("Failed to reset user {Email} to Free tier after subscription deletion: {Errors}", customerEmail, string.Join(", ", result.Errors.Select(e => e.Description))); return false; } return true; } }