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 Index() { const string description = "Compare PlotDirector plans and choose the right subscription for organising your novel, series, characters, plot threads, timelines, and story details."; ViewData["SeoTitle"] = "Pricing | PlotDirector"; ViewData["SeoDescription"] = description; ViewData["SeoKeywords"] = "novel planning software, writing software, story planning app, author tools, plot tracking, character tracking, story timeline, novel organisation, fiction writing software, series writing software, continuity tracking, plot threads, story bible"; ViewData["CanonicalUrl"] = PublicSeo.AbsoluteUrl(configuration, "/pricing"); ViewData["StructuredData"] = new[] { PublicSeo.WebPageJsonLd(configuration, "WebPage", "Pricing", "/pricing", description) }; 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 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)); } }