Xamarin InAppBilling showing ItemUnavailable for purchase on Android - xamarin

I'm implementing the InAppBilling plugin in my Xamarin Forms 5 app for auto-renewing subscriptions.
I have the "subscriptions" set up on Google Play and they're active. When I ask for a list of subscription items, I get the list fine but when I try make a purchase, I get the following error that indicates the item is not available.
I'm running this on a real device connected to my laptop via USB. Any idea what I'm doing wrong?
Here's my purchase subscription method which is directly from documentation here:
public async Task<bool> Subscribe(string productId)
{
var billing = CrossInAppBilling.Current;
try
{
var connected = await billing.ConnectAsync();
if (!connected)
return false;
//check purchases
var purchase = await billing.PurchaseAsync(productId, ItemType.Subscription);
//possibility that a null came through.
if (purchase == null)
{
//did not purchase
return false;
}
else
{
//purchased!
if (Device.RuntimePlatform == Device.Android)
{
// Must call AcknowledgePurchaseAsync else the purchase will be refunded
//await billing.AcknowledgePurchaseAsync(purchase.PurchaseToken);
}
return true;
}
}
catch (InAppBillingPurchaseException purchaseEx)
{
//Billing Exception handle this based on the type
throw new Exception("Error: " + purchaseEx);
}
catch (Exception ex)
{
//Something else has gone wrong, log it
throw new Exception();
}
finally
{
await billing.DisconnectAsync();
}
}
As I mentioned before, I see the subscription items available and active on Google Play. I also made sure, I'm getting them from "Subscriptions" and NOT "In-App Products". I'm using the ID that I copy and paste from the "Product ID" column on Google Play Console -- see below:
Any idea what the issue here may be?

Related

Fetching data from the internet after a page opens in Xamarin forms

I am having some performance issues with my code. I am working with basic MVVM in Xamarin forms project, and I want to fetch data from the internet when someone navigates to another page. Below is what I have done;
This is how I am navigating to another page via Command; (To be honest I don't really know if this method of navigation has some performance penalties)
if (Application.Current.MainPage.Navigation.NavigationStack.Last().GetType() != typeof(SubcategoryPage))
{
await Application.Current.MainPage.Navigation.PushAsync(new SubcategoryPage());
}
Here, I am passing Id of a particular category to get its corresponding subcategories
public SubcategoryPage(int id)
{
InitializeComponent();
this.BindingContext = new SubcategoryPageViewModel(id);
}
In the constructor of the SubcategoryPageViewModel, I used the Id to fetch the data online like so;
public SubcategoryPageViewModel(int id)
{
SubcategoryLoader(id);
}
Below is the method that is fetching the data from the internet through my DataService class. The code below work well in getting the data from the internet;
private async Task SubcategoryLoader(int id)
{
try
{
var subCategories = await SubcategoryDataService.GetSubcategories(id);
if (subCategories.code == 1) // StatusCode = Successful
{
SubCategories = subCategories.document;
}
else
{
await Application.Current.MainPage.DisplayAlert("Oops!","Something went wrong", "Ok");
}
}
catch (Exception ex)
{
await Application.Current.MainPage.DisplayAlert("Oops!", ex.Message, "Ok");
}
}
Now my problem is that the SubcategoryPage doesn't open until the online service is over which leads to a serious lags. So what I want to have happen is to open the SubcategoryPage before the internet services happen.
Please any assistance is appreciated.
Move it to OnAppearing method .
Add a simple check .
protected override void OnAppearing()
{
base.OnAppearing();
if(BindingContext == null)
{
BindingContext = new SubcategoryPageViewModel(id);
}
}

How to integrate google pay in my android app for payment

Hi I am developing a xamarin forms application that targets both android and ios. I want to add google pay as my payment option to order items in android.
please help me is there any documentation available.
welcome to StackOverflow.
Google Pay does not process payments, and as such, it needs to reference your existing processor or gateway to do that. Here is a list of currently supported processors.
As Leo pointed out, you can integrate Google Pay in Xamarin using Xamarin's libs including Google Play Services.
During the integration, make sure to use the right configuration based on the payment processor of your choice. Follow this link to see some examples for different processors.
Hope it helps.
First of all, Google pay must be installed on the device.
Place these three methods in your MainActivity.cs
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
if (requestCode == gpay_requestCode)
{
if (data != null)
{
string response = data.GetStringExtra("response");
string[] resArray = response.Split("&");
string txnId = resArray[0].Split("=")[1].ToString();
string responseCode = resArray[1].Split("=")[1].ToString();
string status = resArray[2].Split("=")[1].ToString();
string txnRef = resArray[3].Split("=")[1].ToString();
if (status == "SUCCESS")
{
Toast.MakeText(this, "Payment Success", ToastLength.Long).Show();
}
else
{
Toast.MakeText(this, "Payment Failed", ToastLength.Long).Show();
}
}
else
{
Toast.MakeText(this, "Payment Failed", ToastLength.Long).Show();
}
}
}
private void PayViaGooglePay()
{
if (IsGooglePayInstalled())
{
//Generate random unique number for transaction reference ID
string transId = $"UPI{Guid.NewGuid().ToString().Substring(0, 8)}";
using (var uri = new Android.Net.Uri.Builder()
.Scheme("upi")
.Authority("pay")
.AppendQueryParameter("pa", "yourgooglepayusername#test")//Google pay email
.AppendQueryParameter("pn", "Test Name") //Google pay name
.AppendQueryParameter("pn", "Sending Amount of 1 JOD") // Google pay note
.AppendQueryParameter("mc", "0000") //
.AppendQueryParameter("tr", transId) //transaction reference ID
.AppendQueryParameter("tn", "Pay to Test Name") //Transaction note
.AppendQueryParameter("am", "1") // Amount
.AppendQueryParameter("cu", "JOD") //Currency
.Build())
{
Intent = new Intent(Intent.ActionView);
Intent.SetData(uri);
Intent.SetPackage("com.google.android.apps.nbu.paisa.user");
StartActivityForResult(Intent, gpay_requestCode);
}
}
}
private bool IsGooglePayInstalled()
{
PackageManager pm = this.PackageManager;
bool installed = false;
try
{
pm.GetPackageInfo("com.google.android.apps.nbu.paisa.user", PackageInfoFlags.Activities);
installed = true;
}
catch (PackageManager.NameNotFoundException e)
{
Toast.MakeText(this, "Google Pay Is Not Installed", ToastLength.Long).Show();
}
return installed;
}
Because you can't call any of these methods from your shared project, you need to create a messaging center and call it wherever you wish.
Place the messaging center in your OnCreate method in the same MainActivity.cs
MessagingCenter.Subscribe<string>(this, "PayViaGooglePay", (value) =>
{
PayViaGooglePay();
});
And then you can call it wherever you want, just put this code wherever you need it
MessagingCenter.Send<string>("", "PayViaGooglePay");
I hope this helps :)

