How to verify in-app purchase transaction itself successful Windows Phone 8 - c#

I have this code for my in-app purchase
string receipt = await Store.CurrentApp.RequestProductPurchaseAsync(pID, false);
The documentation states that this method returns a success value even if:
there's no network connection available
the user cancels out of the dialog
the user's authentication fails
You should treat a success result as indicating the async process completed without
errors. To ensure that the transaction itself was successful, check the
LicenseInformation element in the returned receipt.
Is there a quick way to verify the receipt for the particular item actually successful before I unlocked the purchase feature? Currently if there's no error, the feature will always be unlocked because the receipt isn't verified.
My code:
private async void inAppPurchase(string key)
{
if (!Store.CurrentApp.LicenseInformation.ProductLicenses[key].IsActive)
{
try
{
// The customer doesn't own this feature, so
// show the purchase dialog.
MockIAPLib.ListingInformation li = await Store.CurrentApp.LoadListingInformationAsync();
string pID = li.ProductListings[key].ProductId;
//purchase successful
string receipt = await Store.CurrentApp.RequestProductPurchaseAsync(pID, false);
//Check the receipt for
switch (key)
{
case "IsAdsVisibleSetting":
settings.IsAdsVisibleSetting = false;
break;
case "style_2":
fontFolder[key] = true;
settings.AllowedFontSetting = fontFolder;
break;
case "backgrounds_2":
backgroundGroups[key] = true;
settings.AllowedBackgroundGroupsSetting = backgroundGroups;
break;
case "backgrounds_3":
backgroundGroups[key] = true;
settings.AllowedBackgroundGroupsSetting = backgroundGroups;
break;
}
RenderStoreItems();
}
catch (Exception)
{
MessageBox.Show("Sorry, the in-app purchase was not completed because an error occurred.", "Purchasing Error", MessageBoxButton.OK);
}
}
}

after purchase method call, make sure to check the license is active or not before full fulling the goods. If it is consumable type, call ReportProductFulfillment() method.
CurrentApp.LicenseInformation.ProductLicenses[key].IsActive
BTW, in your code includeReceipt value is set as false - will keeps 'receipt' as empty always.

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.

C# Xamarin Forms InAppBilling PurchaseAsync not returning after handle of playstore

I am using jamesmontenegro InAppBilling and I have a small issue regarding payments.
The code is working fine but after the payment is handled by Google Play Store, the app doesn't handle the procedure and doesn't return if the purchase was successfully leading to not being able to consume the purchased item.
Here is the simple code
public static async Task<bool> PurchaseItem(string productId, string payload)
{
var billing = CrossInAppBilling.Current;
try
{
var connected = await billing.ConnectAsync(ItemType.InAppPurchase);
if (!connected)
{
//we are offline or can't connect, don't try to purchase
return false;
}
//check purchases
var purchase = await billing.PurchaseAsync(productId, ItemType.InAppPurchase, payload);
//possibility that a null came through.
if (purchase == null)
{
//did not purchase
Debug.WriteLine("purchased but not consumed");
return true;
}
else if (purchase.State == PurchaseState.Purchased)
{
//purchased, we can now consume the item or do it later
//If we are on iOS we are done, else try to consume the purchase
//Device.RuntimePlatform comes from Xamarin.Forms, you can also use a conditional flag or the DeviceInfo plugin
if (Device.RuntimePlatform == Device.iOS)
return true;
var consumedItem = await CrossInAppBilling.Current.ConsumePurchaseAsync(purchase.ProductId, purchase.PurchaseToken);
if (consumedItem != null)
{
//Consumed!!
Debug.WriteLine("purchased and consumed");
return true;
}
}
return false;
}
Nothing is actually running after PurchaseAsync is called as the thread changes to Google Play Store.
Once the payment is finished and the thread goes back to the app it is not possible to handle anything.
Any ideas? There is a workaround for this but I am just wondering if I am doing something wrong here?

Twilio Transcribe voice in ASP.NET MVC

Im trying to transcribe vocal response of a caller and programmaticaly read out the users vocal response via twilio.
So, when user initially calls to the twilio number the call gets hooked to below action method (looked in to https://www.twilio.com/docs/voice/twiml/record?code-sample=code-record-a-voicemail&code-language=C%23&code-sdk-version=5.x) of the ASP.NET MVC Application.
[HttpPost]
public TwiMLResult Welcome()
{
var response = new VoiceResponse();
try
{
response.Say("Please say your user Id, example ABC123, \n and press star when done", Say.VoiceEnum.Alice, null, Say.LanguageEnum.EnGb);
// record and transcribe users voice
response.Record(
transcribe: true,
transcribeCallback: new Uri("https://35eb31e3.ngrok.io/Ivr/HandleTranscribedVrn"),
finishOnKey: "*");
response.Say("I did not receive a recording");
}
catch (Exception e)
{
ErrorLog.LogError(e, "Error within ivr/Welcome");
response = RejectCall();
}
return TwiML(response);
}
Note - https://35eb31e3.ngrok.io/Ivr/HandleTranscribedVrn is the ngRok tunneled public URL to call back method.
So, Im trying to record the users voice input after user says his/her user Id and then presses * key. So, after pressing * , I expect twilio to transcribe and respond to below callback action method (https://35eb31e3.ngrok.io/Ivr/HandleTranscribedVrn) with the transcription text and other transcribed information.
[HttpPost]
public TwiMLResult HandleTranscribedVrn()
{
var response = new VoiceResponse();
try
{
// get the transcribed result - https://www.twilio.com/docs/voice/twiml/record#transcribe
var result = new TranscribedResult
{
TranscriptionSid = Request.Params["TranscriptionSid"],
TranscriptionText = Request.Params["TranscriptionText"],
TranscriptionUrl = Request.Params["TranscriptionUrl"],
TranscriptionStatus = Request.Params["TranscriptionStatus"],
RecordingSid = Request.Params["RecordingSid"],
RecordingUrl = Request.Params["RecordingUrl"],
AccountSid = Request.Params["AccountSid"]
};
// reading the transcibed result
response.Say("You said,\n {0}", result.TranscriptionText);
// done
response.Say("Good Bye", Say.VoiceEnum.Alice, null, Say.LanguageEnum.EnGb);
}
catch (Exception e)
{
ErrorLog.LogError(e, "Error within ivr/HandleTranscribedVrn");
response.Say(ConversationHelper.NothingReceived, ConversationHelper.SpeakVoice, 1, ConversationHelper.SpeakLanguage);
}
return TwiML(response);
}
In brief, I want above callback action to grab the transcript to user voice input and read it out, like
You said, {Users Voice Transcript - example - abc123}, Good Bye
The Problem
When user calls to twilio number it executes Welcome() action controller, and says
"Please say your user Id, example ABC123, \n and press star when done"
The user says his/her user Id - EFG456 and presses * key as usual.
Then it again says (infinitely till user disconnects call), without going to transcribed call back action - HandleTranscribedVrn - "Please say your user Id, example ABC123, \n and press star when done"
Any help will be much appreciated.
With the help of the Twilio support we managed to find this solution. So, instead of <record> we have to use <gather> feature provided by Twilio. On gather we could either use speech, DTMF tones (keyboard inputs) or both.
The gather complete callback method will be executed when the speech transcription is ready. More information can be found on https://www.twilio.com/docs/voice/twiml/gather
Below is the sample code. Hope it would be helpful to anyone who faces a similar issue.
[HttpPost]
public ActionResult Welcome()
{
var response = new VoiceResponse();
try
{
var gatherOptionsList = new List<Gather.InputEnum>
{
Gather.InputEnum.Speech,
//Gather.InputEnum.Dtmf
};
var gather = new Gather(
input: gatherOptionsList,
timeout: 60,
finishOnKey:"*",
action: Url.ActionUri("OnGatherComplete", "Ivr")
);
gather.Say("Please say \n", Say.VoiceEnum.Alice, 1, Say.LanguageEnum.EnGb);
response.Append(gather);
}
catch (Exception e)
{
ErrorLog.LogError(e, "Error within ivr/Welcome");
}
return TwiML(response);
}
[HttpPost]
public TwiMLResult OnGatherComplete(string SpeechResult, double Confidence)
{
var response = new VoiceResponse();
try
{
var identifyingConfidence = Math.Round(Confidence * 100, 2);
var transcript = $"You said {SpeechResult} with Confidence {identifyingConfidence}.\n Good Bye";
var say = new Say(transcript);
response.Append(say);
}
catch (Exception e)
{
ErrorLog.LogError(e, "Error within ivr/OnGatherComplete");
}
return TwiML(response);
}

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));
}
}

