LicenseRenewalForm.xaml.cs 22 KB

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