title: How to get Unity IAP work with Google Play IAB?
body: <p>I have complete all steps from Unity IAP Tutorial.</p>
  <p><a href="https://i.stack.imgur.com/VeyOM.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/VeyOM.png" alt="Unity IAP Setup" /></a></p>
  <p>But my game is still being review in Google Play Console. I know I don't need to wait for review under internal testing. But all informations I can get from internet tell me that, at least, I need to put my app in Alpha state. If I am wrong, please let me know.</p>
  <p><a href="https://i.stack.imgur.com/MO57b.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/MO57b.png" alt="Alpha Test" /></a></p>
  <p>And my code is like this:</p>
  <pre class="lang-cs prettyprint-override"><code>using System;
  using System.Collections.Generic;
  using UnityEngine;
  using UnityEngine.Purchasing;
  using Veewo.Systems.Base;
  
  // Placing the Purchaser class in the CompleteProject namespace allows it to interact with ScoreManager, 
  // one of the existing Survival Shooter scripts.
  namespace Veewo.Systems
  {
      // Deriving the Purchaser class from IStoreListener enables it to receive messages from Unity Purchasing.
      public class IAPSystem : SystemBase, IStoreListener
      {
          private static IStoreController m_StoreController;          // The Unity Purchasing system.
          private static IExtensionProvider m_StoreExtensionProvider; // The store-specific Purchasing subsystems.
  
          // Product identifiers for all products capable of being purchased: 
          // &quot;convenience&quot; general identifiers for use with Purchasing, and their store-specific identifier 
          // counterparts for use with and outside of Unity Purchasing. Define store-specific identifiers 
          // also on each platform's publisher dashboard (iTunes Connect, Google Play Developer Console, etc.)
  
          // General product identifiers for the consumable, non-consumable, and subscription products.
          // Use these handles in the code to reference which product to purchase. Also use these values 
          // when defining the Product Identifiers on the store. Except, for illustration purposes, the 
          // kProductIDSubscription - it has custom Apple and Google identifiers. We declare their store-
          // specific mapping to Unity Purchasing's AddProduct, below.
          public static string kProductIDConsumable =    &quot;veewo.pi.diamond600&quot;;   
          public static string kProductIDNonConsumable = &quot;veewo.pi.limited.goldheropiece&quot;;
          public static string kProductIDSubscription =  &quot;subscription&quot;; 
  
          // Apple App Store-specific product identifier for the subscription product.
          private static string kProductNameAppleSubscription =  &quot;com.unity3d.subscription.new&quot;;
  
          // Google Play Store-specific product identifier subscription product.
          private static string kProductNameGooglePlaySubscription =  &quot;com.unity3d.subscription.original&quot;;
  
          protected override void OnInitialize()
          {
              base.OnInitialize();
              // If we haven't set up the Unity Purchasing reference
              if (m_StoreController == null)
              {
                  // Begin to configure our connection to Purchasing
                  InitializePurchasing();
              }
          }
  
          public void InitializePurchasing() 
          {
              Debug.Log(&quot;InitializePurchasing&quot;);
              // 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.
              ConfigurationBuilder builder;
  //            if (Application.platform == RuntimePlatform.Android
  //                &amp;&amp; StandardPurchasingModule.Instance().appStore == AppStore.GooglePlay)
  //            {
  //                builder = ConfigurationBuilder.Instance(
  //                    Google.Play.Billing.GooglePlayStoreModule.Instance());
  //            }
  //            else
  //            {
                  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.
              builder.AddProduct(kProductIDConsumable, ProductType.Consumable);
              // Continue adding the non-consumable product.
              builder.AddProduct(kProductIDNonConsumable, ProductType.NonConsumable);
              // And finish adding the subscription product. Notice this uses store-specific IDs, illustrating
              // if the Product ID was configured differently between Apple and Google stores. Also note that
              // one uses the general kProductIDSubscription handle inside the game - the store-specific IDs 
              // must only be referenced here. 
  //            builder.AddProduct(kProductIDSubscription, ProductType.Subscription, new IDs(){
  //                { kProductNameAppleSubscription, AppleAppStore.Name },
  //                { kProductNameGooglePlaySubscription, GooglePlay.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);
              Debug.Log(&quot;UnityPurchasing.Initialize&quot;);
          }
  
  
          private bool IsInitialized()
          {
              // Only say we are initialized if both the Purchasing references are set.
              return m_StoreController != null &amp;&amp; m_StoreExtensionProvider != null;
          }
  
  
          public void BuyConsumable()
          {
              // Buy the consumable product using its general identifier. Expect a response either 
              // through ProcessPurchase or OnPurchaseFailed asynchronously.
              BuyProductID(kProductIDConsumable);
          }
  
  
          public void BuyNonConsumable()
          {
              // Buy the non-consumable product using its general identifier. Expect a response either 
              // through ProcessPurchase or OnPurchaseFailed asynchronously.
              BuyProductID(kProductIDNonConsumable);
          }
  
  
          public void BuySubscription()
          {
              // Buy the subscription product using its the general identifier. Expect a response either 
              // through ProcessPurchase or OnPurchaseFailed asynchronously.
              // Notice how we use the general product identifier in spite of this ID being mapped to
              // custom store-specific identifiers above.
              BuyProductID(kProductIDSubscription);
          }
  
  
          void BuyProductID(string productId)
          {
              // If Purchasing has been initialized ...
              if (IsInitialized())
              {
                  // ... 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 &amp;&amp; product.availableToPurchase)
                  {
                      Debug.Log(string.Format(&quot;Purchasing product asychronously: '{0}'&quot;, 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(&quot;BuyProductID: FAIL. Not purchasing product, either is not found or is not available for purchase&quot;);
                  }
              }
              // Otherwise ...
              else
              {
                  // ... report the fact Purchasing has not succeeded initializing yet. Consider waiting longer or 
                  // retrying initiailization.
                  Debug.Log(&quot;BuyProductID FAIL. Not initialized.&quot;);
              }
          }
  
  
          // 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(&quot;RestorePurchases FAIL. Not initialized.&quot;);
                  return;
              }
  
              // If we are running on an Apple device ... 
              if (Application.platform == RuntimePlatform.IPhonePlayer || 
                  Application.platform == RuntimePlatform.OSXPlayer)
              {
                  // ... begin restoring purchases
                  Debug.Log(&quot;RestorePurchases started ...&quot;);
  
                  // Fetch the Apple store-specific subsystem.
                  var apple = m_StoreExtensionProvider.GetExtension&lt;IAppleExtensions&gt;();
                  // Begin the asynchronous process of restoring purchases. Expect a confirmation response in 
                  // the Action&lt;bool&gt; below, and ProcessPurchase if there are previously purchased products to restore.
                  apple.RestoreTransactions((result) =&gt; {
                      // The first phase of restoration. If no more responses are received on ProcessPurchase then 
                      // no purchases are available to be restored.
                      Debug.Log(&quot;RestorePurchases continuing: &quot; + result + &quot;. If no further messages, no purchases available to restore.&quot;);
                  });
              }
              // Otherwise ...
              else
              {
                  // We are not running on an Apple device. No work is necessary to restore purchases.
                  Debug.Log(&quot;RestorePurchases FAIL. Not supported on this platform. Current = &quot; + Application.platform);
              }
          }
  
  
          //  
          // --- IStoreListener
          //
  
          public void OnInitialized(IStoreController controller, IExtensionProvider extensions)
          {
              // Purchasing has succeeded initializing. Collect our Purchasing references.
              Debug.Log(&quot;OnInitialized: PASS&quot;);
  
              // Overall Purchasing system, configured with products for this application.
              m_StoreController = controller;
              // Store specific subsystem, for accessing device-specific store features.
              m_StoreExtensionProvider = extensions;
          }
  
  
          public void OnInitializeFailed(InitializationFailureReason error)
          {
              // Purchasing set-up has not succeeded. Check error for reason. Consider sharing this reason with the user.
              Debug.Log(&quot;OnInitializeFailed InitializationFailureReason:&quot; + error);
          }
  
  
          public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs args) 
          {
              // A consumable product has been purchased by this user.
              if (String.Equals(args.purchasedProduct.definition.id, kProductIDConsumable, StringComparison.Ordinal))
              {
                  Debug.Log(string.Format(&quot;ProcessPurchase: PASS. Product: '{0}'&quot;, args.purchasedProduct.definition.id));
                  // The consumable item has been successfully purchased, add 100 coins to the player's in-game score.
                  //TODO:
              }
              // Or ... a non-consumable product has been purchased by this user.
              else if (String.Equals(args.purchasedProduct.definition.id, kProductIDNonConsumable, StringComparison.Ordinal))
              {
                  Debug.Log(string.Format(&quot;ProcessPurchase: PASS. Product: '{0}'&quot;, args.purchasedProduct.definition.id));
                  // TODO: The non-consumable item has been successfully purchased, grant this item to the player.
              }
              // Or ... a subscription product has been purchased by this user.
              else if (String.Equals(args.purchasedProduct.definition.id, kProductIDSubscription, StringComparison.Ordinal))
              {
                  Debug.Log(string.Format(&quot;ProcessPurchase: PASS. Product: '{0}'&quot;, args.purchasedProduct.definition.id));
                  // TODO: The subscription item has been successfully purchased, grant this to the player.
              }
              // Or ... an unknown product has been purchased by this user. Fill in additional products here....
              else 
              {
                  Debug.Log(string.Format(&quot;ProcessPurchase: FAIL. Unrecognized product: '{0}'&quot;, 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;
          }
  
  
          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(&quot;OnPurchaseFailed: FAIL. Product: '{0}', PurchaseFailureReason: {1}&quot;, product.definition.storeSpecificId, failureReason));
          }
  
          public IEnumerable&lt;Product&gt; GetAllProducts()
          {
              if (!IsInitialized()) yield break;
  
              foreach (var product in m_StoreController.products.set)
              {
                  yield return product;
              }
          }
      }
  }
  </code></pre>
  <p>I am quite sure that product key is correct.</p>
  <p><a href="https://i.stack.imgur.com/0QofA.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/0QofA.png" alt="Product keys" /></a></p>
  <p>But I still can't get this work. I tested in BlueStack4 and Android9 mobile, all failed. Can't get <code>UnityPurchasing.Initialize</code> done with <code>OnInitialized</code> called.</p>
  <p>Logs in bluestack are:</p>
  <pre><code>10-29 09:51:17.193  5183  5203 I Unity   : UnityIAP Version: 2.1.1
  10-29 09:51:17.219  5183  5203 I UnityIAP: Starting in-app billing setup.
  10-29 09:51:17.221  5183  5183 I UnityIAP: Billing service connected.
  10-29 09:51:17.222  5183  5203 I Unity   : UnityPurchasing.Initialize
  10-29 09:51:17.224  5183  5238 I UnityIAP: Billing Service Manager invoking callback 1 of 1
  10-29 09:51:17.224  5183  5238 I UnityIAP: Checking for in-app billing 3 support.
  10-29 09:51:17.224  4671  4683 W Finsky  : [291] iec.a(16): com.veewo.alienland: No account found.
  10-29 09:51:17.226  5183  5238 D UnityIAP: onIabSetupFinished: 3
  10-29 09:51:17.226  5183  5238 I UnityIAP: Failed to setup IAB. Notifying Unity...
  10-29 09:51:17.276  5183  5203 I Unity   : OnInitializeFailed InitializationFailureReason:PurchasingUnavailable
  10-29 09:51:17.276  5183  5203 I Unity   : UnityEngine.Logger:Log(LogType, Object)
  10-29 09:51:17.276  5183  5203 I Unity   : Veewo.Systems.IAPSystem:OnInitializeFailed(InitializationFailureReason)
  10-29 09:51:17.276  5183  5203 I Unity   : UnityEngine.Purchasing.Extension.UnityUtil:Update()
  10-29 09:51:17.276  5183  5203 I Unity   :
  10-29 09:51:17.276  5183  5203 I Unity   : (Filename: ./Runtime/Export/Debug.bindings.h Line: 45)
  10-29 09:51:17.276  5183  5203 I Unity   :
  10-29 09:51:30.322  1962  3360 I NetworkScheduler.Stats: Task com.google.android.gms/com.google.android.gms.auth.account.be.legacy.AuthCronService started execution. cause:4 exec_start_elapsed_seconds: 1056 [CONTEXT service_id=218 ]
  10-29 09:51:32.652  4671  4707 E Finsky  : [314] log.b(1): Error while refreshing device settings: network time: 32342, HTTP status code: na, exception com.android.volley.TimeoutError. Retrying.
  10-29 09:51:32.652  4671  4707 E Finsky  : network time: 32342, HTTP status code: na, exception com.android.volley.TimeoutError
  10-29 09:51:32.652  4671  4707 E Finsky  :      at vqf.hq(PG)
  10-29 09:51:32.652  4671  4707 E Finsky  :      at czn.r(PG:2)
  10-29 09:51:32.652  4671  4707 E Finsky  :      at exo.r(PG:3)
  10-29 09:51:32.652  4671  4707 E Finsky  :      at czg.run(PG:3)
  10-29 09:51:32.652  4671  4707 E Finsky  :      at android.os.Handler.handleCallback(Handler.java:751)
  10-29 09:51:32.652  4671  4707 E Finsky  :      at android.os.Handler.dispatchMessage(Handler.java:95)
  10-29 09:51:32.652  4671  4707 E Finsky  :      at android.os.Looper.loop(Looper.java:154)
  10-29 09:51:32.652  4671  4707 E Finsky  :      at android.app.ActivityThread.main(ActivityThread.java:6138)
  10-29 09:51:32.652  4671  4707 E Finsky  :      at java.lang.reflect.Method.invoke(Native Method)
  10-29 09:51:32.652  4671  4707 E Finsky  :      at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:893)
  10-29 09:51:32.652  4671  4707 E Finsky  :      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:783)
  10-29 09:51:32.653  4671  4704 E Finsky  : [311] lon.b(-1): Error getting device settings.
  10-29 09:51:32.653  4671  4704 E Finsky  : network time: 32342, HTTP status code: na, exception com.android.volley.TimeoutError
  10-29 09:51:32.653  4671  4704 E Finsky  :      at vqf.hq(PG)
  10-29 09:51:32.653  4671  4704 E Finsky  :      at czn.r(PG:2)
  10-29 09:51:32.653  4671  4704 E Finsky  :      at exo.r(PG:3)
  10-29 09:51:32.653  4671  4704 E Finsky  :      at czg.run(PG:3)
  10-29 09:51:32.653  4671  4704 E Finsky  :      at android.os.Handler.handleCallback(Handler.java:751)
  10-29 09:51:32.653  4671  4704 E Finsky  :      at android.os.Handler.dispatchMessage(Handler.java:95)
  10-29 09:51:32.653  4671  4704 E Finsky  :      at android.os.Looper.loop(Looper.java:154)
  10-29 09:51:32.653  4671  4704 E Finsky  :      at android.app.ActivityThread.main(ActivityThread.java:6138)
  10-29 09:51:32.653  4671  4704 E Finsky  :      at java.lang.reflect.Method.invoke(Native Method)
  10-29 09:51:32.653  4671  4704 E Finsky  :      at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:893)
  10-29 09:51:32.653  4671  4704 E Finsky  :      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:783)
  10-29 09:51:32.653  4671  4707 I Finsky  : [314] abij.n(4): SCH: jobFinished: 31-305419896. TimeElapsed: 32344ms.
  10-29 09:51:32.653  4671  4671 I Finsky  : [1] abgy.c(7): SCH: Job 31-305419896 finished. Rescheduling.
  10-29 09:51:32.653  4671  4671 I Finsky  : [1] abgx.handleMessage(60): SCH: RunningQueue size: 0, PendingQueue size: 0
  10-29 09:51:32.654  4671  4671 I Finsky  : [1] abgx.handleMessage(26): SCH: Executor finished
  10-29 09:51:32.658  4671  4671 I Finsky  : [1] abjk.b(5): SCH: Jobs in database: 1-1337 24-77777777 26-1414141414 31-305419896
  10-29 09:51:32.659  4671  4671 I Finsky  : [1] abhw.d(37): SCH: ConstraintMapping: 24-77777777,  -&gt; L: 0ms, D: 24834708ms, C: false, I: true, N: 0
  10-29 09:51:32.660  4671  4671 I Finsky  : [1] abhw.d(37): SCH: ConstraintMapping: 1-1337, 31-305419896,  -&gt; L: 127994ms, D: 32437706ms, C: false, I: false, N: 1
  10-29 09:51:32.660  4671  4671 I Finsky  : [1] abhw.d(37): SCH: ConstraintMapping: 26-1414141414,  -&gt; L: 29458222ms, D: 30358222ms, C: false, I: false, N: 0
  10-29 09:51:32.662  4671  4671 I Finsky  : [1] abhw.f(7): SCH: Cancelling existing jobscheduler jobs: 9000 9005
  10-29 09:51:32.662  4671  4671 I Finsky  : [1] abhw.e(23): SCH: Scheduling job Id: 9001, L: 0, D: 24834708, C: false, I: true, N: 0
  10-29 09:51:32.663  4671  4671 I Finsky  : [1] abhw.e(23): SCH: Scheduling job Id: 9002, L: 127994, D: 32437706, C: false, I: false, N: 1
  10-29 09:51:32.664  4671  4671 I Finsky  : [1] abhw.e(23): SCH: Scheduling job Id: 9003, L: 29458222, D: 30358222, C: false, I: false, N: 0
  
  </code></pre>
  <p>It is been 4 days that I can't get this work. All my guess are:</p>
  <ol>
  <li>Network problem, I am in China, and can't get connect to Google Service, although I use VPN, but it is not stable.</li>
  <li>I don't setup Unity IAP correct</li>
  <li>I need to wait for Google Play Console review finished, and wait for its state become published.</li>
  <li>I don't use correct Google Account login in my phone. I use the same account to google play account, and I already add it to test account list, should I use a new account?</li>
  </ol>
  <p>I am so confused now, and don't know what to do next. Please help me.</p>