Getting around System.UnauthorizedAccessException : Access is denied

I am using Waitin RC2, WatiN-2.0.20.1089, Windows XP OS with IE8 With VS2008 and NUnit-2.5.7.10213. I have added the sites to the trusted list, I have thread sleeps, I have tried "WaitForComplete". yet when the script goes "back" I am still getting an unauthorized access exception.
Here is a chunk of my code, the exceptions are never caught inspite of the fact that most of the code is in try catch blocks.
public string FindAllLinks()
{
/*
* This function is designed to find and use all of the links on a given page.
* After clicking on a link it waits for 400 milliseconds on the page so the page
* has some time to load and then the function "hits the back button" reseting
* to the originating page.
* This function is not meant to be recursive.
*/
string message = "";
bool flag = true;
//Get a list of all links from the browser instance
foreach (Link link in browserInstance.Links)
{
System.Threading.Thread.Sleep(1000);
Console.WriteLine(link);
try
{//clicking on the link to make sure it leads somewhere
link.Click(); //If the click fails hopefull we will thrwo out of the try block and not execute the next two commands.
//Console.WriteLine(link);
}
catch (Exception)
{//OOPs we have an error let's log a message.
message = message + "The link titled " + link + " was not found, or did not work.\n";
flag = false;
}
if (flag)
{
System.Threading.Thread.Sleep(1000);
//browserInstance.WaitForComplete;
try { browserInstance.Back(); }
catch (UnauthorizedAccessException)
{
//do nothing
}
}//close if flag
}//close for each
//return the message
return (message);
}//Close function
[STAThread]
[Test]
public void TestTitleHomePage()
{
bool testPassed = false;
if (browserInstance.Title.Contains("<title>"))
{
string message = FindAllLinks();
if (message == "") { testPassed = true; }
}//close if
else { message = "The Title was not the same."; }
Assert.IsTrue(testPassed, message);
}// end TestTitleHomePage
I tried your code and I also get the exception. I think I understand what happens. When you first do Browser.Links, you get all the links of the current page, then you navigate to another page and return to the first page, but for WatiN it is a new page. So your enumeration cannot work because you enumerate though the links of the first page.
What I suggest you could do is to get all the Uri of the links, then try them one by one in a new browser
IEnumerable<Uri> uris = Browser.Links.Select(l => l.Uri);
foreach(Uri uri in Uris)
{
try
{
using(var browser = new IE(uri))
{
// do nothing or detect 404, 403, etc errors
}
// no error
}
catch(exception)
{
// log error
}
}

Categories