I am trying to detect In-App-Purchases made by a client app.
I am using the following code
public async Task InitializeInAppPurchase()
{
CurrentApp.LicenseInformation.LicenseChanged += LicenseInformation_LicenseChanged;
var listingInformationTask = CurrentApp.LoadListingInformationAsync();
var listingInformation = await listingInformationTask;
PurchaseProduct(listingInformation.ProductListings.First().Value.ProductId);
}
private void LicenseInformation_LicenseChanged()
{
var receipt = CurrentApp.GetAppReceiptAsync().AsTask().Result;
Console.Writeline(receipt);
}
async void PurchaseProduct(string productId)
{
try
{
// Kick off purchase; don't ask for a receipt when it returns
var result = await CurrentApp.RequestProductPurchaseAsync(productId);
// Now that purchase is done, give the user the goods they paid for
// (DoFulfillment is defined later)
await DoFulfillment(result);
}
catch (Exception ex)
{
// When the user does not complete the purchase (e.g. cancels or navigates back from the Purchase Page), an exception with an HRESULT of E_FAIL is expected.
}
}
//
// Fulfillment of consumable in-app products
public async Task DoFulfillment(PurchaseResults result)
{
var productLicenses = CurrentApp.LicenseInformation.ProductLicenses;
// Check fulfillment for consumable products with hard-coded asset counts
await MaybeGiveMeGold(productLicenses["Test1"], 50, result);
}
// Count is passed in as a parameter
async Task MaybeGiveMeGold(ProductLicense license, int goldCount, PurchaseResults result)
{
if (license.IsConsumable && license.IsActive)
{
await CurrentApp.ReportConsumableFulfillmentAsync(license.ProductId, result.TransactionId);
}
}
When the event LicenseChanged is raised, I am surprised to see that the receipt does not include the transaction which just occurred. I get this
<Receipt Version="1.0" ReceiptDate="2015-06-18T04:41:31.867Z" ReceiptDeviceId="4e362949-acc3-fe3a-e71b-89893eb4f528" CertificateId="FB3D3A6455095D2C4A841AA8B8E20661B10A6112" xmlns="http://schemas.microsoft.com/windows/2012/store/receipt">
<AppReceipt Id="8ffa256d-eca8-712a-7cf8-cbf5522df24b" AppId="01e34c43-fdd8-47a6-a8ba-36ad5b880de9" PurchaseDate="2015-06-18T04:41:31.867Z" LicenseType="Full" />
</Receipt>
whereas the receipt should include a element as documented here
I am using the emulator and I am also using a mock server hosted locally in IIS to return my fake ProductListings from here
Can anyone tell me if there is something that I am doing wrong or if this simply what has been designed by Microsoft?
I know that In App Purchases behave differently on emulators, does anyone know if I would have had the expected behavior if I were using a real device?
Thank you
That behavior was normal because the Marketplace mock hosted locally could not return receipts for any transactions.
To test my In-App-Purchases, I had to
Publish my app in beta mode
Create test IAPs at 0.00$
The code above worked perfectly and the LicenseChanged events is raised almost instantaneously depending on network conditions.
Related
I tried to use plugin Xamarin.Forms.Contacts and followed the github link,
https://gist.github.com/enisn/25fd0a63a849854fb6103aa681be9963
But, when I compile and debug nothing is shown on screen. I added the plugin to Android and iOS also and setup the required permissions.
In the first line of GetContacts() Debugger dies and does not move to another line.
public ContactList()
{
InitializeComponent();
GetContacs();
}
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
async Task GetContacs()
{
var contacts = await Plugin.ContactService.CrossContactService.Current.GetContactListAsync();
lstContacts.BindingContext = contacts;
},
I also followed the tutorial on link, https://www.xamboy.com/2019/10/10/getting-phone-contacts-in-xamarin-forms/
In this tutorial, i found issue with the permission function, debugger dies here also, in the if condition to verify permissions.
`public async Task<bool> RequestPermissionAsync()
{
contactPermissionTcs = new TaskCompletionSource<bool>();
// Verify that all required contact permissions have been granted.
if (Android.Support.V4.Content.ContextCompat.CheckSelfPermission(CrossCurrentActivity.Current.Activity, Manifest.Permission.ReadContacts) != (int)Permission.Granted
|| Android.Support.V4.Content.ContextCompat.CheckSelfPermission(CrossCurrentActivity.Current.Activity, Manifest.Permission.WriteContacts) != (int)Permission.Granted)
{
// Contacts permissions have not been granted.
RequestContactsPermissions();
}
else
{
// Contact permissions have been granted.
contactPermissionTcs.TrySetResult(true);
}
return await contactPermissionTcs.Task;
}`
This also did not work.
Is there anyway or suggestion that would make my task easier?
Starting from Xamarin.Essentials 1.6.0-pre5 and latter, the new API GetAllAsync() that has been introduced, allows you to grab all contacts.
Example from Microsoft Documentation
ObservableCollection<Contact> contactsCollect = new ObservableCollection<Contact>();
try
{
// cancellationToken parameter is optional
var cancellationToken = default(CancellationToken);
var contacts = await Contacts.GetAllAsync(cancellationToken);
if (contacts == null)
return;
foreach (var contact in contacts)
contactsCollect.Add(contact);
}
catch (Exception ex)
{
// Handle exception here.
}
It is supported on Android, iOS and UWP, as already mentioned in the question adding the required permissions on each platform is important in order for this API to work, fore details refers to Xamarin.Essentials: Contacts.
I am making a Http.JsonAsync<MyType>("api/MyController/Users"); call which returns very quickly but then times out in 5 secs rather than giving me the data.
I have made other such calls that have worked fine, but something weird is going on.
I started from the standard weather service Blazor client template and all worked fine
I kick it off from a button event
<button onclick=#load_click>LOAD!</button>
#functions {
private void load_click()
{
try
{
Logger.Log($"about to call PopulateExistingUsers");
var taskUsers = Http.GetJsonAsync<UsersPageData>("api/ShowerQ/Users");
Logger.Log($"returned from async requesting users");
if (!taskUsers.Wait(5000))
{
throw new Exception($"timeout!");
}
var users = taskUsers.Result;
Logger.Log($"populated existing users ok. Users.Count = {users.Users.Count()}");
}
catch (Exception e)
{
Logger.Log(e.ToString());
}
}
Server side Controller code:
[HttpGet("Users")]
public UsersPageData GetUsers()
{
try
{
var users = _repos.GetAllUsers().ToList();
Log($"returned from GetALlUsers() with {users.Count} users");
return new UsersPageData() {Users = users};
}
finally
{
Log($"aft6er returning from method {nameof(GetUsers)}");
}
}
Logger.Log just does Console.Writeline, which goes to the browser console - very handy!
Expected:
log output to console:
"populated existing users ok. Users.Count = 3"
ACtual logs:
The output I'm getting is:
WASM: 09:31:26.49:about to call PopulateExistingUsers
WASM: 09:31:26.56:returned from async requesting users
WASM: 09:31:31.57:System.Exception: timeout!
WASM: at ShowerQWeb2.Client.Pages.Index.load_click () [0x00048] in C:\Users\XXXX\Source\repos\ShowerQWeb2\ShowerQWeb2.Client\Pages\Index.razor:62
and on server side (which is running on same machine so clocks are in sync apart from a hour)
10:31:26.68:returned from GetALlUsers() with 3 users
10:31:26.68:aft6er returning from method GetUsers
The chrome network debug shows whats being returned:
response header:
Date: Mon, 13 May 2019 09:31:26 GMT
{"users":[{"id":1,"name":"David XX"},{"id":2,"name":"Sumith YY"},{"id":3,"name":"David ZZ"}]}
So, it looks like its getting stuck deserializing maybe?
You're probably getting stuck in a deadlock because you are abusing async. You shouldn't be calling .Wait() and .Result, use async code properly.
First make the method async and return a Task:
private async Task load_click()
{
// snip
}
Then await your HTTP calls properly:
var users = await Http.GetJsonAsync<UsersPageData>("api/ShowerQ/Users");
I'm trying to build a Dialog using the Microsoft Bot Framework which helps users consult purchase order status (currently, just a mock). I am using a LuisDialog which, when it detects the "ConsultPO" intent, it's supposed to ask for the user's 'customer id' and wait a follow up message from the user. However, it keeps going back to the start of the Luis Dialog and processing the intent instead of resuming from the waited method. This is the intent's code, which runs correctly:
[LuisIntent("ConsultPO")]
public async Task POIntent(IDialogContext context, LuisResult result)
{
string PO = "";
foreach (var entity in result.Entities)
{
if (entity.Type == "purchaseOrder")
PO = entity.Entity;
}
if (PO.Length != 0)
{
po_query = PO;
}
await context.PostAsync("Ok, can you confirm your customer id and I'll check for you?");
context.Wait(confirmCustomer_getPO);
}
This is the code I would expect to be executed after the user responds with a follow up message:
public async Task confirmCustomer_getPO(IDialogContext context, IAwaitable<object> argument)
{
await context.PostAsync("DEBUG TEST");
IMessageActivity activity = (IMessageActivity)await argument;
customer_query = activity.Text;
if (po_query.Length > 0)
{
PurchaseOrder po = POservice.findPO(po_query, customer_query);
await buildSendResponse(po, context);
//more non relevant code
When I answer to the bot's inquiry after context.Wait(confirmCustomer_getPO) is executed, it just goes into LUIS then runs the code respective to "None" intent. The message "DEBUG TEST" is never sent.
Why is "confirmCustomer_getPO" never getting called?
EDIT:
I added a debug message in the StartAsync method. I'm not sure whether this is supposed to happen but it pops up every time I send a message to the bot, which makes me believe the Dialog is simply restarting every time I message the bot:
public class EchoDialog : LuisDialog<object>
{
public EchoDialog() : base(new LuisService(new LuisModelAttribute(
ConfigurationManager.AppSettings["LuisAppId"],
ConfigurationManager.AppSettings["LuisAPIKey"],
domain: ConfigurationManager.AppSettings["LuisAPIHostName"])))
{
}
public override Task StartAsync(IDialogContext context)
{
context.PostAsync("I'm in startAsync");
return base.StartAsync(context);
}
Local debugging shows no exceptions are occurring and that any breakpoint in the waited method is never reached, although the context.Wait call does happen.
I figured out the issue myself after fighting with it for a while. The issue was with the bot store. I was using an InMemoryDataStore which was not working - switching to TableBotDataStore fixed the problem. The issue with the DataStore meant that states weren't being saved so my "waits" and "forwards" were not being saved into the dialog stack - any new incoming message was sent to the RootDialog.
Broken - not working while this was in global.asax.cs:
Conversation.UpdateContainer(
builder =>
{
builder.RegisterModule(new AzureModule(Assembly.GetExecutingAssembly()));
var store = new InMemoryDataStore(); // volatile in-memory store
builder.Register(c => store)
.Keyed<IBotDataStore<BotData>>(AzureModule.Key_DataStore)
.AsSelf()
.SingleInstance();
});
GlobalConfiguration.Configure(WebApiConfig.Register);
As soon as I updated store to:
var store = new TableBotDataStore(ConfigurationManager.AppSettings["AzureWebJobsStorage"]);
Having a valid "AzureWebJobsStorage" setting in web.config from my application settings in Azure, the problem was fixed without any other changes in the code.
First of all, I'm using, Xamarin with MvvmCross.
In my ViewModel, I'm using the ZXing MobileBarcodeScanner class to scan a barcode when the user clicks a button:
var scanner = new MobileBarcodeScanner();
var result = await scanner.Scan();
if (result != null)
{
CodigoProduto = result.Text;
InternalPesquisarProduto();
}
After the scan, I run the InternalPesquisarProduto void, that search for data on a remote server, based of course, on the barcode that was read. This method, also display some loading message while the data is fetched:
Ui.DisplayLoading("Searching...", "Searching data");
// Code that fetches the data
Ui.DismissLoading();
The Ui is a property on my ViewModel defined like this:
protected IUiInteractor Ui { get; set; }
I receive it by dependency injection. Here is the relevant code from the implementation being used in this scenario:
public class AndroidUiInteractor : IUiInteractor
{
private IMvxAndroidCurrentTopActivity _mvxCurrentTopActivity;
public AndroidUiInteractor(IMvxAndroidCurrentTopActivity mvxCurrentTopActivity)
{
_mvxCurrentTopActivity = mvxCurrentTopActivity;
}
public void DisplayLoading(string title, string message)
{
_mvxCurrentTopActivity.Activity.RunOnUiThread(() =>
{
_progressDlg = new ProgressDialog(_mvxCurrentTopActivity.Activity);
// Configuring the title and the message
_progressDlg.Show();
});
}
}
The problem is that when the scanner.Scan is called, my caller activity is destroyed, so when I call the Ui.DisplayLoading, the _mvxCurrentTopActivity.Activity is null.
What is most weird about this case, is that I have two Samsungs with Android 5.0 API 21 that I use in my tests, and this problem only happens in one of them, on the other, the activity is not destroyed when calling the scanner.Scan.
Note: I'm sorry for anything wrong in the code, but because of company policies, I can only access the internet by Terminal Service, and the Ctrl + V is disabled on it.
It turns out the problem was in the device. After reseting it's configurations it worked properly.
This might not be a definitive solution for everyone that faces that problem, but in my scenario it could be done.
I would like to handle situation in such case, user performs purchasing in this machine, but which to run the feature in another machine.
I know I have to check for receipt. I was wondering, is this the correct way to do so? Note that, I bypass signature check. As, I need to setup a backend system which I do not want to consider at this moment. I can't even perform signature check locally, as Windows 8 Store App doesn't provide System.Security.Cryptography API.
I was wondering, whether the following code snippet is good enough to handle most of the cases? Note, I unable to test the code, as I need to publish a new version, in order for me to test the code.
private async void history_Click(object sender, RoutedEventArgs e)
{
bool OK = true;
// The next line is commented out for production/release.
LicenseInformation licenseInformation = CurrentApp.LicenseInformation;
if (!licenseInformation.ProductLicenses["PremiumFeatures"].IsActive)
{
try
{
// The customer doesn't own this feature, so
// show the purchase dialog.
String receipt = await CurrentApp.RequestProductPurchaseAsync("PremiumFeatures", true);
// the in-app purchase was successful
licenseInformation = CurrentApp.LicenseInformation;
if (licenseInformation.ProductLicenses["PremiumFeatures"].IsActive || receipt.Length > 0)
{
// In some situations, you may need to verify that a user made an in-app purchase.
// For example, imagine a game that offers downloaded content. If the user who
// purchased the content wants to play the game on another PC, you need to verify
// that the user purchased the content.
//
// Receipt is used to handle such situation.
//
// Validate receipt is complicated, as it requires us to setup a backend system.
// http://msdn.microsoft.com/en-US/library/windows/apps/jj649137
//
// I will just assume non empty receipt string means valid receipt.
OK = true;
}
else
{
OK = false;
}
}
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)
{
// Launch premium feature.
Frame.Navigate(typeof(HistoryPage));
}
}
You can test your code using CurrentAppSimulator instead of CurrentApp. It looks ok to me.