fix: planos de pagamentos associados ao appSettings
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 14m57s
BCards Deployment Pipeline / Deploy to Production (ARM - OCI) (push) Successful in 2m39s
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

This commit is contained in:
Ricardo Carneiro 2025-09-09 18:51:48 -03:00
parent ad8cc06fd6
commit 61453b65c9
7 changed files with 336 additions and 108 deletions

View File

@ -17,6 +17,7 @@ public class StripeWebhookController : ControllerBase
private readonly ISubscriptionRepository _subscriptionRepository; private readonly ISubscriptionRepository _subscriptionRepository;
private readonly IUserPageService _userPageService; private readonly IUserPageService _userPageService;
private readonly IUserRepository _userRepository; private readonly IUserRepository _userRepository;
private readonly IPlanConfigurationService _planConfigurationService;
private readonly string _webhookSecret; private readonly string _webhookSecret;
public StripeWebhookController( public StripeWebhookController(
@ -24,12 +25,14 @@ public class StripeWebhookController : ControllerBase
ISubscriptionRepository subscriptionRepository, ISubscriptionRepository subscriptionRepository,
IUserPageService userPageService, IUserPageService userPageService,
IUserRepository userRepository, IUserRepository userRepository,
IPlanConfigurationService planConfigurationService,
IOptions<StripeSettings> stripeSettings) IOptions<StripeSettings> stripeSettings)
{ {
_logger = logger; _logger = logger;
_subscriptionRepository = subscriptionRepository; _subscriptionRepository = subscriptionRepository;
_userPageService = userPageService; _userPageService = userPageService;
_userRepository = userRepository; _userRepository = userRepository;
_planConfigurationService = planConfigurationService;
_webhookSecret = stripeSettings.Value.WebhookSecret ?? ""; _webhookSecret = stripeSettings.Value.WebhookSecret ?? "";
} }
@ -253,7 +256,7 @@ public class StripeWebhookController : ControllerBase
var priceId = stripeSubscription.Items.Data.FirstOrDefault()?.Price.Id; var priceId = stripeSubscription.Items.Data.FirstOrDefault()?.Price.Id;
if (!string.IsNullOrEmpty(priceId)) if (!string.IsNullOrEmpty(priceId))
{ {
subscription.PlanType = MapPriceIdToPlanType(priceId); subscription.PlanType = _planConfigurationService.GetPlanNameFromPriceId(priceId);
} }
await _subscriptionRepository.UpdateAsync(subscription); await _subscriptionRepository.UpdateAsync(subscription);
@ -304,7 +307,7 @@ public class StripeWebhookController : ControllerBase
var priceId = stripeSubscription.Items.Data.FirstOrDefault()?.Price.Id; var priceId = stripeSubscription.Items.Data.FirstOrDefault()?.Price.Id;
if (!string.IsNullOrEmpty(priceId)) if (!string.IsNullOrEmpty(priceId))
{ {
subscription.PlanType = MapPriceIdToPlanType(priceId); subscription.PlanType = _planConfigurationService.GetPlanNameFromPriceId(priceId);
} }
await _subscriptionRepository.UpdateAsync(subscription); await _subscriptionRepository.UpdateAsync(subscription);
@ -346,27 +349,6 @@ public class StripeWebhookController : ControllerBase
} }
} }
private string MapPriceIdToPlanType(string priceId)
{
// Map Stripe price IDs to plan types
// This would be configured based on your actual Stripe price IDs
return priceId switch
{
"price_1RjUskBMIadsOxJVgLwlVo1y" => "Basic",
"price_1RjUv9BMIadsOxJVORqlM4E9" => "Professional",
"price_1RjUw0BMIadsOxJVmdouNV1g" => "Premium",
"price_basic_yearly_placeholder" => "BasicYearly",
"price_professional_yearly_placeholder" => "ProfessionalYearly",
"price_premium_yearly_placeholder" => "PremiumYearly",
var id when id.Contains("basic") && id.Contains("yearly") => "BasicYearly",
var id when id.Contains("professional") && id.Contains("yearly") => "ProfessionalYearly",
var id when id.Contains("premium") && id.Contains("yearly") => "PremiumYearly",
var id when id.Contains("basic") => "Basic",
var id when id.Contains("professional") => "Professional",
var id when id.Contains("premium") => "Premium",
_ => "Trial"
};
}
private string GetSubscriptionId(Event stripeEvent) private string GetSubscriptionId(Event stripeEvent)
{ {
@ -403,10 +385,8 @@ public class StripeWebhookController : ControllerBase
try try
{ {
_logger.LogInformation($"[DEBUG] [TID: {traceId}] - HandleSubscriptionCreatedForNewSubscription started for customer: {stripeSubscription.CustomerId}"); _logger.LogInformation($"[DEBUG] [TID: {traceId}] - HandleSubscriptionCreatedForNewSubscription started for customer: {stripeSubscription.CustomerId}");
// Find user by Stripe Customer ID using a MongoDB query
// Since IUserRepository doesn't have GetByStripeCustomerIdAsync, we'll use MongoDB directly
_logger.LogInformation($"[DEBUG] [TID: {traceId}] - Getting MongoDB database"); _logger.LogInformation($"[DEBUG] [TID: {traceId}] - Getting MongoDB database");
var mongoDatabase = HttpContext.RequestServices.GetRequiredService<IMongoDatabase>(); var mongoDatabase = HttpContext.RequestServices.GetRequiredService<IMongoDatabase>();
var usersCollection = mongoDatabase.GetCollection<Models.User>("users"); var usersCollection = mongoDatabase.GetCollection<Models.User>("users");
@ -423,7 +403,7 @@ public class StripeWebhookController : ControllerBase
// Get plan type from price ID // Get plan type from price ID
var priceId = stripeSubscription.Items.Data.FirstOrDefault()?.Price.Id; var priceId = stripeSubscription.Items.Data.FirstOrDefault()?.Price.Id;
var planType = !string.IsNullOrEmpty(priceId) ? MapPriceIdToPlanType(priceId) : "Trial"; var planType = !string.IsNullOrEmpty(priceId) ? _planConfigurationService.GetPlanNameFromPriceId(priceId) : "Trial";
_logger.LogInformation($"[TID: {traceId}] - PriceId: {priceId}, PlanType: {planType}, User: {user.Id}"); _logger.LogInformation($"[TID: {traceId}] - PriceId: {priceId}, PlanType: {planType}, User: {user.Id}");
@ -446,7 +426,6 @@ public class StripeWebhookController : ControllerBase
await _subscriptionRepository.CreateAsync(newSubscription); await _subscriptionRepository.CreateAsync(newSubscription);
_logger.LogInformation($"[TID: {traceId}] - Created new subscription {newSubscription.Id} for user {user.Id}"); _logger.LogInformation($"[TID: {traceId}] - Created new subscription {newSubscription.Id} for user {user.Id}");
// 🔥 CORREÇÃO: Update user's CurrentPlan
_logger.LogInformation($"[TID: {traceId}] - Updating user {user.Id} CurrentPlan from '{user.CurrentPlan}' to '{planType}'"); _logger.LogInformation($"[TID: {traceId}] - Updating user {user.Id} CurrentPlan from '{user.CurrentPlan}' to '{planType}'");
user.CurrentPlan = planType; user.CurrentPlan = planType;
user.UpdatedAt = DateTime.UtcNow; user.UpdatedAt = DateTime.UtcNow;
@ -455,7 +434,6 @@ public class StripeWebhookController : ControllerBase
await usersCollection2.ReplaceOneAsync(u => u.Id == user.Id, user); await usersCollection2.ReplaceOneAsync(u => u.Id == user.Id, user);
_logger.LogInformation($"[TID: {traceId}] - User {user.Id} CurrentPlan updated to '{planType}'"); _logger.LogInformation($"[TID: {traceId}] - User {user.Id} CurrentPlan updated to '{planType}'");
// Activate user pages that were pending payment or expired
var userPages = await _userPageService.GetUserPagesAsync(user.Id); var userPages = await _userPageService.GetUserPagesAsync(user.Id);
foreach (var page in userPages.Where(p => foreach (var page in userPages.Where(p =>
p.Status == ViewModels.PageStatus.PendingPayment || p.Status == ViewModels.PageStatus.PendingPayment ||
@ -473,7 +451,6 @@ public class StripeWebhookController : ControllerBase
_logger.LogError($"[DEBUG] [TID: {traceId}] - User not found for Stripe customer ID: {stripeSubscription.CustomerId}"); _logger.LogError($"[DEBUG] [TID: {traceId}] - User not found for Stripe customer ID: {stripeSubscription.CustomerId}");
_logger.LogInformation($"[DEBUG] [TID: {traceId}] - Will try to list some users to debug"); _logger.LogInformation($"[DEBUG] [TID: {traceId}] - Will try to list some users to debug");
// Debug: list some users to see what we have
var allUsers = await usersCollection.Find(_ => true).Limit(5).ToListAsync(); var allUsers = await usersCollection.Find(_ => true).Limit(5).ToListAsync();
foreach (var u in allUsers) foreach (var u in allUsers)
{ {

View File

@ -3,9 +3,10 @@ namespace BCards.Web.Models;
public enum PlanType public enum PlanType
{ {
Trial = 0, // Gratuito por 7 dias Trial = 0, // Gratuito por 7 dias
Basic = 1, // R$ 9,90 Basic = 1,
Professional = 2, // R$ 24,90 (Decoy) Professional = 2,
Premium = 3 // R$ 29,90 Premium = 4,
PremiumAffiliate = 5
} }
public static class PlanTypeExtensions public static class PlanTypeExtensions
@ -18,22 +19,28 @@ public static class PlanTypeExtensions
PlanType.Basic => "Básico", PlanType.Basic => "Básico",
PlanType.Professional => "Profissional", PlanType.Professional => "Profissional",
PlanType.Premium => "Premium", PlanType.Premium => "Premium",
PlanType.PremiumAffiliate => "PremiumAffiliate",
_ => "Desconhecido" _ => "Desconhecido"
}; };
} }
// NOTA: Preços agora são configurados dinamicamente via IPlanConfigurationService
// Este método mantém valores fallback para compatibilidade
public static decimal GetPrice(this PlanType planType) public static decimal GetPrice(this PlanType planType)
{ {
return planType switch return planType switch
{ {
PlanType.Trial => 0.00m, PlanType.Trial => 0.00m,
PlanType.Basic => 9.90m, PlanType.Basic => 5.90m,
PlanType.Professional => 24.90m, PlanType.Professional => 12.90m,
PlanType.Premium => 29.90m, PlanType.Premium => 19.90m,
PlanType.PremiumAffiliate => 29.90m,
_ => 0.00m _ => 0.00m
}; };
} }
// NOTA: Limitações agora são configuradas dinamicamente via IPlanConfigurationService
// Este método mantém valores fallback para compatibilidade
public static int GetMaxPages(this PlanType planType) public static int GetMaxPages(this PlanType planType)
{ {
return planType switch return planType switch
@ -42,10 +49,13 @@ public static class PlanTypeExtensions
PlanType.Basic => 3, PlanType.Basic => 3,
PlanType.Professional => 5, // DECOY - not attractive PlanType.Professional => 5, // DECOY - not attractive
PlanType.Premium => 15, PlanType.Premium => 15,
PlanType.PremiumAffiliate => 15,
_ => 1 _ => 1
}; };
} }
// NOTA: Limitações agora são configuradas dinamicamente via IPlanConfigurationService
// Este método mantém valores fallback para compatibilidade
public static int GetMaxLinksPerPage(this PlanType planType) public static int GetMaxLinksPerPage(this PlanType planType)
{ {
return planType switch return planType switch
@ -54,6 +64,7 @@ public static class PlanTypeExtensions
PlanType.Basic => 8, PlanType.Basic => 8,
PlanType.Professional => 20, // DECOY - too expensive for the benefit PlanType.Professional => 20, // DECOY - too expensive for the benefit
PlanType.Premium => int.MaxValue, // Unlimited PlanType.Premium => int.MaxValue, // Unlimited
PlanType.PremiumAffiliate => int.MaxValue, // Unlimited
_ => 3 _ => 3
}; };
} }
@ -71,6 +82,7 @@ public static class PlanTypeExtensions
PlanType.Basic => true, PlanType.Basic => true,
PlanType.Professional => true, PlanType.Professional => true,
PlanType.Premium => true, PlanType.Premium => true,
PlanType.PremiumAffiliate => true,
_ => false _ => false
}; };
} }
@ -83,6 +95,7 @@ public static class PlanTypeExtensions
PlanType.Basic => false, PlanType.Basic => false,
PlanType.Professional => true, PlanType.Professional => true,
PlanType.Premium => true, PlanType.Premium => true,
PlanType.PremiumAffiliate => true,
_ => false _ => false
}; };
} }
@ -96,10 +109,11 @@ public static class PlanTypeExtensions
{ {
return planType switch return planType switch
{ {
PlanType.Trial => 1, // 1 link de produto para trial PlanType.Trial => 0, // 1 link de produto para trial
PlanType.Basic => 3, // 3 links de produto PlanType.Basic => 0, // 3 links de produto
PlanType.Professional => 8, // DECOY - mais caro para poucos benefícios PlanType.Professional => 0, // DECOY - mais caro para poucos benefícios
PlanType.Premium => int.MaxValue, // Ilimitado PlanType.Premium => 0, // Ilimitado
PlanType.PremiumAffiliate => int.MaxValue, // Ilimitado
_ => 0 _ => 0
}; };
} }
@ -108,10 +122,11 @@ public static class PlanTypeExtensions
{ {
return planType switch return planType switch
{ {
PlanType.Trial => 2, // 2 extrações por dia no trial PlanType.Trial => 0, // 2 extrações por dia no trial
PlanType.Basic => 5, // 5 extrações por dia PlanType.Basic => 0, // 5 extrações por dia
PlanType.Professional => 15, // 15 extrações por dia PlanType.Professional => 0, // 15 extrações por dia
PlanType.Premium => int.MaxValue, // Ilimitado PlanType.Premium => 0, // Ilimitado
PlanType.PremiumAffiliate => int.MaxValue, // Ilimitado
_ => 0 _ => 0
}; };
} }

