LicenseRenewalForm.xaml.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527
  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 Stripe;
  11. using System.Diagnostics.CodeAnalysis;
  12. using System.Windows.Controls;
  13. using System.Drawing;
  14. using Image = System.Windows.Controls.Image;
  15. using System.Diagnostics;
  16. using InABox.Configuration;
  17. using PRS.Shared;
  18. namespace PRSServer.Forms.DatabaseLicense
  19. {
  20. public class CreditCardGrid : DynamicGrid<CreditCardDetails>
  21. {
  22. private static readonly EncryptedLocalConfiguration<CreditCardSettings> DetailsConfiguration = new("g6+BoQpyti5bHsTZOY5Nbqq3Q3c90n0m3qZaQ3eAwkk=");
  23. protected override void Reload(Filters<CreditCardDetails> criteria, Columns<CreditCardDetails> columns, ref SortOrder<CreditCardDetails>? sort, Action<CoreTable, Exception> action)
  24. {
  25. }
  26. private static CreditCardDetails LoadSaved()
  27. {
  28. var settings = DetailsConfiguration.Load();
  29. var details = new CreditCardDetails
  30. {
  31. CardNumber = settings.CardNumber,
  32. Month = settings.Month,
  33. Year = settings.Year,
  34. Cvv = settings.Cvv,
  35. // If the card number is not blank, it means we loaded from a saved file, and therefore the default should be to still save details
  36. SaveDetails = !string.IsNullOrWhiteSpace(settings.CardNumber)
  37. };
  38. return details;
  39. }
  40. private static void SaveDetails(CreditCardDetails details)
  41. {
  42. var settings = new CreditCardSettings
  43. {
  44. CardNumber = details.CardNumber,
  45. Month = details.Month,
  46. Year = details.Year,
  47. Cvv = details.Cvv
  48. };
  49. DetailsConfiguration.Save(settings);
  50. }
  51. private static void ClearDetails()
  52. {
  53. DetailsConfiguration.Delete();
  54. }
  55. public static bool DoPayment(double amount, [NotNullWhen(true)] out string? transactionID)
  56. {
  57. var details = LoadSaved();
  58. details.Amount = amount;
  59. var grid = new CreditCardGrid();
  60. if (grid.EditItems(new[] { details }))
  61. {
  62. if (details.SaveDetails)
  63. {
  64. SaveDetails(details);
  65. }
  66. else
  67. {
  68. ClearDetails();
  69. }
  70. transactionID = grid.TransactionID;
  71. return true;
  72. }
  73. transactionID = null;
  74. return false;
  75. }
  76. protected override CreditCardDetails LoadItem(CoreRow row)
  77. {
  78. return new CreditCardDetails();
  79. }
  80. private string TransactionID = "";
  81. protected override void DoValidate(CreditCardDetails[] items, List<string> errors)
  82. {
  83. base.DoValidate(items, errors);
  84. if (items.Any(x => string.IsNullOrWhiteSpace(x.CardNumber)))
  85. errors.Add("Please supply a card number.");
  86. if (items.Any(x => string.IsNullOrWhiteSpace(x.Month)))
  87. errors.Add("Please supply an expiration month.");
  88. if (items.Any(x => string.IsNullOrWhiteSpace(x.Year)))
  89. errors.Add("Please supply an expiration year.");
  90. if (items.Any(x => string.IsNullOrWhiteSpace(x.Cvv)))
  91. errors.Add("Please supply a CVV.");
  92. }
  93. public override void SaveItem(CreditCardDetails item)
  94. {
  95. TransactionID = "";
  96. var error = "";
  97. Progress.ShowModal("Processing Payment", (progress) =>
  98. {
  99. try
  100. {
  101. StripeConfiguration.ApiKey = "sk_test_51MRSuPIyPMVqmkXN8TeGxDAGBeFx0pLzn3fsHBR8X1oMBKQVwGPEbuv6DNIu0qSmuflpmFfQ4N8c3vzdknKa7G0o00wTOXwCeW";
  102. var cardoptions = new TokenCreateOptions()
  103. {
  104. Card = new TokenCardOptions()
  105. {
  106. Number = item.CardNumber.Replace(" ", ""),
  107. ExpMonth = item.Month,
  108. ExpYear = item.Year,
  109. Cvc = item.Cvv
  110. }
  111. };
  112. var service = new TokenService();
  113. var token = service.Create(cardoptions);
  114. var chargeoptions = new ChargeCreateOptions()
  115. {
  116. Amount = Convert.ToInt64(item.Amount * 100),
  117. Currency = "AUD",
  118. Description = "PRS Renewal",
  119. Source = token.Id
  120. };
  121. var chargeservice = new ChargeService();
  122. var charge = chargeservice.Create(chargeoptions);
  123. if (charge.Paid)
  124. TransactionID = charge.Id;
  125. else
  126. error = string.Format("{0}: {1}", charge.FailureCode, charge.FailureMessage);
  127. }
  128. catch (Exception ex)
  129. {
  130. error = $"{ex.Message}";
  131. }
  132. });
  133. if (!String.IsNullOrWhiteSpace(error))
  134. throw new Exception(error);
  135. }
  136. protected override void DeleteItems(params CoreRow[] rows)
  137. {
  138. }
  139. protected override void Init()
  140. {
  141. }
  142. protected override void DoReconfigure(FluentList<DynamicGridOption> options)
  143. {
  144. }
  145. }
  146. /// <summary>
  147. /// Interaction logic for LicenseRenewal.xaml
  148. /// </summary>
  149. public partial class LicenseRenewalForm : ThemableWindow
  150. {
  151. private LicenseData? _currentLicense;
  152. private double _gross;
  153. private double _discount;
  154. private DateTime _renewalAvailable;
  155. public LicenseData? CurrentLicense
  156. {
  157. get => _currentLicense;
  158. set
  159. {
  160. if(value == null)
  161. {
  162. return;
  163. }
  164. PayWithStripe.IsEnabled = false;
  165. PayTooltip.Visibility = Visibility.Visible;
  166. PayTooltip.Content = new Label { Content = "Loading..." };
  167. _currentLicense = value;
  168. LastRenewal.Value = value.LastRenewal;
  169. CurrentExpiry.Value = value.Expiry;
  170. Modules.Renewed = value.LastRenewal;
  171. RenewalAvailableFrom = value.RenewalAvailable;
  172. Modules.Refresh(false, true);
  173. }
  174. }
  175. public int RenewalPeriod
  176. {
  177. get
  178. {
  179. if (int.TryParse(RenewalPeriodEditor.Value?.ToString(), out var period))
  180. {
  181. return period;
  182. }
  183. return 0;
  184. }
  185. set
  186. {
  187. RenewalPeriodEditor.Value = value;
  188. CalculateDiscounts();
  189. }
  190. }
  191. public DateTime RenewalDate
  192. {
  193. get
  194. {
  195. if(CurrentLicense == null)
  196. {
  197. return DateTime.MinValue;
  198. }
  199. if (CurrentLicense.Expiry > DateTime.Now)
  200. {
  201. return CurrentLicense.Expiry.Date;
  202. }
  203. return DateTime.Today;
  204. }
  205. }
  206. public DateTime RenewalAvailableFrom
  207. {
  208. get => _renewalAvailable;
  209. set
  210. {
  211. _renewalAvailable = value;
  212. if (!CanRenew)
  213. {
  214. RenewalAvailableLabel.Content = $"Renewal available from {value:dd MMM yyyy}";
  215. }
  216. }
  217. }
  218. public bool CanRenew => RenewalAvailableFrom.Date <= DateTime.Today || (CurrentLicense != null && CurrentLicense.Expiry <= DateTime.Now);
  219. public DateTime NewExpiration
  220. {
  221. get => RenewalDate.AddMonths(RenewalPeriod);
  222. }
  223. public double Gross { get => _gross; }
  224. public double Discount { get => _discount; }
  225. public double Net { get => _gross - _discount; }
  226. public LicenseRenewalForm()
  227. {
  228. InitializeComponent();
  229. LastRenewal.EditorDefinition = new DateEditor();
  230. LastRenewal.Configure();
  231. LastRenewal.IsEnabled = false;
  232. CurrentExpiry.EditorDefinition = new DateEditor();
  233. CurrentExpiry.Configure();
  234. CurrentExpiry.IsEnabled = false;
  235. RenewalPeriodEditor.EditorDefinition = new ComboLookupEditor(typeof(RenewalPeriodLookups));
  236. RenewalPeriodEditor.Configure();
  237. var lookups = new RenewalPeriodLookups(null).AsTable("RenewalPeriod");
  238. RenewalPeriodEditor.LoadLookups(lookups);
  239. NewExpiry.EditorDefinition = new DateEditor();
  240. NewExpiry.Configure();
  241. NewExpiry.IsEnabled = false;
  242. GrossLicenseFee.EditorDefinition = new CurrencyEditor();
  243. GrossLicenseFee.Configure();
  244. GrossLicenseFee.IsEnabled = false;
  245. DiscountEditor.EditorDefinition = new DoubleEditor();
  246. DiscountEditor.Configure();
  247. DiscountEditor.IsEnabled = false;
  248. NettLicenseFee.EditorDefinition = new CurrencyEditor();
  249. NettLicenseFee.Configure();
  250. NettLicenseFee.IsEnabled = false;
  251. Help.Content = new Image { Source = Properties.Resources.help.AsBitmapImage(Color.White) };
  252. Help.Click += Help_Click;
  253. Modules.AfterRefresh += ModulesRefreshed;
  254. //double months = Math.Max(0.0F,CalcMonths(CurrentExpiry.Value, NewExpiry.Value));
  255. //double total = 0.0F;
  256. //foreach (var row in Modules.Data.Rows)
  257. // total += row.Get<LicenseRenewal, double>(x => x.ExGST) * months;
  258. //GrossLicenseFee.Value = total;
  259. ////GrossLicenseFee.Width = 150;
  260. //Discount.Value = LicenseUtils.GetUserDiscount(Modules.Licenses);
  261. ////Discount.Width = 150;
  262. //NettLicenseFee.Value = total * ((100.0F - Discount.Value) / 100.0F);
  263. ////NettLicenseFee.Width = 150;
  264. }
  265. private void Help_Click(object sender, RoutedEventArgs e)
  266. {
  267. Process.Start(new ProcessStartInfo("https://prsdigital.com.au/wiki/index.php/License_Renewal") { UseShellExecute = true });
  268. }
  269. private void Window_Loaded(object sender, RoutedEventArgs e)
  270. {
  271. if (!LicenseTrackingItemGrid.LicenseClient.Ping("ping"))
  272. {
  273. MessageBox.Show("The PRS server is not available right now. Please try again later.");
  274. Close();
  275. return;
  276. }
  277. Modules.Refresh(true, false);
  278. var license = LoadCurrentLicense();
  279. if(license != null)
  280. {
  281. CurrentLicense = license;
  282. }
  283. LastRenewal.Loaded = true;
  284. CurrentExpiry.Loaded = true;
  285. NewExpiry.Loaded = true;
  286. RenewalPeriodEditor.Loaded = true;
  287. }
  288. private void ModulesRefreshed(object sender, AfterRefreshEventArgs args)
  289. {
  290. if (CurrentLicense == null) return;
  291. var lookups = new RenewalPeriodLookups(null).AsTable("RenewalPeriod");
  292. RenewalPeriodEditor.LoadLookups(lookups);
  293. RenewalPeriod = LicenseUtils.TimeDiscountLevels().OrderBy(x => x).First();
  294. PayWithStripe.IsEnabled = CanRenew;
  295. if (!PayWithStripe.IsEnabled)
  296. {
  297. PayTooltip.Visibility = Visibility.Visible;
  298. PayTooltip.Content = new Label { Content = $"Renewal available from {RenewalAvailableFrom:dd MMM yyyy}" };
  299. }
  300. else
  301. {
  302. PayTooltip.Visibility = Visibility.Collapsed;
  303. }
  304. OK.IsEnabled = true;
  305. }
  306. private static LicenseData? LoadCurrentLicense()
  307. {
  308. var license = new Client<License>().Query(new Filter<License>().All(), new Columns<License>(x => x.Data))
  309. .Rows.FirstOrDefault()?.ToObject<License>();
  310. if(license == null)
  311. {
  312. MessageBox.Show("No current license found. Please see the documentation on how to proceed.");
  313. return null;
  314. }
  315. if(!LicenseUtils.TryDecryptLicense(license.Data, out var data, out var error))
  316. {
  317. MessageBox.Show("Current license is corrupt. Please see the documentation on how to proceed.");
  318. return null;
  319. }
  320. return data;
  321. }
  322. private void RenewalPeriod_OnEditorValueChanged(IDynamicEditorControl sender, Dictionary<string, object> values)
  323. {
  324. CalculateDiscounts();
  325. }
  326. private void CalculateDiscounts()
  327. {
  328. var periodInMonths = RenewalPeriod;
  329. NewExpiry.Value = NewExpiration;
  330. double total = 0.0F;
  331. foreach (var row in Modules.Data.Rows)
  332. total += row.Get<LicenseTrackingItem, double>(x => x.ExGST) * periodInMonths;
  333. _gross = total;
  334. _discount = total * (LicenseUtils.GetUserDiscount(Modules.Licenses) / 100.0F) +
  335. total * (LicenseUtils.GetTimeDiscount(periodInMonths) / 100.0F);
  336. GrossLicenseFee.Value = Gross;
  337. DiscountEditor.Value = Discount;
  338. NettLicenseFee.Value = Net;
  339. PayWithStripe.IsEnabled = CanRenew && Net > 0;
  340. }
  341. private class RenewalPeriodLookups : LookupGenerator<object>
  342. {
  343. public RenewalPeriodLookups(object[] items) : base(items)
  344. {
  345. }
  346. protected override void DoGenerateLookups()
  347. {
  348. foreach (var period in LicenseUtils.TimeDiscountLevels())
  349. AddValue(period, string.Format("{0} month{1}", period, period > 1 ? "s" : ""));
  350. }
  351. }
  352. private License GetNewLicense(LicenseRenewal licenseRenewal)
  353. {
  354. var license = LicenseTrackingItemGrid.LicenseClient.PostRequest<License>(licenseRenewal, "RenewLicense");
  355. if(license == null)
  356. {
  357. throw new Exception("The server did not return a valid license.");
  358. }
  359. return license;
  360. }
  361. private void SaveLicense(License license)
  362. {
  363. using var client = new Client<License>();
  364. var oldLicenses = client
  365. .Query(new Filter<License>().All(), new Columns<License>(x => x.ID))
  366. .Rows.Select(x => x.ToObject<License>()).ToList();
  367. client.Delete(oldLicenses, "");
  368. client.Save(license, "");
  369. }
  370. private void PayNowClick(object sender, RoutedEventArgs e)
  371. {
  372. if (!LicenseTrackingItemGrid.LicenseClient.Ping("ping"))
  373. {
  374. MessageBox.Show("The PRS server is not available right now. Please try again later.");
  375. return;
  376. }
  377. var companyInformation = new Client<CompanyInformation>().Load().FirstOrDefault();
  378. var isCompanyEmpty = string.IsNullOrWhiteSpace(companyInformation?.CompanyName);
  379. var isABNEmpty = string.IsNullOrWhiteSpace(companyInformation?.ABN);
  380. if (companyInformation == null || (isCompanyEmpty && isABNEmpty))
  381. {
  382. MessageBox.Show("The company information for this database has not been set. Please set company information before proceeding. The company name and ABN are required. This can be done through PRS Desktop, via System -> Company Information");
  383. return;
  384. }
  385. else if (isCompanyEmpty)
  386. {
  387. MessageBox.Show("Please set your company's name before proceeding. This can be done through PRS Desktop, via System -> Company Information");
  388. return;
  389. }
  390. else if (isABNEmpty)
  391. {
  392. MessageBox.Show("Please set your company's ABN before proceeding. This can be done through PRS Desktop, via System -> Company Information");
  393. return;
  394. }
  395. if (!CreditCardGrid.DoPayment(Net, out var transactionID))
  396. return;
  397. /*var grid = new CreditCardGrid();
  398. var ccDetails = grid.LoadSaved();
  399. ccDetails.Amount = Net;
  400. if (grid.EditItems(new CreditCardDetails[] { ccDetails }))
  401. {
  402. var transactionID = grid.TransactionID;*/
  403. var licenseRenewal = new LicenseRenewal
  404. {
  405. Company = companyInformation,
  406. DateRenewed = RenewalDate,
  407. OldLicense = CurrentLicense,
  408. NewExpiry = NewExpiration,
  409. LicenseTracking = Modules.Data.Rows.Select(x => x.ToObject<LicenseTrackingItem>()).ToArray(),
  410. Gross = Gross,
  411. Discount = Discount,
  412. Net = Net,
  413. TransactionID = transactionID
  414. };
  415. string? error = null;
  416. License? license = null;
  417. Progress.ShowModal("Payment success, renewing license", progress =>
  418. {
  419. try
  420. {
  421. license = GetNewLicense(licenseRenewal);
  422. SaveLicense(license);
  423. }
  424. catch(Exception e)
  425. {
  426. error = $"There was an error retrieving your new license:\n\n{e.Message}\n\nPlease contact the PRS team, and keep your transaction ID for future reference. Your transaction ID is '{transactionID}'";
  427. }
  428. });
  429. if(error != null)
  430. {
  431. MessageBox.Show(error);
  432. }
  433. else
  434. {
  435. MessageBox.Show("License Renewed!");
  436. if(!LicenseUtils.TryDecryptLicense(license.Data, out var licenseData, out error))
  437. {
  438. MessageBox.Show(error);
  439. }
  440. CurrentLicense = licenseData ?? new LicenseData();
  441. }
  442. //}
  443. }
  444. private void OK_Click(object sender, RoutedEventArgs e)
  445. {
  446. DialogResult = true;
  447. Close();
  448. }
  449. private void Cancel_Click(object sender, RoutedEventArgs e)
  450. {
  451. DialogResult = false;
  452. Close();
  453. }
  454. }
  455. }