Browse Source

Fixed billing webhook endpoints

feature/billing
Teknikode 7 months ago
parent
commit
162a8c5e4e
  1. 4
      BillingCore/BillingService.cs
  2. 12
      BillingCore/StripeService.cs
  3. 6
      Configuration/BillingConfig.cs
  4. 22
      Teknik/App_Data/endpointMappings.json
  5. 60
      Teknik/Areas/API/V1/Controllers/BillingAPIv1Controller.cs
  6. 56
      Teknik/Areas/Billing/BillingHelper.cs
  7. 11
      Utilities/TagHelpers/VersionHelper.cs

4
BillingCore/BillingService.cs

@ -36,8 +36,8 @@ namespace Teknik.BillingCore @@ -36,8 +36,8 @@ namespace Teknik.BillingCore
public abstract CheckoutSession CreateCheckoutSession(string customerId, string priceId, string successUrl, string cancelUrl);
public abstract CheckoutSession GetCheckoutSession(string sessionId);
public abstract Task<Event> ParseEvent(HttpRequest request);
public abstract Task<Event> ParseEvent(HttpRequest request, string apiKey);
public abstract CheckoutSession ProcessCheckoutCompletedEvent(Event e);
public abstract Customer ProcessCustomerEvent(Event e);
public abstract Subscription ProcessSubscriptionEvent(Event e);
}
}

12
BillingCore/StripeService.cs

@ -264,7 +264,7 @@ namespace Teknik.BillingCore @@ -264,7 +264,7 @@ namespace Teknik.BillingCore
return ConvertCheckoutSession(session);
}
public override async Task<Models.Event> ParseEvent(HttpRequest request)
public override async Task<Models.Event> ParseEvent(HttpRequest request, string apiKey)
{
var json = await new StreamReader(request.Body).ReadToEndAsync();
@ -273,7 +273,7 @@ namespace Teknik.BillingCore @@ -273,7 +273,7 @@ namespace Teknik.BillingCore
var stripeEvent = EventUtility.ConstructEvent(
json,
request.Headers["Stripe-Signature"],
Config.StripeWebhookSecret
apiKey
);
return ConvertEvent(stripeEvent);
@ -292,12 +292,12 @@ namespace Teknik.BillingCore @@ -292,12 +292,12 @@ namespace Teknik.BillingCore
return ConvertCheckoutSession(session);
}
public override Models.Customer ProcessCustomerEvent(Models.Event ev)
public override Models.Subscription ProcessSubscriptionEvent(Models.Event ev)
{
// Handle the checkout.session.completed event
var customer = ev.Data as Stripe.Customer;
var subscription = ev.Data as Stripe.Subscription;
return ConvertCustomer(customer);
return ConvertSubscription(subscription);
}
public override CheckoutSession GetCheckoutSession(string sessionId)
@ -433,7 +433,7 @@ namespace Teknik.BillingCore @@ -433,7 +433,7 @@ namespace Teknik.BillingCore
return new CheckoutSession()
{
PaymentIntentId = session.PaymentIntentId,
CustomerId = session.Customer.Id,
CustomerId = session.Customer?.Id ?? session.CustomerId,
SubscriptionId = session.SubscriptionId,
PaymentStatus = paymentStatus,
Url = session.Url

6
Configuration/BillingConfig.cs

@ -11,7 +11,8 @@ namespace Teknik.Configuration @@ -11,7 +11,8 @@ namespace Teknik.Configuration
public BillingType Type { get; set; }
public string StripePublishApiKey { get; set; }
public string StripeSecretApiKey { get; set; }
public string StripeWebhookSecret { get; set; }
public string StripeCheckoutWebhookSecret { get; set; }
public string StripeCustomerWebhookSecret { get; set; }
public string UploadProductId { get; set; }
public string EmailProductId { get; set; }
@ -21,7 +22,8 @@ namespace Teknik.Configuration @@ -21,7 +22,8 @@ namespace Teknik.Configuration
Type = BillingType.Stripe;
StripePublishApiKey = null;
StripeSecretApiKey = null;
StripeWebhookSecret = null;
StripeCheckoutWebhookSecret = null;
StripeCustomerWebhookSecret = null;
}
}
}

