LicenseRenewalForm.xaml.cs 20 KB

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