using System; using System.Collections.Generic; using System.Linq; using System.Windows; using InABox.Clients; using InABox.Core; using InABox.DynamicGrid; using InABox.Wpf; using InABox.WPF; using System.Windows.Controls; using System.Drawing; using Image = System.Windows.Controls.Image; using System.Diagnostics; using InABox.Client.Remote.Json; using InABox.Configuration; using Microsoft.Win32; using PRS.Shared; using Stripe; namespace PRSServer.Forms.DatabaseLicense; /// /// Interaction logic for LicenseRenewal.xaml /// public partial class LicenseRenewalForm : ThemableWindow, IDynamicEditorHost { public static HttpJsonClient LicenseClient = new("https", "remote.prsdigital.com.au", 5000); //public static HttpJsonClient LicenseClient = new("http", "127.0.0.1", 8001); private static readonly EncryptedLocalConfiguration _config = new("g6+BoQpyti5bHsTZOY5Nbqq3Q3c90n0m3qZaQ3eAwkk="); private LicenseRegistrationDetails _licenseRegistrationDetails; private LicenseData? _currentLicense; public LicenseData? CurrentLicense { get => _currentLicense; set { _currentLicense = value; Modules.CurrentLicense = value; } } public int RenewalPeriod { get { if (int.TryParse(RenewalPeriodEditor.Value?.ToString(), out var period)) { return period; } return 0; } set { RenewalPeriodEditor.Value = value; CalculateDiscounts(); } } public DateTime RenewalDate { get { if(CurrentLicense == null) { return DateTime.MinValue; } if (CurrentLicense.Expiry > DateTime.Now) { return CurrentLicense.Expiry.Date; } return DateTime.Today; } } private DateTime _renewalAvailable; public DateTime RenewalAvailableFrom { get => _renewalAvailable; set { _renewalAvailable = value; if (!CanRenew) { RenewalAvailableLabel.Content = $"Renewal available from {value:dd MMM yyyy}"; } } } public bool CanRenew => RenewalAvailableFrom.Date <= DateTime.Today || (CurrentLicense != null && CurrentLicense.Expiry <= DateTime.Now); public DateTime NewExpiration { get => RenewalDate.AddMonths(RenewalPeriod); } public List LicenseItems { get; private set; } public int Licenses { get; private set; } public double Gross { get; private set; } public double Discount { get; private set; } public double Net => Gross - Discount; public LicenseRenewalForm() { InitializeComponent(); // This should get us the RegistrationID, which we need in order // to get the License Pricing from the Server _licenseRegistrationDetails = _config.Load(false); LastRenewal.EditorDefinition = new DateEditor(); LastRenewal.Configure(); LastRenewal.IsEnabled = false; CurrentExpiry.EditorDefinition = new DateEditor(); CurrentExpiry.Configure(); CurrentExpiry.IsEnabled = false; RenewalPeriodEditor.EditorDefinition = new ComboLookupEditor(typeof(RenewalPeriodLookups)); RenewalPeriodEditor.Host = this; RenewalPeriodEditor.Configure(); var lookups = new RenewalPeriodLookups(null).AsTable("RenewalPeriod"); RenewalPeriodEditor.LoadLookups(lookups); NewExpiry.EditorDefinition = new DateEditor(); NewExpiry.Configure(); NewExpiry.IsEnabled = false; GrossLicenseFee.EditorDefinition = new CurrencyEditor(); GrossLicenseFee.Configure(); GrossLicenseFee.IsEnabled = false; DiscountEditor.EditorDefinition = new DoubleEditor(); DiscountEditor.Configure(); DiscountEditor.IsEnabled = false; NettLicenseFee.EditorDefinition = new CurrencyEditor(); NettLicenseFee.Configure(); NettLicenseFee.IsEnabled = false; Help.Content = new Image { Source = Properties.Resources.help.AsBitmapImage(Color.White) }; Help.Click += Help_Click; } private void Help_Click(object sender, RoutedEventArgs e) { Process.Start(new ProcessStartInfo("https://prsdigital.com.au/wiki/index.php/License_Renewal") { UseShellExecute = true }); } private void LoadData() { var result = Progress.ShowModal("Getting License", progress => { try { progress.Report("Loading License"); CurrentLicense = LoadCurrentLicense(); progress.Report("Checking Server"); if (!LicenseClient.Ping("ping")) return Result.Error("Server Unavailable"); progress.Report("Retrieving Data"); RetrieveFees(); progress.Report("Scanning Data"); LoadUserTracking(); foreach (var item in LicenseItems) item.Rate = LicenseUtils.GetLicenseFee(item.Type); return Result.Ok(true); } catch (Exception e) { return Result.Error(e.Message); } }); if (result.Get(out var success, out var error) && success) { UpdateWindow(); } else { MessageWindow.ShowMessage(error ?? "Error", "Error"); LicenseItems = new(); IsEnabled = false; UpdateWindow(); } } private void UpdateWindow() { // Always a minimum of one license required! Licenses = Math.Max(1, LicenseItems.OrderByDescending(x => x.Users).FirstOrDefault()?.Users ?? 0); Modules.Items = LicenseItems; Modules.Refresh(true, true); var lookups = new RenewalPeriodLookups(null).AsTable("RenewalPeriod"); RenewalPeriodEditor.LoadLookups(lookups); RenewalPeriodEditor.Loaded = true; RenewalPeriod = LicenseUtils.TimeDiscountLevels().OrderBy(x => x).FirstOrDefault(); PayWithStripe.IsEnabled = CanRenew; if (!PayWithStripe.IsEnabled) { PayTooltip.Visibility = Visibility.Visible; PayTooltip.Content = new Label { Content = $"Renewal available from {RenewalAvailableFrom:dd MMM yyyy}" }; } else PayTooltip.Visibility = Visibility.Collapsed; LastRenewal.Loaded = true; CurrentExpiry.Loaded = true; NewExpiry.Loaded = true; //PayWithStripe.IsEnabled = false; PayTooltip.Visibility = Visibility.Visible; PayTooltip.Content = new Label { Content = "Loading..." }; LastRenewal.Value = CurrentLicense?.LastRenewal ?? DateTime.MinValue; CurrentExpiry.Value = CurrentLicense?.Expiry ?? DateTime.MinValue; RenewalAvailableFrom = CurrentLicense?.RenewalAvailable ?? DateTime.MinValue; } private void Window_Loaded(object sender, RoutedEventArgs e) { LoadData(); } private void RetrieveFees() { var summary = LicenseClient.PostRequest( new LicenseFeeRequest() { RegistrationID = CurrentLicense?.CustomerID ?? Guid.Empty }, nameof(LicenseFeeRequest) ); LicenseUtils.LoadSummary(summary); } private void LoadUserTracking() { var renewaldate = _currentLicense?.LastRenewal ?? DateTime.MinValue; var filter = !renewaldate.IsEmpty() ? new Filter(x => x.Date).IsGreaterThanOrEqualTo(renewaldate).And(x => x.TotalWrite).IsNotEqualTo(0) : new Filter(x => x.TotalWrite).IsNotEqualTo(0); var data = new Client() .Query( filter, InABox.Core.Columns.None().Add(x => x.User.ID) .Add(x => x.Type)); var typesummary = data.Rows.GroupBy(r => r.Get(c => c.Type)).ToArray(); //Licenses = data.ExtractValues(x => x.User.ID).Distinct().Count(); var licensemap = LicenseUtils.LicenseMap(); var result = new List(); foreach (var map in licensemap) { var type = map.Value.EntityName(); var item = result.FirstOrDefault(x => Equals(x.Type, type)); if (item == null) { item = new LicenseTrackingItem() { Type = type, Caption = map.Value.GetCaption(), }; result.Add(item); } var users = typesummary .FirstOrDefault(x => Equals(x.Key, map.Key.EntityName().Split('.').Last()))? .Select(r=>r.Get(x=>x.User.ID)) .Distinct() .Where(x=>!item.UserIDs.Contains(x)); if (users != null) item.UserIDs.AddRange(users); } LicenseItems = result .OrderBy(x=>x.Caption) .ToList(); } private static LicenseData? LoadCurrentLicense() { var license = new Client().Query( new Filter().All(), InABox.Core.Columns.None().Add(x => x.Data)) .Rows.FirstOrDefault()?.ToObject(); if(license == null) { MessageBox.Show("No current license found. Please see the documentation on how to proceed."); return null; } if(!LicenseUtils.TryDecryptLicense(license.Data, out var data, out var error)) { MessageBox.Show("Current license is corrupt. Please see the documentation on how to proceed."); return null; } return data; } private void RenewalPeriod_OnEditorValueChanged(IDynamicEditorControl sender, Dictionary values) { CalculateDiscounts(); } private void CalculateDiscounts() { double CalcDiscount(double amount, int months) { return (amount * (LicenseUtils.GetUserDiscount(Licenses) / 100.0F)) + (amount * (LicenseUtils.GetTimeDiscount(months) / 100.0F)); } var periodInMonths = RenewalPeriod; NewExpiry.Value = NewExpiration; double total = 0.0F; if (CurrentLicense?.IsDynamic == true) { foreach (var row in Modules.Data.Rows) total += row.Get(x => x.ExGST) * periodInMonths; Gross = total; Discount = CalcDiscount(total, periodInMonths); } else { foreach (var row in Modules.Data.Rows) total += Licenses * LicenseUtils.GetLicenseFee(row.Get(x => x.Type)) * periodInMonths; Gross = Math.Round(total) - 0.05; Gross = total; Discount = Math.Round(CalcDiscount(total, periodInMonths) * 20F) / 20F; } GrossLicenseFee.Value = Gross; DiscountEditor.Value = Discount; NettLicenseFee.Value = Net; PayWithStripe.IsEnabled = CanRenew && Net > 0; } private class RenewalPeriodLookups : LookupGenerator { public RenewalPeriodLookups(object[] items) : base(items) { } protected override void DoGenerateLookups() { foreach (var period in LicenseUtils.TimeDiscountLevels()) AddValue(period, string.Format("{0} month{1}", period, period > 1 ? "s" : "")); } } private void PayNowClick(object sender, RoutedEventArgs e) { if (!LicenseClient.Ping("ping")) { MessageBox.Show("The PRS server is not available right now. Please try again later."); return; } var grid = new LicenseRegistrationGrid(); if (grid.EditItems(new LicenseRegistrationDetails[] { _licenseRegistrationDetails })) { _config.Save(_licenseRegistrationDetails); Result result = Result.Error("Incomplete"); var renewalRequest = CreateRenewal(); Progress.ShowModal("Processing", progress => { if (renewalRequest.Net > 0.0F) { // Process the Stripe Payment progress.Report("Processing Payment"); result = ProcessStripePayment(); if (!result.IsOK) return; } else result = Result.Ok("no payment required"); progress.Report("Creating Renewal"); if (result.Get(out var value, out var error)) { result = RenewLicense(renewalRequest, value); } if(result.Get(out value, out error)) { progress.Report("Saving License"); SaveLicense(value); } }); if (!result.Get(out var value, out var error)) MessageWindow.ShowMessage(error, "Error"); else { MessageWindow.ShowMessage("License Updated Successfully!","Success"); Close(); } } } private Result ProcessStripePayment() { var result = ""; var error = ""; try { StripeConfiguration.ApiKey = "pk_live_51MRSuPIyPMVqmkXNiIu0BjTHWrfIvsWalXqYsebuTr27JF08jwk9KunHfvTrI30bojbQJgr0fWD3RkBXVnDsF75v00ryCeqOav"; //StripeConfiguration.ApiKey = "sk_test_51MRSuPIyPMVqmkXN8TeGxDAGBeFx0pLzn3fsHBR8X1oMBKQVwGPEbuv6DNIu0qSmuflpmFfQ4N8c3vzdknKa7G0o00wTOXwCeW"; var cardoptions = new TokenCreateOptions() { Card = new TokenCardOptions() { Number = _licenseRegistrationDetails.Card.CardNumber.Replace(" ", ""), ExpMonth = _licenseRegistrationDetails.Card.Month, ExpYear = _licenseRegistrationDetails.Card.Year, Cvc = _licenseRegistrationDetails.Card.Cvv } }; var service = new TokenService(); var token = service.Create(cardoptions); var chargeoptions = new ChargeCreateOptions() { Amount = Convert.ToInt64(Gross * 100), Currency = "AUD", Description = "PRS Renewal", Source = token.Id }; var chargeservice = new ChargeService(); var charge = chargeservice.Create(chargeoptions); if (charge.Paid) result = charge.Id; else error = string.Format("{0}: {1}", charge.FailureCode, charge.FailureMessage); } catch (Exception ex) { error = $"{ex.Message}"; } if (!string.IsNullOrWhiteSpace(error)) return Result.Error(error); return Result.Ok(result); } private LicenseRenewalRequest CreateRenewal() { return new LicenseRenewalRequest { Company = _licenseRegistrationDetails.Company, DateRenewed = RenewalDate, OldLicense = CurrentLicense, NewExpiry = NewExpiration, LicenseTracking = LicenseItems.ToArray(), Gross = Gross, Discount = Discount, Net = Net, Addresses = LicenseUtils.GetMacAddresses(), }; } private Result RenewLicense(LicenseRenewalRequest renewalRequest, String transactionID) { renewalRequest.TransactionID = transactionID; try { var result = LicenseClient.PostRequest(renewalRequest, nameof(LicenseRenewalRequest)); return Result.Ok(result.License); } catch (Exception e) { return Result.Error(e.Message); } } private void SaveLicense(string license) { using var client = new Client(); var oldLicenses = client .Query( new Filter().All(), InABox.Core.Columns.None().Add(x => x.ID)) .Rows.Select(x => x.ToObject()).ToList(); client.Delete(oldLicenses, ""); client.Save(new License() { Data = license }, ""); } #region IDynamicEditorHost public void LoadLookups(ILookupEditorControl editor) { } public BaseObject[] GetItems() { return new BaseObject[] { }; } public Type GetEditorType() => typeof(BaseObject); public BaseEditor? GetEditor(DynamicGridColumn column) { return new NullEditor(); } #endregion private void EnterKey_Click(object sender, RoutedEventArgs e) { if (EnterKey?.ContextMenu != null) EnterKey.ContextMenu.IsOpen = true; } private void CreateManualRequest(object sender, RoutedEventArgs e) { var request = new LicenseRequest() { CustomerID = CurrentLicense?.CustomerID ?? Guid.Empty, Addresses = LicenseUtils.GetMacAddresses(), IsDynamic = CurrentLicense?.IsDynamic ?? false, }; SaveFileDialog sfd = new SaveFileDialog() { FileName = "license.request" }; if (sfd.ShowDialog() == true) { System.IO.File.WriteAllText(sfd.FileName, LicenseUtils.EncryptLicenseRequest(request)); MessageWindow.ShowMessage("Please email this file to support@prsdigital.com.au!","Request Created"); } } private void LoadManualResponse(object sender, RoutedEventArgs e) { var ofd = new OpenFileDialog() { FileName = "license.key", Filter = "License Files (*.key)|*.key" }; if (ofd.ShowDialog() == true && System.IO.File.Exists(ofd.FileName)) { var text = System.IO.File.ReadAllText(ofd.FileName); if (LicenseUtils.TryDecryptLicense(text, out var data, out var _)) { if (LicenseUtils.ValidateMacAddresses(data.Addresses)) { SaveLicense(text); MessageWindow.ShowMessage("License Updated", "Success"); Close(); } else MessageWindow.ShowMessage("License Key is not valid!","Invalid Key"); } else MessageWindow.ShowMessage("License Key is not valid!","Bad Key"); } } }