- Cleared the cache and still have the same error.
- I have confirmed that I am signed into my Google Account. I have actually tried signing in with 2 different Google accounts. My other tester has also downloaded the test app and is getting the same error.
- I have deleted the play store and added it back
- I have removed and reinstalled my Google Accounts to my device
I always get the same error message.
Any suggestions in what I am missing would be appreciated. For the life of me I cannot find anything wrong!!
Thanks in advance.
Here is the error:
There is only the 1 scene in this test app and the code is attached to a panel of the Canvas. I have 5 buttons that award consumables of (100 coins, 500 coins, 1000 coins, 10 scatter bombs, 5 splash bombs), 1 button to award a non-consumable jumbo jet and 1 button that does not have a product ID (to generate an error message).
footerText.text messages are displayed in the bottom left panel area.
coinText and bombText are displayed in the bottom right panel
Here is the entire code
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Purchasing;
using UnityEngine.UI; //for testing only
// Deriving the Purchaser class from IStoreListener enables it to receive messages from Unity Purchasing.
public class MyIAPPurchaser : MonoBehaviour, IStoreListener
{
//turn this script into a singleton so that it can be called from anywhere
public static MyIAPPurchaser instance;
private static IStoreController m_StoreController; // The Unity Purchasing system.
private static IExtensionProvider m_StoreExtensionProvider; // The store-specific Purchasing subsystems.
// Product identifiers for all products (consumable, non-consumable, and subscription)capable of being purchased:
// We declare their store specific mapping to Unity Purchasing's AddProduct, below.
public static string kProductIDConsumable = "consumable"; //not used
public static string kProductIDNonConsumable = "nonconsumable"; //not used
public static string kProductIDSubscription = "subscription"; //not used
//test data *********************************
/* Product ID, Title, Descripti on
* coin.100, Title: 100 Coins, Description: You will get 100 coins added to your coin count
* coin.500, Title: 500 Coins, Description: You will get 500 coins added to your coin count
* coin.1000, Title: 1000 Coins, Description: You will get 1000 coins added to your coin count
* bomb.scatter.10, Title: 10 Scatter bombs, Description: You will get 10 scatter bombs added to your bomb count
* bomb.splash.5, Title: 5 Splash Bombs, Description: You will get 5 splash bombs added to your bomb count
* plane.jumbo, Title: Jumbo Jet, Description: You will get the Jumbo Jet plane FOREVER!!!
*/
public static string Product_100_coin = "coin.100";
public static string Product_500_coin = "coin.500";
public static string Product_1000_coin = "coin.1000";
public static string Product_10_scatter = "bomb.scatter.10";
public static string Product_5_splash = "bomb.splash.5";
public static string Product_plane_jumbo = "plane.jumbo";
//example data *********************************
// Apple App Store-specific product identifier for the subscription product.
private static string kProductNameAppleSubscription = "com.unity3d.subscription.new";
// Google Play Store-specific product identifier subscription product.
private static string kProductNameGooglePlaySubscription = "com.unity3d.subscription.original";
//for testing only*********
public Text footerText;
public Text coinText;
public Text bombText;
private int numOfCoins;
private int numOfScatterBombs;
private int numOfSplashBombs;
private void Awake()
{
instance = this;
displayFooterTextClear();
}
void Start()
{
// If we haven't set up the Unity Purchasing reference
if (m_StoreController == null)
{
// Begin to configure our connection to Purchasing
InitializePurchasing();
}
}
//Initializes the IAP builder, adds products that are available for sale and supplies a listener to handle purchasing events.
//called from Start
public void InitializePurchasing()
{
// If we have already connected to Purchasing ...
if (IsInitialized())
{
// ... we are done here.
return;
}
// Create a builder, first passing in a suite of Unity provided stores.
var builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance());
// Add a product to sell / restore by way of its identifier, associating the general identifier
// with its store-specific identifiers.
//*********** CONSUMABLE PRODUCTS ******************
builder.AddProduct(Product_100_coin, ProductType.Consumable);
builder.AddProduct(Product_500_coin, ProductType.Consumable);
builder.AddProduct(Product_1000_coin, ProductType.Consumable);
builder.AddProduct(Product_10_scatter, ProductType.Consumable);
builder.AddProduct(Product_5_splash, ProductType.Consumable);
//*********** NON CONSUMABLE PRODUCTS ******************
builder.AddProduct(Product_plane_jumbo, ProductType.NonConsumable, new IDs
{
{"plane.jumbo.mac", MacAppStore.Name},
{"000000596583", TizenStore.Name},
});
//*********** SUBSCRIPTION PRODUCTS ******************
builder.AddProduct("subscription1", ProductType.Subscription, new IDs
{
{"subscription1.mac", MacAppStore.Name}
});
// Kick off the remainder of the set-up with an asynchrounous call, passing the configuration
// and this class' instance. Expect a response either in OnInitialized or OnInitializeFailed.
UnityPurchasing.Initialize(this, builder);
}
//called from InitializePurchasing()
private bool IsInitialized()
{
// Only say we are initialized if both the Purchasing references are set.
return m_StoreController != null && m_StoreExtensionProvider != null;
}
// use individual functions that are called by each button in order to purchase the products
public void Buy100Coin()
{
displayFooterTextClear();
BuyProductID(Product_100_coin);
}
public void Buy500Coin()
{
displayFooterTextClear();
BuyProductID(Product_500_coin);
}
public void Buy1000Coin()
{
displayFooterTextClear();
BuyProductID(Product_1000_coin);
}
public void Buy10ScatterBomb()
{
displayFooterTextClear();
BuyProductID(Product_10_scatter);
}
public void Buy5SplashBomb()
{
displayFooterTextClear();
BuyProductID(Product_5_splash);
}
public void BuyJumboJet()
{
//NEED to check if the jumboJet is already owned, if it is, then do not buy again ******!!!!
displayFooterTextClear();
BuyProductID(Product_plane_jumbo);
}
public void BuyInvalidItem()
{
//This should cause an error
displayFooterTextClear();
BuyProductID("InvalidProductID");
}
//A private function which allows us to buy a product we’ve added using it’s product ID string.
//called by each productID
private void BuyProductID(string productId)
{
// If Purchasing has been initialized ...
if (IsInitialized())
{
Debug.Log("isInitialized, productId= " + productId);
displayFooterText("isInitialized, productId= " + productId);
// ... look up the Product reference with the general product identifier and the Purchasing
// system's products collection.
Product product = m_StoreController.products.WithID(productId);
// If the look up found a product for this device's store and that product is ready to be sold ...
if (product != null && product.availableToPurchase)
{
Debug.Log(string.Format("Purchasing product asychronously: '{0}'", product.definition.id));
displayFooterText(string.Format("Purchasing product asychronously: '{0}'", product.definition.id));
// ... buy the product. Expect a response either through ProcessPurchase or OnPurchaseFailed
// asynchronously.
m_StoreController.InitiatePurchase(product);
}
// Otherwise ...
else
{
// ... report the product look-up failure situation
Debug.Log("BuyProductID: FAIL. Not purchasing product, Product is either not found or is not available for purchase");
displayFooterText("BuyProductID: FAIL. Not purchasing product, Product is either not found or is not available for purchase");
}
}
// Otherwise ...
else
{
// ... report the fact Purchasing has not succeeded initializing yet. Consider waiting longer or
// retrying initiailization.
Debug.Log("BuyProductID FAIL. Not initialized.");
displayFooterText("BuyProductID FAIL. Not initialized.");
}
}
//*************** FOR IOS ONLY - START *************************
//On iOS we can call RestorePurchases to restore products previously purchased
// Restore purchases previously made by this customer. Some platforms automatically restore purchases, like Google.
// Apple currently requires explicit purchase restoration for IAP, conditionally displaying a password prompt.
public void RestorePurchases()
{
// If Purchasing has not yet been set up ...
if (!IsInitialized())
{
// ... report the situation and stop restoring. Consider either waiting longer, or retrying initialization.
Debug.Log("RestorePurchases FAIL. Not initialized.");
displayFooterText("RestorePurchases FAIL. Not initialized.");
return;
}
// If we are running on an Apple device ...
if (Application.platform == RuntimePlatform.IPhonePlayer ||
Application.platform == RuntimePlatform.OSXPlayer)
{
// ... begin restoring purchases
Debug.Log("RestorePurchases started ...");
displayFooterText("RestorePurchases started ...");
// Fetch the Apple store-specific subsystem.
var apple = m_StoreExtensionProvider.GetExtension<IAppleExtensions>();
// Begin the asynchronous process of restoring purchases. Expect a confirmation response in
// the Action<bool> below, and ProcessPurchase if there are previously purchased products to restore.
apple.RestoreTransactions((result) => {
// The first phase of restoration. If no more responses are received on ProcessPurchase then
// no purchases are available to be restored.
Debug.Log("RestorePurchases continuing: " + result + ". If no further messages, no purchases available to restore.");
displayFooterText("RestorePurchases continuing: " + result + ". If no further messages, no purchases available to restore.");
});
}
// Otherwise ...
else
{
// We are not running on an Apple device. No work is necessary to restore purchases.
Debug.Log("RestorePurchases FAIL. Not supported on this platform. Current = " + Application.platform);
displayFooterText("RestorePurchases FAIL. Not supported on this platform. Current = " + Application.platform);
}
}
//*************** FOR IOS ONLY - END *************************
//Called to check if the app can connect to Unity IAP or not.
//OnInitialize will keep trying in the background and will only fail if there is a configuration problem that cannot be recovered from.
public void OnInitialized(IStoreController controller, IExtensionProvider extensions)
{
// Purchasing has succeeded initializing. Collect our Purchasing references.
Debug.Log("OnInitialized: PASS");
displayFooterText("OnInitialized: PASS");
// Overall Purchasing system, configured with products for this application.
m_StoreController = controller;
// Store specific subsystem, for accessing device-specific store features.
m_StoreExtensionProvider = extensions;
}
//Called when IAP have failed to initialize and logs a message to the console.
public void OnInitializeFailed(InitializationFailureReason error)
{
// Purchasing set-up has not succeeded. Check error for reason. Consider sharing this reason with the user.
Debug.Log("OnInitializeFailed InitializationFailureReason:" + error);
displayFooterText("OnInitializeFailed InitializationFailureReason:" + error);
}
//Checks to see if a product purchase was successful and logs the result to the console.
public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs args)
{
// A consumable product has been purchased by this user.
if (String.Equals(args.purchasedProduct.definition.id, Product_100_coin, StringComparison.Ordinal))
{
Debug.Log(string.Format("ProcessPurchase: Consumable PASS. Product: '{0}'", args.purchasedProduct.definition.id));
displayFooterText(string.Format("ProcessPurchase: Consumable PASS. Product: '{0}'", args.purchasedProduct.definition.id));
Debug.Log("You got 100 coins");
displayFooterText("You got 100 coins");
numOfCoins = numOfCoins + 100;
displayCoinText(numOfCoins);
// The consumable item has been successfully purchased, add 100 coins to the player's in-game score.
}
else if (String.Equals(args.purchasedProduct.definition.id, Product_500_coin, StringComparison.Ordinal))
{
Debug.Log(string.Format("ProcessPurchase: Consumable PASS. Product: '{0}'", args.purchasedProduct.definition.id));
displayFooterText(string.Format("ProcessPurchase: Consumable PASS. Product: '{0}'", args.purchasedProduct.definition.id));
Debug.Log("You got 500 coins");
displayFooterText("You got 500 coins");
numOfCoins = numOfCoins + 500;
displayCoinText(numOfCoins);
}
else if (String.Equals(args.purchasedProduct.definition.id, Product_1000_coin, StringComparison.Ordinal))
{
Debug.Log(string.Format("ProcessPurchase: Consumable PASS. Product: '{0}'", args.purchasedProduct.definition.id));
displayFooterText(string.Format("ProcessPurchase: Consumable PASS. Product: '{0}'", args.purchasedProduct.definition.id));
Debug.Log("You got 1000 coins");
displayFooterText("You got 1000 coins");
numOfCoins = numOfCoins + 1000;
displayCoinText(numOfCoins);
}
else if (String.Equals(args.purchasedProduct.definition.id, Product_10_scatter, StringComparison.Ordinal))
{
Debug.Log(string.Format("ProcessPurchase: Consumable PASS. Product: '{0}'", args.purchasedProduct.definition.id));
displayFooterText(string.Format("ProcessPurchase: Consumable PASS. Product: '{0}'", args.purchasedProduct.definition.id));
Debug.Log("You got 10 scatter bombs");
displayFooterText("You got 10 scatter bombs");
numOfScatterBombs = numOfScatterBombs + 10;
displayBombText("Scatter", numOfScatterBombs);
}
else if (String.Equals(args.purchasedProduct.definition.id, Product_5_splash, StringComparison.Ordinal))
{
Debug.Log(string.Format("ProcessPurchase: Consumable PASS. Product: '{0}'", args.purchasedProduct.definition.id));
displayFooterText(string.Format("ProcessPurchase: Consumable PASS. Product: '{0}'", args.purchasedProduct.definition.id));
Debug.Log("You got 5 splash bombs");
displayFooterText("You got 5 splash bombs");
numOfSplashBombs = numOfSplashBombs + 5;
displayBombText("splash", numOfSplashBombs);
}
else if (String.Equals(args.purchasedProduct.definition.id, Product_plane_jumbo, StringComparison.Ordinal))
{
Debug.Log(string.Format("ProcessPurchase: NON-Consumable PASS. Product: '{0}'", args.purchasedProduct.definition.id));
displayFooterText(string.Format("ProcessPurchase: NON-Consumable PASS. Product: '{0}'", args.purchasedProduct.definition.id));
Debug.Log("You got yourself a JUMBO JET");
displayFooterText("You got yourself a JUMBO JET");
}
// Or ... an unknown product has been purchased by this user. Fill in additional products here....
else
{
Debug.Log(string.Format("ProcessPurchase: FAIL. Unrecognized product: '{0}'", args.purchasedProduct.definition.id));
displayFooterText(string.Format("ProcessPurchase: FAIL. Unrecognized product: '{0}'", args.purchasedProduct.definition.id));
}
// Return a flag indicating whether this product has completely been received, or if the application needs
// to be reminded of this purchase at next app launch. Use PurchaseProcessingResult.Pending when still
// saving purchased products to the cloud, and when that save is delayed.
return PurchaseProcessingResult.Complete;
}
//Logs a message to the console telling us when a purchase failed.
public void OnPurchaseFailed(Product product, PurchaseFailureReason failureReason)
{
// A product purchase attempt did not succeed. Check failureReason for more detail. Consider sharing
// this reason with the user to guide their troubleshooting actions.
Debug.Log(string.Format("OnPurchaseFailed: FAIL. Product: '{0}', PurchaseFailureReason: {1}", product.definition.storeSpecificId, failureReason));
displayFooterText(string.Format("OnPurchaseFailed: FAIL. Product: '{0}', PurchaseFailureReason: {1}", product.definition.storeSpecificId, failureReason));
switch (product.definition.storeSpecificId)
{
case "coin.100":
Debug.Log("You DID NOT get 100 coins!");
displayFooterText("You DID NOT get 100 coins!");
break;
case "coin.500":
Debug.Log("You DID NOT get 500 coins!");
displayFooterText("You DID NOT get 500 coins!");
break;
case "coin.1000":
Debug.Log("You DID NOT get 1000 coins!");
displayFooterText("You DID NOT get 1000 coins!");
break;
case "bomb.scatter.10":
Debug.Log("You DID NOT get 10 scatter bombs!");
displayFooterText("You DID NOT get 10 scatter bombs!");
break;
case "bomb.splash.5":
Debug.Log("You DID NOT get 5 splash bombs!");
displayFooterText("You DID NOT get 5 splash bombs!");
break;
case "plane.jumbo":
Debug.Log("You DID NOT get a Jumbo Jet!");
displayFooterText("You DID NOT get a Jumbo Jet!");
break;
default:
Debug.Log("You tried to buy an invalid product Item");
displayFooterText("You tried to buy an invalid product Item");
break;
}
}
void displayFooterText(string txt)
{
footerText.text += txt + "\n";
}
void displayFooterTextClear()
{
footerText.text = "";
}
void displayCoinText(int num)
{
coinText.text = "Coins= " + num.ToString();
}
void displayBombText(string txt, int num)
{
bombText.text = txt + "= " + num.ToString();
}
}