InAppBillingPlugin: Restore from redeemed codes does not work (iOS)

If you are creating an issue for a BUG please fill out this information. If you are asking a question or requesting a feature you can delete the sections below.

Failure to fill out this information will result in this issue being closed. If you post a full stack trace in a bug it will be closed, please post it to http://gist.github.com and then post the link here.

Bug Information

Version Number of Plugin: 1.1.0.35-beta Device Tested On: Multiple iOS devices Simulator Tested On: - Version of VS: 15.2 (26430.12) Release Version of Xamarin: 2.3.4.247 Versions of other things you are using: Mapsui 1.0.9

Steps to reproduce the Behavior

Redeem multiple an IAP codes on iTunes and try to restore it on the iOS device (does not happend all the time. It happens in 1/20 tries).

Expected Behavior

On restore there should be each product that was redeemed with a code or purchased.

Actual Behavior

The last redeemed code returns multiple times. So if you have a code for “Unlock XY” and one for “Remove Ads” the last redeemed code is returned for each redeemed code. In this case if you redeem “Unlock XY” first and “Remove Ads” than, you got 2x “Remove Ads” on the restore function.

Code snippet

        private async void OnRestorePurchasesCommandExecute()
        {
            try
            {
                ProcessIsRunning = true;
                var restoredProducts = new List<string>();

                var connection = await CrossInAppBilling.Current.ConnectAsync();
                if (!connection)
                {
                    await page.DisplayAlert("Error", "Error while connecting to Store.", "OK");
                    return;
                }

                var purchases = (await CrossInAppBilling.Current.GetPurchasesAsync(ItemType.InAppPurchase, new InAppBillingVerifyPurchase()))?.ToArray();
                if (purchases != null && purchases.Any())
                {
                    foreach (var purchase in purchases)
                    {
                        if (Products.Any(p => p.ID == purchase.ProductId))
                        {
                            context.Send(async c =>
                                {
                                    if (purchase.State == PurchaseState.Purchased || purchase.State == PurchaseState.Restored)
                                    {
                                        var product = Products.First(p => p.ID == purchase.ProductId);
                                        product.Purchased = true;

                                        if (await MarkProductAsPurchased(purchase.ProductId))
                                            restoredProducts.Add(product.Name);
                                    }
                                }, null);
                        }
                        else
                        {
                            await page.DisplayAlert("Error", $"Can not identify product with ID \"{purchase.ProductId}\".", "OK");
                        }
                        
                        if (purchase.ProductId == PurchaseableProduct.ALL_PAGES && (purchase.State == PurchaseState.Purchased || purchase.State == PurchaseState.Restored))
	                    {
		                    foreach (var product in Products.Where(p => p.IsPageUnlock))
			                    context.Send(c => { product.Purchased = true; }, null);
	                    }

                        await DependencyService.Get<IWebclient>().SendPurchaseReportAsync(purchase);
                    }

                    await page.DisplayAlert("Status", restoredProducts.Any() ? $"Products have been successfully restored. {Environment.NewLine}{Environment.NewLine}Restored: {string.Join(", ", restoredProducts)}" : "Products could not be restored.", "OK");
                }
            }
            catch (Exception ex)
            {
                await page.DisplayAlert("Error", ex.Message, "OK");
            }
            finally
            {
                await CrossInAppBilling.Current.DisconnectAsync();
                ProcessIsRunning = false;
            }
        }

Screenshotst

Here is entry in my DB from the PurchaseReport-Method I built. The customer gets 2x the last product he restored. http://puu.sh/wdD3q/55a9cd9e74.png

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Comments: 57 (48 by maintainers)

Most upvoted comments

Got the exact same feedback as @desunit from my TestFlight users so far. I hope it works as well on the release version it is currently in review by apple.

  1. Sounds great. I will test it when you updated your library. The next version of my app will be uploaded at the beginning of next week, at least if I got a working Mac until than. My 8 year old Mac died and the new ordered one comes defect yesterday and I have to wait for the replacement from Apple… But actual I’m not so confident that the replacement comes so soon but the order page says it (custom configured and last time I have to wait 3 weeks).
  2. Total forgot about this case. So I will ignore this exception in the future.