Restore In App Purchase

Jun 10, 2012 at 9:41 PM
Edited Jun 10, 2012 at 9:43 PM

Hi,

One of my games was rejected and here is the message I received:

We found that your app offers In-App Purchase/s that can be restored but it does not include a "Restore" feature to allow users to restore the previously purchased In-App Purchase/s.

To restore previously purchased In-App Purchase products, it would be appropriate to provide a "Restore" button and initiate the restore process when the "Restore" button is tapped.

I don't understand what this means. Can anyone help me? Here is the code I am using:

http://www.jmawebtechnologies.com/company-blog/april-2012/in-app-purchase-monogame-ios

How do I add a restore button? What functionality are they seeking?

Jun 11, 2012 at 9:46 AM

My game didn't get rejected on this, but I wish it had (I have had a lot of emails from customers asking about this!) Essentially if you don't have this, players need to go through the buying process again, and only find out the item is free once they have entered their password to pay for it. I have no idea how you actually check for purchases so I am not much help...but the current apple flow is horrible. 

Coordinator
Jun 11, 2012 at 11:28 AM

One of the guys at work has been going through in-app purchase fun recently.  There is a way to get a list of non-consumable purchased items from the AppStore, and your app can use this list to restore (re-download) previously purchased items if the user has lost them for some reason (uninstalled/reinstalled, got a new device, etc).

Jun 22, 2012 at 4:26 PM

Does this seem right:

 

#region File Description
//-----------------------------------------------------------------------------
// MainMenuScreen.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
using MonoTouch.Foundation;
using MonoTouch.StoreKit;

#endregion

#region Using Statements
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Media;
using GameStateManagement;
using System;
using Microsoft.Xna.Framework.GamerServices;

#if IOS
using MonoTouch.UIKit;
#endif
#endregion

namespace SpaceRacer.Screens
{
	/// <summary>
	/// The main menu screen is the first thing displayed when the game starts up.
	/// </summary>
	class MainMenuScreen : MenuScreen
	{
        #region variables

        #endregion

        #region Initialization

		/// <summary>
		/// Constructor fills in the menu contents.
		/// </summary>
		public MainMenuScreen ()
            : base("SPACE RACER")
		{

			
			MenuEntry purchaseMenuEntry = new MenuEntry ("Purchase");
			MenuEntry restoreEntry = new MenuEntry("Restore Purchases");
			
			purchaseMenuEntry.Selected += PurchaseMenuEntrySelected;
			restoreEntry.Selected += RestoreMenuEntrySelected;

			

            MenuEntries.Add(purchaseMenuEntry);
		MenuEntries.Add(restoreEntry);


		}

        #endregion

        #region Handle Input

		void RestoreMenuEntrySelected(object sender, PlayerIndexEventArgs e)
		{
			SKPaymentQueue.DefaultQueue.RestoreCompletedTransactions ();
		}

		void PurchaseMenuEntrySelected (object sender, PlayerIndexEventArgs e)
		{		
			ScreenManager.AddScreen (new PurchaseScreen (false), e.PlayerIndex);
		}

		

        #endregion
	}

}


Jun 29, 2012 at 8:54 PM

Hi,

I added this code, but Apple has yet to approve. My code seems right, based on the forums in Objective C:

// Handles custom observer
		private class MySKPaymentObserver : SKPaymentTransactionObserver
		{
			private InAppPurchaseManager theManager;
			public MySKPaymentObserver(InAppPurchaseManager manager)
			{
				theManager = manager;
			}

			public override void PaymentQueueRestoreCompletedTransactionsFinished (SKPaymentQueue queue)
			{
				foreach(SKPaymentTransaction transaction in queue.Transactions)
				{

					#if DEBUG
					Console.WriteLine("Restoring Transaction " + transaction.Payment.ProductIdentifier);
					#endif
					theManager.restoreTransaction(transaction);
				}
			}
			//
			// called when the transaction status is updated
			//
			public override void UpdatedTransactions (SKPaymentQueue queue, SKPaymentTransaction[] transactions)
			{
				foreach (SKPaymentTransaction transaction in transactions)
				{
#if DEBUG
				Console.WriteLine(transaction.Payment.ProductIdentifier + " " + transaction.TransactionState);
#endif
				    switch (transaction.TransactionState)
				    {
				        case SKPaymentTransactionState.Purchased:
				           theManager.completeTransaction(transaction);
				            break;
				        case SKPaymentTransactionState.Failed:
				           theManager.failedTransaction(transaction);
				            break;
				        case SKPaymentTransactionState.Restored:
				            theManager.restoreTransaction(transaction);
				            break;
				        default:
				            break;
				    }
				}

			}

		}	

