feat: ajustes de callback do stripe e atualização do stripe.net
This commit is contained in:
parent
c6129a1c63
commit
5fc7eb5ad3
@ -23,7 +23,7 @@
|
|||||||
<PackageReference Include="PuppeteerSharp" Version="13.0.2" />
|
<PackageReference Include="PuppeteerSharp" Version="13.0.2" />
|
||||||
<PackageReference Include="MongoDB.Driver" Version="2.25.0" />
|
<PackageReference Include="MongoDB.Driver" Version="2.25.0" />
|
||||||
<PackageReference Include="Testcontainers.MongoDb" Version="3.6.0" />
|
<PackageReference Include="Testcontainers.MongoDb" Version="3.6.0" />
|
||||||
<PackageReference Include="Stripe.net" Version="44.7.0" />
|
<PackageReference Include="Stripe.net" Version="48.4.0" />
|
||||||
<PackageReference Include="FluentAssertions" Version="6.12.0" />
|
<PackageReference Include="FluentAssertions" Version="6.12.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
|
||||||
|
|||||||
@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="MongoDB.Driver" Version="2.25.0" />
|
<PackageReference Include="MongoDB.Driver" Version="2.25.0" />
|
||||||
<PackageReference Include="Stripe.net" Version="44.7.0" />
|
<PackageReference Include="Stripe.net" Version="48.4.0" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.Google" Version="8.0.4" />
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.Google" Version="8.0.4" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.MicrosoftAccount" Version="8.0.4" />
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.MicrosoftAccount" Version="8.0.4" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Localization" Version="2.2.0" />
|
<PackageReference Include="Microsoft.AspNetCore.Localization" Version="2.2.0" />
|
||||||
|
|||||||
@ -278,7 +278,7 @@ public class AdminController : Controller
|
|||||||
null,
|
null,
|
||||||
previewUrl);
|
previewUrl);
|
||||||
|
|
||||||
TempData["Success"] = "Página atualizada e enviada para moderação!";
|
TempData["Success"] = "Página atualizada! Teste e envie para moderação.";
|
||||||
}
|
}
|
||||||
|
|
||||||
return RedirectToAction("Dashboard");
|
return RedirectToAction("Dashboard");
|
||||||
|
|||||||
@ -54,21 +54,13 @@ public class PaymentController : Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IActionResult> Success()
|
public async Task<IActionResult> Success()
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
var user = await _authService.GetCurrentUserAsync(User);
|
var user = await _authService.GetCurrentUserAsync(User);
|
||||||
var planType = TempData[$"PlanType|{user.Id}"].ToString();
|
var planType = TempData[$"PlanType|{user.Id}"].ToString();
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (!string.IsNullOrEmpty(planType) && Enum.TryParse<PlanType>(planType, out var plan))
|
|
||||||
{
|
|
||||||
user.CurrentPlan = plan.ToString();
|
|
||||||
user.SubscriptionStatus = "active";
|
|
||||||
await _userService.UpdateAsync(user); // ou o método equivalente
|
|
||||||
|
|
||||||
TempData["Success"] = $"Assinatura {planType} ativada com sucesso!";
|
TempData["Success"] = $"Assinatura {planType} ativada com sucesso!";
|
||||||
}
|
|
||||||
|
|
||||||
return RedirectToAction("Dashboard", "Admin");
|
return RedirectToAction("Dashboard", "Admin");
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|||||||
@ -59,19 +59,19 @@ public class StripeWebhookController : ControllerBase
|
|||||||
|
|
||||||
switch (stripeEvent.Type)
|
switch (stripeEvent.Type)
|
||||||
{
|
{
|
||||||
case Events.InvoicePaymentSucceeded:
|
case "invoice.payment_succeeded":
|
||||||
await HandlePaymentSucceeded(stripeEvent);
|
await HandlePaymentSucceeded(stripeEvent);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Events.InvoicePaymentFailed:
|
case "invoice.payment_failed":
|
||||||
await HandlePaymentFailed(stripeEvent);
|
await HandlePaymentFailed(stripeEvent);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Events.CustomerSubscriptionDeleted:
|
case "customer.subscription.deleted":
|
||||||
await HandleSubscriptionDeleted(stripeEvent);
|
await HandleSubscriptionDeleted(stripeEvent);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Events.CustomerSubscriptionUpdated:
|
case "customer.subscription.updated":
|
||||||
await HandleSubscriptionUpdated(stripeEvent);
|
await HandleSubscriptionUpdated(stripeEvent);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -100,7 +100,8 @@ public class StripeWebhookController : ControllerBase
|
|||||||
{
|
{
|
||||||
_logger.LogInformation($"Payment succeeded for customer: {invoice.CustomerId}");
|
_logger.LogInformation($"Payment succeeded for customer: {invoice.CustomerId}");
|
||||||
|
|
||||||
var subscription = await _subscriptionRepository.GetByStripeSubscriptionIdAsync(invoice.SubscriptionId);
|
var subscriptionId = GetSubscriptionId(stripeEvent);
|
||||||
|
var subscription = await _subscriptionRepository.GetByStripeSubscriptionIdAsync(subscriptionId);
|
||||||
if (subscription != null)
|
if (subscription != null)
|
||||||
{
|
{
|
||||||
subscription.Status = "active";
|
subscription.Status = "active";
|
||||||
@ -127,7 +128,8 @@ public class StripeWebhookController : ControllerBase
|
|||||||
{
|
{
|
||||||
_logger.LogInformation($"Payment failed for customer: {invoice.CustomerId}");
|
_logger.LogInformation($"Payment failed for customer: {invoice.CustomerId}");
|
||||||
|
|
||||||
var subscription = await _subscriptionRepository.GetByStripeSubscriptionIdAsync(invoice.SubscriptionId);
|
var subscriptionId = GetSubscriptionId(stripeEvent);
|
||||||
|
var subscription = await _subscriptionRepository.GetByStripeSubscriptionIdAsync(subscriptionId);
|
||||||
if (subscription != null)
|
if (subscription != null)
|
||||||
{
|
{
|
||||||
subscription.Status = "past_due";
|
subscription.Status = "past_due";
|
||||||
@ -184,9 +186,12 @@ public class StripeWebhookController : ControllerBase
|
|||||||
var subscription = await _subscriptionRepository.GetByStripeSubscriptionIdAsync(stripeSubscription.Id);
|
var subscription = await _subscriptionRepository.GetByStripeSubscriptionIdAsync(stripeSubscription.Id);
|
||||||
if (subscription != null)
|
if (subscription != null)
|
||||||
{
|
{
|
||||||
|
var service = new SubscriptionItemService();
|
||||||
|
var subItem = service.Get(stripeSubscription.Items.Data[0].Id);
|
||||||
|
|
||||||
subscription.Status = stripeSubscription.Status;
|
subscription.Status = stripeSubscription.Status;
|
||||||
subscription.CurrentPeriodStart = stripeSubscription.CurrentPeriodStart;
|
subscription.CurrentPeriodStart = subItem.CurrentPeriodStart;
|
||||||
subscription.CurrentPeriodEnd = stripeSubscription.CurrentPeriodEnd;
|
subscription.CurrentPeriodEnd = subItem.CurrentPeriodEnd;
|
||||||
subscription.CancelAtPeriodEnd = stripeSubscription.CancelAtPeriodEnd;
|
subscription.CancelAtPeriodEnd = stripeSubscription.CancelAtPeriodEnd;
|
||||||
subscription.UpdatedAt = DateTime.UtcNow;
|
subscription.UpdatedAt = DateTime.UtcNow;
|
||||||
|
|
||||||
@ -216,4 +221,33 @@ public class StripeWebhookController : ControllerBase
|
|||||||
_ => "trial"
|
_ => "trial"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string GetSubscriptionId(Event stripeEvent)
|
||||||
|
{
|
||||||
|
if (stripeEvent.Data.Object is Invoice invoice)
|
||||||
|
{
|
||||||
|
var subscriptionLineItem = invoice.Lines?.Data
|
||||||
|
.FirstOrDefault(line =>
|
||||||
|
!string.IsNullOrEmpty(line.SubscriptionId) ||
|
||||||
|
line.Subscription != null
|
||||||
|
);
|
||||||
|
|
||||||
|
string subscriptionId = null;
|
||||||
|
|
||||||
|
if (subscriptionLineItem != null)
|
||||||
|
{
|
||||||
|
// Tenta obter o ID da assinatura de duas formas diferentes
|
||||||
|
subscriptionId = subscriptionLineItem.SubscriptionId
|
||||||
|
?? subscriptionLineItem.Subscription?.Id;
|
||||||
|
}
|
||||||
|
|
||||||
|
return subscriptionId;
|
||||||
|
}
|
||||||
|
else if (stripeEvent.Data.Object is Subscription stripeSubscription)
|
||||||
|
{
|
||||||
|
return stripeSubscription.Id;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -5,6 +5,7 @@ using Microsoft.Extensions.Options;
|
|||||||
using Stripe;
|
using Stripe;
|
||||||
using Stripe.Checkout;
|
using Stripe.Checkout;
|
||||||
using Stripe.BillingPortal;
|
using Stripe.BillingPortal;
|
||||||
|
using System.Numerics;
|
||||||
|
|
||||||
namespace BCards.Web.Services;
|
namespace BCards.Web.Services;
|
||||||
|
|
||||||
@ -115,22 +116,22 @@ public class PaymentService : IPaymentService
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var stripeEvent = EventUtility.ConstructEvent(requestBody, signature, _stripeSettings.WebhookSecret);
|
var stripeEvent = EventUtility.ConstructEvent(requestBody, signature, _stripeSettings.WebhookSecret, throwOnApiVersionMismatch: false);
|
||||||
|
|
||||||
switch (stripeEvent.Type)
|
switch (stripeEvent.Type)
|
||||||
{
|
{
|
||||||
case Events.CheckoutSessionCompleted:
|
case "checkout.session.completed":
|
||||||
var session = stripeEvent.Data.Object as Stripe.Checkout.Session;
|
var session = stripeEvent.Data.Object as Stripe.Checkout.Session;
|
||||||
await HandleCheckoutSessionCompletedAsync(session!);
|
await HandleCheckoutSessionCompletedAsync(session!);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Events.InvoicePaymentSucceeded:
|
case "invoice.finalized":
|
||||||
var invoice = stripeEvent.Data.Object as Invoice;
|
var invoice = stripeEvent.Data.Object as Invoice;
|
||||||
await HandleInvoicePaymentSucceededAsync(invoice!);
|
await HandleInvoicePaymentSucceededAsync(invoice!);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Events.CustomerSubscriptionUpdated:
|
case "customer.subscription.updated":
|
||||||
case Events.CustomerSubscriptionDeleted:
|
case "customer.subscription.deleted":
|
||||||
var subscription = stripeEvent.Data.Object as Stripe.Subscription;
|
var subscription = stripeEvent.Data.Object as Stripe.Subscription;
|
||||||
await HandleSubscriptionUpdatedAsync(subscription!);
|
await HandleSubscriptionUpdatedAsync(subscription!);
|
||||||
break;
|
break;
|
||||||
@ -255,6 +256,9 @@ public class PaymentService : IPaymentService
|
|||||||
var subscriptionService = new SubscriptionService();
|
var subscriptionService = new SubscriptionService();
|
||||||
var stripeSubscription = await subscriptionService.GetAsync(session.SubscriptionId);
|
var stripeSubscription = await subscriptionService.GetAsync(session.SubscriptionId);
|
||||||
|
|
||||||
|
var service = new SubscriptionItemService();
|
||||||
|
var subItem = service.Get(stripeSubscription.Items.Data[0].Id);
|
||||||
|
|
||||||
var limitations = await GetPlanLimitationsAsync(planType);
|
var limitations = await GetPlanLimitationsAsync(planType);
|
||||||
|
|
||||||
var subscription = new Models.Subscription
|
var subscription = new Models.Subscription
|
||||||
@ -263,8 +267,8 @@ public class PaymentService : IPaymentService
|
|||||||
StripeSubscriptionId = session.SubscriptionId,
|
StripeSubscriptionId = session.SubscriptionId,
|
||||||
PlanType = planType,
|
PlanType = planType,
|
||||||
Status = stripeSubscription.Status,
|
Status = stripeSubscription.Status,
|
||||||
CurrentPeriodStart = stripeSubscription.CurrentPeriodStart,
|
CurrentPeriodStart = subItem.CurrentPeriodStart,
|
||||||
CurrentPeriodEnd = stripeSubscription.CurrentPeriodEnd,
|
CurrentPeriodEnd = subItem.CurrentPeriodEnd,
|
||||||
MaxLinks = limitations.MaxLinks,
|
MaxLinks = limitations.MaxLinks,
|
||||||
AllowCustomThemes = limitations.AllowCustomThemes,
|
AllowCustomThemes = limitations.AllowCustomThemes,
|
||||||
AllowAnalytics = limitations.AllowAnalytics,
|
AllowAnalytics = limitations.AllowAnalytics,
|
||||||
@ -287,7 +291,8 @@ public class PaymentService : IPaymentService
|
|||||||
|
|
||||||
private async Task HandleInvoicePaymentSucceededAsync(Invoice invoice)
|
private async Task HandleInvoicePaymentSucceededAsync(Invoice invoice)
|
||||||
{
|
{
|
||||||
var subscription = await _subscriptionRepository.GetByStripeSubscriptionIdAsync(invoice.SubscriptionId);
|
var subscriptionId = GetSubscriptionId(invoice);
|
||||||
|
var subscription = await _subscriptionRepository.GetByStripeSubscriptionIdAsync(subscriptionId);
|
||||||
if (subscription != null)
|
if (subscription != null)
|
||||||
{
|
{
|
||||||
subscription.Status = "active";
|
subscription.Status = "active";
|
||||||
@ -300,9 +305,12 @@ public class PaymentService : IPaymentService
|
|||||||
var subscription = await _subscriptionRepository.GetByStripeSubscriptionIdAsync(stripeSubscription.Id);
|
var subscription = await _subscriptionRepository.GetByStripeSubscriptionIdAsync(stripeSubscription.Id);
|
||||||
if (subscription != null)
|
if (subscription != null)
|
||||||
{
|
{
|
||||||
|
var service = new SubscriptionItemService();
|
||||||
|
var subItem = service.Get(stripeSubscription.Items.Data[0].Id);
|
||||||
|
|
||||||
subscription.Status = stripeSubscription.Status;
|
subscription.Status = stripeSubscription.Status;
|
||||||
subscription.CurrentPeriodStart = stripeSubscription.CurrentPeriodStart;
|
subscription.CurrentPeriodStart = subItem.CurrentPeriodStart;
|
||||||
subscription.CurrentPeriodEnd = stripeSubscription.CurrentPeriodEnd;
|
subscription.CurrentPeriodEnd = subItem.CurrentPeriodEnd;
|
||||||
subscription.CancelAtPeriodEnd = stripeSubscription.CancelAtPeriodEnd;
|
subscription.CancelAtPeriodEnd = stripeSubscription.CancelAtPeriodEnd;
|
||||||
|
|
||||||
await _subscriptionRepository.UpdateAsync(subscription);
|
await _subscriptionRepository.UpdateAsync(subscription);
|
||||||
@ -386,4 +394,29 @@ public class PaymentService : IPaymentService
|
|||||||
throw new InvalidOperationException($"Erro ao criar sessão do portal: {ex.Message}");
|
throw new InvalidOperationException($"Erro ao criar sessão do portal: {ex.Message}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string GetSubscriptionId(Invoice? invoice)
|
||||||
|
{
|
||||||
|
if (invoice!=null)
|
||||||
|
{
|
||||||
|
var subscriptionLineItem = invoice.Lines?.Data
|
||||||
|
.FirstOrDefault(line =>
|
||||||
|
!string.IsNullOrEmpty(line.SubscriptionId) ||
|
||||||
|
line.Subscription != null
|
||||||
|
);
|
||||||
|
|
||||||
|
string subscriptionId = null;
|
||||||
|
|
||||||
|
if (subscriptionLineItem != null)
|
||||||
|
{
|
||||||
|
// Tenta obter o ID da assinatura de duas formas diferentes
|
||||||
|
subscriptionId = subscriptionLineItem.SubscriptionId
|
||||||
|
?? subscriptionLineItem.Subscription?.Id;
|
||||||
|
}
|
||||||
|
|
||||||
|
return subscriptionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -126,5 +126,6 @@ public enum PageStatus
|
|||||||
Inactive, // Pausada pelo usuário
|
Inactive, // Pausada pelo usuário
|
||||||
PendingModeration = 4, // Aguardando moderação
|
PendingModeration = 4, // Aguardando moderação
|
||||||
Rejected = 5, // Rejeitada na moderação
|
Rejected = 5, // Rejeitada na moderação
|
||||||
Creating = 6 // Em desenvolvimento/criação
|
Creating = 6, // Em desenvolvimento/criação
|
||||||
|
Approved = 7 // Aprovada
|
||||||
}
|
}
|
||||||
@ -19,7 +19,7 @@ public class ManageSubscriptionViewModel
|
|||||||
public bool CanDowngrade => HasActiveSubscription && User.CurrentPlan != "basic";
|
public bool CanDowngrade => HasActiveSubscription && User.CurrentPlan != "basic";
|
||||||
public bool WillCancelAtPeriodEnd => StripeSubscription?.CancelAtPeriodEnd == true;
|
public bool WillCancelAtPeriodEnd => StripeSubscription?.CancelAtPeriodEnd == true;
|
||||||
|
|
||||||
public DateTime? CurrentPeriodEnd => StripeSubscription?.CurrentPeriodEnd;
|
public DateTime? CurrentPeriodEnd => null;
|
||||||
public DateTime? NextBillingDate => !WillCancelAtPeriodEnd ? CurrentPeriodEnd : null;
|
public DateTime? NextBillingDate => !WillCancelAtPeriodEnd ? CurrentPeriodEnd : null;
|
||||||
|
|
||||||
public decimal? MonthlyAmount => StripeSubscription?.Items?.Data?.FirstOrDefault()?.Price?.UnitAmount / 100m;
|
public decimal? MonthlyAmount => StripeSubscription?.Items?.Data?.FirstOrDefault()?.Price?.UnitAmount / 100m;
|
||||||
|
|||||||
@ -111,7 +111,7 @@
|
|||||||
onclick="openPreview('@pageItem.Id')"
|
onclick="openPreview('@pageItem.Id')"
|
||||||
data-page-category="@pageItem.Category"
|
data-page-category="@pageItem.Category"
|
||||||
data-page-slug="@pageItem.Slug">
|
data-page-slug="@pageItem.Slug">
|
||||||
<i class="fas fa-eye me-1"></i>Preview
|
<i class="fas fa-eye me-1"></i>Testar
|
||||||
</button>
|
</button>
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -212,7 +212,7 @@
|
|||||||
<i class="fas fa-exclamation-triangle me-3"></i>
|
<i class="fas fa-exclamation-triangle me-3"></i>
|
||||||
<div>
|
<div>
|
||||||
<strong>Página em criação!</strong>
|
<strong>Página em criação!</strong>
|
||||||
Você pode editar e fazer preview quantas vezes quiser. <br />
|
Você pode editar e testar quantas vezes quiser. <br />
|
||||||
Ao terminar, clique em <i class="fas fa-ellipsis-v"></i> para enviar a página <b><span id="pageNameDisplay"></span></b> para moderação!
|
Ao terminar, clique em <i class="fas fa-ellipsis-v"></i> para enviar a página <b><span id="pageNameDisplay"></span></b> para moderação!
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||||
</div>
|
</div>
|
||||||
@ -268,7 +268,7 @@
|
|||||||
{
|
{
|
||||||
<!-- Limite atingido -->
|
<!-- Limite atingido -->
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<div class="alert alert-warning d-flex align-items-center">
|
<div class="alert alert-warning d-flex align-items-center alert-permanent">
|
||||||
<i class="fas fa-exclamation-triangle me-3"></i>
|
<i class="fas fa-exclamation-triangle me-3"></i>
|
||||||
<div>
|
<div>
|
||||||
<strong>Limite atingido!</strong>
|
<strong>Limite atingido!</strong>
|
||||||
|
|||||||
@ -340,7 +340,7 @@
|
|||||||
var instagram = Model.Links.Where(x => x.Icon.Contains("instagram")).FirstOrDefault();
|
var instagram = Model.Links.Where(x => x.Icon.Contains("instagram")).FirstOrDefault();
|
||||||
var facebookUrl = facebook !=null ? facebook.Url : "";
|
var facebookUrl = facebook !=null ? facebook.Url : "";
|
||||||
var twitterUrl = twitter !=null ? twitter.Url : "";
|
var twitterUrl = twitter !=null ? twitter.Url : "";
|
||||||
var whatsappUrl = whatsapp !=null ? whatsapp.Url : "";
|
var whatsappUrl = whatsapp !=null ? whatsapp.Url.Replace("https://wa.me/","") : "";
|
||||||
var instagramUrl = instagram !=null ? instagram.Url : "";
|
var instagramUrl = instagram !=null ? instagram.Url : "";
|
||||||
}
|
}
|
||||||
<!-- Passo 4: Redes Sociais (Opcional) -->
|
<!-- Passo 4: Redes Sociais (Opcional) -->
|
||||||
@ -1423,6 +1423,12 @@
|
|||||||
userInput.on('input', function() {
|
userInput.on('input', function() {
|
||||||
let value = $(this).val().trim();
|
let value = $(this).val().trim();
|
||||||
|
|
||||||
|
// Se o usuário colou uma URL completa, extrair apenas a parte do usuário
|
||||||
|
if (value.startsWith(prefix)) {
|
||||||
|
value = value.replace(prefix, '');
|
||||||
|
$(this).val(value);
|
||||||
|
}
|
||||||
|
|
||||||
if (isWhatsApp) {
|
if (isWhatsApp) {
|
||||||
// WhatsApp: apenas números
|
// WhatsApp: apenas números
|
||||||
value = value.replace(/\D/g, '');
|
value = value.replace(/\D/g, '');
|
||||||
|
|||||||
@ -6,6 +6,31 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"DetailedErrors": true,
|
"DetailedErrors": true,
|
||||||
|
"Stripe": {
|
||||||
|
"PublishableKey": "pk_test_51RjUmIBMIadsOxJVP4bWc54pHEOSf5km1hpOkOBSoGVoKxI46N4KSWtevpXCSq68OjFazBuXmPJGBwZ1KDN5MNJy003lj1YmAS",
|
||||||
|
"SecretKey": "sk_test_51RjUmIBMIadsOxJVeqsMFxnZ8ePR7d8IbnaF4sAwBVJv9rrfODPEQ2C9fF3beoABpITdfzEk0ZDzGTTQfvKv63xI00PeZoABGO",
|
||||||
|
"WebhookSecret": "whsec_8d189c137ff170ab5e62498003512b9d073e2db50c50ed7d8712b7ef11a37543"
|
||||||
|
},
|
||||||
|
"Plans": {
|
||||||
|
"Basic": {
|
||||||
|
"PriceId": "price_1RjUskBMIadsOxJVgLwlVo1y",
|
||||||
|
"Price": 9.90,
|
||||||
|
"MaxLinks": 5,
|
||||||
|
"Features": [ "basic_themes", "simple_analytics" ]
|
||||||
|
},
|
||||||
|
"Professional": {
|
||||||
|
"PriceId": "price_1RjUv9BMIadsOxJVORqlM4E9",
|
||||||
|
"Price": 24.90,
|
||||||
|
"MaxLinks": 15,
|
||||||
|
"Features": [ "all_themes", "advanced_analytics", "custom_domain" ]
|
||||||
|
},
|
||||||
|
"Premium": {
|
||||||
|
"PriceId": "price_1RjUw0BMIadsOxJVmdouNV1g",
|
||||||
|
"Price": 29.90,
|
||||||
|
"MaxLinks": -1,
|
||||||
|
"Features": [ "custom_themes", "full_analytics", "multiple_domains", "priority_support" ]
|
||||||
|
}
|
||||||
|
},
|
||||||
"MongoDb": {
|
"MongoDb": {
|
||||||
"ConnectionString": "mongodb://localhost:27017",
|
"ConnectionString": "mongodb://localhost:27017",
|
||||||
"DatabaseName": "BCardsDB_Dev"
|
"DatabaseName": "BCardsDB_Dev"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user