diff --git a/BillingCore/BillingService.cs b/BillingCore/BillingService.cs index 4b09da2..e4ff59c 100644 --- a/BillingCore/BillingService.cs +++ b/BillingCore/BillingService.cs @@ -32,6 +32,7 @@ namespace Teknik.BillingCore public abstract Subscription GetSubscription(string subscriptionId); public abstract Subscription CreateSubscription(string customerId, string priceId); public abstract Subscription EditSubscriptionPrice(string subscriptionId, string priceId); + public abstract Subscription RenewSubscription(string subscriptionId); public abstract bool CancelSubscription(string subscriptionId, bool atEndOfPeriod); public abstract CheckoutSession CreateCheckoutSession(string customerId, string priceId, string successUrl, string cancelUrl); diff --git a/BillingCore/Models/Subscription.cs b/BillingCore/Models/Subscription.cs index afb4d8c..b12c50c 100644 --- a/BillingCore/Models/Subscription.cs +++ b/BillingCore/Models/Subscription.cs @@ -11,6 +11,8 @@ namespace Teknik.BillingCore.Models public string Id { get; set; } public string CustomerId { get; set; } public SubscriptionStatus Status { get; set; } + public DateTime BillingPeriodEnd { get; set; } + public bool CancelAtBillingEnd { get; set; } public List Prices { get; set; } public string ClientSecret { get; set; } } diff --git a/BillingCore/StripeService.cs b/BillingCore/StripeService.cs index 71428ea..621f1b0 100644 --- a/BillingCore/StripeService.cs +++ b/BillingCore/StripeService.cs @@ -230,6 +230,27 @@ namespace Teknik.BillingCore return null; } + public override Models.Subscription RenewSubscription(string subscriptionId) + { + if (!string.IsNullOrEmpty(subscriptionId)) + { + var subscriptionService = new SubscriptionService(); + var subscription = subscriptionService.Get(subscriptionId); + if (subscription != null) + { + var subscriptionOptions = new SubscriptionUpdateOptions() + { + CancelAtPeriodEnd = false + }; + subscriptionOptions.AddExpand("latest_invoice.payment_intent"); + var result = subscriptionService.Update(subscriptionId, subscriptionOptions); + if (result != null) + return ConvertSubscription(result); + } + } + return null; + } + public override bool CancelSubscription(string subscriptionId, bool atEndOfperiod) { if (!string.IsNullOrEmpty(subscriptionId)) @@ -454,6 +475,8 @@ namespace Teknik.BillingCore Id = subscription.Id, CustomerId = subscription.CustomerId, Status = status, + BillingPeriodEnd = subscription.CurrentPeriodEnd, + CancelAtBillingEnd = subscription.CancelAtPeriodEnd, Prices = prices, ClientSecret = subscription.LatestInvoice?.PaymentIntent?.ClientSecret }; diff --git a/Teknik/Areas/User/Controllers/UserController.cs b/Teknik/Areas/User/Controllers/UserController.cs index 3bcbaa5..35ca097 100644 --- a/Teknik/Areas/User/Controllers/UserController.cs +++ b/Teknik/Areas/User/Controllers/UserController.cs @@ -386,6 +386,8 @@ namespace Teknik.Areas.Users.Controllers Storage = price.Storage, Price = price.Amount, Interval = price.Interval.ToString(), + BillingPeriodEnd = sub.BillingPeriodEnd, + Canceled = sub.CancelAtBillingEnd }; model.Subscriptions.Add(subView); } @@ -1499,7 +1501,7 @@ namespace Teknik.Areas.Users.Controllers [HttpPost] [ValidateAntiForgeryToken] - public IActionResult CancelSubscription(string subscriptionId, string productId) + public IActionResult CancelSubscription(string subscriptionId) { // Get Subscription Info var billingService = BillingFactory.GetBillingService(_config.BillingConfig); @@ -1508,13 +1510,6 @@ namespace Teknik.Areas.Users.Controllers if (subscription == null) return Json(new { error = "Invalid Subscription Id" }); - if (!subscription.Prices.Exists(p => p.ProductId == productId)) - return Json(new { error = "Subscription does not relate to product" }); - - var product = billingService.GetProduct(productId); - if (product == null) - return Json(new { error = "Product does not exist" }); - var result = billingService.CancelSubscription(subscriptionId, true); if (result) @@ -1522,5 +1517,25 @@ namespace Teknik.Areas.Users.Controllers return Json(new { error = "Unable to cancel subscription" }); } + + [HttpPost] + [ValidateAntiForgeryToken] + public IActionResult RenewSubscription(string subscriptionId) + { + // Get Subscription Info + var billingService = BillingFactory.GetBillingService(_config.BillingConfig); + + var subscription = billingService.GetSubscription(subscriptionId); + if (subscription == null) + return Json(new { error = "Invalid Subscription Id" }); + + var result = billingService.RenewSubscription(subscriptionId); + + if (result != null && + !result.CancelAtBillingEnd) + return Json(new { result = true }); + + return Json(new { error = "Unable to cancel subscription" }); + } } } diff --git a/Teknik/Areas/User/ViewModels/SubscriptionViewModel.cs b/Teknik/Areas/User/ViewModels/SubscriptionViewModel.cs index 7b3ada2..a5ec6c6 100644 --- a/Teknik/Areas/User/ViewModels/SubscriptionViewModel.cs +++ b/Teknik/Areas/User/ViewModels/SubscriptionViewModel.cs @@ -19,5 +19,9 @@ namespace Teknik.Areas.Users.ViewModels public string Interval { get; set; } public long Storage { get; set; } + + public DateTime BillingPeriodEnd { get; set; } + + public bool Canceled { get; set; } } } diff --git a/Teknik/Areas/User/Views/User/Settings/BillingSettings.cshtml b/Teknik/Areas/User/Views/User/Settings/BillingSettings.cshtml index 99490c3..7932919 100644 --- a/Teknik/Areas/User/Views/User/Settings/BillingSettings.cshtml +++ b/Teknik/Areas/User/Views/User/Settings/BillingSettings.cshtml @@ -7,7 +7,8 @@ } @if (Config.BillingConfig.Enabled) @@ -28,7 +29,7 @@ }
-

Active Subscriptions

+

Current Subscriptions


@@ -41,8 +42,24 @@ foreach (var subscription in Model.Subscriptions) {
  • -

    @subscription.ProductName: @(StringHelper.GetBytesReadable(subscription.Storage)) for @($"${subscription.Price:0.00} / {subscription.Interval}")

    - +
    +

    @subscription.ProductName: @(StringHelper.GetBytesReadable(subscription.Storage)) for @($"${subscription.Price:0.00} / {subscription.Interval}")

    +

    + @if (subscription.Canceled) + { + @:Canceled - + } + Billing period ends @(subscription.BillingPeriodEnd.ToShortDateString()). +

    +
    + @if (subscription.Canceled) + { + + } + else + { + + }
  • } diff --git a/Teknik/Scripts/User/BillingSettings.js b/Teknik/Scripts/User/BillingSettings.js index ead1d90..546082d 100644 --- a/Teknik/Scripts/User/BillingSettings.js +++ b/Teknik/Scripts/User/BillingSettings.js @@ -1,17 +1,16 @@ -/* globals cancelSubscriptionURL */ +/* globals cancelSubscriptionURL, renewSubscriptionURL */ $(document).ready(function () { $('.cancel-subscription-button').click(function () { disableButton('#cancel_subscription', 'Canceling Subscription...'); var subscriptionId = $(this).data('subscription-id'); - var productId = $(this).data('product-id'); - confirmDialog('Confirm', 'Back', 'Are you sure you want to cancel your subscription? Your plan will be canceled, but is still available until the end of your billing period.', function (result) { + confirmDialog('Confirm', 'Back', 'Are you sure you want to cancel your subscription?

    Your plan will be canceled, but is still available until the end of your billing period.', function (result) { if (result) { $.ajax({ type: "POST", url: cancelSubscriptionURL, - data: AddAntiForgeryToken({ subscriptionId: subscriptionId, productId: productId }), + data: AddAntiForgeryToken({ subscriptionId: subscriptionId }), headers: { 'X-Requested-With': 'XMLHttpRequest' }, @@ -38,4 +37,42 @@ $(document).ready(function () { } }); }); + + $('.renew-subscription-button').click(function () { + disableButton('#renew_subscription', 'Renewing Subscription...'); + + var subscriptionId = $(this).data('subscription-id'); + + confirmDialog('Renew', 'Back', 'Are you sure you want to renew your subscription?', function (result) { + if (result) { + $.ajax({ + type: "POST", + url: renewSubscriptionURL, + data: AddAntiForgeryToken({ subscriptionId: subscriptionId }), + headers: { + 'X-Requested-With': 'XMLHttpRequest' + }, + xhrFields: { + withCredentials: true + }, + success: function (response) { + if (response.result) { + $("#top_msg").css('display', 'inline', 'important'); + $("#top_msg").html('
    Subscription renewed successfully!
    '); + } + else { + $("#top_msg").css('display', 'inline', 'important'); + $("#top_msg").html('
    ' + parseErrorMessage(response) + '
    '); + } + }, + error: function (response) { + $("#top_msg").css('display', 'inline', 'important'); + $("#top_msg").html('
    ' + parseErrorMessage(response.responseText) + '
    '); + } + }); + } else { + enableButton('#renew_subscription', 'Renew Subscription'); + } + }); + }); }); \ No newline at end of file