Android In-App Billing

Hello,

We are trying to use the Xamarin.InAppBilling component to make in-app purchases. We were able to successfully buy a product and to enumerate the products. However we are facing a problem to get the already purchased products. We get the following exception when we execute the following code.

06-18 10:59:28.638 I/mono-stdout(24862): BillingHandler_InAppBillingProcesingError - Erro Message: Error validating previous purchase : System.NullReferenceException: Object reference not set to an instance of an object
06-18 10:59:28.648 I/mono-stdout(24862): at Xamarin.InAppBilling.InAppBillingHandler.GetPurchases (System.String itemType) [0x00000] in :0

We call this method with the following code:

var _purchases = _serviceConnection.BillingHandler.GetPurchases(ItemType.Product);

We instantiate the billing class with the following code. The value variable is our public key I got from Google Play. We used to use the Unify method, but we removed it just to make sure it was not causing the problem.

var value = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAs/EobJD6fxSBDdGDxaXqSK5g+GCKRRqp3AN/p/862leJpEgV75iyu45UsCVJ4vYUb51IF5j/lbEdCiYeweGUw9DwbORTKH+1Ahv8A7wVpFGtVP2TtD7tEqadsad/+FOiUZamwQgdfdgdd6yxu5KQqKWHpHqGHuDSoLybcDQGUybqIU4DYUpTNskq1AKCOKpA2SbuTxkktooVVviUAs/HPDAn9rHY1Eqfqa8yAI1tAMqL/qbj0uqKcyt4zKx5GFH+q5qozhtYMamizkn5KiS7mePZ5Yhwlb01eGyMRXZp8pICNnVrxTMM4fqzZK9Y3bcXFm8EwhQ72Xr2J7TP90VcXiHP9KzdWxCFwIDAQAB";

_serviceConnection = new InAppBillingServiceConnection(MainActivity.Main, value);

The code that get the purchases is executed inside the OnConnected event.

I do not know what else to look. Can you help us?

Posts

  • KMullinsKMullins USMember, Xamarin Team Xamurai

    @IntegralInformtica‌,

    A couple of things:

    • This one is just a friendly notice... you should never post any of your public/private keys in an open forum. It opens your apps and you company up for hacking.
    • Make sure that you are using the latest version of the Xamarin.InAppBilling component. Older versions had an issue raising a null exception because of a breaking change made to the Google Play Services API.
    • Ensure that the connection to the Google Play Services API hasn't been closed, there are several situations that can cause GPS to close the connection without your app explicitly close it.

    Thanks,

    Kevin

  • Hello Kevin,

    Thanks for your response.

    Regarding the public key I added, changed and removed some bytes before posting our public key. ;)

    After a lot of tests we found that if we have the Linking option configured as Sdk and Users Assemblies we get the return value as null. If we change it to None or Sdk the purchases are listed correctly. To avoid losing the benefit of the linking feature we added the Xamarin.InAppBilling assembly to the Skip linking assemblies option.

    Kevin,

    Do you know if this behavior is expected?

  • KMullinsKMullins USMember, Xamarin Team Xamurai

    @IntegralInformtica‌,

    Ah, linking... that does make sense and thanks for the feedback. I'll adjust the documentation to include this.

    Actually I didn't realize this was happening but I understand why. It's an artifact of how Google Play Services gets linked and called by the component (or actually Xamarin in general). There's no way for the linker to figure this out automatically and it is being stripped.

    I does sound like you have a workable solution though. Please let me know if you require anything else.

    Thanks,

    Kevin

  • Hello Kevin,

    Yes, it is working as expected. I am glad to hear that it makes sense. I will proceed this way.

    -George

  • LearnEverythingLearnEverything USMember ✭✭✭
    edited October 10

    Xamarin.Android.Google.BillingClient

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    using Android.App;
    using Android.BillingClient.Api;
    using Android.Content;
    using Android.OS;
    using Android.Runtime;
    using Android.Views;
    using Android.Widget;
    using static Android.BillingClient.Api.BillingClient;
    
    namespace PojectForm.Droid.Specifics
    {
        class BilingSupport : IPurchasesUpdatedListener, IBillingClientStateListener, ISkuDetailsResponseListener, IConsumeResponseListener
        {
            Context Context;
            public IList<SkuDetails> SkuDetails;
            BillingClient billingClient;
            private List<string> skuList = new List<string>() { "get_5_coins", "get_10_coins" };
            public IntPtr Handle { get; set; }
            public BilingSupport(Context context)
            {
                this.Context = context;
                billingClient = BillingClient.NewBuilder(this.Context).SetListener(this).Build();
                billingClient.StartConnection(this);
            }
    
            public void LoadPurchases()
            {
    
                if (billingClient.IsReady)
                {
                    var paramse = SkuDetailsParams.NewBuilder().SetSkusList(skuList).SetType(BillingClient.SkuType.Inapp).Build();
                    billingClient.QuerySkuDetailsAsync(paramse, this);
                }
            }
    
            public void PurchaseNow(SkuDetails skuDetails)
            {
                var billingFlowParams = BillingFlowParams.NewBuilder().SetSkuDetails(skuDetails).Build();
                billingClient.LaunchBillingFlow(this.Context as Activity, billingFlowParams);
            }
            public void Dispose()
            {
    
            }
    
            public void OnBillingServiceDisconnected()
            {
                Console.WriteLine("BILLING | onBillingServiceDisconnected | DISCONNECTED");
            }
    
            public void OnBillingSetupFinished(BillingResult p0)
            {
                if (p0.ResponseCode == BillingResponseCode.Ok)
                {
    
                    Console.WriteLine("BILLING | startConnection | RESULT OK");
    
                }
                else
                {
                    Console.WriteLine("BILLING | startConnection | RESULT: $billingResponseCode");
                }
            }
           //Response code 7 in OnPurchasesUpdated. It means: Item Already Owned.
            public void OnPurchasesUpdated(BillingResult p0, IList<Purchase> p1)
            {
    
            }
            public void OnSkuDetailsResponse(BillingResult p0, IList<SkuDetails> p1)
            {
                if (p0.ResponseCode == BillingResponseCode.Ok)
                {
                    Console.WriteLine("querySkuDetailsAsync, responseCode: $responseCode");
                    InitProductAdapter(p1);
    
                }
                else
                {
                    Console.WriteLine("Can't querySkuDetailsAsync, responseCode: $responseCode");
                }
            }
            public void InitProductAdapter(IList<SkuDetails> skuDetails)
            {
                this.SkuDetails = skuDetails;
            }
            public void ClearOrConsumeAllPurchases()
            {
    
                var querylist = billingClient.QueryPurchases(BillingClient.SkuType.Inapp).PurchasesList;
                foreach (var query in querylist)
                {
                    var consumeParams = ConsumeParams.NewBuilder().SetPurchaseToken(query.PurchaseToken).Build();
                    billingClient.ConsumeAsync(consumeParams, this);
                }        
    
            }
    
            public void OnConsumeResponse(BillingResult p0, string p1)
            {
                if (p0.ResponseCode == BillingResponseCode.Ok && p1 != null)
                {
                    Console.WriteLine("onPurchases Updated consumeAsync, purchases token removed: $purchaseToken");
                }
                else
                {
                    Console.WriteLine("onPurchases some troubles happened: $responseCode");
                }
            }
        }
    }
    
Sign In or Register to comment.