LicenseRenewalForm.xaml.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Windows;
  5. using InABox.Clients;
  6. using InABox.Core;
  7. using InABox.DynamicGrid;
  8. using InABox.Wpf;
  9. using InABox.WPF;
  10. using System.Windows.Controls;
  11. using System.Drawing;
  12. using Image = System.Windows.Controls.Image;
  13. using System.Diagnostics;
  14. using FluentResults;
  15. using InABox.Client.Remote.Json;
  16. using InABox.Configuration;
  17. using Microsoft.Win32;
  18. using PRS.Shared;
  19. using Stripe;
  20. namespace PRSServer.Forms.DatabaseLicense
  21. {
  22. /// <summary>
  23. /// Interaction logic for LicenseRenewal.xaml
  24. /// </summary>
  25. public partial class LicenseRenewalForm : ThemableWindow, IDynamicEditorHost
  26. {
  27. public static HttpJsonClient LicenseClient = new("https", "remote.prsdigital.com.au", 5000);
  28. //public static HttpJsonClient LicenseClient = new("http", "127.0.0.1", 8001);
  29. private static readonly EncryptedLocalConfiguration<LicenseRegistrationDetails> _config = new("g6+BoQpyti5bHsTZOY5Nbqq3Q3c90n0m3qZaQ3eAwkk=");
  30. private LicenseRegistrationDetails _licenseRegistrationDetails;
  31. private LicenseData? _currentLicense;
  32. public LicenseData? CurrentLicense
  33. {
  34. get => _currentLicense;
  35. set => _currentLicense = value;
  36. }
  37. public int RenewalPeriod
  38. {
  39. get
  40. {
  41. if (int.TryParse(RenewalPeriodEditor.Value?.ToString(), out var period))
  42. {
  43. return period;
  44. }
  45. return 0;
  46. }
  47. set
  48. {
  49. RenewalPeriodEditor.Value = value;
  50. CalculateDiscounts();
  51. }
  52. }
  53. public DateTime RenewalDate
  54. {
  55. get
  56. {
  57. if(CurrentLicense == null)
  58. {
  59. return DateTime.MinValue;
  60. }
  61. if (CurrentLicense.Expiry > DateTime.Now)
  62. {
  63. return CurrentLicense.Expiry.Date;
  64. }
  65. return DateTime.Today;
  66. }
  67. }
  68. private DateTime _renewalAvailable;
  69. public DateTime RenewalAvailableFrom
  70. {
  71. get => _renewalAvailable;
  72. set
  73. {
  74. _renewalAvailable = value;
  75. if (!CanRenew)
  76. {
  77. RenewalAvailableLabel.Content = $"Renewal available from {value:dd MMM yyyy}";
  78. }
  79. }
  80. }
  81. public bool CanRenew => RenewalAvailableFrom.Date <= DateTime.Today || (CurrentLicense != null && CurrentLicense.Expiry <= DateTime.Now);
  82. public DateTime NewExpiration
  83. {
  84. get => RenewalDate.AddMonths(RenewalPeriod);
  85. }
  86. public List<LicenseTrackingItem> LicenseItems { get; private set; }
  87. public int Licenses { get; private set; }
  88. public double Gross { get; private set; }
  89. public double Discount { get; private set; }
  90. public double Net => Gross - Discount;
  91. public LicenseRenewalForm()
  92. {
  93. InitializeComponent();
  94. Modules.OnCustomiseColumns += Modules_OnOnCustomiseColumns;
  95. // This should get us the RegistrationID, which we need in order
  96. // to get the License Pricing from the Server
  97. _licenseRegistrationDetails = _config.Load(false);
  98. LastRenewal.EditorDefinition = new DateEditor();
  99. LastRenewal.Configure();
  100. LastRenewal.IsEnabled = false;
  101. CurrentExpiry.EditorDefinition = new DateEditor();
  102. CurrentExpiry.Configure();
  103. CurrentExpiry.IsEnabled = false;
  104. RenewalPeriodEditor.EditorDefinition = new ComboLookupEditor(typeof(RenewalPeriodLookups));
  105. RenewalPeriodEditor.Host = this;
  106. RenewalPeriodEditor.Configure();
  107. var lookups = new RenewalPeriodLookups(null).AsTable("RenewalPeriod");
  108. RenewalPeriodEditor.LoadLookups(lookups);
  109. NewExpiry.EditorDefinition = new DateEditor();
  110. NewExpiry.Configure();
  111. NewExpiry.IsEnabled = false;
  112. GrossLicenseFee.EditorDefinition = new CurrencyEditor();
  113. GrossLicenseFee.Configure();
  114. GrossLicenseFee.IsEnabled = false;
  115. DiscountEditor.EditorDefinition = new DoubleEditor();
  116. DiscountEditor.Configure();
  117. DiscountEditor.IsEnabled = false;
  118. NettLicenseFee.EditorDefinition = new CurrencyEditor();
  119. NettLicenseFee.Configure();
  120. NettLicenseFee.IsEnabled = false;
  121. Help.Content = new Image { Source = Properties.Resources.help.AsBitmapImage(Color.White) };
  122. Help.Click += Help_Click;
  123. }
  124. private void Help_Click(object sender, RoutedEventArgs e)
  125. {
  126. Process.Start(new ProcessStartInfo("https://prsdigital.com.au/wiki/index.php/License_Renewal") { UseShellExecute = true });
  127. }
  128. private void LoadData()
  129. {
  130. Result<bool> result = Progress.ShowModal<bool>("Getting License", progress =>
  131. {
  132. try
  133. {
  134. progress.Report("Loading License");
  135. CurrentLicense = LoadCurrentLicense();
  136. progress.Report("Checking Server");
  137. if (!LicenseClient.Ping("ping"))
  138. return Result.Fail<bool>("Server Unavailable");
  139. progress.Report("Retrieving Data");
  140. RetrieveFees();
  141. progress.Report("Scanning Data");
  142. LoadUserTracking();
  143. foreach (var item in LicenseItems)
  144. item.Rate = LicenseUtils.GetLicenseFee(item.Type);
  145. return Result.Ok<bool>(true);
  146. }
  147. catch (Exception e)
  148. {
  149. return Result.Fail<bool>(e.Message);
  150. }
  151. });
  152. if (result.IsSuccess && result.Value)
  153. UpdateWindow();
  154. else
  155. {
  156. MessageWindow.ShowMessage(String.Join("\n",result.Errors.Select(x=>x.Message)), "Error");
  157. UpdateWindow();
  158. }
  159. }
  160. private void UpdateWindow()
  161. {
  162. // Always a minimum of one license required!
  163. Licenses = Math.Max(1, LicenseItems.OrderByDescending(x => x.Users).FirstOrDefault()?.Users ?? 0);
  164. Modules.Items = LicenseItems;
  165. Modules.Refresh(true, true);
  166. var lookups = new RenewalPeriodLookups(null).AsTable("RenewalPeriod");
  167. RenewalPeriodEditor.LoadLookups(lookups);
  168. RenewalPeriodEditor.Loaded = true;
  169. RenewalPeriod = LicenseUtils.TimeDiscountLevels().OrderBy(x => x).FirstOrDefault();
  170. PayWithStripe.IsEnabled = CanRenew;
  171. if (!PayWithStripe.IsEnabled)
  172. {
  173. PayTooltip.Visibility = Visibility.Visible;
  174. PayTooltip.Content = new Label { Content = $"Renewal available from {RenewalAvailableFrom:dd MMM yyyy}" };
  175. }
  176. else
  177. PayTooltip.Visibility = Visibility.Collapsed;
  178. LastRenewal.Loaded = true;
  179. CurrentExpiry.Loaded = true;
  180. NewExpiry.Loaded = true;
  181. //PayWithStripe.IsEnabled = false;
  182. PayTooltip.Visibility = Visibility.Visible;
  183. PayTooltip.Content = new Label { Content = "Loading..." };
  184. LastRenewal.Value = CurrentLicense?.LastRenewal ?? DateTime.MinValue;
  185. CurrentExpiry.Value = CurrentLicense?.Expiry ?? DateTime.MinValue;
  186. RenewalAvailableFrom = CurrentLicense?.RenewalAvailable ?? DateTime.MinValue;
  187. }
  188. private void Window_Loaded(object sender, RoutedEventArgs e)
  189. {
  190. LoadData();
  191. }
  192. private void RetrieveFees()
  193. {
  194. var summary = LicenseClient.PostRequest<LicenseFeeResponse>(
  195. new LicenseFeeRequest() { RegistrationID = CurrentLicense?.CustomerID ?? Guid.Empty },
  196. nameof(LicenseFeeRequest)
  197. );
  198. LicenseUtils.LoadSummary(summary);
  199. }
  200. private void LoadUserTracking()
  201. {
  202. var renewaldate = _currentLicense?.LastRenewal ?? DateTime.MinValue;
  203. var filter = !renewaldate.IsEmpty()
  204. ? new Filter<UserTracking>(x => x.Date).IsGreaterThanOrEqualTo(renewaldate).And(x => x.TotalWrite).IsNotEqualTo(0)
  205. : new Filter<UserTracking>(x => x.TotalWrite).IsNotEqualTo(0);
  206. var data = new Client<UserTracking>()
  207. .Query(
  208. filter,
  209. InABox.Core.Columns.None<UserTracking>().Add(x => x.User.ID)
  210. .Add(x => x.Type));
  211. var typesummary = data.Rows.GroupBy(r => r.Get<UserTracking, String>(c => c.Type)).ToArray();
  212. //Licenses = data.ExtractValues<UserTracking, Guid>(x => x.User.ID).Distinct().Count();
  213. var licensemap = LicenseUtils.LicenseMap();
  214. var result = new List<LicenseTrackingItem>();
  215. foreach (var map in licensemap)
  216. {
  217. var type = map.Value.EntityName();
  218. var item = result.FirstOrDefault(x => Equals(x.Type, type));
  219. if (item == null)
  220. {
  221. item = new LicenseTrackingItem()
  222. {
  223. Type = type,
  224. Caption = map.Value.GetCaption(),
  225. };
  226. result.Add(item);
  227. }
  228. var users = typesummary
  229. .FirstOrDefault(x => Equals(x.Key, map.Key.EntityName().Split('.').Last()))?
  230. .Select(r=>r.Get<UserTracking,Guid>(x=>x.User.ID))
  231. .Distinct()
  232. .Where(x=>!item.UserIDs.Contains(x));
  233. if (users != null)
  234. item.UserIDs.AddRange(users);
  235. }
  236. LicenseItems = result
  237. .OrderBy(x=>x.Caption)
  238. .ToList();
  239. }
  240. private static LicenseData? LoadCurrentLicense()
  241. {
  242. var license = new Client<License>().Query(
  243. new Filter<License>().All(),
  244. InABox.Core.Columns.None<License>().Add(x => x.Data))
  245. .Rows.FirstOrDefault()?.ToObject<License>();
  246. if(license == null)
  247. {
  248. MessageBox.Show("No current license found. Please see the documentation on how to proceed.");
  249. return null;
  250. }
  251. if(!LicenseUtils.TryDecryptLicense(license.Data, out var data, out var error))
  252. {
  253. MessageBox.Show("Current license is corrupt. Please see the documentation on how to proceed.");
  254. return null;
  255. }
  256. return data;
  257. }
  258. private void RenewalPeriod_OnEditorValueChanged(IDynamicEditorControl sender, Dictionary<string, object> values)
  259. {
  260. CalculateDiscounts();
  261. }
  262. private void CalculateDiscounts()
  263. {
  264. double CalcDiscount(double amount, int months)
  265. {
  266. return (amount * (LicenseUtils.GetUserDiscount(Licenses) / 100.0F)) +
  267. (amount * (LicenseUtils.GetTimeDiscount(months) / 100.0F));
  268. }
  269. var periodInMonths = RenewalPeriod;
  270. NewExpiry.Value = NewExpiration;
  271. double total = 0.0F;
  272. if (CurrentLicense?.IsDynamic == true)
  273. {
  274. foreach (var row in Modules.Data.Rows)
  275. total += row.Get<LicenseTrackingItem, double>(x => x.ExGST) * periodInMonths;
  276. Gross = total;
  277. Discount = CalcDiscount(total, periodInMonths);
  278. }
  279. else
  280. {
  281. foreach (var row in Modules.Data.Rows)
  282. total += Licenses * LicenseUtils.GetLicenseFee(row.Get<LicenseTrackingItem, String>(x => x.Type)) * periodInMonths;
  283. Gross = Math.Round(total) - 0.05;
  284. Gross = total;
  285. Discount = Math.Round(CalcDiscount(total, periodInMonths) * 20F) / 20F;
  286. }
  287. GrossLicenseFee.Value = Gross;
  288. DiscountEditor.Value = Discount;
  289. NettLicenseFee.Value = Net;
  290. PayWithStripe.IsEnabled = CanRenew && Net > 0;
  291. }
  292. private class RenewalPeriodLookups : LookupGenerator<object>
  293. {
  294. public RenewalPeriodLookups(object[] items) : base(items)
  295. {
  296. }
  297. protected override void DoGenerateLookups()
  298. {
  299. foreach (var period in LicenseUtils.TimeDiscountLevels())
  300. AddValue(period, string.Format("{0} month{1}", period, period > 1 ? "s" : ""));
  301. }
  302. }
  303. private void PayNowClick(object sender, RoutedEventArgs e)
  304. {
  305. if (!LicenseClient.Ping("ping"))
  306. {
  307. MessageBox.Show("The PRS server is not available right now. Please try again later.");
  308. return;
  309. }
  310. var grid = new LicenseRegistrationGrid();
  311. if (grid.EditItems(new LicenseRegistrationDetails[] { _licenseRegistrationDetails }))
  312. {
  313. _config.Save(_licenseRegistrationDetails);
  314. Result<String> result = Result.Fail("Incomplete");
  315. var renewalRequest = CreateRenewal();
  316. Progress.ShowModal("Processing", progress =>
  317. {
  318. if (renewalRequest.Net > 0.0F)
  319. {
  320. // Process the Stripe Payment
  321. progress.Report("Processing Payment");
  322. result = ProcessStripePayment();
  323. if (result.IsFailed)
  324. return;
  325. }
  326. else
  327. result = Result.Ok("no payment required");
  328. progress.Report("Creating Renewal");
  329. result = RenewLicense(renewalRequest, result.Value);
  330. if (result.IsFailed)
  331. return;
  332. progress.Report("Saving License");
  333. SaveLicense(result.Value);
  334. });
  335. if (result.IsFailed)
  336. MessageWindow.ShowMessage(String.Join("\n",result.Errors.Select(x=>x.Message)),"Error");
  337. else
  338. {
  339. MessageWindow.ShowMessage("License Updated Successfully!","Success");
  340. Close();
  341. }
  342. }
  343. }
  344. private Result<String> ProcessStripePayment()
  345. {
  346. var result = "";
  347. var error = "";
  348. try
  349. {
  350. StripeConfiguration.ApiKey =
  351. "pk_live_51MRSuPIyPMVqmkXNiIu0BjTHWrfIvsWalXqYsebuTr27JF08jwk9KunHfvTrI30bojbQJgr0fWD3RkBXVnDsF75v00ryCeqOav";
  352. //StripeConfiguration.ApiKey = "sk_test_51MRSuPIyPMVqmkXN8TeGxDAGBeFx0pLzn3fsHBR8X1oMBKQVwGPEbuv6DNIu0qSmuflpmFfQ4N8c3vzdknKa7G0o00wTOXwCeW";
  353. var cardoptions = new TokenCreateOptions()
  354. {
  355. Card = new TokenCardOptions()
  356. {
  357. Number = _licenseRegistrationDetails.Card.CardNumber.Replace(" ", ""),
  358. ExpMonth = _licenseRegistrationDetails.Card.Month,
  359. ExpYear = _licenseRegistrationDetails.Card.Year,
  360. Cvc = _licenseRegistrationDetails.Card.Cvv
  361. }
  362. };
  363. var service = new TokenService();
  364. var token = service.Create(cardoptions);
  365. var chargeoptions = new ChargeCreateOptions()
  366. {
  367. Amount = Convert.ToInt64(Gross * 100),
  368. Currency = "AUD",
  369. Description = "PRS Renewal",
  370. Source = token.Id
  371. };
  372. var chargeservice = new ChargeService();
  373. var charge = chargeservice.Create(chargeoptions);
  374. if (charge.Paid)
  375. result = charge.Id;
  376. else
  377. error = string.Format("{0}: {1}", charge.FailureCode, charge.FailureMessage);
  378. }
  379. catch (Exception ex)
  380. {
  381. error = $"{ex.Message}";
  382. }
  383. if (!String.IsNullOrWhiteSpace(error))
  384. return Result.Fail(error);
  385. return Result.Ok(result);
  386. }
  387. private LicenseRenewalRequest CreateRenewal()
  388. {
  389. return new LicenseRenewalRequest
  390. {
  391. Company = _licenseRegistrationDetails.Company,
  392. DateRenewed = RenewalDate,
  393. OldLicense = CurrentLicense,
  394. NewExpiry = NewExpiration,
  395. LicenseTracking = LicenseItems.ToArray(),
  396. Gross = Gross,
  397. Discount = Discount,
  398. Net = Net,
  399. Addresses = LicenseUtils.GetMacAddresses(),
  400. };
  401. }
  402. private Result<string> RenewLicense(LicenseRenewalRequest renewalRequest, String transactionID)
  403. {
  404. renewalRequest.TransactionID = transactionID;
  405. try
  406. {
  407. var result = LicenseClient.PostRequest<LicenseRenewalResult>(renewalRequest, nameof(LicenseRenewalRequest));
  408. return Result.Ok(result.License);
  409. }
  410. catch (Exception e)
  411. {
  412. return Result.Fail(e.Message);
  413. }
  414. }
  415. private void SaveLicense(string license)
  416. {
  417. using var client = new Client<License>();
  418. var oldLicenses = client
  419. .Query(
  420. new Filter<License>().All(),
  421. InABox.Core.Columns.None<License>().Add(x => x.ID))
  422. .Rows.Select(x => x.ToObject<License>()).ToList();
  423. client.Delete(oldLicenses, "");
  424. client.Save(new License() { Data = license }, "");
  425. }
  426. #region IDynamicEditorHost
  427. public IEnumerable<DynamicGridColumn> Columns => new DynamicGridColumn[] { };
  428. public void LoadColumns(string column, Dictionary<string, string> columns)
  429. {
  430. }
  431. public void LoadLookups(ILookupEditorControl editor)
  432. {
  433. }
  434. public BaseObject[] GetItems()
  435. {
  436. return new BaseObject[] { };
  437. }
  438. public Type GetEditorType() => typeof(BaseObject);
  439. public BaseEditor? GetEditor(DynamicGridColumn column)
  440. {
  441. return new NullEditor();
  442. }
  443. #endregion
  444. private void EnterKey_Click(object sender, RoutedEventArgs e)
  445. {
  446. if (EnterKey?.ContextMenu != null)
  447. EnterKey.ContextMenu.IsOpen = true;
  448. }
  449. private void CreateManualRequest(object sender, RoutedEventArgs e)
  450. {
  451. var request = new LicenseRequest()
  452. {
  453. CustomerID = CurrentLicense?.CustomerID ?? Guid.Empty,
  454. Addresses = LicenseUtils.GetMacAddresses(),
  455. IsDynamic = CurrentLicense?.IsDynamic ?? false,
  456. };
  457. SaveFileDialog sfd = new SaveFileDialog()
  458. {
  459. FileName = "license.request"
  460. };
  461. if (sfd.ShowDialog() == true)
  462. {
  463. System.IO.File.WriteAllText(sfd.FileName, LicenseUtils.EncryptLicenseRequest(request));
  464. MessageWindow.ShowMessage("Please email this file to support@prsdigital.com.au!","Request Created");
  465. }
  466. }
  467. private void LoadManualResponse(object sender, RoutedEventArgs e)
  468. {
  469. var ofd = new OpenFileDialog()
  470. {
  471. FileName = "license.key",
  472. Filter = "License Files (*.key)|*.key"
  473. };
  474. if (ofd.ShowDialog() == true && System.IO.File.Exists(ofd.FileName))
  475. {
  476. var text = System.IO.File.ReadAllText(ofd.FileName);
  477. if (LicenseUtils.TryDecryptLicense(text, out var data, out var _))
  478. {
  479. if (LicenseUtils.ValidateMacAddresses(data.Addresses))
  480. {
  481. SaveLicense(text);
  482. MessageWindow.ShowMessage("License Updated", "Success");
  483. Close();
  484. }
  485. else
  486. MessageWindow.ShowMessage("License Key is not valid!","Invalid Key");
  487. }
  488. else
  489. MessageWindow.ShowMessage("License Key is not valid!","Bad Key");
  490. }
  491. }
  492. private void Modules_OnOnCustomiseColumns(object sender, DynamicGridColumns columns)
  493. {
  494. var userscol = columns.FirstOrDefault(x => String.Equals(x.ColumnName, nameof(LicenseTrackingItem.Users)));
  495. if (userscol != null)
  496. userscol.Alignment = Alignment.MiddleCenter;
  497. if (_currentLicense?.IsDynamic != true)
  498. {
  499. var ratecol = columns.FirstOrDefault(x => String.Equals(x.ColumnName, nameof(LicenseTrackingItem.Rate)));
  500. if (ratecol != null)
  501. columns.Remove(ratecol);
  502. var exgstcol = columns.FirstOrDefault(x => String.Equals(x.ColumnName, nameof(LicenseTrackingItem.ExGST)));
  503. if (exgstcol != null)
  504. columns.Remove(exgstcol);
  505. }
  506. }
  507. }
  508. }