diff --git a/ModernKeePass/Data/WindowsStoreProxy.xml b/ModernKeePass/Data/WindowsStoreProxy.xml
new file mode 100644
index 0000000..57b2fde
--- /dev/null
+++ b/ModernKeePass/Data/WindowsStoreProxy.xml
@@ -0,0 +1,57 @@
+
+
+
+
+ 0719A91A-C322-4EE0-A257-E60733EECF06
+ https://www.microsoft.com/store/apps/9mwq48zk8nhv
+ en-us
+ 3
+
+ App with several in-app products
+ Sample app for demonstrating an expiring in-app product and a consumable in-app product
+ 5.99
+ $
+
+
+
+
+ Small Donation
+ 0.99
+ $
+
+
+
+
+ Medium Donation
+ 4.99
+ $
+
+
+
+
+ Large Donation
+ 9.99
+ $
+
+
+
+
+ Generous Donation
+ 19.99
+ $
+
+
+
+
+
+ true
+ false
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ModernKeePass/Interfaces/ILicenseService.cs b/ModernKeePass/Interfaces/ILicenseService.cs
new file mode 100644
index 0000000..94e4895
--- /dev/null
+++ b/ModernKeePass/Interfaces/ILicenseService.cs
@@ -0,0 +1,10 @@
+using System.Collections.Generic;
+using Windows.ApplicationModel.Store;
+
+namespace ModernKeePass.Interfaces
+{
+ public interface ILicenseService
+ {
+ IReadOnlyDictionary Products { get; }
+ }
+}
diff --git a/ModernKeePass/ModernKeePassApp.csproj b/ModernKeePass/ModernKeePassApp.csproj
index 1347c09..0ec278b 100644
--- a/ModernKeePass/ModernKeePassApp.csproj
+++ b/ModernKeePass/ModernKeePassApp.csproj
@@ -113,6 +113,7 @@
App.xaml
+
@@ -127,6 +128,7 @@
+
@@ -410,6 +412,9 @@
+
+ Designer
+
12.0
diff --git a/ModernKeePass/Pages/MainPageFrames/DonatePage.xaml b/ModernKeePass/Pages/MainPageFrames/DonatePage.xaml
index 12d92c5..1c7d09e 100644
--- a/ModernKeePass/Pages/MainPageFrames/DonatePage.xaml
+++ b/ModernKeePass/Pages/MainPageFrames/DonatePage.xaml
@@ -8,7 +8,10 @@
-
+
+
+
+
diff --git a/ModernKeePass/Services/LicenseService.cs b/ModernKeePass/Services/LicenseService.cs
new file mode 100644
index 0000000..d563762
--- /dev/null
+++ b/ModernKeePass/Services/LicenseService.cs
@@ -0,0 +1,97 @@
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Windows.ApplicationModel;
+using Windows.ApplicationModel.Store;
+using ModernKeePass.Interfaces;
+
+namespace ModernKeePass.Services
+{
+ public class LicenseService : ILicenseService
+ {
+ public enum PurchaseResult
+ {
+ Succeeded,
+ NothingToFulfill,
+ PurchasePending,
+ PurchaseReverted,
+ ServerError,
+ NotPurchased,
+ AlreadyPurchased
+ }
+
+ public IReadOnlyDictionary Products { get; }
+
+ //private LicenseInformation _licenseInformation;
+ private readonly HashSet _consumedTransactionIds = new HashSet();
+
+ public LicenseService()
+ {
+ // Initialize the license info for use in the app that is uploaded to the Store.
+ // Uncomment the following line in the release version of your app.
+ //_licenseInformation = CurrentApp.LicenseInformation;
+
+ // Initialize the license info for testing.
+ // Comment the following line in the release version of your app.
+ //_licenseInformation = CurrentAppSimulator.LicenseInformation;
+#if DEBUG
+ var proxyFile = Package.Current.InstalledLocation.GetFileAsync("data\\WindowsStoreProxy.xml").GetAwaiter().GetResult();
+ CurrentAppSimulator.ReloadSimulatorAsync(proxyFile).GetAwaiter().GetResult();
+ var listing = CurrentAppSimulator.LoadListingInformationAsync().GetAwaiter().GetResult();
+#else
+ var listing = CurrentApp.LoadListingInformationAsync().GetAwaiter().GetResult();
+#endif
+ Products = listing.ProductListings;
+ }
+
+ public async Task Purchase(string addOn)
+ {
+#if DEBUG
+ var purchaseResults = await CurrentAppSimulator.RequestProductPurchaseAsync(addOn);
+#else
+ var purchaseResults = await CurrentApp.RequestProductPurchaseAsync(addOn);
+#endif
+ switch (purchaseResults.Status)
+ {
+ case ProductPurchaseStatus.Succeeded:
+ GrantFeatureLocally(purchaseResults.TransactionId);
+ return await ReportFulfillmentAsync(purchaseResults.TransactionId, addOn);
+ case ProductPurchaseStatus.NotFulfilled:
+ // The purchase failed because we haven't confirmed fulfillment of a previous purchase.
+ // Fulfill it now.
+ if (!IsLocallyFulfilled(purchaseResults.TransactionId))
+ {
+ GrantFeatureLocally(purchaseResults.TransactionId);
+ }
+ return await ReportFulfillmentAsync(purchaseResults.TransactionId, addOn);
+ case ProductPurchaseStatus.NotPurchased:
+ return PurchaseResult.NotPurchased;
+ case ProductPurchaseStatus.AlreadyPurchased:
+ return PurchaseResult.AlreadyPurchased;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+ }
+
+ private async Task ReportFulfillmentAsync(Guid transactionId, string productName)
+ {
+ FulfillmentResult result;
+#if DEBUG
+ result = await CurrentAppSimulator.ReportConsumableFulfillmentAsync(productName, transactionId);
+#else
+ result = await CurrentApp.ReportConsumableFulfillmentAsync(productName, transactionId);
+#endif
+ return (PurchaseResult) result;
+ }
+
+ private void GrantFeatureLocally(Guid transactionId)
+ {
+ _consumedTransactionIds.Add(transactionId);
+ }
+
+ private bool IsLocallyFulfilled(Guid transactionId)
+ {
+ return _consumedTransactionIds.Contains(transactionId);
+ }
+ }
+}
diff --git a/ModernKeePass/Strings/en-US/Resources.resw b/ModernKeePass/Strings/en-US/Resources.resw
index 862ef53..c8d0411 100644
--- a/ModernKeePass/Strings/en-US/Resources.resw
+++ b/ModernKeePass/Strings/en-US/Resources.resw
@@ -168,6 +168,9 @@
Password
+
+ Donate
+
Like this app? Why not make a small donation to support my work and help this app stay ad-free :) ?
diff --git a/ModernKeePass/Strings/fr-FR/Resources.resw b/ModernKeePass/Strings/fr-FR/Resources.resw
index 09fd9ff..51721be 100644
--- a/ModernKeePass/Strings/fr-FR/Resources.resw
+++ b/ModernKeePass/Strings/fr-FR/Resources.resw
@@ -168,6 +168,9 @@
Mot de passe
+
+ Donner
+
Vous aimez cette app? Pourquoi ne pas faire un petit don afin de m'encourager et d'éviter le recours aux publicités :) ?