how to implement Android In App BillingClient in Xamarin.Android Asynchronously
Asked Answered
D

1

0

I am trying to implement below java code in c# referring to Android documentation

List<String> skuList = new ArrayList<> ();
skuList.add("premium_upgrade");
skuList.add("gas");
SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder();
params.setSkusList(skuList).setType(SkuType.INAPP);
billingClient.querySkuDetailsAsync(params.build(),
    new SkuDetailsResponseListener() {
        @Override
        public void onSkuDetailsResponse(BillingResult billingResult,
                List<SkuDetails> skuDetailsList) {
            // Process the result.
        }
    });

I have here 2 questions. I thought that i would run this code on a separate thread than UI thread like below to keep my ui responsive while network connection is done. is that the correct approach? QuerySkuDetailsAsync is called async but doesnt implement as async. how should this be working and how to handle in c# because it will fire and forget but Listener to handle the response.

public async Task<List<InAppBillingProduct>> GetProductsAsync(List<string> ProductIds)
        {
 var getSkuDetailsTask = Task.Factory.StartNew(() =>
            {

                var prms = SkuDetailsParams.NewBuilder();
                var type =   BillingClient.SkuType.Inapp;
                prms.SetSkusList(ProductIds).SetType(type);

                BillingClient.QuerySkuDetailsAsync(prms.Build(), new SkuDetailsResponseListener());

                return InAppBillingProducts;
            });
     return await getSkuDetailsTask;
        }

2nd question regarding how to handle with the listener as below. How do I return value from the listener. I need return list of InAppBillingProduct object.

 public class SkuDetailsResponseListener : Java.Lang.Object, ISkuDetailsResponseListener
    {
        public void OnSkuDetailsResponse(BillingResult billingResult, IList<SkuDetails> skus)
        {
             if (billingResult.ResponseCode == BillingResponseCode.Ok)
            {
                   // get list of Products here and return
            }
        }
    }
Dray answered 7/10, 2019 at 15:41 Comment(2)
about implementing in-app billing, you can take a look:Integrating In-App Purchases in Mobile Apps, and this sample :github.com/jamesmontemagno/InAppBillingPluginSheeran
@CherryBu-MSFT it is using older version of android in app billing. i already use it in my existing apps but i would like to program using new BillingClient.Dray
I
0
FYI. This is how I did it. This is not a complete code but this will give you and idea.

Listener - PCL
============
private async Task EventClicked()
{
  var skuList = new List<string>();
  skuList.Add("[nameofsubscriptionfoundinyourgoogleplay]");

  if (await _billingClientLifecycle.Initialize(skuList, DisconnectedConnection))
        {
            var firstProduct = _billingClientLifecycle?.ProductsInStore?.FirstOrDefault();

            if (firstProduct != null)
            {
                //purchase here
            }
        }
}

private void DisconnectedConnection()
    { 
        //Todo.alfon. handle disconnection here...
    }


Interface - PCL
===========
public interface IInAppBillingMigratedNew
{
    List<InAppBillingPurchase> PurchasedProducts { get; set; }
    List<InAppBillingProduct> ProductsInStore { get; set; }
    Task<bool> Initialize(List<String> skuList, Action onDisconnected = null);
}


