Windows phone - C# - link to buy app - c#

How can you implement a 'buy' link into a trial app?
Do you use the same method as for implementing a 'rate' link?
Thanks a lot.

Use the MarketplaceDetailTask launcher:
MarketplaceDetailTask marketplaceDetailTask = new MarketplaceDetailTask();
marketplaceDetailTask.Show();
See:
How to use the Marketplace detail task for Windows Phone

first of all you will have to register that buy in your developer accound as in-app product and then use this ...
public static LicenseInformation licenseInformation;
if (!App.licenseInformation.ProductLicenses["productid"].IsActive)
{
try
{
// The user doesn't own this feature, so
// show the purchase dialog.
await CurrentApp.RequestProductPurchaseAsync("productid", false);
// the in-app purchase was successful
}
catch (Exception)
{
// The in-app purchase was not completed because
// an error occurred.
}
}
else
{
// The user already owns this feature.
}
}

Related

StorePurchaseStatus.NotPurchased immediately after RequestPurchaseAsync()

The bounty expires in 23 hours. Answers to this question are eligible for a +100 reputation bounty.
JaSHin wants to draw more attention to this question.
In my WPF application which is packaged via Desktop Bridge I found a problem that some users can't buy addon via in-app purchase. It displays my "Canceled" alert which represents StorePurchaseStatus.NotPurchased where result.ExtendedError is null.
Target framework is:
<TargetFramework>net7.0-windows10.0.19041.0</TargetFramework>
Here is the simplified code that procures the purchase:
namespace MyApp {
public partial class MainWindow: Window {
private readonly StoreContext context;
public MainWindow(){
context = StoreContext.GetDefault();
}
private bool IsAdministrator()
{
var identity = WindowsIdentity.GetCurrent();
var principal = new WindowsPrincipal(identity);
return principal.IsInRole(WindowsBuiltInRole.Administrator);
}
private async void BuyButtonClick(object sender, RoutedEventArgs e) {
if (IsAdministrator())
{
ShowAlert("Cannot run under administrator rights");
return;
}
if (sender is Button button)
{
StoreProduct? storeProduct = ((Product)dataContext).storeProduct;
if (storeProduct != null)
{
Application.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new ThreadStart(async delegate
{
var hwnd = new WindowInteropHelper(this).Handle;
WinRT.Interop.InitializeWithWindow.Initialize(context, hwnd);
var result = await context.RequestPurchaseAsync(storeProduct.StoreId);
switch (result.Status)
{
case StorePurchaseStatus.Succeeded:
ShowAlert("Succeeded");
break;
case StorePurchaseStatus.AlreadyPurchased:
ShowAlert("AlreadyPurchased");
break;
case StorePurchaseStatus.NotPurchased:
var extendedError = result.ExtendedError;
if (extendedError != null)
{
ShowAlert(extendedError.Message);
}
else
{
ShowAlert("Canceled");
}
break;
case StorePurchaseStatus.NetworkError:
ShowAlert("NetworkError");
break;
case StorePurchaseStatus.ServerError:
ShowAlert("ServerError");
break;
}
}
}
}
}
}
It works everywhere on my devices (Windows 11 and Windows 10). The user who cannot buy has Windows 11.
This might be caused by the account type that the customer is using.
First of all, the store purchase will fail if the app is running as administrator.
Normal “admin” accounts (people in the administrators group with a split token) will just run your desktop bridge app as a standard user, unless they right-click and launch something elevated explicitly.
But if the customer is using the system built-in account on their device, the purchase will be failed as the app will be running as administrator. This is not allowed for Microsoft Store purchase API.
It's possible that the issue is related to the user's Windows account or some settings on their device. Here are some steps you can take to troubleshoot the issue:
Check if the user has signed in to the Windows Store app with their Microsoft account. They may need to sign in or create an account if they haven't already done so.
Check if the user has a valid payment method associated with their Microsoft account. They may need to add or update their payment information if it's not valid or up-to-date.
Check if the user has disabled in-app purchases on their device. They can enable this feature in the Windows Settings app under "Apps & features" > "Microsoft Store" > "Advanced options" > "In-app purchases".
Try to reproduce the issue on a test device with the same configuration as the user's device. This can help you isolate the issue and identify any device-specific settings or configurations that may be causing the problem.
Contact Microsoft support for further assistance. They can help you troubleshoot the issue and provide guidance on how to resolve it.
Here's an updated version of your code that includes error handling and logging:
public partial class MainWindow : Window
{
private readonly StoreContext context;
public MainWindow()
{
context = StoreContext.GetDefault();
}
private async void BuyButtonClick(object sender, RoutedEventArgs e)
{
if (IsAdministrator())
{
ShowAlert("Cannot run under administrator rights");
return;
}
var product = (Product)dataContext;
if (product == null || product.storeProduct == null)
{
ShowAlert("Product is null or storeProduct is null");
return;
}
try
{
var hwnd = new WindowInteropHelper(this).Handle;
WinRT.Interop.InitializeWithWindow.Initialize(context, hwnd);
var result = await context.RequestPurchaseAsync(product.storeProduct.StoreId);
switch (result.Status)
{
case StorePurchaseStatus.Succeeded:
ShowAlert("Purchase succeeded");
break;
case StorePurchaseStatus.AlreadyPurchased:
ShowAlert("Already purchased");
break;
case StorePurchaseStatus.NotPurchased:
if (result.ExtendedError != null)
{
ShowAlert($"Purchase failed: {result.ExtendedError.Message}");
}
else
{
ShowAlert("Purchase cancelled");
}
break;
case StorePurchaseStatus.NetworkError:
ShowAlert("Network error");
break;
case StorePurchaseStatus.ServerError:
ShowAlert("Server error");
break;
default:
ShowAlert("Unknown error");
break;
}
}
catch (Exception ex)
{
// Log the exception
Debug.WriteLine(ex.Message);
ShowAlert($"Purchase failed: {ex.Message}");
}
}
private bool IsAdministrator()
{
var identity = WindowsIdentity.GetCurrent();
var principal = new WindowsPrincipal(identity);
return principal.IsInRole(WindowsBuiltInRole.Administrator);
}
private void ShowAlert(string message)
{
MessageBox.Show(message, "Purchase Status", MessageBoxButton.OK, MessageBoxImage.Information);
}
}
This code logs any exceptions that occur during the purchase process and displays a message box with the error message. This can help you troubleshoot the issue and provide more detailed information to the user if the purchase fails.

Trial version in Xamarin.Forms application

I'm trying to implement trial version for my xamarin.forms application. What is the best way to achieve it?
I had an idea to store a unique ID inside the application with help of Xamarin.Essentials.SecureStorage or inside Xamarin.Essentials.Preferences and compare it with data inside my database at the server, but problem is that all the data getting deleted after each reinstallation of application.
Also, I had an idea to access Xamarin.Essentials.DeviceInfo.Name and verify device name during each application launch, but I'm not sure does this property returns the unique device name during each attempt or not.
I'm new to Xamarin.Forms so I'll appreciate any help. Thanks in advance.
//using Xamarin.Essentials.Preferences
var id = Preferences.Get("my_id", string.Empty);
if (string.IsNullOrWhiteSpace(id))
{
id = System.Guid.NewGuid().ToString();
Preferences.Set("my_id", id);
}
// using Xamarin.Essentials.SecureStorage
private async void Save()
{
try
{
await SecureStorage.SetAsync("bla", "secret-oauth-token-
value");
}
catch (Exception ex)
{
// Possible that device doesn't support secure storage on
device.
}
}
private async Task<string> Get()
{
try
{
return await SecureStorage.GetAsync("bla");
}
catch (Exception ex)
{
// Possible that device doesn't support secure storage on
device.
}
return null;
}
Is there some sort of login? Then you can always just store a key on the server that determines if it is paid. And save on the device for offline use. That way you will know if its a paid version. Otherwise publish a lite version seperate on the store

How to make the bot know if its messaged someone before? C# based SteamBot

Ok, firstly, I'm not very advanced at this, xD
In public override void OnMessage (string message, EChatEntryType type) I want to execute a series of tests to figure out which response to send.
The first test is to see if the message starts with a command. I'm an admin for a CSGO server group called "Warmup Servers" and this will be a string of parts which will generate and feed back information into a separate log file.
So I'm working on a custom UserHandler.cs for my steambot. I plan on writing a simple script, so that I can do exec [botname] TGTOnline & exec [botname] TGTOffline to tell the bot if using its account. (Like playing games or something on that account).
To test if the message starts with any of the predefined responses. (More to be added later)
To test if the bot has sent a message before. I don't want someone to start a conversation with me and the bot to be sending its default response every time it recieves a message, it would be annoying for both parties.
So before the default message is called, I need to have a function which checks to see if the bot account (Both bot response or human response) has sent a message to the user recently (Last 5 Minutes) and if it has, tell the bot to do nothing...
However I have no clue on where to start with this script and would love some advice!
Here is my TGTUserHandler.cs section which relates:
using System;
using SteamKit2;
using System.Threading.Tasks;
using System.Collections.Generic;
using SteamTrade;
using SteamTrade.TradeOffer;
using SteamTrade.TradeWebAPI;
namespace SteamBot
{
public class TGTUserHandler : UserHandler
{
private bool TGTOnline = false;
private TaskCompletionSource<string> _UserReport;
public TGTUserHandler(Bot bot, SteamID sid) : base(bot, sid) { }
private bool HandleUserReport(string message)
{
if (_UserReport == null)
return false;
//to be scripted in the morning
_UserReport = null;
return true;
}
public override void OnMessage(string message, EChatEntryType type)
{
if (message.StartsWith("!"))
{
if (message.StartsWith("!help"))
{
SendChatMessage("Welcome to the TGTGaming Automated Bots, We have very limited commands and currently do not do any type of automated trade. Here is the list of current commands you can use:");
SendChatMessage("!WarmupServers [help | Report | Issue] | This command is used for all issues related to the WarmupServer's. Do `!Warmupservers help` for more information");
SendChatMessage("!getadmin | This command is used to contact a TGTGaming administrator. Please do `!getadmin help` for more information");
}
if (message.StartsWith("!WarmupServers"))
{
if (message.StartsWith("!WarmupServers help"))
{
SendChatMessage("Warmup Servers Automated Support by TGTGamer");
SendChatMessage("Commands:");
SendChatMessage("!WarmupServers Report | Used to report a user for specific reasons. This is step by step process");
SendChatMessage("!WarmupServers issue | Used to report a issue with the servers. This is a step by step process");
}
if (message.StartsWith("!WarmupServers Report"))
{
GetUserResponse("You have started the user reporting process, please confirm this is what you wanted to do. [y/n]");
if (message.StartsWith("y"))
{
SendChatMessage("A few quick questions first, please respond yes or no");
GetUserResponse("Are you still in the server?");
if (message.StartsWith("y"))
{
GetUserResponse("Is the User you wish to report still in your server?");
if (message.StartsWith("y"))
{
GetUserResponse("Is there a VIP in the server?");
if (message.StartsWith("y"))
{
SendChatMessage("Please ask the VIP to do a !startvote on this user with your reason.");
}
else
{
_UserReport = new TaskCompletionSource<string>();
return _UserReport.Task;
}
}
else
{
_UserReport = new TaskCompletionSource<string>();
return _UserReport.Task;
}
}
else
{
_UserReport = new TaskCompletionSource<string>();
return _UserReport.Task;
}
}
else
{
SendChatMessage("You have cancelled this action");
}
}
}
}
else
{
if (TGTOnline)
{
//general Responses
if (message.StartsWith("Hello"))
{
SendChatMessage("What's up?");
}
if (message.StartsWith("Heya"))
{
SendChatMessage("Hello");
}
if (message.StartsWith("Hi"))
{
SendChatMessage("Heya");
}
else
{
//defualt response
SendChatMessage(Bot.ChatResponse);
}
}
else
{
//defualt response
SendChatMessage(Bot.ChatResponse);
}
}
}
}
}
All and any help will be appreciated.
Kind regards.
TGT
External Links you may find useful:
Bot Github: https://github.com/Jessecar96/SteamBot/
UserHandler.cs: https://github.com/Jessecar96/SteamBot/blob/master/SteamBot/UserHandler.cs
Footnotes:
Steam Subscriber Agreement:
The steam Subscriber Agreement States the following:
You may not use Cheats, automation software (bots), mods, hacks, or
any other unauthorized third-party software, to modify or automate any
Subscription Marketplace process.
As I am using this bot to automate 3rd party server actions (Rcon Kick's, Server restarts & such) it is within the steam subscriber agreement acceptance for bots. If I then started to sell or buy automatically with this bot, it would breach the subscriber agreement.

Windows Phone In-app purchase Error

I used this wrapper for store functions to add to my Windows Phone 7 project condition to find out if user hase WP8 and enabled him in-apps and if not then just tell him.
I am using it like this:
if (Environment.OSVersion.Version.Major >= 8)
{
_store = StoreLauncher.GetStoreInterface("InAppPurchaseWrapper.Store, InAppPurchaseWrapper, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");
}
if (_store != null)
{
var productListAsync = _store.LoadListingInformationAsync();
productListAsync.Completed = (async, status) =>
{
var listingInformation = async.GetResults();
var removeAdItem = listingInformation.ProductListings[Constants.RemoveAddKey];
PurchaseItem(removeAdItem.ProductId, Result);
};
}
else
{
MessageBox.Show(AppResources.remove_ads_notworking);
}
public void PurchaseItem(string id, Action<bool> result)
{
var purchaseAsync = _store.RequestProductPurchaseAsync(id, false);
purchaseAsync.Completed = (async, status) =>
{
try
{
if (status == StoreAsyncStatus.Completed)
{
var licenseInfo = _store.LicenseInformation;
if (licenseInfo.ProductLicenses[id].IsActive)
{
MessageBox.Show(AppResources.remove_ad_success);
appSettings.IsAdVisible = false;
}
else
{
MessageBox.Show(AppResources.remove_ads_something_goes_wrong);
}
}
else
{
if (status == StoreAsyncStatus.Error)
{
var forceException = async.GetResults();
}
result(false);
}
}
catch (Exception)
{
throw;
}
};
}
The problem is when I am calling RequestProductPurchaseAsync then I get: Catastrophic failure (Exception from HRESULT: 0x8000FFFF (E_UNEXPECTED)). But It was working. For one time I can buy that in-app and when I downloaded licenses I get it there. But not anymore. I was trying to restart emulator, OS but so far nothing helps. Anyone knows where could be problem? Thanks
Edit:
I find out that if I first run some sample application with same ProductId and that app is target for WP8. Then it's okay and I can buy in-app. If then I run original app (target for WP7) then I can buy in-apps too. So where could be problem? Is it posible to have working wrapper for Store functions for WP7 project? Should I send first WP8 app to Beta test and then use it's productId for WP7?
WP7 is not allowed to use Windows Phone Store in-app purchases
You should implement your own in-app purchases service

Correct steps after in-app purchase is successful

I tried the following code for in-app purchase. I'm using CurrentAppSimulator for testing purpose.
private async void history_Click(object sender, RoutedEventArgs e)
{
bool OK = true;
// Get the license info
// The next line is commented out for testing.
// licenseInformation = CurrentApp.LicenseInformation;
// The next line is commented out for production/release.
LicenseInformation licenseInformation = CurrentAppSimulator.LicenseInformation;
if (!licenseInformation.ProductLicenses["PremiumFeatures"].IsActive)
{
try
{
// The customer doesn't own this feature, so
// show the purchase dialog.
await CurrentAppSimulator.RequestProductPurchaseAsync("PremiumFeatures", false);
// the in-app purchase was successful
OK = true;
}
catch (Exception)
{
// The in-app purchase was not completed because
// an error occurred.
OK = false;
}
}
else
{
// The customer already owns this feature.
OK = true;
}
if (OK)
{
Frame.Navigate(typeof(HistoryPage));
}
}
However, the dialog keep pop up, every time I perform history_Click, even I choose S_OK. I expect licenseInformation.ProductLicenses["PremiumFeatures"].IsActive should changed to true, after I buy the PremiumFeatures.
I guess, perhaps when the in-app purchase was successful, I need to turn on IsActive flag, so that it will not show me purchase dialog again.
// the in-app purchase was successful
OK = true;
// Compile error!!!
licenseInformation.ProductLicenses["PremiumFeatures"].IsActive = true;
OK. Seems like IsActive is a read only field. So, may I know, what is the correct way to handle purchase successful case?
Update :
After looking at Trial app and in-app purchase sample, I realize having the following code can have IsActive changed from false to true automatically, after purchasing successful. (But why it works?)
private async Task LoadInAppPurchaseProxyFileAsync()
{
StorageFolder proxyDataFolder = await Package.Current.InstalledLocation.GetFolderAsync("data");
StorageFile proxyFile = await proxyDataFolder.GetFileAsync("in-app-purchase.xml");
licenseChangeHandler = new LicenseChangedEventHandler(InAppPurchaseRefreshScenario);
CurrentAppSimulator.LicenseInformation.LicenseChanged += licenseChangeHandler;
await CurrentAppSimulator.ReloadSimulatorAsync(proxyFile);
// setup application upsell message
ListingInformation listing = await CurrentAppSimulator.LoadListingInformationAsync();
var product1 = listing.ProductListings["product1"];
var product2 = listing.ProductListings["product2"];
Product1SellMessage.Text = "You can buy " + product1.Name + " for: " + product1.FormattedPrice + ".";
Product2SellMessage.Text = "You can buy " + product2.Name + " for: " + product2.FormattedPrice + ".";
}
However, the "Is already bought" status is not persistence. If I close the app and start it again, it will thought my "Premium Feature" to "is not bought yet".
How am I suppose the make this behavior persistence?
PLEASE READ
I would assume, based on your experience, that App.IsTrial is set to true. You cannot commit a purchase while the app is in trial mode or (for any other reason) is not active. Please note that I am referring to the App and not the feature/product. Once the app is active your purchase will succeed.
As far as I know it's not automatically updated when you click OK. It only returns the correct status like it should do in the Windows Store.
What I think is an option to do, but I haven't implemented it myself is to use ReloadSimulatorAsync(proxyFile) method. Before you call this method you have to update the in-app-purchase.xml yourself save it and pass the file to the ReloadSimulatorAsync method.
Personally I would use the manual update of the xml file. But I can't Judge about that for your application / application flow.
A good start for automating this process is the sample "In-app purchase" app which you can find here. If you look in the code file InAppPurchase.xaml.cs you will see already a bit of code for your own implementation.
I'm just gonna clear this out. When executing
try {
await CurrentAppSimulator.RequestProductPurchaseAsync("PremiumFeatures", false);
OK = true
}
catch (Exception)
{
OK = false;
}
OK will not only be true if the user bought the feature, it will also be true if he cancels the buy or he cant be identified or he disables his network connection. So this is the wrong way to check if the purchase was successfull. Basically Anobik was right, you have to check afterwards if the license is purchased so the right code would be:
if (!licenseInformation.ProductLicenses["PremiumFeatures"].IsActive)
{
try
{
// The customer doesn't own this feature, so
// show the purchase dialog.
await CurrentAppSimulator.RequestProductPurchaseAsync("PremiumFeatures", false);
if (licenseInformation.ProductLicenses["PremiumFeatures"].IsActive)
{
// the in-app purchase was successful
OK = true;
//Success in purchase use ur own code block
}
else
{
OK = false;
//error in purchase use your own code block
}
}
catch (Exception)
{
// The in-app purchase was not completed because
// an error occurred.
OK = false;
}
}
else
{
// The customer already owns this feature.
OK = true;
}
if (OK)
{
Frame.Navigate(typeof(HistoryPage));
}
But still, if you compile you will see that it doesn't work. I think, this is because the CurrentAppSimulator will not make a permanent change to the license.
I hope this is true :)
It is not necessary to check if the license IsActive immediately after the purchase. Instead, you can (you'd better) store the result of RequestProductPurchaseAsync, which is of type PurchaseResults and has a Status property. The latter in its turn is a ProductPurchaseStatus enum value, and if it is Succeeded, you can proceed without checking the license.
You are only missing one if else condition to handle the in app purchase
Here's the code
private async void history_Click(object sender, RoutedEventArgs e)
{
bool OK = true;
// Get the license info
// The next line is commented out for testing.
// licenseInformation = CurrentApp.LicenseInformation;
// The next line is commented out for production/release.
LicenseInformation licenseInformation = CurrentAppSimulator.LicenseInformation;
if (!licenseInformation.ProductLicenses["PremiumFeatures"].IsActive)
{
try
{
// The customer doesn't own this feature, so
// show the purchase dialog.
await CurrentAppSimulator.RequestProductPurchaseAsync("PremiumFeatures", false);
if (licenseInformation.ProductLicenses["PremiumFeatures"].IsActive)
{
// the in-app purchase was successful
OK = true;
//Success in purchase use ur own code block
}
else
{
OK = false;
//error in purchase use your own code block
}
}
catch (Exception)
{
// The in-app purchase was not completed because
// an error occurred.
OK = false;
}
}
else
{
// The customer already owns this feature.
OK = true;
}
if (OK)
{
Frame.Navigate(typeof(HistoryPage));
}
}

Categories