Authorization Failed for Deployed Chatbot

I am having difficulty with a chatbot that I developed which works fine locally but after it was deployed to dev.botframework.com it does not appear to work.
My code is below and it breaks at the line...
await Conversation.SendAsync(activity, () => new MyBot.AppServices.ServiceLUIS()); where it states
{"Authorization for Microsoft App ID a8641a16-932c-49a5-af8b-a58ab2ce251f failed with status code Unauthorized and reason phrase 'Unauthorized'"}.
I have tried the instructions at Troubleshooting Bot Framework Authentication with the following results:
Step 1: Connect without password on localhost - Worked fine!
Step 2: Verify AppID and Password are Correct - They are!
Step 3: Enable security and run on localhost - This does not work :( However the endpoint is correct as are the MicrosoftAppID and Password
Step 4: Connect to your bot using the Bot Framework Developer Portal - This also works! However when using the web chat feature within the portal I also get an internal server error
Where it is breaking is on a call to my LUIS service so to me it seems like my Bot doesn't have authorization to call my LUIS model. However I can't seem to find anything in Azure where my bot is published and LUIS Model resides where I would allow LUIS model to authorize access to my BOT. Also don't see anything in Luis.ai or Bot Framework Portal.
Any ideas on how best to resolve would really be helpful!
namespace MyBot
{
[BotAuthentication]
public class MessagesController : ApiController
{
/// <summary>
/// POST: api/Messages
/// Receive a message from a user and reply to it
/// </summary>
public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
{
try
{
if (activity.Type == ActivityTypes.Message)
{
**await Conversation.SendAsync(activity, () => new MyBot.AppServices.ServiceLUIS());**
}
else
{
ConnectorClient connector = new ConnectorClient(new Uri(activity.ServiceUrl));
var reply = HandleSystemMessage(activity);
if (reply != null)
await connector.Conversations.ReplyToActivityAsync(reply);
HandleSystemMessage(activity);
}
var response = Request.CreateResponse(HttpStatusCode.OK);
return response;
}
catch (Exception e)
{
return null;
}
}
private Activity HandleSystemMessage(Activity message)
{
if (message.Type == ActivityTypes.DeleteUserData)
{
// Implement user deletion here
// If we handle user deletion, return a real message
}
else if (message.Type == ActivityTypes.ConversationUpdate)
{
// Handle conversation state changes, like members being added and removed
// Use Activity.MembersAdded and Activity.MembersRemoved and Activity.Action for info
// Not available in all channels
}
else if (message.Type == ActivityTypes.ContactRelationUpdate)
{
// Handle add/remove from contact lists
// Activity.From + Activity.Action represent what happened
}
else if (message.Type == ActivityTypes.Typing)
{
// Handle knowing tha the user is typing
}
else if (message.Type == ActivityTypes.Ping)
{
}
return null;
}
}
}
Solved! Ended up creating another new key and password for the bot and republishing and it worked! –

App.OnResume error in Xamarin forms on Android and IOS devices

We are using xamarin forms. After an Android or IOS device resumes from background, we are making a REST call in .net that is being triggered by a timer. The first attempt on IOS returns a "The Descriptor is not a socket" error and the Android returns a "Connection refused" error. The same code works fine in Windows. Future attempts (every few seconds) in all 3 platforms work fine. Has anyone seen this and have a fix?
Code
//app on resume event
protected async override void OnResume()
{
// Handle when your app resumes
if (MainPage is RootPage)
{
RootPage mainPage = MainPage as RootPage;
if (mainPage.Detail is NavigationPage)
{
NavigationPage nvPage = mainPage.Detail as NavigationPage;
if(nvPage.CurrentPage is ThingsPage)
{
ThingsPage thPage = nvPage.CurrentPage as ThingsPage;
thPage.TurnOnTimer();
}
}
}
}
//code on the page
public void TurnOnTimer()
{
if (viewModel != null)
{
viewModel.ContinueTimer = true;
viewModel.StartAnotherTimer();
}
}
//code in view model
public async void StartAnotherTimer()
{
while (ContinueTimer)
{
try
{
DevicesUpdate devicesUpdate = await DataSource.GetDevices(LocationID, ControllerID, lastDevicesUpdateReceivedAt);
}
catch (Exception ex)
{
}
// Update the UI (because of async/await magic, this is still in the UI thread!)
if (ContinueTimer)
{
await Task.Delay(TimeSpan.FromSeconds(3));
}
}
}
public static async Task<DevicesUpdate> GetDevices(Guid locationID, Guid controllerID, DateTime lastUpdateReceivedAt)
{
DevicesUpdate devicesUpdate = await GetLastUpdatedDevices(controllerID, lastUpdateReceivedAt);
}
//code in view model
public static async Task<DevicesUpdate> GetLastUpdatedDevices(Guid controllerID,
DateTime lastUpdate)
{
System.Net.Http.HttpClient client = new System.Net.Http.HttpClient();
string url = string.Format("http://appname.azurewebsites.net/api/devices?controllerid={1}&lastUpdate={2}"
, Constants.WebServerURL, controllerID, lastUpdate);
System.Net.Http.HttpResponseMessage response = await client.GetAsync(new Uri(url));
string result = await response.Content.ReadAsStringAsync();
if (response.IsSuccessStatusCode)
{
DevicesUpdate devices = JSONHelper.Deserialize<DevicesUpdate>(result);
return devices;
}
else
{
if (response.ReasonPhrase == "UserException")
{
throw new UserException(result);
}
else
{
//throw error because the response from rest api is not a success
throw new System.Net.Http.HttpRequestException(result);
}
}
}
You might have a few things happening here that's causing problems.
GetDevices doesn't return anything. (I hope you just left out the return for brevity sake)
You are never setting ContinueTimer to false.
What iOS version are you on? In later versions, you HAVE to use HTTPS or explicitly allow non-secure connections. This shouldn't be a problem because Azure has ssl.
If you plan on running this in the background, you need to register your app as a background process.
If you don't plan on running this in the background, you might have issues with previous attempts being ran (or still trying to execute, or just have failed) and then calling more.
What is the reason for calling the 3 second timer for the network calls? What if the call takes more than 3 seconds (then you are making duplicate calls even though the first might succeed).
If you want to make your network calls more robust, check out this Blog Post by Rob Gibbons about resilient network calls.
First thing I would do is remove it from the timer because it seems like the underlying sockets are having issues cross-thread.

UWP IAP : This in-app purchase item is no longer available

I create a new IAP in store with the Product ID "Donate".
Then I make request to this IAP in the app, here is the code:
var purchaseResults = await CurrentApp.RequestProductPurchaseAsync("Donate");
However, when I call the function in my windows app, an error message dialog is shown:
Choose another item : This in-app purchase item is no longer available in My App Name
and I tried call var listingInfo = await CurrentApp.LoadListingInformationAsync();var productListings = listingInfo.ProductListings;It seems that there is no product in the list.
How would this happened? Thanks!
If you are building your app based on Windows SDK 14393(or higher), I strongly recommend StoreContext class instead of CurrentApp, StoreContext is more convenient to implement an IAP action, and it is also easy to debug.
For your case, you could use the purchase api like this:
private readonly StoreContext context = StoreContext.GetDefault();
// you can get the StoreId of Iap product from your dashboard of Windows Dev Center
var result = await context.RequestPurchaseAsync(product.StoreId);
if (result.Status == StorePurchaseStatus.Succeeded)
{
// successfully purchased
}
else if (result.Status == StorePurchaseStatus.AlreadyPurchased)
{
// purchased already
}
else if (result.Status == StorePurchaseStatus.NotPurchased)
{
// purchase progress cancelled by user
}
else if (result.Status == StorePurchaseStatus.ServerError || result.Status == StorePurchaseStatus.NetworkError)
{
// error occured
}
and you could also list your IAP product like this:
var result = await context.GetAssociatedStoreProductsAsync(new string[] { "Durable" });
if (result.ExtendedError != null)
{
// error
}
else
{
// list all durable iap product
var products = result.Products.Values;
}

Resources