View File

@ -401,6 +401,7 @@ builder.Services.AddScoped<IUserPageService, UserPageService>();
builder.Services.AddScoped<IThemeService, ThemeService>(); builder.Services.AddScoped<IThemeService, ThemeService>();
builder.Services.AddScoped<ISeoService, SeoService>(); builder.Services.AddScoped<ISeoService, SeoService>();
builder.Services.AddScoped<IAuthService, AuthService>(); builder.Services.AddScoped<IAuthService, AuthService>();
builder.Services.AddSingleton<IPlanConfigurationService, PlanConfigurationService>();
builder.Services.AddScoped<IPaymentService, PaymentService>(); builder.Services.AddScoped<IPaymentService, PaymentService>();
builder.Services.AddScoped<ICategoryService, CategoryService>(); builder.Services.AddScoped<ICategoryService, CategoryService>();
builder.Services.AddScoped<IOpenGraphService, OpenGraphService>(); builder.Services.AddScoped<IOpenGraphService, OpenGraphService>();

View File

@ -0,0 +1,57 @@
using BCards.Web.Models;
namespace BCards.Web.Services;
public interface IPlanConfigurationService
{
/// <summary>
/// Mapeia um PriceId do Stripe para o PlanType correspondente
/// </summary>
PlanType GetPlanTypeFromPriceId(string priceId);
/// <summary>
/// Mapeia um PriceId do Stripe para o nome string do plano
/// </summary>
string GetPlanNameFromPriceId(string priceId);
/// <summary>
/// Obtém as limitações de um plano baseado no PlanType
/// </summary>
PlanLimitations GetPlanLimitations(PlanType planType);
/// <summary>
/// Obtém o PriceId de um plano (mensal por padrão)
/// </summary>
string GetPriceId(PlanType planType, bool yearly = false);
/// <summary>
/// Obtém o preço de um plano
/// </summary>
decimal GetPlanPrice(PlanType planType, bool yearly = false);
/// <summary>
/// Verifica se um plano é anual baseado no PriceId
/// </summary>
bool IsYearlyPlan(string priceId);
/// <summary>
/// Obtém todas as configurações de um plano pelo nome da seção
/// </summary>
PlanConfiguration? GetPlanConfiguration(string planSectionName);
}
public class PlanConfiguration
{
public string Name { get; set; } = string.Empty;
public string PriceId { get; set; } = string.Empty;
public decimal Price { get; set; }
public int MaxPages { get; set; }
public int MaxLinks { get; set; }
public bool AllowPremiumThemes { get; set; }
public bool AllowProductLinks { get; set; }
public bool AllowAnalytics { get; set; }
public bool? SpecialModeration { get; set; }
public List<string> Features { get; set; } = new();
public string Interval { get; set; } = "month";
public PlanType BasePlanType { get; set; }
}

