diff --git a/BillingCore/BillingCore.csproj b/BillingCore/BillingCore.csproj
new file mode 100644
index 0000000..9a9dc18
--- /dev/null
+++ b/BillingCore/BillingCore.csproj
@@ -0,0 +1,17 @@
+
+
+
+ net5.0
+ Teknik.BillingCore
+ Teknik.BillingCore
+
+
+
+
+
+
+
+
+
+
+
diff --git a/BillingCore/BillingService.cs b/BillingCore/BillingService.cs
new file mode 100644
index 0000000..41839fc
--- /dev/null
+++ b/BillingCore/BillingService.cs
@@ -0,0 +1,30 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Teknik.Configuration;
+
+namespace Teknik.BillingCore
+{
+ public abstract class BillingService
+ {
+ protected readonly BillingConfig Config;
+
+ public BillingService(BillingConfig billingConfig)
+ {
+ Config = billingConfig;
+ }
+
+ public abstract bool CreateCustomer(string email);
+ public abstract Tuple CreateSubscription(string customerId, string priceId);
+
+ public abstract bool EditSubscription();
+
+ public abstract Subscription GetSubscription(string subscriptionId);
+
+ public abstract bool RemoveSubscription();
+
+ public abstract void SyncSubscriptions();
+ }
+}
diff --git a/BillingCore/IBillingService.cs b/BillingCore/IBillingService.cs
new file mode 100644
index 0000000..1653f5e
--- /dev/null
+++ b/BillingCore/IBillingService.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Teknik.BillingCore
+{
+ public interface IBillingService
+ {
+ public bool AddSubscription();
+ public bool EditSubscription();
+ public bool RemoveSubscription();
+ public void GetSubscription();
+ public void SyncSubscriptions();
+ }
+}
diff --git a/BillingCore/IntervalType.cs b/BillingCore/IntervalType.cs
new file mode 100644
index 0000000..a7e519d
--- /dev/null
+++ b/BillingCore/IntervalType.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Teknik.BillingCore
+{
+ public enum IntervalType
+ {
+ Monthly,
+ Yearly
+ }
+}
diff --git a/BillingCore/Product.cs b/BillingCore/Product.cs
new file mode 100644
index 0000000..2df3327
--- /dev/null
+++ b/BillingCore/Product.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Teknik.BillingCore
+{
+ public class Product
+ {
+ public int ProductId { get; set; }
+ public string Name { get; set; }
+ public string Description { get; set; }
+ }
+}
diff --git a/BillingCore/StripeService.cs b/BillingCore/StripeService.cs
new file mode 100644
index 0000000..deee621
--- /dev/null
+++ b/BillingCore/StripeService.cs
@@ -0,0 +1,84 @@
+using Stripe;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Teknik.Configuration;
+
+namespace Teknik.BillingCore
+{
+ public class StripeService : BillingService
+ {
+ public StripeService(BillingConfig config) : base(config)
+ { }
+
+ public override bool CreateCustomer(string email)
+ {
+ var options = new CustomerCreateOptions
+ {
+ Email = email,
+ };
+ var service = new CustomerService();
+ var customer = service.Create(options);
+ return customer != null;
+ }
+
+ public override Subscription GetSubscription(string subscriptionId)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override Tuple CreateSubscription(string customerId, string priceId)
+ {
+ // Create the subscription. Note we're expanding the Subscription's
+ // latest invoice and that invoice's payment_intent
+ // so we can pass it to the front end to confirm the payment
+ var subscriptionOptions = new SubscriptionCreateOptions
+ {
+ Customer = customerId,
+ Items = new List
+ {
+ new SubscriptionItemOptions
+ {
+ Price = priceId,
+ },
+ },
+ PaymentBehavior = "default_incomplete",
+ };
+ subscriptionOptions.AddExpand("latest_invoice.payment_intent");
+ var subscriptionService = new SubscriptionService();
+ try
+ {
+ Stripe.Subscription subscription = subscriptionService.Create(subscriptionOptions);
+
+ return new Tuple(true, subscription.Id, subscription.LatestInvoice.PaymentIntent.ClientSecret);
+ }
+ catch (StripeException e)
+ {
+ return new Tuple(false, $"Failed to create subscription. {e}", null);
+ }
+ }
+
+ public override bool EditSubscription()
+ {
+ throw new NotImplementedException();
+ }
+
+ public override bool RemoveSubscription()
+ {
+ throw new NotImplementedException();
+ }
+
+ public override void SyncSubscriptions()
+ {
+ throw new NotImplementedException();
+ }
+
+ private Customer GetCustomer(string id)
+ {
+ var service = new CustomerService();
+ return service.Get(id);
+ }
+ }
+}
diff --git a/BillingCore/Subscription.cs b/BillingCore/Subscription.cs
new file mode 100644
index 0000000..5dc5884
--- /dev/null
+++ b/BillingCore/Subscription.cs
@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Teknik.BillingCore
+{
+ public class Subscription
+ {
+ public int SubscriptionId { get; set; }
+ public string Name { get; set; }
+ public string Description { get; set; }
+ public double Amount { get; set; }
+ }
+}
diff --git a/BillingCore/UserSubscription.cs b/BillingCore/UserSubscription.cs
new file mode 100644
index 0000000..1c2a476
--- /dev/null
+++ b/BillingCore/UserSubscription.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Teknik.BillingCore
+{
+ public class UserSubscription
+ {
+ public int UserId { get; set; }
+ public int SubscriptionId { get; set; }
+ public Subscription Subscription { get; set; }
+ public int ProductId { get; set; }
+ public Product Product { get; set; }
+ }
+}
diff --git a/BillingService/ArgumentOptions.cs b/BillingService/ArgumentOptions.cs
new file mode 100644
index 0000000..ca37a96
--- /dev/null
+++ b/BillingService/ArgumentOptions.cs
@@ -0,0 +1,20 @@
+using CommandLine;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Teknik.BillingService
+{
+ public class ArgumentOptions
+ {
+ [Option('s', "sync", Default = false, Required = false, HelpText = "Syncs the current subscriptions with the invoice system")]
+ public bool SyncSubscriptions { get; set; }
+
+ [Option('c', "config", Required = false, HelpText = "The path to the teknik config file")]
+ public string Config { get; set; }
+
+ // Omitting long name, default --verbose
+ [Option(HelpText = "Prints all messages to standard output.")]
+ public bool Verbose { get; set; }
+ }
+}
diff --git a/BillingService/BillingService.csproj b/BillingService/BillingService.csproj
new file mode 100644
index 0000000..2f8e28f
--- /dev/null
+++ b/BillingService/BillingService.csproj
@@ -0,0 +1,19 @@
+
+
+
+ Exe
+ net5.0
+ Teknik.BillingService
+ Teknik.BillingService
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/BillingService/Program.cs b/BillingService/Program.cs
new file mode 100644
index 0000000..30de78e
--- /dev/null
+++ b/BillingService/Program.cs
@@ -0,0 +1,77 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Reflection;
+using CommandLine;
+using Microsoft.EntityFrameworkCore;
+using Teknik.Configuration;
+using Teknik.Data;
+using Teknik.Utilities;
+
+namespace Teknik.BillingService
+{
+ public class Program
+ {
+ private static string currentPath = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
+ private static string errorFile = Path.Combine(currentPath, "errorLogs.txt");
+ private static string configPath = currentPath;
+
+ private static readonly object dbLock = new object();
+ private static readonly object scanStatsLock = new object();
+
+ public static event Action OutputEvent;
+
+ public static int Main(string[] args)
+ {
+ try
+ {
+ Parser.Default.ParseArguments(args).WithParsed(options =>
+ {
+ if (!string.IsNullOrEmpty(options.Config))
+ configPath = options.Config;
+
+ if (Directory.Exists(configPath))
+ {
+ Config config = Config.Load(configPath);
+ Output(string.Format("[{0}] Started Billing Service Process.", DateTime.Now));
+
+ var optionsBuilder = new DbContextOptionsBuilder();
+ optionsBuilder.UseSqlServer(config.DbConnection);
+
+ using (TeknikEntities db = new TeknikEntities(optionsBuilder.Options))
+ {
+ if (options.SyncSubscriptions)
+ {
+ // Sync subscription information
+ }
+ }
+
+ Output(string.Format("[{0}] Finished Billing Service Process.", DateTime.Now));
+ }
+ else
+ {
+ string msg = string.Format("[{0}] Config File does not exist.", DateTime.Now);
+ File.AppendAllLines(errorFile, new List { msg });
+ Output(msg);
+ }
+ });
+ }
+ catch (Exception ex)
+ {
+ string msg = string.Format("[{0}] Exception: {1}", DateTime.Now, ex.GetFullMessage(true));
+ File.AppendAllLines(errorFile, new List { msg });
+ Output(msg);
+ }
+ return -1;
+ }
+
+ public static void Output(string message)
+ {
+ Console.WriteLine(message);
+ if (OutputEvent != null)
+ {
+ OutputEvent(message);
+ }
+ }
+ }
+}
diff --git a/Configuration/BillingConfig.cs b/Configuration/BillingConfig.cs
new file mode 100644
index 0000000..4e857b5
--- /dev/null
+++ b/Configuration/BillingConfig.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Teknik.Configuration
+{
+ public class BillingConfig
+ {
+ public string StripePublishApiKey { get; set; }
+ public string StripeSecretApiKey { get; set; }
+
+ public BillingConfig()
+ {
+ StripePublishApiKey = null;
+ StripeSecretApiKey = null;
+ }
+ }
+}
diff --git a/Configuration/Config.cs b/Configuration/Config.cs
index 143f6f9..02a9ed6 100644
--- a/Configuration/Config.cs
+++ b/Configuration/Config.cs
@@ -52,6 +52,7 @@ namespace Teknik.Configuration
private LoggingConfig _LoggingConfig;
private PiwikConfig _PiwikConfig;
private IRCConfig _IRCConfig;
+ private BillingConfig _BillingConfig;
public bool DevEnvironment { get { return _DevEnvironment; } set { _DevEnvironment = value; } }
public bool Migrate { get { return _Migrate; } set { _Migrate = value; } }
@@ -120,9 +121,12 @@ namespace Teknik.Configuration
// Piwik Configuration
public PiwikConfig PiwikConfig { get { return _PiwikConfig; } set { _PiwikConfig = value; } }
- // Piwik Configuration
+ // IRC Configuration
public IRCConfig IRCConfig { get { return _IRCConfig; } set { _IRCConfig = value; } }
+ // Billing Configuration
+ public BillingConfig BillingConfig { get { return _BillingConfig; } set { _BillingConfig = value; } }
+
public Config()
{
_ConfigRWLock = new ReaderWriterLockSlim();
@@ -167,6 +171,7 @@ namespace Teknik.Configuration
LoggingConfig = new LoggingConfig();
PiwikConfig = new PiwikConfig();
IRCConfig = new IRCConfig();
+ BillingConfig = new BillingConfig();
}
public static Config Deserialize(string text)
diff --git a/ServiceWorker/Program.cs b/ServiceWorker/Program.cs
index af8336c..5ee3145 100644
--- a/ServiceWorker/Program.cs
+++ b/ServiceWorker/Program.cs
@@ -1,7 +1,6 @@
using CommandLine;
using Microsoft.EntityFrameworkCore;
using nClam;
-using StorageService;
using System;
using System.Collections.Generic;
using System.IO;
@@ -17,6 +16,7 @@ using Teknik.Areas.Users.Models;
using Teknik.Areas.Users.Utility;
using Teknik.Configuration;
using Teknik.Data;
+using Teknik.StorageService;
using Teknik.Utilities;
using Teknik.Utilities.Cryptography;
diff --git a/StorageService/IStorageService.cs b/StorageService/IStorageService.cs
index 4758697..9818696 100644
--- a/StorageService/IStorageService.cs
+++ b/StorageService/IStorageService.cs
@@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.IO;
using Teknik.Configuration;
-namespace StorageService
+namespace Teknik.StorageService
{
public interface IStorageService
{
diff --git a/StorageService/LocalStorageService.cs b/StorageService/LocalStorageService.cs
index d567ced..4a44887 100644
--- a/StorageService/LocalStorageService.cs
+++ b/StorageService/LocalStorageService.cs
@@ -8,7 +8,7 @@ using Teknik.Configuration;
using Teknik.Utilities;
using Teknik.Utilities.Cryptography;
-namespace StorageService
+namespace Teknik.StorageService
{
public class LocalStorageService : StorageService
{
diff --git a/StorageService/MemoryStorageService.cs b/StorageService/MemoryStorageService.cs
index cb2939e..a7e7c42 100644
--- a/StorageService/MemoryStorageService.cs
+++ b/StorageService/MemoryStorageService.cs
@@ -8,7 +8,7 @@ using Teknik.Configuration;
using Teknik.Utilities;
using Teknik.Utilities.Cryptography;
-namespace StorageService
+namespace Teknik.StorageService
{
public class MemoryStorageService : StorageService
{
diff --git a/StorageService/StorageService.cs b/StorageService/StorageService.cs
index 4d724cd..55e9c05 100644
--- a/StorageService/StorageService.cs
+++ b/StorageService/StorageService.cs
@@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.IO;
using Teknik.Configuration;
-namespace StorageService
+namespace Teknik.StorageService
{
public abstract class StorageService : IStorageService
{
diff --git a/StorageService/StorageService.csproj b/StorageService/StorageService.csproj
index 3261d92..dfd6cc2 100644
--- a/StorageService/StorageService.csproj
+++ b/StorageService/StorageService.csproj
@@ -1,7 +1,9 @@
-
+net5.0
+ Teknik.StorageService
+ Teknik.StorageService
diff --git a/StorageService/StorageServiceFactory.cs b/StorageService/StorageServiceFactory.cs
index df042e7..83c6c1d 100644
--- a/StorageService/StorageServiceFactory.cs
+++ b/StorageService/StorageServiceFactory.cs
@@ -5,7 +5,7 @@ using System.Text;
using System.Threading.Tasks;
using Teknik.Configuration;
-namespace StorageService
+namespace Teknik.StorageService
{
public static class StorageServiceFactory
{
diff --git a/Teknik.sln b/Teknik.sln
index 61353a0..8be588c 100644
--- a/Teknik.sln
+++ b/Teknik.sln
@@ -34,7 +34,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ContentScanningService", "C
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebCommon", "WebCommon\WebCommon.csproj", "{32E85A7F-871A-437C-9BA3-00499AAB442C}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StorageService", "StorageService\StorageService.csproj", "{4A600C17-C772-462F-A37F-307E7893B2DB}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StorageService", "StorageService\StorageService.csproj", "{4A600C17-C772-462F-A37F-307E7893B2DB}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BillingService", "BillingService\BillingService.csproj", "{AF417E48-8137-4677-9058-E4DD2745FEC5}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BillingCore", "BillingCore\BillingCore.csproj", "{A9ED275B-4004-4A5B-8F1C-134A29B999E7}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -115,6 +119,18 @@ Global
{4A600C17-C772-462F-A37F-307E7893B2DB}.Release|Any CPU.Build.0 = Release|Any CPU
{4A600C17-C772-462F-A37F-307E7893B2DB}.Test|Any CPU.ActiveCfg = Debug|Any CPU
{4A600C17-C772-462F-A37F-307E7893B2DB}.Test|Any CPU.Build.0 = Debug|Any CPU
+ {AF417E48-8137-4677-9058-E4DD2745FEC5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {AF417E48-8137-4677-9058-E4DD2745FEC5}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {AF417E48-8137-4677-9058-E4DD2745FEC5}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {AF417E48-8137-4677-9058-E4DD2745FEC5}.Release|Any CPU.Build.0 = Release|Any CPU
+ {AF417E48-8137-4677-9058-E4DD2745FEC5}.Test|Any CPU.ActiveCfg = Debug|Any CPU
+ {AF417E48-8137-4677-9058-E4DD2745FEC5}.Test|Any CPU.Build.0 = Debug|Any CPU
+ {A9ED275B-4004-4A5B-8F1C-134A29B999E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A9ED275B-4004-4A5B-8F1C-134A29B999E7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A9ED275B-4004-4A5B-8F1C-134A29B999E7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A9ED275B-4004-4A5B-8F1C-134A29B999E7}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A9ED275B-4004-4A5B-8F1C-134A29B999E7}.Test|Any CPU.ActiveCfg = Debug|Any CPU
+ {A9ED275B-4004-4A5B-8F1C-134A29B999E7}.Test|Any CPU.Build.0 = Debug|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/Teknik/App_Data/endpointMappings.json b/Teknik/App_Data/endpointMappings.json
index 9c2dc8f..d07bb2b 100644
--- a/Teknik/App_Data/endpointMappings.json
+++ b/Teknik/App_Data/endpointMappings.json
@@ -1045,6 +1045,50 @@
"action": "ViewRawPGP"
}
},
+ {
+ "Name": "Billing.Index",
+ "HostTypes": [ "Full" ],
+ "SubDomains": [ "billing" ],
+ "Pattern": "",
+ "Area": "Billing",
+ "Defaults": {
+ "controller": "Billing",
+ "action": "Index"
+ }
+ },
+ {
+ "Name": "Billing.Subscriptions",
+ "HostTypes": [ "Full" ],
+ "SubDomains": [ "billing" ],
+ "Pattern": "Subscriptions",
+ "Area": "Billing",
+ "Defaults": {
+ "controller": "Billing",
+ "action": "Subscriptions"
+ }
+ },
+ {
+ "Name": "Billing.ViewPaymentInfo",
+ "HostTypes": [ "Full" ],
+ "SubDomains": [ "billing" ],
+ "Pattern": "PaymentInfo",
+ "Area": "Billing",
+ "Defaults": {
+ "controller": "Billing",
+ "action": "ViewPaymentInfo"
+ }
+ },
+ {
+ "Name": "Billing.Action",
+ "HostTypes": [ "Full" ],
+ "SubDomains": [ "billing" ],
+ "Pattern": "Action/{action}",
+ "Area": "Billing",
+ "Defaults": {
+ "controller": "Billing",
+ "action": "Index"
+ }
+ },
{
"Name": "Vault.Index",
"HostTypes": [ "Full" ],
diff --git a/Teknik/Areas/Billing/Controllers/BillingController.cs b/Teknik/Areas/Billing/Controllers/BillingController.cs
new file mode 100644
index 0000000..4a96c21
--- /dev/null
+++ b/Teknik/Areas/Billing/Controllers/BillingController.cs
@@ -0,0 +1,39 @@
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Logging;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using Teknik.Areas.Billing.ViewModels;
+using Teknik.Configuration;
+using Teknik.Controllers;
+using Teknik.Data;
+using Teknik.Logging;
+
+namespace Teknik.Areas.Billing.Controllers
+{
+ [Area("Billing")]
+ public class BillingController : DefaultController
+ {
+ public BillingController(ILogger logger, Config config, TeknikEntities dbContext) : base(logger, config, dbContext) { }
+
+ [AllowAnonymous]
+ public IActionResult Index()
+ {
+ return View(new BillingViewModel() { StripePublishKey = _config.BillingConfig.StripePublishApiKey });
+ }
+
+ [AllowAnonymous]
+ public IActionResult Subscriptions()
+ {
+ return View(new BillingViewModel() { StripePublishKey = _config.BillingConfig.StripePublishApiKey });
+ }
+
+ [AllowAnonymous]
+ public IActionResult ViewPaymentInfo()
+ {
+ return View(new PaymentViewModel() { StripePublishKey = _config.BillingConfig.StripePublishApiKey });
+ }
+ }
+}
diff --git a/Teknik/Areas/Billing/ViewModels/BillingViewModel.cs b/Teknik/Areas/Billing/ViewModels/BillingViewModel.cs
new file mode 100644
index 0000000..002e179
--- /dev/null
+++ b/Teknik/Areas/Billing/ViewModels/BillingViewModel.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using Teknik.ViewModels;
+
+namespace Teknik.Areas.Billing.ViewModels
+{
+ public class BillingViewModel : ViewModelBase
+ {
+ public string StripePublishKey { get; set; }
+ }
+}
diff --git a/Teknik/Areas/Billing/ViewModels/PaymentViewModel.cs b/Teknik/Areas/Billing/ViewModels/PaymentViewModel.cs
new file mode 100644
index 0000000..91c7f12
--- /dev/null
+++ b/Teknik/Areas/Billing/ViewModels/PaymentViewModel.cs
@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using Teknik.ViewModels;
+
+namespace Teknik.Areas.Billing.ViewModels
+{
+ public class PaymentViewModel : ViewModelBase
+ {
+ public string StripePublishKey { get; set; }
+ public string PaymentName { get; set; }
+ public string CreditCardNumber { get; set; }
+ public string ExpirationMonth { get; set; }
+ public string ExpirationYear { get; set; }
+ public string CCV { get; set; }
+ public string ZipCode { get; set; }
+ }
+}
diff --git a/Teknik/Areas/Billing/Views/Billing/PaymentDetails.cshtml b/Teknik/Areas/Billing/Views/Billing/PaymentDetails.cshtml
new file mode 100644
index 0000000..9f54583
--- /dev/null
+++ b/Teknik/Areas/Billing/Views/Billing/PaymentDetails.cshtml
@@ -0,0 +1,54 @@
+@model Teknik.Areas.Billing.ViewModels.PaymentViewModel
+
+
+
+@{
+
+
+
+ @if (Model.Error)
+ {
+
@Model.ErrorMessage
+ }
+
+
+
+
+}
+
+
+
\ No newline at end of file
diff --git a/Teknik/Areas/Billing/Views/Billing/Subscriptions.cshtml b/Teknik/Areas/Billing/Views/Billing/Subscriptions.cshtml
new file mode 100644
index 0000000..28ae1ae
--- /dev/null
+++ b/Teknik/Areas/Billing/Views/Billing/Subscriptions.cshtml
@@ -0,0 +1,89 @@
+@model Teknik.Areas.Billing.ViewModels.BillingViewModel
+
+@{
+ string extraUsage = "If you need more than 100 GB of monthly upload bandwidth, please contact support for assistance.";
+}
+
+
\ No newline at end of file
diff --git a/Teknik/Areas/Billing/Views/Billing/ViewPaymentInfo.cshtml b/Teknik/Areas/Billing/Views/Billing/ViewPaymentInfo.cshtml
new file mode 100644
index 0000000..8e307cd
--- /dev/null
+++ b/Teknik/Areas/Billing/Views/Billing/ViewPaymentInfo.cshtml
@@ -0,0 +1,14 @@
+@model Teknik.Areas.Billing.ViewModels.PaymentViewModel
+
+