22
Teknik/App_Data/endpointMappings.json

@ -132,6 +132,28 @@ @@ -132,6 +132,28 @@
"action": "Dashboard"
}
},
{
"Name": "API.v1.HandleCheckoutComplete",
"HostTypes": [ "Full" ],
"SubDomains": [ "api" ],
"Pattern": "v1/Billing/HandleCheckoutComplete",
"Area": "API",
"Defaults": {
"controller": "BillingAPIv1",
"action": "HandleCheckoutCompleteEvent"
}
},
{
"Name": "API.v1.HandleSubscriptionChange",
"HostTypes": [ "Full" ],
"SubDomains": [ "api" ],
"Pattern": "v1/Billing/HandleSubscriptionChange",
"Area": "API",
"Defaults": {
"controller": "BillingAPIv1",
"action": "HandleSubscriptionChange"
}
},
{
"Name": "API.v1.Claims",
"HostTypes": [ "Full" ],

60
Teknik/Areas/API/V1/Controllers/BillingAPIv1Controller.cs

@ -6,6 +6,7 @@ using System.Collections.Generic; @@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Teknik.Areas.Billing;
using Teknik.Areas.Users.Models;
using Teknik.Areas.Users.Utility;
using Teknik.BillingCore;
@ -24,7 +25,7 @@ namespace Teknik.Areas.API.V1.Controllers @@ -24,7 +25,7 @@ namespace Teknik.Areas.API.V1.Controllers
{
var billingService = BillingFactory.GetBillingService(_config.BillingConfig);
var billingEvent = await billingService.ParseEvent(Request);
var billingEvent = await billingService.ParseEvent(Request, _config.BillingConfig.StripeCheckoutWebhookSecret);
if (billingEvent == null)
return BadRequest();
@ -34,7 +35,7 @@ namespace Teknik.Areas.API.V1.Controllers @@ -34,7 +35,7 @@ namespace Teknik.Areas.API.V1.Controllers
{
var subscription = billingService.GetSubscription(session.SubscriptionId);
ProcessSubscription(session.CustomerId, subscription);
BillingHelper.ProcessSubscription(_config, _dbContext, session.CustomerId, subscription);
}
return Ok();
@ -44,61 +45,18 @@ namespace Teknik.Areas.API.V1.Controllers @@ -44,61 +45,18 @@ namespace Teknik.Areas.API.V1.Controllers
{
var billingService = BillingFactory.GetBillingService(_config.BillingConfig);
var billingEvent = await billingService.ParseEvent(Request);
var billingEvent = await billingService.ParseEvent(Request, _config.BillingConfig.StripeCustomerWebhookSecret);
if (billingEvent == null)
return BadRequest();
var customerEvent = billingService.ProcessCustomerEvent(billingEvent);
foreach (var subscription in customerEvent.Subscriptions)
{
ProcessSubscription(customerEvent.CustomerId, subscription);
}
return Ok();
}
var subscriptionEvent = billingService.ProcessSubscriptionEvent(billingEvent);
if (subscriptionEvent == null)
return BadRequest();
private void ProcessSubscription(string customerId, Subscription subscription)
{
// They have paid, so let's get their subscription and update their user settings
var user = _dbContext.Users.FirstOrDefault(u => u.BillingCustomer != null &&
u.BillingCustomer.CustomerId == customerId);
if (user != null)
{
var isActive = subscription.Status == SubscriptionStatus.Active;
foreach (var price in subscription.Prices)
{
ProcessPrice(user, price, isActive);
}
}
}
BillingHelper.ProcessSubscription(_config, _dbContext, subscriptionEvent.CustomerId, subscriptionEvent);
private void ProcessPrice(User user, Price price, bool active)
{
// What type of subscription is this
if (_config.BillingConfig.UploadProductId == price.ProductId)
{
// Process Upload Settings
user.UploadSettings.MaxUploadStorage = active ? price.Storage : _config.UploadConfig.MaxStorage;
user.UploadSettings.MaxUploadFileSize = active ? price.FileSize : _config.UploadConfig.MaxUploadFileSize;
_dbContext.Entry(user).State = EntityState.Modified;
_dbContext.SaveChanges();
}
else if (_config.BillingConfig.EmailProductId == price.ProductId)
{
// Process an email subscription
string email = UserHelper.GetUserEmailAddress(_config, user.Username);
if (active)
{
UserHelper.EnableUserEmail(_config, email);
UserHelper.EditUserEmailMaxSize(_config, email, (int)price.Storage);
}
else
{
UserHelper.DisableUserEmail(_config, email);
}
}
return Ok();
}
}
}