View File

@ -15,17 +15,20 @@ public class PaymentService : IPaymentService
private readonly IUserRepository _userRepository; private readonly IUserRepository _userRepository;
private readonly ISubscriptionRepository _subscriptionRepository; private readonly ISubscriptionRepository _subscriptionRepository;
private readonly IConfiguration _configuration; private readonly IConfiguration _configuration;
private readonly IPlanConfigurationService _planConfigurationService;
public PaymentService( public PaymentService(
IOptions<StripeSettings> stripeSettings, IOptions<StripeSettings> stripeSettings,
IUserRepository userRepository, IUserRepository userRepository,
ISubscriptionRepository subscriptionRepository, ISubscriptionRepository subscriptionRepository,
IConfiguration configuration) IConfiguration configuration,
IPlanConfigurationService planConfigurationService)
{ {
_stripeSettings = stripeSettings.Value; _stripeSettings = stripeSettings.Value;
_userRepository = userRepository; _userRepository = userRepository;
_subscriptionRepository = subscriptionRepository; _subscriptionRepository = subscriptionRepository;
_configuration = configuration; _configuration = configuration;
_planConfigurationService = planConfigurationService;
StripeConfiguration.ApiKey = _stripeSettings.SecretKey; StripeConfiguration.ApiKey = _stripeSettings.SecretKey;
} }
@ -201,70 +204,17 @@ public class PaymentService : IPaymentService
public Task<PlanLimitations> GetPlanLimitationsAsync(string planType) public Task<PlanLimitations> GetPlanLimitationsAsync(string planType)
{ {
var limitations = planType.ToLower() switch // Mapear string planType para PlanType enum
var planTypeEnum = planType.ToLower() switch
{ {
"basic" => new PlanLimitations "basic" or "basicyearly" => PlanType.Basic,
{ "professional" or "professionalyearly" => PlanType.Professional,
MaxLinks = 8, "premium" or "premiumyearly" => PlanType.Premium,
AllowCustomThemes = false, "premiumaffiliate" or "premiumaffiliateyearly" => PlanType.PremiumAffiliate,
AllowAnalytics = true, _ => PlanType.Trial
AllowCustomDomain = true, // URL personalizada em todos os planos pagos
AllowMultipleDomains = false,
PrioritySupport = false,
AllowProductLinks = false,
MaxProductLinks = 0,
PlanType = "basic"
},
"professional" => new PlanLimitations
{
MaxLinks = 20,
AllowCustomThemes = false,
AllowAnalytics = true,
AllowCustomDomain = true,
AllowMultipleDomains = false,
PrioritySupport = false,
AllowProductLinks = false,
MaxProductLinks = 0,
PlanType = "professional"
},
"premium" => new PlanLimitations
{
MaxLinks = -1, // Unlimited
AllowCustomThemes = true,
AllowAnalytics = true,
AllowCustomDomain = true,
AllowMultipleDomains = true,
PrioritySupport = true,
AllowProductLinks = false,
MaxProductLinks = 0,
PlanType = "premium"
},
"premiumaffiliate" => new PlanLimitations
{
MaxLinks = -1, // Unlimited
AllowCustomThemes = true,
AllowAnalytics = true,
AllowCustomDomain = true,
AllowMultipleDomains = true,
PrioritySupport = true,
AllowProductLinks = true,
MaxProductLinks = 10,
PlanType = "premiumaffiliate"
},
_ => new PlanLimitations
{
MaxLinks = 3,
AllowCustomThemes = false,
AllowAnalytics = false,
AllowCustomDomain = false,
AllowMultipleDomains = false,
PrioritySupport = false,
AllowProductLinks = false,
MaxProductLinks = 0,
PlanType = "trial"
}
}; };
var limitations = _planConfigurationService.GetPlanLimitations(planTypeEnum);
return Task.FromResult(limitations); return Task.FromResult(limitations);
} }