Dependency - Platform Droid
===============
[assembly: XF.Dependency(typeof(InAppBillingMigratedNew))]
public class InAppBillingMigratedNew : Java.Lang.Object, IBillingClientStateListener
    , ISkuDetailsResponseListener, IInAppBillingMigratedNew
{
    private Activity Context => CrossCurrentActivity.Current.Activity 
        ?? throw new NullReferenceException("Current Context/Activity is null");
    private BillingClient _billingClient;
    private List<string> _skuList = new List<string>();
    private TaskCompletionSource<bool> _tcsInitialized;
    private Action _disconnectedAction;
    private Dictionary<string, SkuDetails> _skusWithSkuDetails = new Dictionary<string, SkuDetails>();

    public List<InAppBillingPurchase> PurchasedProducts { get; set; }
    public List<InAppBillingProduct> ProductsInStore { get; set; }
    public IntPtr Handle => throw new NotImplementedException();

    public Task<bool> Initialize(List<string> skuList, Action disconnectedAction = null)
    {
        _disconnectedAction = disconnectedAction;
        _tcsInitialized = new TaskCompletionSource<bool>();
        var taskInit = _tcsInitialized.Task;
        _skuList = skuList;
        _billingClient = BillingClient.NewBuilder(Context)
           .SetListener(this)
           .EnablePendingPurchases()
           .Build();

        if (!_billingClient.IsReady)
        {
            _billingClient.StartConnection(this);
        }

        return taskInit;
    }

    #region IBillingClientStateListener

    public void OnBillingServiceDisconnected()
    {
        Console.WriteLine($"Connection disconnected.");
        _tcsInitialized?.TrySetResult(false);
        _disconnectedAction?.Invoke();
    }

    public void OnBillingSetupFinished(BillingResult billingResult)
    {  
        var responseCode = billingResult.ResponseCode;
        var debugMessage = billingResult.DebugMessage;

        if (responseCode == BillingResponseCode.Ok)
        {
            QuerySkuDetails();
            QueryPurchases();
            _tcsInitialized?.TrySetResult(true);
        }
        else
        {
            Console.WriteLine($"Failed connection {debugMessage}");
            _tcsInitialized?.TrySetResult(false);
        }
    }

    #endregion

    #region ISkuDetailsResponseListener

    public void OnSkuDetailsResponse(BillingResult billingResult, IList<SkuDetails> skuDetailsList)
    {
        if (billingResult == null)
        {
            Console.WriteLine("onSkuDetailsResponse: null BillingResult");
            return;
        }

        var responseCode = billingResult.ResponseCode;
        var debugMessage = billingResult.DebugMessage;

        switch (responseCode)
        {
            case BillingResponseCode.Ok:
                if (skuDetailsList == null)
                {
                    _skusWithSkuDetails.Clear();
                }
                else
                {
                    if (skuDetailsList.Count > 0)
                    {
                        ProductsInStore = new List<InAppBillingProduct>();
                    }

                    foreach (var skuDetails in skuDetailsList)
                    {
                        _skusWithSkuDetails.Add(skuDetails.Sku, skuDetails);

                        //ToDo.alfon. make use mapper here
                        ProductsInStore.Add(new InAppBillingProduct
                        {
                            Name = skuDetails.Title,
                            Description = skuDetails.Description,
                            ProductId = skuDetails.Sku,
                            CurrencyCode = skuDetails.PriceCurrencyCode,
                            LocalizedIntroductoryPrice = skuDetails.IntroductoryPrice,
                            LocalizedPrice = skuDetails.Price,
                            MicrosIntroductoryPrice = skuDetails.IntroductoryPriceAmountMicros,
                            MicrosPrice = skuDetails.PriceAmountMicros                                
                        });
                    }
                }
                break;
            case BillingResponseCode.ServiceDisconnected:
            case BillingResponseCode.ServiceUnavailable:
            case BillingResponseCode.BillingUnavailable:
            case BillingResponseCode.ItemUnavailable:
            case BillingResponseCode.DeveloperError:
            case BillingResponseCode.Error:
                Console.WriteLine("onSkuDetailsResponse: " + responseCode + " " + debugMessage);
                break;
            case BillingResponseCode.UserCancelled:
                Console.WriteLine("onSkuDetailsResponse: " + responseCode + " " + debugMessage);
                break;
            // These response codes are not expected.
            case BillingResponseCode.FeatureNotSupported:
            case BillingResponseCode.ItemAlreadyOwned:
            case BillingResponseCode.ItemNotOwned:
            default:
                Console.WriteLine("onSkuDetailsResponse: " + responseCode + " " + debugMessage);
                break;
        }
    }

    #endregion

    #region Helper Methods Private

    private void ProcessPurchases(List<Purchase> purchasesList)
    {
        if (purchasesList == null)
        {
            Console.WriteLine("No purchases done.");
            return;
        }

        if (IsUnchangedPurchaseList(purchasesList))
        {
            Console.WriteLine("Purchases has not changed.");
            return;
        }

        _purchases.AddRange(purchasesList);
        PurchasedProducts = _purchases.Select(sku => new InAppBillingPurchase
        {
            PurchaseToken = sku.PurchaseToken
        })?.ToList();

        if (purchasesList != null)
        {
            LogAcknowledgementStatus(purchasesList);
        }
    }

    private bool IsUnchangedPurchaseList(List<Purchase> purchasesList)
    {
        // TODO: Optimize to avoid updates with identical data.
        return false;
    }

    private void LogAcknowledgementStatus(List<Purchase> purchasesList)
    {
        int ack_yes = 0;
        int ack_no = 0;

        foreach (var purchase in purchasesList)
        {
            if (purchase.IsAcknowledged)
            {
                ack_yes++;
            }
            else
            {
                ack_no++;
            }
        }
        //Log.d(TAG, "logAcknowledgementStatus: acknowledged=" + ack_yes +
        //        " unacknowledged=" + ack_no);
    }

    private void QuerySkuDetails()
    {
        var parameters = SkuDetailsParams
            .NewBuilder()
            .SetType(BillingClient.SkuType.Subs)
            .SetSkusList(_skuList)
            .Build();
        _billingClient.QuerySkuDetailsAsync(parameters, this);
    }

    private void QueryPurchases()
    {
        if (!_billingClient.IsReady)
        {
            Console.WriteLine("queryPurchases: BillingClient is not ready");
        }

        var result = _billingClient.QueryPurchases(BillingClient.SkuType.Subs);
        ProcessPurchases(result?.PurchasesList?.ToList());
    }

    #endregion
}
Incommunicable answered 13/6, 2020 at 6:13 Comment(1)
thanks for your answer. i have already found a solution to that long time ago. you can find the entire code here and i published a nuget for it if you like to use. github.com/EmilAlipiev/XFInAppBilling/blob/master/…Dray

© 2022 - 2024 — McMap. All rights reserved.