56
Teknik/Areas/Billing/BillingHelper.cs

@ -0,0 +1,56 @@ @@ -0,0 +1,56 @@
using Microsoft.EntityFrameworkCore;
using System.Linq;
using Teknik.Areas.Users.Models;
using Teknik.Areas.Users.Utility;
using Teknik.BillingCore;
using Teknik.BillingCore.Models;
using Teknik.Configuration;
using Teknik.Data;
namespace Teknik.Areas.Billing
{
public static class BillingHelper
{
public static void ProcessSubscription(Config config, TeknikEntities db, string customerId, Subscription subscription)
{
// They have paid, so let's get their subscription and update their user settings
var user = db.Users.FirstOrDefault(u => u.BillingCustomer != null &&
u.BillingCustomer.CustomerId == customerId);
if (user != null)
{
var isActive = subscription.Status == SubscriptionStatus.Active;
foreach (var price in subscription.Prices)
{
ProcessPrice(config, db, user, price, isActive);
}
}
}
public static void ProcessPrice(Config config, TeknikEntities db, User user, Price price, bool active)
{
// What type of subscription is this
if (config.BillingConfig.UploadProductId == price.ProductId)
{
// Process Upload Settings
user.UploadSettings.MaxUploadStorage = active ? price.Storage : config.UploadConfig.MaxStorage;
user.UploadSettings.MaxUploadFileSize = active ? price.FileSize : config.UploadConfig.MaxUploadFileSize;
db.Entry(user).State = EntityState.Modified;
db.SaveChanges();
}
else if (config.BillingConfig.EmailProductId == price.ProductId)
{
// Process an email subscription
string email = UserHelper.GetUserEmailAddress(config, user.Username);
if (active)
{
UserHelper.EnableUserEmail(config, email);
UserHelper.EditUserEmailMaxSize(config, email, (int)price.Storage);
}
else
{
UserHelper.DisableUserEmail(config, email);
}
}
}
}
}

11
Utilities/TagHelpers/VersionHelper.cs

@ -1,4 +1,5 @@ @@ -1,4 +1,5 @@
using Microsoft.AspNetCore.Razor.TagHelpers;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Razor.TagHelpers;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
@ -11,8 +12,14 @@ namespace Teknik.Utilities.TagHelpers @@ -11,8 +12,14 @@ namespace Teknik.Utilities.TagHelpers
{
private const string _verFile = "version.json";
private readonly IWebHostEnvironment _env;
public string Source { get; set; }
public VersionHelper(IWebHostEnvironment env)
{
_env = env;
}
public override void Process(TagHelperContext context, TagHelperOutput output)
{
// Clear the initial wrap tag
@ -32,7 +39,7 @@ namespace Teknik.Utilities.TagHelpers @@ -32,7 +39,7 @@ namespace Teknik.Utilities.TagHelpers
string commitVer = res["version"].ToString();
string commitHash = res["hash"].ToString();
output.Content.AppendHtml($"Version: {commitVer} - Hash: <a href=\"{Source}{commitHash}\">{commitHash.Truncate(10)}</a>");
output.Content.AppendHtml($"Version: {commitVer} - Hash: <a href=\"{Source}{commitHash}\">{commitHash.Truncate(10)}</a> | {_env.EnvironmentName}");
}
}
}

Loading…
Cancel
Save