Browse Source

Added renewals of subscriptions

tags/5.2.0
Teknikode 1 month ago
parent
commit
98c99b3336

+ 1
- 0
BillingCore/BillingService.cs View File

@@ -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
- 0
BillingCore/Models/Subscription.cs View File

@@ -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
- 0
BillingCore/StripeService.cs View File

@@ -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
};

+ 23
- 8
Teknik/Areas/User/Controllers/UserController.cs View File

@@ -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" });
}
}
}

+ 4
- 0
Teknik/Areas/User/ViewModels/SubscriptionViewModel.cs View File

@@ -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; }
}
}

+ 21
- 4
Teknik/Areas/User/Views/User/Settings/BillingSettings.cshtml View File

@@ -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 @@
}
<div class="row">
<div class="col-sm-12">
<h2>Active Subscriptions</h2>
<h2>Current Subscriptions</h2>
<hr />
</div>
</div>
@@ -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>
}

+ 41
- 4
Teknik/Scripts/User/BillingSettings.js View File

@@ -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 () {
}
});
});

$('.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