Search Unity

[Solved] NoProductsAvailable on Android

Discussion in 'Unity IAP' started by marcobrei, Jun 22, 2017.

Thread Status:
Not open for further replies.
  1. marcobrei

    marcobrei

    Joined:
    Nov 17, 2016
    Posts:
    4
    I followed the official tutorial (SurvivalShooter_IAP_Demo), then checked with other tutorials online, but can't manage to get IAP working on Android. It works on Unity Editor, but on Android I keep getting failure on initialization with the message "NoProductsAvailable".
    My products are "NonConsumable" (one-time buy) and I configured in Google Play Console as "Managed Products".
    I double checked every item below:
    - Products IDs (all lowercase) in store matches the products IDs in code.
    - Products are active.
    - App is published (beta).
    - App was builded with a key (signed).
    - Google play Merchant Account is active.
    - Installed App from Store, with correct version number.
    - Tested with another google account, not the developer account (not sure if this still matters).
    - Inserted Google License Key from Google Console to Unity Analytics (not sure if that was needed).
    - And also waited several hours.

    Still getting "NoProductsAvailable".

    Using Unity 5.6.0f3

    Any help?

    Edit: Sorry for the weird username, I never noticed it, and don't know how it was created like that. I already opened a support ticket to change it.
     
    Last edited: Jun 22, 2017
  2. ap-unity

    ap-unity

    Unity Technologies

    Joined:
    Aug 3, 2016
    Posts:
    1,519
    @marcobrei

    I assume you mean this tutorial:
    https://unity3d.com/learn/tutorials/topics/analytics/integrating-unity-iap-your-game

    You will also need to follow all of the steps in this guide (though it sounds like you did):
    https://docs.unity3d.com/Manual/UnityIAPGoogleConfiguration.html

    These are all the suggestions I would have made.

    Would you be able to provide some more information?

    * Would you be able to post your purchasing script?
    * What version of the IAP plugin are you using? (You can find it in the Changelog.md file in the /Plugins/UnityPurchasing/)
     
  3. marcobrei

    marcobrei

    Joined:
    Nov 17, 2016
    Posts:
    4
    Thanks for the reply.
    Yes, I used these tutorials.

    IAP Plugin Version [1.11.2] - 2017-05-30

    Here's my purchasing code. The customizations I made referes to: GameManager (main class of my game), PlayerPrefs and using Singleton.

    Code (CSharp):
    1. using System;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.Purchasing;
    5.  
    6.  
    7. public class Purchaser : SingletonMonoBehaviour<Purchaser>, IStoreListener
    8. {
    9.     private IStoreController m_StoreController;          // The Unity Purchasing system.
    10.     private IExtensionProvider m_StoreExtensionProvider; // The store-specific Purchasing subsystems.
    11.  
    12.     public string kProductIDRemoveAds = "com.prettystandardgames.bribe.removeads";
    13.     public string kProductIDDoubleContracts = "com.prettystandardgames.bribe.doublecontracts";
    14.  
    15.     private GameManager _gameManager;
    16.  
    17.     void Awake()
    18.     {
    19.         _gameManager = FindObjectOfType<GameManager>();
    20.     }
    21.  
    22.     void Start()
    23.     {
    24.         if (m_StoreController == null)
    25.         {
    26.             InitializePurchasing();
    27.         }
    28.     }
    29.  
    30.  
    31.     public void InitializePurchasing()
    32.     {
    33.         if (IsInitialized())
    34.         {
    35.             return;
    36.         }
    37.  
    38.         var builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance());
    39.  
    40.         builder.AddProduct(kProductIDRemoveAds, ProductType.NonConsumable);
    41.         builder.AddProduct(kProductIDDoubleContracts, ProductType.NonConsumable);
    42.  
    43.         UnityPurchasing.Initialize(this, builder);
    44.     }
    45.  
    46.  
    47.     private bool IsInitialized()
    48.     {
    49.         return m_StoreController != null && m_StoreExtensionProvider != null;
    50.     }
    51.  
    52.     public void BuyRemoveAds()
    53.     {
    54.         BuyProductID(kProductIDRemoveAds);
    55.     }
    56.     public void BuyDoubleContracts()
    57.     {
    58.         BuyProductID(kProductIDDoubleContracts);
    59.     }
    60.  
    61.     void BuyProductID(string productId)
    62.     {
    63.         if (IsInitialized())
    64.         {
    65.             Product product = m_StoreController.products.WithID(productId);
    66.  
    67.             if (product != null && product.availableToPurchase)
    68.             {
    69.                 _gameManager.Log(string.Format("Purchasing product asychronously: '{0}'", product.definition.id));
    70.                 m_StoreController.InitiatePurchase(product);
    71.             }
    72.             else
    73.             {
    74.                 _gameManager.Log("BuyProductID: FAIL. Not purchasing product, either is not found or is not available for purchase");
    75.             }
    76.         }
    77.         else
    78.         {
    79.             _gameManager.Log("BuyProductID FAIL. Not initialized.");
    80.         }
    81.     }
    82.  
    83.    
    84.     public void OnInitialized(IStoreController controller, IExtensionProvider extensions)
    85.     {
    86.         _gameManager.Log("OnInitialized: PASS");
    87.  
    88.         m_StoreController = controller;
    89.         m_StoreExtensionProvider = extensions;
    90.  
    91.         Product p1 = m_StoreController.products.WithID(kProductIDRemoveAds);
    92.         if (p1 != null && p1.hasReceipt)
    93.         {
    94.             PlayerPrefs.SetInt("removeads", 1);
    95.         }
    96.         else
    97.         {
    98.             PlayerPrefs.SetInt("removeads", 0);
    99.         }
    100.  
    101.         Product p2 = m_StoreController.products.WithID(kProductIDDoubleContracts);
    102.         if (p2 != null && p2.hasReceipt)
    103.         {
    104.             PlayerPrefs.SetInt("doublecontracts", 1);
    105.         }
    106.         else
    107.         {
    108.             PlayerPrefs.SetInt("doublecontracts", 0);
    109.         }
    110.  
    111.         _gameManager.IAPInit = true;
    112.     }
    113.  
    114.  
    115.     public void OnInitializeFailed(InitializationFailureReason error)
    116.     {
    117.         _gameManager.Log("OnInitializeFailed InitializationFailureReason:" + error);
    118.     }
    119.  
    120.  
    121.     public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs args)
    122.     {
    123.  
    124.         if (String.Equals(args.purchasedProduct.definition.id, kProductIDRemoveAds, StringComparison.Ordinal))
    125.         {
    126.             _gameManager.Log(string.Format("ProcessPurchase: PASS. Product: '{0}'", args.purchasedProduct.definition.id));
    127.             PlayerPrefs.SetInt("removeads", 1);
    128.  
    129.         }
    130.         else if (String.Equals(args.purchasedProduct.definition.id, kProductIDDoubleContracts, StringComparison.Ordinal))
    131.         {
    132.             _gameManager.Log(string.Format("ProcessPurchase: PASS. Product: '{0}'", args.purchasedProduct.definition.id));
    133.             PlayerPrefs.SetInt("doublecontracts", 1);
    134.         }
    135.         else
    136.         {
    137.             _gameManager.Log(string.Format("ProcessPurchase: FAIL. Unrecognized product: '{0}'", args.purchasedProduct.definition.id));
    138.         }
    139.  
    140.         return PurchaseProcessingResult.Complete;
    141.     }
    142.  
    143.  
    144.     public void OnPurchaseFailed(Product product, PurchaseFailureReason failureReason)
    145.     {
    146.         _gameManager.Log(string.Format("OnPurchaseFailed: FAIL. Product: '{0}', PurchaseFailureReason: {1}", product.definition.storeSpecificId, failureReason));
    147.     }
    148. }
     
  4. ap-unity

    ap-unity

    Unity Technologies

    Joined:
    Aug 3, 2016
    Posts:
    1,519
    @marcobrei

    Typically, the NoProductsAvailable error occurs when something is misconfigured in the app store, so that's where I would focus my debugging on.

    Your code looks like a pretty standard implementation.

    Would you be able to provide an adb log from your Android device?
     
  5. marcobrei

    marcobrei

    Joined:
    Nov 17, 2016
    Posts:
    4
    Here it is.
    I found interesting to note these lines:
    W/Unity (13528): Unavailable product RemoveAds -RemoveAds
    W/Unity (13528): Unavailable product DoubleContracts -DoubleContracts

    The interesting thing about it is that these names were used in an older version of the game. I first named the products as "RemoveAds" and "DoubleContracts", then realizing uppercase cannot be used in product ID's, and learning the format convention, changed them to: com.prettystandardgames.bribe.removeads and com.prettystandardgames.bribe.doublecontracts (as you can see in my code above).

    I am 100% sure the old names are not present in the code anymore (searched in the whole project, not only in the purchaser class). And I'm 100% sure the version I'm testing in the phone is the apk generated with this code. Unless the unity build or android publishing process let something behind...

    Any ideas?

    EDIT: Oh my god, I found it! I made a minor "irrelevant" change in the purchaser class (variable name) to force it to be included in the build. Now the IAP is being initialized with success. I don't know exactly how the unity build process works, but it seems that changing only a constant value in a string it's not enough to make the unity recognize it has been changed. Another theory (even more weird) is that the variable name store some trash value from previous version... Either way, it seems a bug, didn't it?




    Code (CSharp):
    1. --------- beginning of main
    2.  
    3. D/SensorService(  746): [SO] 0.115 0.077 9.558
    4.  
    5. I/InputReader(  746): Touch event's action is 0x0 (deviceType=0) [pCnt=1, s=0.5296 ] when=23648964285000
    6.  
    7. D/InputReader(  746): lastThreadEndTime = 23646164877929, currentThreadStartTime = 23646164877929, diff = 0
    8.  
    9. I/InputDispatcher(  746): Delivering touch to (13528): action: 0x0, toolType: 1
    10.  
    11. D/ViewRootImpl(13528): ViewPostImeInputStage ACTION_DOWN
    12.  
    13. I/InputReader(  746): Touch event's action is 0x1 (deviceType=0) [pCnt=1, s=] when=23649074088000
    14.  
    15. I/InputDispatcher(  746): Delivering touch to (13528): action: 0x1, toolType: 1
    16.  
    17. I/UnityIAP(13528): IAB helper created.
    18.  
    19. I/UnityIAP(13528): Starting in-app billing setup.
    20.  
    21. I/UnityIAP(13528): Billing service connected.
    22.  
    23. D/NetworkPolicy(  746): updateRulesForUidStateChangeLocked, uid: 10017, newUidState : 2
    24.  
    25. I/UnityIAP(13528): invoking callback
    26.  
    27. I/UnityIAP(13528): Checking for in-app billing 3 support.
    28.  
    29. I/Finsky  (12597): [2864] com.google.android.finsky.billing.iab.z.b(29): com.PrettyStandardGames.BRibe: Account determined from installer data - [OfP633vEU7paRX9sJQZZ_CjJzf8]
    30.  
    31. I/UnityIAP(13528): In-app billing version 3 supported for com.PrettyStandardGames.BRibe
    32.  
    33. I/Finsky  (12597): [2898] com.google.android.finsky.billing.iab.z.b(29): com.PrettyStandardGames.BRibe: Account determined from installer data - [OfP633vEU7paRX9sJQZZ_CjJzf8]
    34.  
    35. I/UnityIAP(13528): Subscriptions AVAILABLE.
    36.  
    37. I/Finsky  (12597): [2865] com.google.android.finsky.billing.iab.z.b(29): com.PrettyStandardGames.BRibe: Account determined from installer data - [OfP633vEU7paRX9sJQZZ_CjJzf8]
    38.  
    39. I/UnityIAP(13528): VR supported.
    40.  
    41. I/UnityIAP(13528): onIabSetupFinished: 0
    42.  
    43. I/UnityIAP(13528): Requesting 2 products
    44.  
    45. I/UnityIAP(13528): QueryInventory: 2
    46.  
    47. I/UnityIAP(13528): invoking callback
    48.  
    49. I/UnityIAP(13528): Querying owned items, item type: inapp
    50.  
    51. I/UnityIAP(13528): Package name: com.PrettyStandardGames.BRibe
    52.  
    53. I/UnityIAP(13528): Calling getPurchases with continuation token: null
    54.  
    55. I/Finsky  (12597): [2864] com.google.android.finsky.billing.iab.z.b(29): com.PrettyStandardGames.BRibe: Account determined from installer data - [OfP633vEU7paRX9sJQZZ_CjJzf8]
    56.  
    57. I/UnityIAP(13528): Owned items response: 0
    58.  
    59. I/UnityIAP(13528): Continuation token: null
    60.  
    61. I/UnityIAP(13528): Querying SKU details.
    62.  
    63. I/Finsky  (12597): [2898] com.google.android.finsky.billing.iab.z.b(29): com.PrettyStandardGames.BRibe: Account determined from installer data - [OfP633vEU7paRX9sJQZZ_CjJzf8]
    64.  
    65. I/UnityIAP(13528): Querying owned items, item type: subs
    66.  
    67. I/UnityIAP(13528): Package name: com.PrettyStandardGames.BRibe
    68.  
    69. I/UnityIAP(13528): Calling getPurchases with continuation token: null
    70.  
    71. I/Finsky  (12597): [2865] com.google.android.finsky.billing.iab.z.b(29): com.PrettyStandardGames.BRibe: Account determined from installer data - [OfP633vEU7paRX9sJQZZ_CjJzf8]
    72.  
    73. I/UnityIAP(13528): Owned items response: 0
    74.  
    75. I/UnityIAP(13528): Continuation token: null
    76.  
    77. I/UnityIAP(13528): Querying SKU details.
    78.  
    79. I/Finsky  (12597): [2864] com.google.android.finsky.billing.iab.z.b(29): com.PrettyStandardGames.BRibe: Account determined from installer data - [OfP633vEU7paRX9sJQZZ_CjJzf8]
    80.  
    81. I/UnityIAP(13528): onQueryInventoryFinished: true
    82.  
    83. I/UnityIAP(13528): Inventory refresh successful. (response: 0:OK)
    84.  
    85. W/Unity   (13528): Unavailable product RemoveAds -RemoveAds
    86.  
    87. W/Unity   (13528):
    88.  
    89. W/Unity   (13528): (Filename: ./artifacts/generated/common/runtime/DebugBindings.gen.cpp Line: 51)
    90.  
    91. W/Unity   (13528):
    92.  
    93. W/Unity   (13528): Unavailable product DoubleContracts -DoubleContracts
    94.  
    95. W/Unity   (13528):
    96.  
    97. W/Unity   (13528): (Filename: ./artifacts/generated/common/runtime/DebugBindings.gen.cpp Line: 51)
    98.  
    99. W/Unity   (13528):
    100.  
    101. --------- beginning of system
    102.  
    103. D/StatusBar.MSimNetworkController(  881): refreshViews connected={ wifi } phoneId = 0 level=4 mMSimcombinedSignalIconId=0x7f02067b/com.android.systemui:drawable/stat_sys_wifi_signal_4 mMSimcombinedActivityIconId=0x7f02062a mAirplaneMode=false mMSimDataActivity=0 mMSimPhoneSignalIconId=0x7f0205f4/com.android.systemui:drawable/stat_sys_signal_4_auto_rotate mMSimDataDirectionIconId=0x0 mMSimDataSignalIconId=0x7f0205f4 mMSimDataTypeIconId=0x0/(null) mNoMSimIconId=0x0/(null) mMSimMobileActivityIconId=0x0/(null) mWifiIconId=0x7f02067b mBluetoothTetherIconId=0x7f020650 mRoamingIconId=0x 0/(null)
    104.  
    105. D/StatusBar.MSimNetworkController(  881): refreshSignalCluster : called
    106.  
    107. D/WCND    (  173): is_cp2_alive_ok: open polling interface: /proc/mdbg/loopcheck, fd = 14
    108.  
    109. I/InputReader(  746): Touch event's action is 0x0 (deviceType=0) [pCnt=1, s=0.5297 ] when=23650636954000
    110.  
    111. D/InputReader(  746): lastThreadEndTime = 23649074362792, currentThreadStartTime = 23649074362792, diff = 0
    112.  
    113. I/InputDispatcher(  746): Delivering touch to (13528): action: 0x0, toolType: 1
    114.  
    115. D/ViewRootImpl(13528): ViewPostImeInputStage ACTION_DOWN
    116.  
    117. D/WCND    (  173): is_cp2_alive_ok: loop: /proc/mdbg/loopcheck is OK
    118.  
    119. I/InputReader(  746): Touch event's action is 0x1 (deviceType=0) [pCnt=1, s=] when=23650735220000
    120.  
    121. I/InputDispatcher(  746): Delivering touch to (13528): action: 0x1, toolType: 1
    122.  
    123. D/StatusBar.MSimNetworkController(  881): refreshViews connected={ wifi } phoneId = 0 level=4 mMSimcombinedSignalIconId=0x7f02067b/com.android.systemui:drawable/stat_sys_wifi_signal_4 mMSimcombinedActivityIconId=0x7f020625 mAirplaneMode=false mMSimDataActivity=0 mMSimPhoneSignalIconId=0x7f0205f4/com.android.systemui:drawable/stat_sys_signal_4_auto_rotate mMSimDataDirectionIconId=0x0 mMSimDataSignalIconId=0x7f0205f4 mMSimDataTypeIconId=0x0/(null) mNoMSimIconId=0x0/(null) mMSimMobileActivityIconId=0x0/(null) mWifiIconId=0x7f02067b mBluetoothTetherIconId=0x7f020650 mRoamingIconId=0x 0/(null)
    124.  
    125. D/StatusBar.MSimNetworkController(  881): refreshSignalCluster : called
    126.  
    127. D/ENGPC   (  213): parse_event: event { 'change', '/devices/sec-battery.4/power_supply/battery', 'power_supply', '' }
    128.  
    129. D/BatteryService(  746): !@BatteryListener : batteryPropertiesChanged!
    130.  
    131. D/BatteryService(  746): level:57, scale:100, status:2, health:2, present:true, voltage: 3892, temperature: 256, technology: Li-ion, AC powered:false, USB powered:true, POGO powered:false, Wireless powered:false, icon:17303411, invalid charger:0
    132.  
    133. D/BatteryService(  746): online:4, current avg:91, charge type:1, power sharing:false, high voltage charger:false, capacity:280000, batterySWSelfDischarging:false, current_now:91
    134.  
    135. D/BatteryService(  746): Sending ACTION_BATTERY_CHANGED.
    136.  
    137. I/MotionRecognitionService(  746): Plugged
    138.  
    139. D/MotionRecognitionService(  746):   cableConnection= 1
    140.  
    141. D/MotionRecognitionService(  746): setPowerConnected | current backoffstate  = 1024 , state =1024
    142.  
    143. D/MotionRecognitionService(  746): skip setTransmitPower.
    144.  
    145. D/KeyguardUpdateMonitor(  881): received broadcast android.intent.action.BATTERY_CHANGED
    146.  
    147. I/PERF    (  881): received broadcast android.intent.action.BATTERY_CHANGED
    148.  
    149. D/KeyguardUpdateMonitor(  881): handleBatteryUpdate
    150.  
    151. D/PhoneStatusBar(  881): ACTION_BATTERY_CHANGED received - mBatteryLevel = 57
    152.  
    153. V/LpnetManagerService(  746): on USB power
    154.  
    155. D/QSTile.TorchLightTile(  881): ACTION_BATTERY_CHANGED - Level :: 57, Status :: 2
    156.  
    157. D/BatteryMeterView(  881): ACTION_BATTERY_CHANGED : level:57 status:2 health:2
    158.  
    159. D/BatteryMeterView(  881): ACTION_BATTERY_CHANGED : level:57 status:2 health:2
    160.  
    161. D/BatteryMeterView(  881): ACTION_BATTERY_CHANGED : level:57 status:2 health:2
    162.  
    163. D/StatusBar.MSimNetworkController(  881): refreshViews connected={ wifi } phoneId = 0 level=4 mMSimcombinedSignalIconId=0x7f02067b/com.android.systemui:drawable/stat_sys_wifi_signal_4 mMSimcombinedActivityIconId=0x7f02062a mAirplaneMode=false mMSimDataActivity=0 mMSimPhoneSignalIconId=0x7f0205f4/com.android.systemui:drawable/stat_sys_signal_4_auto_rotate mMSimDataDirectionIconId=0x0 mMSimDataSignalIconId=0x7f0205f4 mMSimDataTypeIconId=0x0/(null) mNoMSimIconId=0x0/(null) mMSimMobileActivityIconId=0x0/(null) mWifiIconId=0x7f02067b mBluetoothTetherIconId=0x7f020650 mRoamingIconId=0x 0/(null)
    164.  
    165. D/StatusBar.MSimNetworkController(  881): refreshSignalCluster : called
    166.  
    167. D/SSRM:n  (  746): SIOP:: AP = 370, CUR = 91, LCD = 157
    168.  
    169. D/StatusBar.MSimNetworkController(  881): refreshViews connected={ wifi } phoneId = 0 level=4 mMSimcombinedSignalIconId=0x7f02067b/com.android.systemui:drawable/stat_sys_wifi_signal_4 mMSimcombinedActivityIconId=0x7f020625 mAirplaneMode=false mMSimDataActivity=0 mMSimPhoneSignalIconId=0x7f0205f4/com.android.systemui:drawable/stat_sys_signal_4_auto_rotate mMSimDataDirectionIconId=0x0 mMSimDataSignalIconId=0x7f0205f4 mMSimDataTypeIconId=0x0/(null) mNoMSimIconId=0x0/(null) mMSimMobileActivityIconId=0x0/(null) mWifiIconId=0x7f02067b mBluetoothTetherIconId=0x7f020650 mRoamingIconId=0x 0/(null)
    170.  
    171. D/StatusBar.MSimNetworkController(  881): refreshSignalCluster : called
    172.  
    173. D/StatusBar.MSimNetworkController(  881): refreshViews connected={ wifi } phoneId = 0 level=4 mMSimcombinedSignalIconId=0x7f02067b/com.android.systemui:drawable/stat_sys_wifi_signal_4 mMSimcombinedActivityIconId=0x7f020627 mAirplaneMode=false mMSimDataActivity=0 mMSimPhoneSignalIconId=0x7f0205f4/com.android.systemui:drawable/stat_sys_signal_4_auto_rotate mMSimDataDirectionIconId=0x0 mMSimDataSignalIconId=0x7f0205f4 mMSimDataTypeIconId=0x0/(null) mNoMSimIconId=0x0/(null) mMSimMobileActivityIconId=0x0/(null) mWifiIconId=0x7f02067b mBluetoothTetherIconId=0x7f020650 mRoamingIconId=0x 0/(null)
    174.  
    175. D/StatusBar.MSimNetworkController(  881): refreshSignalCluster : called
     
    Last edited: Jun 23, 2017
  6. ap-unity

    ap-unity

    Unity Technologies

    Joined:
    Aug 3, 2016
    Posts:
    1,519
    @marcobrei

    I'm glad you got it working!

    Yeah, that sounds like odd behavior. If you can make a repro case, you should submit it as a bug report:
    https://unity3d.com/unity/qa/bug-reporting
     
  7. marcobrei

    marcobrei

    Joined:
    Nov 17, 2016
    Posts:
    4
    Ooops... The bug was not in unity, was in my brain.
    I was playing with the Purchaser code and didn't notice the variable that stores the ID's are PUBLIC (despite having a default value).
    What happened was that I changed the default value AFTER attached the purchaser code to a gameobject. So the gameobject keeped the older value.
    That's a reason why I think using default values for public variables a bad practice, but anyway, I should have noticed it.

    Finally, @ap-unity thanks for the help. In the quest to fix this problem I learned a lot.
    (And hey, look at my username, now a pretty standard one!)
     
    PoweredOnSoftware and ap-unity like this.
Thread Status:
Not open for further replies.