Browse Source

Added renewals of subscriptions

master
Teknikode 7 months ago
parent
commit
98c99b3336
  1. 1
      BillingCore/BillingService.cs
  2. 2
      BillingCore/Models/Subscription.cs
  3. 23
      BillingCore/StripeService.cs
  4. 31
      Teknik/Areas/User/Controllers/UserController.cs
  5. 4
      Teknik/Areas/User/ViewModels/SubscriptionViewModel.cs
  6. 25
      Teknik/Areas/User/Views/User/Settings/BillingSettings.cshtml
  7. 45
      Teknik/Scripts/User/BillingSettings.js

1
BillingCore/BillingService.cs

@ -32,6 +32,7 @@ namespace Teknik.BillingCore @@ -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);

2
BillingCore/Models/Subscription.cs

@ -11,6 +11,8 @@ namespace Teknik.BillingCore.Models @@ -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<Price> Prices { get; set; }
public string ClientSecret { get; set; }
}

23
BillingCore/StripeService.cs

@ -230,6 +230,27 @@ namespace Teknik.BillingCore @@ -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 @@ -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
};

31
Teknik/Areas/User/Controllers/UserController.cs

@ -386,6 +386,8 @@ namespace Teknik.Areas.Users.Controllers @@ -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 @@ -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 @@ -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 @@ -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" });
}
}
}

4
Teknik/Areas/User/ViewModels/SubscriptionViewModel.cs

@ -19,5 +19,9 @@ namespace Teknik.Areas.Users.ViewModels @@ -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; }
}
}

25
Teknik/Areas/User/Views/User/Settings/BillingSettings.cshtml

@ -7,7 +7,8 @@ @@ -7,7 +7,8 @@
}
<script>
var cancelSubscriptionURL = '@Url.SubRouteUrl("billing", "User.Action", new { action = "CancelSubscription" })';
var cancelSubscriptionURL = '@Url.SubRouteUrl("account", "User.Action", new { action = "CancelSubscription" })';
var renewSubscriptionURL = '@Url.SubRouteUrl("account", "User.Action", new { action = "RenewSubscription" })';
</script>
@if (Config.BillingConfig.Enabled)
@ -28,7 +29,7 @@ @@ -28,7 +29,7 @@
}
<div class="row">
<div class="col-sm-12">
<h2>Active Subscriptions</h2>
<h2>Current Subscriptions</h2>
<hr />
</div>
</div>
@ -41,8 +42,24 @@ @@ -41,8 +42,24 @@
foreach (var subscription in Model.Subscriptions)
{
<li class="list-group-item" id="@subscription.SubscriptionId">
<h4 class="list-group-item-heading pull-left">@subscription.ProductName: <strong>@(StringHelper.GetBytesReadable(subscription.Storage))</strong> for <strong>@($"${subscription.Price:0.00} / {subscription.Interval}")</strong></h4>
<button role="button" class="btn btn-danger cancel-subscription-button pull-right" data-subscription-id="@subscription.SubscriptionId" data-product-id="@subscription.ProductId">Cancel Subscription</button>
<div class="pull-left">
<h4 class="list-group-item-heading pull-left">@subscription.ProductName: <strong>@(StringHelper.GetBytesReadable(subscription.Storage))</strong> for <strong>@($"${subscription.Price:0.00} / {subscription.Interval}")</strong></h4>
<p>
@if (subscription.Canceled)
{
@:<strong>Canceled</strong> -
}
Billing period ends @(subscription.BillingPeriodEnd.ToShortDateString()).
</p>
</div>
@if (subscription.Canceled)
{
<button role="button" class="btn btn-primary renew-subscription-button pull-right" data-subscription-id="@subscription.SubscriptionId">Renew Subscription</button>
}
else
{
<button role="button" class="btn btn-danger cancel-subscription-button pull-right" data-subscription-id="@subscription.SubscriptionId">Cancel Subscription</button>
}
<div class="clearfix"></div>
</li>
}

45
Teknik/Scripts/User/BillingSettings.js

@ -1,17 +1,16 @@ @@ -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?<br /><br />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 () { @@ -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('<div class="alert alert-success alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>Subscription renewed successfully!</div>');
}
else {
$("#top_msg").css('display', 'inline', 'important');
$("#top_msg").html('<div class="alert alert-danger alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>' + parseErrorMessage(response) + '</div>');
}
},
error: function (response) {
$("#top_msg").css('display', 'inline', 'important');
$("#top_msg").html('<div class="alert alert-danger alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>' + parseErrorMessage(response.responseText) + '</div>');
}
});
} else {
enableButton('#renew_subscription', 'Renew Subscription');
}
});
});
});
Loading…
Cancel
Save