Handling Windows App Store In-app purchase receipt - c#

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.

Related

Android Activity destroyed when calling ZXing barcode scanner. How to recover?

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.

How do I login to OneDrive (after the initial time) without seeing the permissions screen

I have just started working with the OneDrive API and the sample program that comes with it (OneDriveApiBrowser).
As expected, the first time I logged in (using "Sign In to MSA...", I was asked for credentials, my 2-factor code, and finally a permissions screen asking if I approve of the access that the app wants against my OneDrive account.
But, after I exit the program and restart it, I am not logged in. I repeat going to "Sign In to MSA..." and I am no longer prompted for credentials (as I expected), but I am prompted with the permissions screen again.
Is there a way of having the app log back in without always prompting the user for permission?
For learning how to use the OneDrive API, I am just using the sample code that Microsoft supplied as part of the API located at https://github.com/OneDrive/onedrive-sdk-csharp/tree/master/samples/OneDriveApiBrowser. The code can't be downloaded directly from there, but at the root of this project, https://github.com/OneDrive/onedrive-sdk-csharp. This will download the source for the API as well as the sample code and unit tests.
After doing some more poking around, I finally found out how to do this. My explanation here will be in the context of the sample program mentioned in the original question above.
In the program, in the SignIn method, there was some setup being done which included calling OneDriveClient.GetMicrosoftAccountClient(...), then calling the following:
if (!this.oneDriveClient.IsAuthenticated)
{
await this.oneDriveClient.AuthenticateAsync();
}
So, two things needed to be done. We need the save the result from the code above, then save the RefreshToken value somewhere safe... (It's just a very long string).
if (!this.oneDriveClient.IsAuthenticated)
{
AccountSession accountSession = await this.oneDriveClient.AuthenticateAsync();
// Save accountSession.RefreshToken somewhere safe...
}
Finally, I needed to put an if around the OneDriveClient.GetMicrosoftAccountClient(...) call and only call it if the saved refresh token has not been saved yet (or as been deleted due to code added to the logout call...) If we have a saved refresh token, we call `OneDriveClient.GetSilentlyAuthenticatedMicrosoftAccountClient(...) instead. The entire SignIn method looks like this when I am done.
private async Task SignIn(ClientType clientType)
{
string refreshToken = null;
AccountSession accountSession;
// NOT the best place to save this, but will do for an example...
refreshToken = Properties.Settings.Default.RefreshToken;
if (this.oneDriveClient == null)
{
if (string.IsNullOrEmpty(refreshToken))
{
this.oneDriveClient = clientType == ClientType.Consumer
? OneDriveClient.GetMicrosoftAccountClient(
FormBrowser.MsaClientId,
FormBrowser.MsaReturnUrl,
FormBrowser.Scopes,
webAuthenticationUi: new FormsWebAuthenticationUi())
: BusinessClientExtensions.GetActiveDirectoryClient(FormBrowser.AadClientId, FormBrowser.AadReturnUrl);
}
else
{
this.oneDriveClient = await OneDriveClient.GetSilentlyAuthenticatedMicrosoftAccountClient(FormBrowser.MsaClientId,
FormBrowser.MsaReturnUrl,
FormBrowser.Scopes,
refreshToken);
}
}
try
{
if (!this.oneDriveClient.IsAuthenticated)
{
accountSession = await this.oneDriveClient.AuthenticateAsync();
// NOT the best place to save this, but will do for an example...
Properties.Settings.Default.RefreshToken = accountSession.RefreshToken;
Properties.Settings.Default.Save();
}
await LoadFolderFromPath();
UpdateConnectedStateUx(true);
}
catch (OneDriveException exception)
{
// Swallow authentication cancelled exceptions
if (!exception.IsMatch(OneDriveErrorCode.AuthenticationCancelled.ToString()))
{
if (exception.IsMatch(OneDriveErrorCode.AuthenticationFailure.ToString()))
{
MessageBox.Show(
"Authentication failed",
"Authentication failed",
MessageBoxButtons.OK);
var httpProvider = this.oneDriveClient.HttpProvider as HttpProvider;
httpProvider.Dispose();
this.oneDriveClient = null;
}
else
{
PresentOneDriveException(exception);
}
}
}
}
For completeness, I updated the logout code
private async void signOutToolStripMenuItem_Click(object sender, EventArgs e)
{
if (this.oneDriveClient != null)
{
await this.oneDriveClient.SignOutAsync();
((OneDriveClient)this.oneDriveClient).Dispose();
this.oneDriveClient = null;
// NOT the best place to save this, but will do for an example...
Properties.Settings.Default.RefreshToken = null;
Properties.Settings.Default.Save();
}
UpdateConnectedStateUx(false);
}
The wl.offline_access scope is required by applications to save the user consent information and refresh the access token without a UI prompt.
For more details on the scopes that you can use in your application see https://dev.onedrive.com/auth/msa_oauth.htm#authentication-scopes

WP8.1 Silverlight - Unexpected License info on LicenseChanged

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.

LockScreen Images not changing in Beta app

I created an app in window phone 8 which could change the lockscreen images from background task. The app worked well in emulator and device when run through code. It contains the launchforTest() which makes lockscreen change every 30 sec.
I got some comments that told that launchforTest() don't work when app gets uploaded over store. its basically for the testing purpose.
So my problem is that When i install my app from the store which I have put for beta testing the background tasks don't work i,e lockscreen images don't change
What am I missing in the code which has made it not working pls help.
Here is my Code
private ResourceIntensiveTask resourceIntensiveTask;
private string resourceIntensiveTaskName = "SmartLockScreenScheduledTaskAgent";
private string resourceIntensiveTaskDescription = "Displays the latest deals.";
private StartTask()
{
LockScreenChange("ms-appdata:///Local/Image0.jpg", true);
}
private async void LockScreenChange(string filePathOfTheImage, bool isAppResource)
{
if (!LockScreenManager.IsProvidedByCurrentApplication)
{
// If you're not the provider, this call will prompt the user for permission.
// Calling RequestAccessAsync from a background agent is not allowed.
await LockScreenManager.RequestAccessAsync();
}
// Only do further work if the access is granted.
if (LockScreenManager.IsProvidedByCurrentApplication)
{
// At this stage, the app is the active lock screen background provider.
// The following code example shows the new URI schema.
// ms-appdata points to the root of the local app data folder.
// ms-appx points to the Local app install folder, to reference resources bundled in the XAP package
// var schema = isAppResource ? "ms-appx:///" : "ms-appdata:///Local/";
var uri = new Uri(filePathOfTheImage, UriKind.Absolute);
// Set the lock screen background image.
LockScreen.SetImageUri(uri);
// Get the URI of the lock screen background image.
var currentImage = LockScreen.GetImageUri();
System.Diagnostics.Debug.WriteLine("The new lock screen background image is set to {0}", currentImage.ToString());
UpdatePrimaryTile();
StartResourceIntensiveAgent();// StartPeriodicAgent();//
// MessageBox.Show("Lock screen changed. Click F12 or go to lock screen.");
}
else
{
MessageBox.Show("Background can't be updated as you clicked no!!");
}
}
public void RemoveResourceIntensiveAgent()
{
try
{
if (resourceIntensiveTask != null)
{
ScheduledActionService.Remove(resourceIntensiveTaskName);
}
}
catch (Exception)
{
}
}
/// <summary>
/// Code to start the resource intensive task
/// </summary>
private void StartResourceIntensiveAgent()
{
// Variable for tracking enabled status of background agents for this app.
agentsAreEnabled = true;
resourceIntensiveTask = ScheduledActionService.Find(resourceIntensiveTaskName) as ResourceIntensiveTask;
// If the task already exists and background agents are enabled for the
// application, you must remove the task and then add it again to update
// the schedule.
if (resourceIntensiveTask != null)
{
ScheduledActionService.Remove(resourceIntensiveTaskName);
}
resourceIntensiveTask = new ResourceIntensiveTask(resourceIntensiveTaskName);
// The description is required for periodic agents. This is the string that the user
// will see in the background services Settings page on the device.
resourceIntensiveTask.Description = (resourceIntensiveTaskDescription);
// Place the call to Add in a try block in case the user has disabled agents.
try
{
ScheduledActionService.Add(resourceIntensiveTask);
//ResourceIntensiveStackPanel.DataContext = resourceIntensiveTask;
resourceIntensiveTask.ExpirationTime = DateTime.Now.AddDays(14);
// If debugging is enabled, use LaunchForTest to launch the agent in one minute.
ScheduledActionService.LaunchForTest(resourceIntensiveTaskName, TimeSpan.FromSeconds(30));
}
catch (InvalidOperationException exception)
{
if (exception.Message.Contains("BNS Error: The action is disabled"))
{
MessageBox.Show("Background agents for this application have been disabled by the user.");
agentsAreEnabled = false;
}
}
catch (SchedulerServiceException)
{
// No user action required.
}
}
Your problem is that you have implemented your background agent as a ResourceIntensiveTask. ResourceIntensiveTasks run under the following very specific constraints:
Phone is plugged in
Lock screen is engaged
Wireless network (non-cellular) connectivity is available
Consider implementing this background agent as a PeriodicTask. If you are running up against runtime constraints, consider refactoring as much code as possible to preprocess what portions of the the lock screen image as you are able while the main application is running, or by creating static image assets in the package that can be used to convey the same information.
Ref: Background agents for Windows Phone: Constraints for Resource-intensive Agents

Windows Phone Live SDK API - get new session object after restarting the App

so I have succeeded in connecting my Windows Phone 8 Application to the Live API, I also succeeded in reading data from my hotmail account.
I have access to the needed client ID and the live access token.
But when I quit and restart my application, I lose all references to the session and the client objects and I have to start the process anew.
I don't want to annoy the user with the web mask in which he has to agree again that he provides me with the needed permissions every time he is starting the application. But I also haven't found a way to get a reference to a session object without this step.
The login mask is only shown the first time after installing the application, after that, only the screen mentioned above is shown.
But it is still quite annoying for the user to accept this every time.
I already tried serializing the session object, which is not possible, because the class does not have a standard constructor.
Maybe it is possible to create a new session by using the live access token, but I haven't found a way to do so.
Any ideas? What am I doing wrong, I know that there is a way to login again without prompting the user.
I'm thankful for every idea.
Some code I use:
/// <summary>
/// This method is responsible for authenticating an user asyncronesly to Windows Live.
/// </summary>
public void InitAuth()
{
this.authClient.LoginCompleted +=
new EventHandler<LoginCompletedEventArgs>(this.AuthClientLoginCompleted);
this.authClient.LoginAsync(someScopes);
}
/// <summary>
/// This method is called when the Login process has been completed (successfully or with error).
/// </summary>
private void AuthClientLoginCompleted(object sender, LoginCompletedEventArgs e)
{
if (e.Status == LiveConnectSessionStatus.Connected)
{
LiveConnector.ConnectSession = e.Session; // where do I save this session for reuse?
this.connectClient = new LiveConnectClient(LiveConnector.ConnectSession);
// can I use this access token to create a new session later?
LiveConnector.LiveAccessToken = LiveConnector.ConnectSession.AccessToken;
Debug.WriteLine("Logged in.");
}
else if (e.Error != null)
{
Debug.WriteLine("Error signing in: " + e.Error.ToString());
}
}
I have tried to use the LiveAuthClient.InitializeAsync - method to login in background after restarting the application, but the session object stays empty:
// this is called after application is restarted
private void ReLogin()
{
LiveAuthClient client = new LiveAuthClient(LiveConnector.ClientID);
client.InitializeCompleted += OnInitializeCompleted;
client.InitializeAsync(someScopes);
}
private void OnInitializeCompleted(object sender, LoginCompletedEventArgs e)
{
Debug.WriteLine("***************** Inititalisation completed **********");
Debug.WriteLine(e.Status); // is undefined
Debug.WriteLine(e.Session); // is empty
}
Does anyone have an idea how I could get access to a new session after restarting the application?
After two full days searching for the mistake I was making, I finally found out what I was doing wrong: I have to use the wl.offline_access scope to make this work!
Let me quote another user here:
"If your app uses wl.offline_access scope than the live:SignInButton control saves it for you and loads it automatically. Just use the SessionChanged event to capture the session object. This way the user will need to sign in only once."
(see WP7 how to store LiveConnectSession during TombStoning?)
Now everything is fun again. Can't believe that this was the problem. Tested & working. Nice!
Been struggling to get this working on a Windows Live + Azure Mobile Service app myself so thought I would post a complete working code sample here now that I've got it working.
The key parts are the wl.offline_access scope and the call to InitializeAsync.
Note: this sample also connects with Windows Azure Mobile Services. Just remove the stuff related to MobileService if you're not using that.
private static LiveConnectSession _session;
private static readonly string[] scopes = new[] {"wl.signin", "wl.offline_access", "wl.basic"};
private async System.Threading.Tasks.Task Authenticate()
{
try
{
var liveIdClient = new LiveAuthClient("YOUR_CLIENT_ID");
var initResult = await liveIdClient.InitializeAsync(scopes);
_session = initResult.Session;
if (null != _session)
{
await MobileService.LoginWithMicrosoftAccountAsync(_session.AuthenticationToken);
}
if (null == _session)
{
LiveLoginResult result = await liveIdClient.LoginAsync(scopes);
if (result.Status == LiveConnectSessionStatus.Connected)
{
_session = result.Session;
await MobileService.LoginWithMicrosoftAccountAsync(result.Session.AuthenticationToken);
}
else
{
_session = null;
MessageBox.Show("Unable to authenticate with Windows Live.", "Login failed :(", MessageBoxButton.OK);
}
}
}
finally
{
}
}

Categories

Resources