PaymentQueueRestoreCompletedTransactionsFinished had to be implemented. Here is the code I used to call it:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using GameStateManagement;
using Microsoft.Xna.Framework.GamerServices;

#if IOS
using SpaceRacerMT;
using MonoTouch.Foundation;
using MonoTouch.StoreKit;
using MonoTouch.UIKit;
#endif


namespace SpaceRacer.Screens
{
	class PurchaseScreen : MenuScreen
	{

		static InAppPurchaseManager app;
		static MenuEntry buyLevels;
		static MenuEntry buyDifficulty;
		static MenuEntry restoreEntry;
		
		public PurchaseScreen (bool showHigh) : base("PURCHASE")
		{
			PrepareInAppPurchase ();
			buyLevels = new MenuEntry ("Buy 3 More Levels");
			buyDifficulty = new MenuEntry("Buy Hard Difficulty");
			restoreEntry = new MenuEntry("Restore Purchases");
				
			if(Reachability.IsHostReachable("google.com"))
			{
				buyLevels.Selected += BuyLevelsMenuEntrySelected;
				buyDifficulty.Selected += BuyDifficultyMenuEntrySelected;
			}
			else
			{
				UIAlertView alert = new UIAlertView("No Internet", "Please log onto the internet before purchasing.", null, "OK", null);
				alert.Show();
			}


			restoreEntry.Selected += RestoreMenuEntrySelected;

			HaveItemsBeenPurchased();

			MenuEntry mainMenuEntry = new MenuEntry("Main Menu");
            mainMenuEntry.Selected += MainMenuEntrySelected;
            MenuEntries.Add(mainMenuEntry);
		}

		void HaveItemsBeenPurchased ()
		{
			bool isProUpgradePurchased = NSUserDefaults.StandardUserDefaults.BoolForKey("isProUpgradePurchased");
			bool isHardUpgradeDifficultyPurchased = NSUserDefaults.StandardUserDefaults.BoolForKey("isHardUpgradeDifficultyPurchased");

			if(!isProUpgradePurchased)
			{
				MenuEntries.Add(buyLevels);
			}

			if(!isHardUpgradeDifficultyPurchased)
			{
				MenuEntries.Add(buyDifficulty);
			}

			if(!isHardUpgradeDifficultyPurchased || !isProUpgradePurchased)
			{
				MenuEntries.Add(restoreEntry);
			}

			if(isHardUpgradeDifficultyPurchased && isProUpgradePurchased)
			{
				UIAlertView alert = new UIAlertView("No Purchase", "You have purchased all the items in the store", null, "OK", null);
				alert.Show();
			}
		}
		void RestoreMenuEntrySelected(object sender, PlayerIndexEventArgs e)
		{
			SKPaymentQueue.DefaultQueue.RestoreCompletedTransactions();
			ScreenManager.AddScreen(new MainMenuScreen(), e.PlayerIndex);
		}
		
		void BuyLevelsMenuEntrySelected (object sender, PlayerIndexEventArgs e)
		{
            app.PurchaseProUpgrade();
		}
		
		void BuyDifficultyMenuEntrySelected (object sender, PlayerIndexEventArgs e)
		{
            app.PurchaseHardDifficultyUpgrade();
		}

		void MainMenuEntrySelected (object sender, PlayerIndexEventArgs e)
		{
			ScreenManager.AddScreen (new MainMenuScreen (), e.PlayerIndex);
		}

		void PrepareInAppPurchase ()
		{	
			app = new InAppPurchaseManager ();
			app.LoadStore();
		}

	}
}