mirror of
https://github.com/wismna/ModernKeePass.git
synced 2025-10-03 15:40:18 -04:00
Create a LicenseService to handle in-app purchases
This commit is contained in:
57
ModernKeePass/Data/WindowsStoreProxy.xml
Normal file
57
ModernKeePass/Data/WindowsStoreProxy.xml
Normal file
@@ -0,0 +1,57 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<CurrentApp>
|
||||
<ListingInformation>
|
||||
<App>
|
||||
<AppId>0719A91A-C322-4EE0-A257-E60733EECF06</AppId>
|
||||
<LinkUri>https://www.microsoft.com/store/apps/9mwq48zk8nhv</LinkUri>
|
||||
<CurrentMarket>en-us</CurrentMarket>
|
||||
<AgeRating>3</AgeRating>
|
||||
<MarketData xml:lang="en-us">
|
||||
<Name>App with several in-app products</Name>
|
||||
<Description>Sample app for demonstrating an expiring in-app product and a consumable in-app product</Description>
|
||||
<Price>5.99</Price>
|
||||
<CurrencySymbol>$</CurrencySymbol>
|
||||
</MarketData>
|
||||
</App>
|
||||
<Product ProductId="donate1" LicenseDuration="0" ProductType="Consumable">
|
||||
<MarketData xml:lang="en-us">
|
||||
<Name>Small Donation</Name>
|
||||
<Price>0.99</Price>
|
||||
<CurrencySymbol>$</CurrencySymbol>
|
||||
</MarketData>
|
||||
</Product>
|
||||
<Product ProductId="donate5" LicenseDuration="0" ProductType="Consumable">
|
||||
<MarketData xml:lang="en-us">
|
||||
<Name>Medium Donation</Name>
|
||||
<Price>4.99</Price>
|
||||
<CurrencySymbol>$</CurrencySymbol>
|
||||
</MarketData>
|
||||
</Product>
|
||||
<Product ProductId="donate10" LicenseDuration="0" ProductType="Consumable">
|
||||
<MarketData xml:lang="en-us">
|
||||
<Name>Large Donation</Name>
|
||||
<Price>9.99</Price>
|
||||
<CurrencySymbol>$</CurrencySymbol>
|
||||
</MarketData>
|
||||
</Product>
|
||||
<Product ProductId="donate20" LicenseDuration="0" ProductType="Consumable">
|
||||
<MarketData xml:lang="en-us">
|
||||
<Name>Generous Donation</Name>
|
||||
<Price>19.99</Price>
|
||||
<CurrencySymbol>$</CurrencySymbol>
|
||||
</MarketData>
|
||||
</Product>
|
||||
</ListingInformation>
|
||||
<LicenseInformation>
|
||||
<App>
|
||||
<IsActive>true</IsActive>
|
||||
<IsTrial>false</IsTrial>
|
||||
</App>
|
||||
</LicenseInformation>
|
||||
<ConsumableInformation>
|
||||
<Product ProductId="donate1" TransactionId="00000001-0000-0000-0000-000000000000" Status="Active"/>
|
||||
<Product ProductId="donate5" TransactionId="00000001-0000-0000-0000-000000000000" Status="Active"/>
|
||||
<Product ProductId="donate10" TransactionId="00000001-0000-0000-0000-000000000000" Status="Active"/>
|
||||
<Product ProductId="donate20" TransactionId="00000001-0000-0000-0000-000000000000" Status="Active"/>
|
||||
</ConsumableInformation>
|
||||
</CurrentApp>
|
10
ModernKeePass/Interfaces/ILicenseService.cs
Normal file
10
ModernKeePass/Interfaces/ILicenseService.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
using Windows.ApplicationModel.Store;
|
||||
|
||||
namespace ModernKeePass.Interfaces
|
||||
{
|
||||
public interface ILicenseService
|
||||
{
|
||||
IReadOnlyDictionary<string, ProductListing> Products { get; }
|
||||
}
|
||||
}
|
@@ -113,6 +113,7 @@
|
||||
<Compile Include="App.xaml.cs">
|
||||
<DependentUpon>App.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Interfaces\ILicenseService.cs" />
|
||||
<Compile Include="Interfaces\IRecent.cs" />
|
||||
<Compile Include="Interfaces\IRecentItem.cs" />
|
||||
<Compile Include="Interfaces\IResource.cs" />
|
||||
@@ -127,6 +128,7 @@
|
||||
<Compile Include="Common\ObservableDictionary.cs" />
|
||||
<Compile Include="Common\RelayCommand.cs" />
|
||||
<Compile Include="Common\SuspensionManager.cs" />
|
||||
<Compile Include="Services\LicenseService.cs" />
|
||||
<Compile Include="Services\RecentService.cs" />
|
||||
<Compile Include="Services\ResourcesService.cs" />
|
||||
<Compile Include="Services\SettingsService.cs" />
|
||||
@@ -410,6 +412,9 @@
|
||||
<Content Include="Assets\Wide310x150Logo.scale-140.png" />
|
||||
<Content Include="Assets\Wide310x150Logo.scale-180.png" />
|
||||
<Content Include="Assets\Wide310x150Logo.scale-80.png" />
|
||||
<Content Include="Data\WindowsStoreProxy.xml">
|
||||
<SubType>Designer</SubType>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' < '12.0' ">
|
||||
<VisualStudioVersion>12.0</VisualStudioVersion>
|
||||
|
@@ -8,7 +8,10 @@
|
||||
|
||||
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
<TextBlock x:Uid="DonateDesc" />
|
||||
<RadioButton GroupName="DonateOptions" />
|
||||
<RadioButton GroupName="DonateOptions" Content="1" />
|
||||
<RadioButton GroupName="DonateOptions" Content="5" />
|
||||
<RadioButton GroupName="DonateOptions" Content="10" />
|
||||
<RadioButton GroupName="DonateOptions" Content="20" />
|
||||
<Button x:Uid="DonateButton" />
|
||||
</StackPanel>
|
||||
</Page>
|
||||
|
97
ModernKeePass/Services/LicenseService.cs
Normal file
97
ModernKeePass/Services/LicenseService.cs
Normal file
@@ -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<string, ProductListing> Products { get; }
|
||||
|
||||
//private LicenseInformation _licenseInformation;
|
||||
private readonly HashSet<Guid> _consumedTransactionIds = new HashSet<Guid>();
|
||||
|
||||
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<PurchaseResult> 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<PurchaseResult> 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);
|
||||
}
|
||||
}
|
||||
}
|
@@ -168,6 +168,9 @@
|
||||
<data name="CompositeKeyPassword.PlaceholderText" xml:space="preserve">
|
||||
<value>Password</value>
|
||||
</data>
|
||||
<data name="DonateButton.Content" xml:space="preserve">
|
||||
<value>Donate</value>
|
||||
</data>
|
||||
<data name="DonateDesc.Text" xml:space="preserve">
|
||||
<value>Like this app? Why not make a small donation to support my work and help this app stay ad-free :) ?</value>
|
||||
</data>
|
||||
|
@@ -168,6 +168,9 @@
|
||||
<data name="CompositeKeyPassword.PlaceholderText" xml:space="preserve">
|
||||
<value>Mot de passe</value>
|
||||
</data>
|
||||
<data name="DonateButton.Content" xml:space="preserve">
|
||||
<value>Donner</value>
|
||||
</data>
|
||||
<data name="DonateDesc.Text" xml:space="preserve">
|
||||
<value>Vous aimez cette app? Pourquoi ne pas faire un petit don afin de m'encourager et d'éviter le recours aux publicités :) ?</value>
|
||||
</data>
|
||||
|
Reference in New Issue
Block a user