Example #1
0
        /// <summary>
        /// Initializes an async process to purchas the product. Only one purchase request can be happening at a time
        /// </summary>
        /// <param name="productId">Product to buy</param>
        /// <returns>Purchase object</returns>
        public async Task <InAppPurchaseResult> PurchaseAsync(string productId)
        {
            if (_billingClient == null || !_billingClient.IsReady)
            {
                throw new InAppPurchaseException(PurchaseError.DeveloperError, "Billing Client is not connected");
            }

            // Make sure no purchases are being currently made
            if (_transactionPurchased != null && !_transactionPurchased.Task.IsCanceled)
            {
                throw new InAppPurchaseException(PurchaseError.DeveloperError, "Another Purchase is in progress");
            }

            // First, get the SkuDetail
            SkuDetails sku;

            if (!_retrievedProducts.TryGetValue(productId, out sku))
            {
                throw new InAppPurchaseException(PurchaseError.DeveloperError,
                                                 $"Cannot find a retrieved Product with {productId} SKU. Products must be first queried from the Play Store");
            }

            // Build FlowParam for the Purchase
            BillingFlowParams flowParams = BillingFlowParams.NewBuilder()
                                           .SetSkuDetails(sku)
                                           .Build();

            // Set a new Task Source to wait for completion
            _transactionPurchased = new TaskCompletionSource <InAppPurchaseResult>();
            Task <InAppPurchaseResult> taskPurchaseComplete = _transactionPurchased.Task;

            //_billingClient.QueryPurchaseHistoryAsync(BillingClient.SkuType.Inapp, this);

            // Initiate the Billing Process.
            BillingResult response = _billingClient.LaunchBillingFlow(_activity, flowParams);

            if (response.ResponseCode != BillingResponseCode.Ok)
            {
                // Reset the in-app-purchase flow
                _transactionPurchased?.TrySetCanceled();
                _transactionPurchased = null;
                throw new InAppPurchaseException(response.ResponseCode.ToPurchaseError(), response.DebugMessage);
            }

            // Wait till the Task is complete (e.g. Succeeded or Failed - which will result in Exception)
            InAppPurchaseResult purchase = await taskPurchaseComplete;

            _transactionPurchased = null;

            return(purchase);
        }
        public override void UpdatedTransactions(SKPaymentQueue queue, SKPaymentTransaction[] transactions)
        {
            Debug.WriteLine("UpdatedTransactions called...");

            foreach (SKPaymentTransaction transaction in transactions)
            {
                if (transaction?.TransactionState == null)
                {
                    break;
                }

                Debug.WriteLine($"Updated Transaction | {transaction.TransactionState}; ProductId = {transaction.Payment?.ProductIdentifier ?? string.Empty}");

                switch (transaction.TransactionState)
                {
                case SKPaymentTransactionState.Restored:
                    _restoredTransactions.Add(transaction);
                    SKPaymentQueue.DefaultQueue.FinishTransaction(transaction);
                    break;

                case SKPaymentTransactionState.Purchased:
                    InAppPurchaseResult result = transaction.ToInAppPurchase();

                    // If _transactionPurchased Task is not set, then it means the user initiated the out of the app purchase process
                    // e.g. purchasing IAP from the App Store
                    if (_transactionPurchased == null)
                    {
                        Debug.WriteLine($"_transactionPurchased is null. Continue with the OnPurchasedOutOfApp call");

                        InAppPurchaseService.OnPurchasedOutOfApp.Invoke(result);
                    }
                    else
                    {
                        // Normal Purchase flow when the user clicks purchase button.

                        Debug.WriteLine($"_transactionPurchased is NOT null. Continue with the normal flow");

                        _transactionPurchased?.TrySetResult(transaction.ToInAppPurchase());
                        // Reset the Task
                        _transactionPurchased = null;
                    }

                    SKPaymentQueue.DefaultQueue.FinishTransaction(transaction);
                    break;

                case SKPaymentTransactionState.Failed:
                    PurchaseError?error       = transaction?.Error?.ToPurchaseError();
                    string        description = transaction?.Error?.LocalizedDescription ?? string.Empty;
                    // Failed Transaction. Set the Exception to the Task, so the caller can react to the issue
                    _transactionPurchased?.TrySetException(
                        new InAppPurchaseException(error ?? PurchaseError.GeneralError, description));

                    // Reset the Task
                    _transactionPurchased = null;
                    SKPaymentQueue.DefaultQueue.FinishTransaction(transaction);
                    break;

                case SKPaymentTransactionState.Purchasing:
                case SKPaymentTransactionState.Deferred:
                default:
                    break;
                }
            }
        }