View File

@ -0,0 +1,228 @@
using BCards.Web.Models;
namespace BCards.Web.Services;
public class PlanConfigurationService : IPlanConfigurationService
{
private readonly IConfiguration _configuration;
private readonly Dictionary<string, PlanConfiguration> _plans;
private readonly Dictionary<string, string> _priceIdToPlanName;
private readonly Dictionary<string, PlanType> _priceIdToPlanType;
public PlanConfigurationService(IConfiguration configuration)
{
_configuration = configuration;
_plans = LoadPlansFromConfiguration();
_priceIdToPlanName = BuildPriceIdToPlanNameMap();
_priceIdToPlanType = BuildPriceIdToPlanTypeMap();
}
public PlanType GetPlanTypeFromPriceId(string priceId)
{
if (string.IsNullOrEmpty(priceId))
return PlanType.Trial;
return _priceIdToPlanType.TryGetValue(priceId, out var planType) ? planType : PlanType.Trial;
}
public string GetPlanNameFromPriceId(string priceId)
{
if (string.IsNullOrEmpty(priceId))
return "Trial";
return _priceIdToPlanName.TryGetValue(priceId, out var planName) ? planName : "Trial";
}
public PlanLimitations GetPlanLimitations(PlanType planType)
{
return planType switch
{
PlanType.Trial => new PlanLimitations
{
MaxLinks = 3,
AllowCustomThemes = false,
AllowAnalytics = false,
AllowCustomDomain = false,
AllowMultipleDomains = false,
PrioritySupport = false,
AllowProductLinks = false,
MaxProductLinks = 0,
PlanType = "trial"
},
PlanType.Basic => new PlanLimitations
{
MaxLinks = GetConfigValue(PlanType.Basic, "MaxLinks", 8),
AllowCustomThemes = GetConfigValue(PlanType.Basic, "AllowPremiumThemes", false),
AllowAnalytics = GetConfigValue(PlanType.Basic, "AllowAnalytics", true),
AllowCustomDomain = true,
AllowMultipleDomains = false,
PrioritySupport = false,
AllowProductLinks = GetConfigValue(PlanType.Basic, "AllowProductLinks", false),
MaxProductLinks = 0,
PlanType = "basic"
},
PlanType.Professional => new PlanLimitations
{
MaxLinks = GetConfigValue(PlanType.Professional, "MaxLinks", 20),
AllowCustomThemes = GetConfigValue(PlanType.Professional, "AllowPremiumThemes", false),
AllowAnalytics = GetConfigValue(PlanType.Professional, "AllowAnalytics", true),
AllowCustomDomain = true,
AllowMultipleDomains = false,
PrioritySupport = false,
AllowProductLinks = GetConfigValue(PlanType.Professional, "AllowProductLinks", false),
MaxProductLinks = 0,
PlanType = "professional"
},
PlanType.Premium => new PlanLimitations
{
MaxLinks = GetConfigValue(PlanType.Premium, "MaxLinks", -1),
AllowCustomThemes = GetConfigValue(PlanType.Premium, "AllowPremiumThemes", true),
AllowAnalytics = GetConfigValue(PlanType.Premium, "AllowAnalytics", true),
AllowCustomDomain = true,
AllowMultipleDomains = true,
PrioritySupport = true,
AllowProductLinks = GetConfigValue(PlanType.Premium, "AllowProductLinks", false),
MaxProductLinks = 0,
PlanType = "premium"
},
PlanType.PremiumAffiliate => new PlanLimitations
{
MaxLinks = GetConfigValue(PlanType.PremiumAffiliate, "MaxLinks", -1),
AllowCustomThemes = GetConfigValue(PlanType.PremiumAffiliate, "AllowPremiumThemes", true),
AllowAnalytics = GetConfigValue(PlanType.PremiumAffiliate, "AllowAnalytics", true),
AllowCustomDomain = true,
AllowMultipleDomains = true,
PrioritySupport = true,
AllowProductLinks = GetConfigValue(PlanType.PremiumAffiliate, "AllowProductLinks", true),
MaxProductLinks = 10,
PlanType = "premiumaffiliate"
},
_ => new PlanLimitations { PlanType = "trial" }
};
}
public string GetPriceId(PlanType planType, bool yearly = false)
{
var planName = planType switch
{
PlanType.Basic => yearly ? "BasicYearly" : "Basic",
PlanType.Professional => yearly ? "ProfessionalYearly" : "Professional",
PlanType.Premium => yearly ? "PremiumYearly" : "Premium",
PlanType.PremiumAffiliate => yearly ? "PremiumAffiliateYearly" : "PremiumAffiliate",
_ => "Trial"
};
return _plans.TryGetValue(planName, out var config) ? config.PriceId : string.Empty;
}
public decimal GetPlanPrice(PlanType planType, bool yearly = false)
{
var planName = planType switch
{
PlanType.Basic => yearly ? "BasicYearly" : "Basic",
PlanType.Professional => yearly ? "ProfessionalYearly" : "Professional",
PlanType.Premium => yearly ? "PremiumYearly" : "Premium",
PlanType.PremiumAffiliate => yearly ? "PremiumAffiliateYearly" : "PremiumAffiliate",
_ => "Trial"
};
return _plans.TryGetValue(planName, out var config) ? config.Price : 0;
}
public bool IsYearlyPlan(string priceId)
{
if (string.IsNullOrEmpty(priceId) || !_priceIdToPlanName.TryGetValue(priceId, out var planName))
return false;
return _plans.TryGetValue(planName, out var config) && config.Interval == "year";
}
public PlanConfiguration? GetPlanConfiguration(string planSectionName)
{
return _plans.TryGetValue(planSectionName, out var config) ? config : null;
}
private Dictionary<string, PlanConfiguration> LoadPlansFromConfiguration()
{
var plans = new Dictionary<string, PlanConfiguration>();
var plansSection = _configuration.GetSection("Plans");
foreach (var planSection in plansSection.GetChildren())
{
var config = new PlanConfiguration();
planSection.Bind(config);
// Mapear o nome da seção para PlanType base
config.BasePlanType = planSection.Key switch
{
"Basic" or "BasicYearly" => PlanType.Basic,
"Professional" or "ProfessionalYearly" => PlanType.Professional,
"Premium" or "PremiumYearly" => PlanType.Premium,
"PremiumAffiliate" or "PremiumAffiliateYearly" => PlanType.PremiumAffiliate,
_ => PlanType.Trial
};
plans[planSection.Key] = config;
}
return plans;
}
private Dictionary<string, string> BuildPriceIdToPlanNameMap()
{
var map = new Dictionary<string, string>();
foreach (var kvp in _plans)
{
if (!string.IsNullOrEmpty(kvp.Value.PriceId))
{
map[kvp.Value.PriceId] = kvp.Key;
}
}
return map;
}
private Dictionary<string, PlanType> BuildPriceIdToPlanTypeMap()
{
var map = new Dictionary<string, PlanType>();
foreach (var kvp in _plans)
{
if (!string.IsNullOrEmpty(kvp.Value.PriceId))
{
map[kvp.Value.PriceId] = kvp.Value.BasePlanType;
}
}
return map;
}
private T GetConfigValue<T>(PlanType planType, string propertyName, T defaultValue)
{
// Buscar primeira nas configurações mensais, depois anuais
var monthlyPlan = planType switch
{
PlanType.Basic => "Basic",
PlanType.Professional => "Professional",
PlanType.Premium => "Premium",
PlanType.PremiumAffiliate => "PremiumAffiliate",
_ => null
};
if (monthlyPlan != null && _plans.TryGetValue(monthlyPlan, out var config))
{
var property = typeof(PlanConfiguration).GetProperty(propertyName);
if (property != null)
{
var value = property.GetValue(config);
if (value != null && value is T typedValue)
{
return typedValue;
}
}
}
return defaultValue;
}
}