PlotDirector/PlotLine/Controllers/PricingController.cs
2026-06-25 19:46:23 +01:00

122 lines
4.6 KiB
C#

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using PlotLine.Data;
using PlotLine.Models;
using PlotLine.Services;
using PlotLine.ViewModels;
namespace PlotLine.Controllers;
[AllowAnonymous]
public sealed class PricingController(
ICurrentUserService currentUser,
IUserRepository users,
ISubscriptionService subscriptions,
IStripeBillingService stripeBilling,
IConfiguration configuration) : Controller
{
[HttpGet("pricing", Name = PublicRouteNames.Pricing)]
public async Task<IActionResult> Index()
{
const string description = "Compare PlotDirector plans and choose the right subscription for organising your novel, series, characters, plot threads, timelines, and story details.";
PublicSeo.Apply(ViewData, configuration, new SeoMetadata
{
Title = "Pricing",
Description = description,
Keywords = "PlotDirector pricing, novel planning software pricing, writing software plans, author tools subscription",
CanonicalPath = "/pricing",
JsonLd =
[
PublicSeo.WebPageJsonLd(configuration, "WebPage", "Pricing", "/pricing", description),
PublicSeo.JsonLd(new Dictionary<string, object?>
{
["@context"] = "https://schema.org",
["@type"] = "FAQPage",
["mainEntity"] = new[]
{
new Dictionary<string, object?>
{
["@type"] = "Question",
["name"] = "Can I start with a free trial?",
["acceptedAnswer"] = new Dictionary<string, object?>
{
["@type"] = "Answer",
["text"] = "Yes. Creating an account starts a 30-day Draft Desk trial immediately."
}
},
new Dictionary<string, object?>
{
["@type"] = "Question",
["name"] = "Do I need a credit card for the trial?",
["acceptedAnswer"] = new Dictionary<string, object?>
{
["@type"] = "Answer",
["text"] = "No. Payment details are not required for the free trial."
}
}
}
})
]
});
UserSubscriptionDetail? currentSubscription = null;
if (currentUser.UserId is int userId)
{
currentSubscription = await subscriptions.GetCurrentSubscriptionAsync(userId);
}
return View(new PricingViewModel
{
Plans = await subscriptions.GetActiveSubscriptionLevelsAsync(),
CurrentSubscriptionLevelID = currentSubscription?.SubscriptionLevelID,
IsSignedIn = currentUser.UserId.HasValue
});
}
[HttpGet]
public IActionResult StartTrial()
{
if (!currentUser.UserId.HasValue)
{
return RedirectToAction("Register", "Account");
}
return RedirectToAction("Subscription", "ManageAccount");
}
[HttpGet]
public async Task<IActionResult> Checkout(int id, string billingInterval = "Monthly")
{
if (!currentUser.UserId.HasValue)
{
var returnUrl = Url.Action(nameof(Checkout), "Pricing", new { id, billingInterval }) ?? "/pricing";
return RedirectToAction("Login", "Account", new { returnUrl });
}
var user = await users.GetByIdAsync(currentUser.UserId.Value);
if (user is null)
{
return RedirectToAction("Login", "Account");
}
var request = new StripeCheckoutRequest
{
SubscriptionLevelID = id,
BillingInterval = string.Equals(billingInterval, "Annual", StringComparison.OrdinalIgnoreCase)
? "Annual"
: "Monthly"
};
var successUrl = Url.Action("SubscriptionSuccess", "ManageAccount", null, Request.Scheme) ?? string.Empty;
var cancelUrl = Url.Action(nameof(Index), "Pricing", null, Request.Scheme) ?? string.Empty;
var result = await stripeBilling.StartCheckoutOrChangePlanAsync(user, request, successUrl, cancelUrl);
if (result.Succeeded && !string.IsNullOrWhiteSpace(result.RedirectUrl))
{
return Redirect(result.RedirectUrl);
}
TempData["PricingMessage"] = result.Message;
return RedirectToAction(nameof(Index));
}
}