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
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:
parent
ad8cc06fd6
commit
61453b65c9
@ -17,6 +17,7 @@ public class StripeWebhookController : ControllerBase
|
||||
private readonly ISubscriptionRepository _subscriptionRepository;
|
||||
private readonly IUserPageService _userPageService;
|
||||
private readonly IUserRepository _userRepository;
|
||||
private readonly IPlanConfigurationService _planConfigurationService;
|
||||
private readonly string _webhookSecret;
|
||||
|
||||
public StripeWebhookController(
|
||||
@ -24,12 +25,14 @@ public class StripeWebhookController : ControllerBase
|
||||
ISubscriptionRepository subscriptionRepository,
|
||||
IUserPageService userPageService,
|
||||
IUserRepository userRepository,
|
||||
IPlanConfigurationService planConfigurationService,
|
||||
IOptions<StripeSettings> stripeSettings)
|
||||
{
|
||||
_logger = logger;
|
||||
_subscriptionRepository = subscriptionRepository;
|
||||
_userPageService = userPageService;
|
||||
_userRepository = userRepository;
|
||||
_planConfigurationService = planConfigurationService;
|
||||
_webhookSecret = stripeSettings.Value.WebhookSecret ?? "";
|
||||
}
|
||||
|
||||
@ -253,7 +256,7 @@ public class StripeWebhookController : ControllerBase
|
||||
var priceId = stripeSubscription.Items.Data.FirstOrDefault()?.Price.Id;
|
||||
if (!string.IsNullOrEmpty(priceId))
|
||||
{
|
||||
subscription.PlanType = MapPriceIdToPlanType(priceId);
|
||||
subscription.PlanType = _planConfigurationService.GetPlanNameFromPriceId(priceId);
|
||||
}
|
||||
|
||||
await _subscriptionRepository.UpdateAsync(subscription);
|
||||
@ -304,7 +307,7 @@ public class StripeWebhookController : ControllerBase
|
||||
var priceId = stripeSubscription.Items.Data.FirstOrDefault()?.Price.Id;
|
||||
if (!string.IsNullOrEmpty(priceId))
|
||||
{
|
||||
subscription.PlanType = MapPriceIdToPlanType(priceId);
|
||||
subscription.PlanType = _planConfigurationService.GetPlanNameFromPriceId(priceId);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
@ -403,10 +385,8 @@ public class StripeWebhookController : ControllerBase
|
||||
try
|
||||
{
|
||||
_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");
|
||||
|
||||
var mongoDatabase = HttpContext.RequestServices.GetRequiredService<IMongoDatabase>();
|
||||
var usersCollection = mongoDatabase.GetCollection<Models.User>("users");
|
||||
|
||||
@ -423,7 +403,7 @@ public class StripeWebhookController : ControllerBase
|
||||
|
||||
// Get plan type from 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}");
|
||||
|
||||
@ -446,7 +426,6 @@ public class StripeWebhookController : ControllerBase
|
||||
await _subscriptionRepository.CreateAsync(newSubscription);
|
||||
_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}'");
|
||||
user.CurrentPlan = planType;
|
||||
user.UpdatedAt = DateTime.UtcNow;
|
||||
@ -455,7 +434,6 @@ public class StripeWebhookController : ControllerBase
|
||||
await usersCollection2.ReplaceOneAsync(u => u.Id == user.Id, user);
|
||||
_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);
|
||||
foreach (var page in userPages.Where(p =>
|
||||
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.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();
|
||||
foreach (var u in allUsers)
|
||||
{
|
||||
|
||||
@ -3,9 +3,10 @@ namespace BCards.Web.Models;
|
||||
public enum PlanType
|
||||
{
|
||||
Trial = 0, // Gratuito por 7 dias
|
||||
Basic = 1, // R$ 9,90
|
||||
Professional = 2, // R$ 24,90 (Decoy)
|
||||
Premium = 3 // R$ 29,90
|
||||
Basic = 1,
|
||||
Professional = 2,
|
||||
Premium = 4,
|
||||
PremiumAffiliate = 5
|
||||
}
|
||||
|
||||
public static class PlanTypeExtensions
|
||||
@ -18,22 +19,28 @@ public static class PlanTypeExtensions
|
||||
PlanType.Basic => "Básico",
|
||||
PlanType.Professional => "Profissional",
|
||||
PlanType.Premium => "Premium",
|
||||
PlanType.PremiumAffiliate => "PremiumAffiliate",
|
||||
_ => "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)
|
||||
{
|
||||
return planType switch
|
||||
{
|
||||
PlanType.Trial => 0.00m,
|
||||
PlanType.Basic => 9.90m,
|
||||
PlanType.Professional => 24.90m,
|
||||
PlanType.Premium => 29.90m,
|
||||
PlanType.Basic => 5.90m,
|
||||
PlanType.Professional => 12.90m,
|
||||
PlanType.Premium => 19.90m,
|
||||
PlanType.PremiumAffiliate => 29.90m,
|
||||
_ => 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)
|
||||
{
|
||||
return planType switch
|
||||
@ -42,10 +49,13 @@ public static class PlanTypeExtensions
|
||||
PlanType.Basic => 3,
|
||||
PlanType.Professional => 5, // DECOY - not attractive
|
||||
PlanType.Premium => 15,
|
||||
PlanType.PremiumAffiliate => 15,
|
||||
_ => 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)
|
||||
{
|
||||
return planType switch
|
||||
@ -54,6 +64,7 @@ public static class PlanTypeExtensions
|
||||
PlanType.Basic => 8,
|
||||
PlanType.Professional => 20, // DECOY - too expensive for the benefit
|
||||
PlanType.Premium => int.MaxValue, // Unlimited
|
||||
PlanType.PremiumAffiliate => int.MaxValue, // Unlimited
|
||||
_ => 3
|
||||
};
|
||||
}
|
||||
@ -71,6 +82,7 @@ public static class PlanTypeExtensions
|
||||
PlanType.Basic => true,
|
||||
PlanType.Professional => true,
|
||||
PlanType.Premium => true,
|
||||
PlanType.PremiumAffiliate => true,
|
||||
_ => false
|
||||
};
|
||||
}
|
||||
@ -83,6 +95,7 @@ public static class PlanTypeExtensions
|
||||
PlanType.Basic => false,
|
||||
PlanType.Professional => true,
|
||||
PlanType.Premium => true,
|
||||
PlanType.PremiumAffiliate => true,
|
||||
_ => false
|
||||
};
|
||||
}
|
||||
@ -96,10 +109,11 @@ public static class PlanTypeExtensions
|
||||
{
|
||||
return planType switch
|
||||
{
|
||||
PlanType.Trial => 1, // 1 link de produto para trial
|
||||
PlanType.Basic => 3, // 3 links de produto
|
||||
PlanType.Professional => 8, // DECOY - mais caro para poucos benefícios
|
||||
PlanType.Premium => int.MaxValue, // Ilimitado
|
||||
PlanType.Trial => 0, // 1 link de produto para trial
|
||||
PlanType.Basic => 0, // 3 links de produto
|
||||
PlanType.Professional => 0, // DECOY - mais caro para poucos benefícios
|
||||
PlanType.Premium => 0, // Ilimitado
|
||||
PlanType.PremiumAffiliate => int.MaxValue, // Ilimitado
|
||||
_ => 0
|
||||
};
|
||||
}
|
||||
@ -108,10 +122,11 @@ public static class PlanTypeExtensions
|
||||
{
|
||||
return planType switch
|
||||
{
|
||||
PlanType.Trial => 2, // 2 extrações por dia no trial
|
||||
PlanType.Basic => 5, // 5 extrações por dia
|
||||
PlanType.Professional => 15, // 15 extrações por dia
|
||||
PlanType.Premium => int.MaxValue, // Ilimitado
|
||||
PlanType.Trial => 0, // 2 extrações por dia no trial
|
||||
PlanType.Basic => 0, // 5 extrações por dia
|
||||
PlanType.Professional => 0, // 15 extrações por dia
|
||||
PlanType.Premium => 0, // Ilimitado
|
||||
PlanType.PremiumAffiliate => int.MaxValue, // Ilimitado
|
||||
_ => 0
|
||||
};
|
||||
}
|
||||
|
||||
@ -401,6 +401,7 @@ builder.Services.AddScoped<IUserPageService, UserPageService>();
|
||||
builder.Services.AddScoped<IThemeService, ThemeService>();
|
||||
builder.Services.AddScoped<ISeoService, SeoService>();
|
||||
builder.Services.AddScoped<IAuthService, AuthService>();
|
||||
builder.Services.AddSingleton<IPlanConfigurationService, PlanConfigurationService>();
|
||||
builder.Services.AddScoped<IPaymentService, PaymentService>();
|
||||
builder.Services.AddScoped<ICategoryService, CategoryService>();
|
||||
builder.Services.AddScoped<IOpenGraphService, OpenGraphService>();
|
||||
|
||||
57
src/BCards.Web/Services/IPlanConfigurationService.cs
Normal file
57
src/BCards.Web/Services/IPlanConfigurationService.cs
Normal 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; }
|
||||
}
|
||||
@ -15,17 +15,20 @@ public class PaymentService : IPaymentService
|
||||
private readonly IUserRepository _userRepository;
|
||||
private readonly ISubscriptionRepository _subscriptionRepository;
|
||||
private readonly IConfiguration _configuration;
|
||||
private readonly IPlanConfigurationService _planConfigurationService;
|
||||
|
||||
public PaymentService(
|
||||
IOptions<StripeSettings> stripeSettings,
|
||||
IUserRepository userRepository,
|
||||
ISubscriptionRepository subscriptionRepository,
|
||||
IConfiguration configuration)
|
||||
IConfiguration configuration,
|
||||
IPlanConfigurationService planConfigurationService)
|
||||
{
|
||||
_stripeSettings = stripeSettings.Value;
|
||||
_userRepository = userRepository;
|
||||
_subscriptionRepository = subscriptionRepository;
|
||||
_configuration = configuration;
|
||||
_planConfigurationService = planConfigurationService;
|
||||
|
||||
StripeConfiguration.ApiKey = _stripeSettings.SecretKey;
|
||||
}
|
||||
@ -201,70 +204,17 @@ public class PaymentService : IPaymentService
|
||||
|
||||
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
|
||||
{
|
||||
MaxLinks = 8,
|
||||
AllowCustomThemes = false,
|
||||
AllowAnalytics = true,
|
||||
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"
|
||||
}
|
||||
"basic" or "basicyearly" => PlanType.Basic,
|
||||
"professional" or "professionalyearly" => PlanType.Professional,
|
||||
"premium" or "premiumyearly" => PlanType.Premium,
|
||||
"premiumaffiliate" or "premiumaffiliateyearly" => PlanType.PremiumAffiliate,
|
||||
_ => PlanType.Trial
|
||||
};
|
||||
|
||||
var limitations = _planConfigurationService.GetPlanLimitations(planTypeEnum);
|
||||
return Task.FromResult(limitations);
|
||||
}
|
||||
|
||||
|
||||
228
src/BCards.Web/Services/PlanConfigurationService.cs
Normal file
228
src/BCards.Web/Services/PlanConfigurationService.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
@ -57,7 +57,7 @@
|
||||
"Interval": "month"
|
||||
},
|
||||
"PremiumAffiliate": {
|
||||
"Name": "Premium + Afiliados",
|
||||
"Name": "Premium+Afiliados",
|
||||
"PriceId": "price_1RycTaBMIadsOxJVeDLseXQq",
|
||||
"Price": 29.90,
|
||||
"MaxPages": 15,
|
||||
@ -107,7 +107,7 @@
|
||||
"Interval": "year"
|
||||
},
|
||||
"PremiumAffiliateYearly": {
|
||||
"Name": "Premium + Afiliados Anual",
|
||||
"Name": "Premium+Afiliados Anual",
|
||||
"PriceId": "price_1RycaEBMIadsOxJVEhsdB2Y1",
|
||||
"Price": 299.00,
|
||||
"MaxPages": 15,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user