Search Unity

[Solved] Migrating existing app with IAP to Unity IAP

Discussion in 'Unity IAP' started by rxmarccall, Dec 17, 2015.

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

    rxmarccall

    Joined:
    Oct 13, 2011
    Posts:
    353
    I have an app that has been in the app stores for years now that already has IAP implemented. (I built it in Corona SDK years ago). I'm now in the process of rebuilding the app in Unity and am attempting to get all set up with the new IAP service and have a few questions....

    - How do I go about testing Unity IAP with my already existing Android IAP Non-Consumable items?
    - Does the fact that my app is already live on the store change the approach taken to testing IAP?
    - Do I need to upload a new version of the app (My Unity build) to the android developer portal? Would I increment the bundle version?

    Maybe a guide to implement Unity IAP with existing live apps would be helpful for the community?
     
  2. mpinol

    mpinol

    Joined:
    Jul 29, 2015
    Posts:
    317
    Hi @rxmarccall,

    1. The Google Play Store guide posted here, http://forum.unity3d.com/threads/unity-iap-store-guides-google-play-apple-app-store.372647/ should be helpful. If you get stuck go ahead and ask us some more questions!

    2. Nope

    3. Yes you would need to upload a new version of the app to the Android developer portal and increment your bundle version, https://support.google.com/googleplay/android-developer/answer/113476?hl=en

    Thank you for the suggestion! Until we have created this a workflow would be to follow the IAP-store-guides as linked above, and then after testing and when deploying use the android-developer link to ensure a new version code is used.
     
  3. rxmarccall

    rxmarccall

    Joined:
    Oct 13, 2011
    Posts:
    353
    @mpinol
    Thanks for the response, will give it a try tonight and post my results!
     
  4. rxmarccall

    rxmarccall

    Joined:
    Oct 13, 2011
    Posts:
    353
    @mpinol
    I can't seem to get past a hang up with initialization.... When I run my project on windows I get the "Initialization : PASS" message, but not on my android device. When I attempt a purchase I get: "BuyProductID FAIL. Not initialized.".

    I also get the message for each product ID I've tried to add "ProcessPurchase FAIL. Unrecognized product: 'productNameHere'"

    What am I doing wrong?
    Here is my IAP script: (Note: I've verified productID.key holds a matching string to the IAP ID I have on the google play store)
    Code (CSharp):
    1. using System;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.Purchasing;
    5.  
    6. // Deriving the Purchaser class from IStoreListener enables it to receive messages from Unity Purchasing.
    7. public class Purchaser : MonoBehaviour, IStoreListener
    8. {
    9.     private static IStoreController m_StoreController;                                                                  // Reference to the Purchasing system.
    10.     private static IExtensionProvider m_StoreExtensionProvider;                                                         // Reference to store-specific Purchasing subsystems.
    11.  
    12.     // Product identifiers for all products capable of being purchased: "convenience" general identifiers for use with Purchasing, and their store-specific identifier counterparts
    13.     // 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.)
    14.  
    15.  
    16.     private static string kProductIDConsumable =    "consumable";                                                         // General handle for the consumable product.
    17.     private static string kProductIDNonConsumable = "nonconsumable";                                                  // General handle for the non-consumable product.
    18.     private static string kProductIDSubscription =  "subscription";                                                   // General handle for the subscription product.
    19.  
    20.     void Start()
    21.     {
    22.      
    23.         // If we haven't set up the Unity Purchasing reference
    24.         if (m_StoreController == null)
    25.         {
    26.             // Begin to configure our connection to Purchasing
    27.             InitializePurchasing();
    28.         }
    29.     }
    30.  
    31.     public void InitializePurchasing()
    32.     {
    33.         // If we have already connected to Purchasing ...
    34.         if (IsInitialized())
    35.         {
    36.             // ... we are done here.
    37.             return;
    38.         }
    39.  
    40.         // Create a builder, first passing in a suite of Unity provided stores.
    41.         var builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance());
    42.  
    43.         // Add a product to sell / restore by way of its identifier, associating the general identifier with its store-specific identifiers.
    44.         // Cycle through list of product ID's and add products
    45.         foreach (var productID in gamestate.Instance.iapProducts){
    46.             //builder.AddProduct(productID.Key, ProductType.NonConsumable, new IDs(){{ productID.Key, AppleAppStore.Name }, { productID.Key, GooglePlay.Name },});
    47.             builder.AddProduct(productID.Key, ProductType.NonConsumable);
    48.         }
    49.  
    50.         UnityPurchasing.Initialize(this, builder);
    51.     }
    52.  
    53.  
    54.     private bool IsInitialized()
    55.     {
    56.         // Only say we are initialized if both the Purchasing references are set.
    57.         return m_StoreController != null && m_StoreExtensionProvider != null;
    58.     }
    59.  
    60.  
    61.     public void BuyConsumable()
    62.     {
    63.         // Buy the consumable product using its general identifier. Expect a response either through ProcessPurchase or OnPurchaseFailed asynchronously.
    64.         BuyProductID(kProductIDConsumable);
    65.     }
    66.  
    67.  
    68.     public void BuyNonConsumable()
    69.     {
    70.         // Buy the non-consumable product using its general identifier. Expect a response either through ProcessPurchase or OnPurchaseFailed asynchronously.
    71.         BuyProductID(kProductIDNonConsumable);
    72.     }
    73.  
    74.  
    75.     public void BuySubscription()
    76.     {
    77.         // Buy the subscription product using its the general identifier. Expect a response either through ProcessPurchase or OnPurchaseFailed asynchronously.
    78.         BuyProductID(kProductIDSubscription);
    79.     }
    80.  
    81.  
    82.     void BuyProductID(string productId)
    83.     {
    84.         // If the stores throw an unexpected exception, use try..catch to protect my logic here.
    85.         try
    86.         {
    87.             // If Purchasing has been initialized ...
    88.             if (IsInitialized())
    89.             {
    90.                 // ... look up the Product reference with the general product identifier and the Purchasing system's products collection.
    91.                 Product product = m_StoreController.products.WithID(productId);
    92.  
    93.                 // If the look up found a product for this device's store and that product is ready to be sold ...
    94.                 if (product != null && product.availableToPurchase)
    95.                 {
    96.                     Debug.Log (string.Format("Purchasing product asychronously: '{0}'", product.definition.id));// ... buy the product. Expect a response either through ProcessPurchase or OnPurchaseFailed asynchronously.
    97.                     m_StoreController.InitiatePurchase(product);
    98.                 }
    99.                 // Otherwise ...
    100.                 else
    101.                 {
    102.                     // ... report the product look-up failure situation
    103.                     Debug.Log ("BuyProductID: FAIL. Not purchasing product, either is not found or is not available for purchase");
    104.                 }
    105.             }
    106.             // Otherwise ...
    107.             else
    108.             {
    109.                 // ... report the fact Purchasing has not succeeded initializing yet. Consider waiting longer or retrying initiailization.
    110.                 Debug.Log("BuyProductID FAIL. Not initialized.");
    111.             }
    112.         }
    113.         // Complete the unexpected exception handling ...
    114.         catch (Exception e)
    115.         {
    116.             // ... by reporting any unexpected exception for later diagnosis.
    117.             Debug.Log ("BuyProductID: FAIL. Exception during purchase. " + e);
    118.         }
    119.     }
    120.  
    121.  
    122.     // Restore purchases previously made by this customer. Some platforms automatically restore purchases. Apple currently requires explicit purchase restoration for IAP.
    123.     public void RestorePurchases()
    124.     {
    125.         // If Purchasing has not yet been set up ...
    126.         if (!IsInitialized())
    127.         {
    128.             // ... report the situation and stop restoring. Consider either waiting longer, or retrying initialization.
    129.             Debug.Log("RestorePurchases FAIL. Not initialized.");
    130.             return;
    131.         }
    132.  
    133.         // If we are running on an Apple device ...
    134.         if (Application.platform == RuntimePlatform.IPhonePlayer ||
    135.             Application.platform == RuntimePlatform.OSXPlayer)
    136.         {
    137.             // ... begin restoring purchases
    138.             Debug.Log("RestorePurchases started ...");
    139.  
    140.             // Fetch the Apple store-specific subsystem.
    141.             var apple = m_StoreExtensionProvider.GetExtension<IAppleExtensions>();
    142.             // 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.
    143.             apple.RestoreTransactions((result) => {
    144.                 // The first phase of restoration. If no more responses are received on ProcessPurchase then no purchases are available to be restored.
    145.                 Debug.Log("RestorePurchases continuing: " + result + ". If no further messages, no purchases available to restore.");
    146.             });
    147.         }
    148.         // Otherwise ...
    149.         else
    150.         {
    151.             // We are not running on an Apple device. No work is necessary to restore purchases.
    152.             Debug.Log("RestorePurchases FAIL. Not supported on this platform. Current = " + Application.platform);
    153.         }
    154.     }
    155.  
    156.  
    157.     //
    158.     // --- IStoreListener
    159.     //
    160.  
    161.     public void OnInitialized(IStoreController controller, IExtensionProvider extensions)
    162.     {
    163.         // Purchasing has succeeded initializing. Collect our Purchasing references.
    164.         Debug.Log("OnInitialized: PASS");
    165.  
    166.         // Overall Purchasing system, configured with products for this application.
    167.         m_StoreController = controller;
    168.         // Store specific subsystem, for accessing device-specific store features.
    169.         m_StoreExtensionProvider = extensions;
    170.     }
    171.  
    172.  
    173.     public void OnInitializeFailed(InitializationFailureReason error)
    174.     {
    175.         // Purchasing set-up has not succeeded. Check error for reason. Consider sharing this reason with the user.
    176.         Debug.Log("OnInitializeFailed InitializationFailureReason:" + error);
    177.     }
    178.  
    179.  
    180.     public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs args)
    181.     {
    182.         // A consumable product has been purchased by this user.
    183.         if (String.Equals(args.purchasedProduct.definition.id, kProductIDConsumable, StringComparison.Ordinal))
    184.         {
    185.             Debug.Log(string.Format("ProcessPurchase: PASS. Product: '{0}'", args.purchasedProduct.definition.id));//If the consumable item has been successfully purchased, add 100 coins to the player's in-game score.
    186.             //ScoreManager.score += 100;
    187.         }
    188.  
    189.         // Or ... a non-consumable product has been purchased by this user.
    190.         else if (String.Equals(args.purchasedProduct.definition.id, kProductIDNonConsumable, StringComparison.Ordinal))
    191.         {
    192.             Debug.Log(string.Format("ProcessPurchase: PASS. Product: '{0}'", args.purchasedProduct.definition.id));}// Or ... a subscription product has been purchased by this user.
    193.         else if (String.Equals(args.purchasedProduct.definition.id, kProductIDSubscription, StringComparison.Ordinal))
    194.         {
    195.             Debug.Log(string.Format("ProcessPurchase: PASS. Product: '{0}'", args.purchasedProduct.definition.id));}// Or ... an unknown product has been purchased by this user. Fill in additional products here.
    196.         else
    197.         {
    198.             Debug.Log(string.Format("ProcessPurchase: FAIL. Unrecognized product: '{0}'", args.purchasedProduct.definition.id));}// Return a flag indicating wither this product has completely been received, or if the application needs to be reminded of this purchase at next app launch. Is useful when saving purchased products to the cloud, and when that save is delayed.
    199.         return PurchaseProcessingResult.Complete;
    200.     }
    201.  
    202.  
    203.     public void OnPurchaseFailed(Product product, PurchaseFailureReason failureReason)
    204.     {
    205.         // A product purchase attempt did not succeed. Check failureReason for more detail. Consider sharing this reason with the user.
    206.         Debug.Log(string.Format("OnPurchaseFailed: FAIL. Product: '{0}', PurchaseFailureReason: {1}",product.definition.storeSpecificId, failureReason));}
    207.  
    208.     public void PurchaseVol2(){
    209.         BuyProductID("a.test.ID");
    210.     }
    211. }
    212.  
     
  5. rxmarccall

    rxmarccall

    Joined:
    Oct 13, 2011
    Posts:
    353
    *Bump, anyone had issues with initialization on Android? I'm stuck!
     
  6. hytka81

    hytka81

    Joined:
    Jun 22, 2013
    Posts:
    27
    I have issues with initialization. I'm using similar code that you are using and initialization should happen when game is started (debugged on device) but for some reason IsInitialized() returns false so no purchases can be made.

    My device: Samsung Galaxy S6 Edge
    Unity version: 5.3.1f1 Personal

    Some example (or tutorial) was doing it little bit differently by adding Purchaser script to shop window so that initialization would happen when shop is opened and not before that.. In my case I have a gameobject "_Purchaser" on the scene that has this Purchaser script based on examples and is enabled from the start.

    I wonder if this problem has something to do with the fact that I just recently (couple of hours ago) uploaded new APK with this Unity.Purchasing stuff or should I already get IsInitialized()?

    Btw are these permissions enough for Unity IAP (these are what my current APK in beta test requires)?
    • android.permission.ACCESS_NETWORK_STATE
    • android.permission.GET_ACCOUNTS
    • android.permission.INTERNET
    • android.permission.READ_PHONE_STATE
    • android.permission.USE_CREDENTIALS
    • com.android.vending.BILLING
    -Kalle
     
  7. rxmarccall

    rxmarccall

    Joined:
    Oct 13, 2011
    Posts:
    353
    @hytka81
    Those are good questions. I was under the assumption that Unity would add the BILLING permission automatically? If not then that could be part of my problem! Hopefully someone can help us out.

    EDIT: Trying my app again tonight, it now IS initializing on my android device, I get the PASS message. I think it was just a matter of uploading my new .APK to the closed beta section and then waiting long enough for it to be ready for testing. However now when I attempt a purchase the navigation buttons show up and the app freezes, I assume it's thinking it should show the purchase popup?
     
    Last edited: Dec 20, 2015
  8. hytka81

    hytka81

    Joined:
    Jun 22, 2013
    Posts:
    27
    @rxmarccall
    Unity IAP is now working correctly (initialization is successful and also popup is shown) in my project. It seems that I had lost some OnClick events from my shop buttons for some reason which I didn't notice at first and also I had not published my BETA apk if that is required.

    If only I could get Google AdMob and Unity 5.3.1 work together since AdMob also has IAP ads which now cause build problems.

    Hope you get your app freezes sorted out :)

    -Kalle
     
  9. rxmarccall

    rxmarccall

    Joined:
    Oct 13, 2011
    Posts:
    353
    @hytka81
    I made some progress last night as I also realized I had not clicked the "Publish" button on the alpha APK... thanks for the update!
     
  10. Banderous

    Banderous

    Joined:
    Dec 25, 2011
    Posts:
    669
    Yes, you'll need to have published your app as alpha/beta for IAP to start working.
     
  11. ramon_delmondo

    ramon_delmondo

    Joined:
    Aug 19, 2015
    Posts:
    22
    Same problem here. (Unity 5.4.0)
    It works on editor.

    Testing on android:

    08-10 14:03:10.478: I/Unity(30468): BuyProductID FAIL. Not initialized.
    08-10 14:03:10.478: I/Unity(30468): UnityEngine.DebugLogHandler:Internal_Log(LogType, String, Object)
    08-10 14:03:10.478: I/Unity(30468): UnityEngine.DebugLogHandler:LogFormat(LogType, Object, String, Object[])
    08-10 14:03:10.478: I/Unity(30468): UnityEngine.Logger:Log(LogType, Object)
    08-10 14:03:10.478: I/Unity(30468): UnityEngine.Debug:Log(Object)
    08-10 14:03:10.478: I/Unity(30468): CompleteProject.PurchaseController:BuyProductID(String) (at C:\Users\Cliente\Desktop\Coobo Repository 2\Assets\Scripts\PurchaseController.cs:225)
    08-10 14:03:10.478: I/Unity(30468): CompleteProject.PurchaseController:Buy1700coins() (at C:\Users\Cliente\Desktop\Coobo Repository 2\Assets\Scripts\PurchaseController.cs:154)
    08-10 14:03:10.478: I/Unity(30468): UnityEngine.Events.InvokableCall:Invoke(Object[]) (at /Users/builduser/buildslave/unity/build/Runtime/Export/UnityEvent.cs:153)
    08-10 14:03:10.478: I/Unity(30468): UnityEngine.Events.InvokableCallList:Invoke(Object[]) (at /Users/builduser/buildslave/unity/build/Runtime/Export/UnityEvent.cs:630)
    08-10 14:03:10.478: I/Unity(30468): UnityEngine.Events.UnityEventBase:Invoke(Object[]) (at /Users/builduser/buildslave/unity/build/Runtime/Export/UnityEvent.cs:765)
    08-10 14:03:10.478: I/Unity(30468): UnityEngine.Events.UnityEvent:Invoke() (at /Users/builduser/buildslave/unity/build/Runtime/Export/Unity

    Someone can help?
     
  12. erika_d

    erika_d

    Joined:
    Jan 20, 2016
    Posts:
    413
    Hi @ramon_delmondo,

    Have you uploaded your apk as an alpha or beta to Google? And does the version you're testing on have the same bundle id and build id as the one you uploaded? Are all your product id's set up on the Google developer console?

    If all those things are true can you post the full logs from app start to when you get the product fail log? Thanks
     
    Last edited: Aug 11, 2016
  13. ramon_delmondo

    ramon_delmondo

    Joined:
    Aug 19, 2015
    Posts:
    22
    Hi @erika_d

    Yes, all those are true.

    Here is my PurchaseController script:

    using System;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.Purchasing;

    // Placing the Purchaser class in the CompleteProject namespace allows it to interact with ScoreManager,
    // one of the existing Survival Shooter scripts.
    //namespace CompleteProject
    //{
    // Deriving the Purchaser class from IStoreListener enables it to receive messages from Unity Purchasing.
    public class PurchaseController : MonoBehaviour, 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:
    // "convenience" 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 = "consumable";
    //public static string kProductIDNonConsumable = "nonconsumable";
    //public static string kProductIDSubscription = "subscription";

    // 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";

    private static string package500coins = "package500coins";
    //private static string package500coinsAppleStore = "package500coins";
    private static string package500coinsGoogleStore = "package500coins";

    private static string package1700coins = "package1700coins";
    //private static string package1700coinsAppleStore = "package1700coins";
    private static string package1700coinsGoogleStore = "package1700coins";

    private static string package2800coins = "package2800coins";
    //private static string package2800coinsAppleStore = "package2800coins";
    private static string package2800coinsGoogleStore = "package2800coins";

    private static string package6000coins = "package6000coins";
    //private static string package6000coinsAppleStore = "package6000coins";
    private static string package6000coinsGoogleStore = "package6000coins";

    private static string package13000coins = "package13000coins";
    //private static string package13000coinsAppleStore = "package13000coins";
    private static string package13000coinsGoogleStore = "package13000coins";

    private static string package35000coins = "package35000coins";
    //private static string package35000coinsAppleStore = "package35000coins";
    private static string package35000coinsGoogleStore = "package35000coins";

    void Start()
    {
    // If we haven't set up the Unity Purchasing reference
    if (m_StoreController == null)
    {
    Debug.Log("call InitializePurchasing");
    // Begin to configure our connection to Purchasing
    InitializePurchasing();
    }
    }

    public void InitializePurchasing()
    {
    // If we have already connected to Purchasing ...
    if (IsInitialized())
    {
    Debug.Log("already initialized");
    // ... 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.
    //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 },
    //});

    builder.AddProduct(package500coins, ProductType.Consumable, new IDs(){
    //{ package500coinsAppleStore, AppleAppStore.Name },
    { package500coinsGoogleStore, GooglePlay.Name },
    });

    builder.AddProduct(package1700coins, ProductType.Consumable, new IDs(){
    //{ package1700coinsAppleStore, AppleAppStore.Name },
    { package1700coinsGoogleStore, GooglePlay.Name },
    });

    builder.AddProduct(package2800coins, ProductType.Consumable, new IDs(){
    //{ package2800coinsAppleStore, AppleAppStore.Name },
    { package2800coinsGoogleStore, GooglePlay.Name },
    });

    builder.AddProduct(package6000coins, ProductType.Consumable, new IDs(){
    //{ package6000coinsAppleStore, AppleAppStore.Name },
    { package6000coinsGoogleStore, GooglePlay.Name },
    });

    builder.AddProduct(package13000coins, ProductType.Consumable, new IDs(){
    //{ package13000coinsAppleStore, AppleAppStore.Name },
    { package13000coinsGoogleStore, GooglePlay.Name },
    });

    builder.AddProduct(package35000coins, ProductType.Consumable, new IDs(){
    //{ package35000coinsAppleStore, AppleAppStore.Name },
    { package35000coinsGoogleStore, 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);
    }


    private bool IsInitialized()
    {
    // Only say we are initialized if both the Purchasing references are set.
    return m_StoreController != null && 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 Buy500coins()
    {
    BuyProductID(package500coins);
    }

    public void Buy1700coins()
    {
    BuyProductID(package1700coins);
    }

    public void Buy2800coins()
    {
    BuyProductID(package2800coins);
    }

    public void Buy6000coins()
    {
    BuyProductID(package6000coins);
    }

    public void Buy13000coins()
    {
    BuyProductID(package13000coins);
    }

    public void Buy35000coins()
    {
    BuyProductID(package35000coins);
    }


    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 && product.availableToPurchase)
    {
    Debug.Log(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, either is 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.");
    }
    }


    // 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.");
    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 ...");

    // 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.");
    });
    }
    // 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);
    }
    }


    //
    // --- IStoreListener
    //

    public void OnInitialized(IStoreController controller, IExtensionProvider extensions)
    {
    // Purchasing has succeeded initializing. Collect our Purchasing references.
    Debug.Log("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;
    }


    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);
    }


    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("ProcessPurchase: PASS. Product: '{0}'", args.purchasedProduct.definition.id));
    // // The consumable item has been successfully purchased, add 100 coins to the player's in-game score.
    // }
    // // 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("ProcessPurchase: PASS. Product: '{0}'", 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.
    if (String.Equals(args.purchasedProduct.definition.id, package500coins, StringComparison.Ordinal))
    {
    Debug.Log("COMPROU PACOTE COM 500 MOEDAS");
    MainController.Instance.playerData.moedas += 500;
    MainController.Instance.playerData.Save();
    }

    else if (String.Equals(args.purchasedProduct.definition.id, package1700coins, StringComparison.Ordinal))
    {
    Debug.Log("COMPROU PACOTE COM 1700 MOEDAS");
    MainController.Instance.playerData.moedas += 1700;
    MainController.Instance.playerData.Save();
    }

    else if (String.Equals(args.purchasedProduct.definition.id, package2800coins, StringComparison.Ordinal))
    {
    Debug.Log("COMPROU PACOTE COM 2800 MOEDAS");
    MainController.Instance.playerData.moedas += 2800;
    MainController.Instance.playerData.Save();
    }

    else if (String.Equals(args.purchasedProduct.definition.id, package6000coins, StringComparison.Ordinal))
    {
    Debug.Log("COMPROU PACOTE COM 6000 MOEDAS");
    MainController.Instance.playerData.moedas += 6000;
    MainController.Instance.playerData.Save();
    }

    else if (String.Equals(args.purchasedProduct.definition.id, package13000coins, StringComparison.Ordinal))
    {
    Debug.Log("COMPROU PACOTE COM 13000 MOEDAS");
    MainController.Instance.playerData.moedas += 13000;
    MainController.Instance.playerData.Save();
    }

    else if (String.Equals(args.purchasedProduct.definition.id, package35000coins, StringComparison.Ordinal))
    {
    Debug.Log("COMPROU PACOTE COM 35000 MOEDAS");
    MainController.Instance.playerData.moedas += 35000;
    MainController.Instance.playerData.Save();
    }

    // else if (String.Equals(args.purchasedProduct.definition.id, kProductIDSubscription, StringComparison.Ordinal))
    // {
    // Debug.Log(string.Format("ProcessPurchase: PASS. Product: '{0}'", 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("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;
    }


    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));
    }
    }
    //}




    Debug when I start the scene with PurchaseController:

    08-11 18:50:35.397: D/AdsUnity(28443): Calling loadAd() on Android
    08-11 18:50:35.397: I/Unity(28443): call InitializePurchasing
    08-11 18:50:35.397: I/Unity(28443):
    08-11 18:50:35.397: I/Unity(28443): (Filename: ./artifacts/generated/common/runtime/UnityEngineDebugBindings.gen.cpp Line: 42)
    08-11 18:50:35.397: I/Ads(28443): Starting ad request.
    08-11 18:50:35.397: I/Ads(28443): Use AdRequest.Builder.addTestDevice("879748F98D189B3FBB6363BE9335747C") to get test ads on this device.
    08-11 18:50:35.397: D/Unity Purchasing Amazon(28443): RetrieveProducts 6
    08-11 18:50:35.397: D/c(28443): In App Purchasing SDK - Sandbox Mode: sendGetUserDataRequest
    08-11 18:50:35.397: I/Ads(28443): Please set theme of AdActivity to @android:style/Theme.Translucent to enable transparent background interstitial ad.
    08-11 18:50:35.397: W/ActivityManager(531): Unable to start service Intent { act=com.amazon.testclient.iap.appUserId flg=0x10000000 cmp=com.amazon.sdktestclient/.command.CommandBroker (has extras) } U=0: not found
    08-11 18:50:35.527: D/AdsUnity(28443): Calling show() on Android
    08-11 18:50:35.527: D/AwPasswordHandler(28443): setViewInfo, awcontents = com.android.org.chromium.android_webview.AwContents@21fbcd58
    08-11 18:50:35.547: D/KeyguardViewMediator(658): setHidden false
    08-11 18:50:35.547: D/KeyguardUpdateMonitor(658): sendKeyguardVisibilityChanged(true)
    08-11 18:50:35.547: D/KeyguardUpdateMonitor(658): handleKeyguardVisibilityChanged(1)
    08-11 18:50:35.647: D/GassUtils(2601): Found app info for package com.intercode.coobo:15. Hash: f3f605fc811ece3a18a02e4c61f8bcc726aaaa43127bbed222fa37c72a658552
    08-11 18:50:35.647: D/GassSignalComposer(2601): Ad attest is not enabled
    08-11 18:50:36.377: W/Ads(28443): The webview is destroyed. Ignoring action.
    08-11 18:50:36.387: D/AwPasswordHandler(28443): getInstance, mInstance = com.android.org.chromium.android_webview.AwPasswordHandler@21ed5670
    08-11 18:50:36.387: W/chromium(28443): [WARNING:password_handler.cc(27)] create-->contents = 0x6b871380, delegate = 0x6a9a3e80
    08-11 18:50:36.387: W/chromium(28443): [WARNING:password_handler.cc(35)] attaching to web_contents
    08-11 18:50:36.387: D/AwPasswordHandler(28443): setSavePassword false
    08-11 18:50:36.417: D/dalvikvm(28443): GC_FOR_ALLOC freed 1502K, 24% free 13349K/17432K, paused 12ms, total 12ms
    08-11 18:50:36.817: I/Ads(28443): Ad finished loading.
    08-11 18:50:36.817: W/chromium(28443): [WARNING:password_handler.cc(202)] OnPasswordFormsRendered called
    08-11 18:50:36.827: D/AwPasswordHandler(28443): getSavePassword false
    08-11 18:50:36.827: W/chromium(28443): [WARNING:aw_password_handler_delegate_impl.cc(203)] IsSavePasswordEnabled ret is
    08-11 18:50:36.827: W/chromium(28443): [WARNING:password_handler.cc(204)] OnPasswordFormsRendered IsEnabledSavePassword is false
    08-11 18:50:36.847: W/AwContents(28443): nativeOnDraw failed; clearing to background color.
    08-11 18:50:37.517: D/WifiWatchdogStateMachine(531): CurrentRSSI:-59
    08-11 18:50:37.517: I/WifiHW(531): CMD: IFNAME=wlan0 SIGNAL_POLL
    08-11 18:50:37.517: D/wpa_supplicant(648): RX ctrl_iface - hexdump(len=11): 53 49 47 4e 41 4c 5f 50 4f 4c 4c
    08-11 18:50:37.517: D/wpa_supplicant(648): wlan0: Control interface command 'SIGNAL_POLL'
    08-11 18:50:37.517: D/wpa_supplicant(648): nl80211: survey data missing!
    08-11 18:50:37.517: I/WifiHW(531): REPLY: RSSI=-32
    08-11 18:50:37.517: I/WifiHW(531): REPLY: LINKSPEED=65
    08-11 18:50:37.517: I/WifiHW(531): REPLY: NOISE=9999
    08-11 18:50:37.517: I/WifiHW(531): REPLY: FREQUENCY=2412
    08-11 18:50:37.517: I/WifiHW(531): CMD: IFNAME=wlan0 PKTCNT_POLL
    08-11 18:50:37.517: D/wpa_supplicant(648): RX ctrl_iface - hexdump(len=11): 50 4b 54 43 4e 54 5f 50 4f 4c 4c
    08-11 18:50:37.517: D/wpa_supplicant(648): wlan0: Control interface command 'PKTCNT_POLL'
    08-11 18:50:37.527: I/WifiHW(531): REPLY: TXGOOD=296437
    08-11 18:50:37.527: I/WifiHW(531): REPLY: TXBAD=484
    08-11 18:50:37.527: I/WifiHW(531): REPLY: RXGOOD=627511
    08-11 18:50:37.627: I/WifiHW(531): CMD: IFNAME=wlan0 SIGNAL_POLL
    08-11 18:50:37.627: D/wpa_supplicant(648): RX ctrl_iface - hexdump(len=11): 53 49 47 4e 41 4c 5f 50 4f 4c 4c
    08-11 18:50:37.627: D/wpa_supplicant(648): wlan0: Control interface command 'SIGNAL_POLL'
    08-11 18:50:37.627: D/wpa_supplicant(648): nl80211: survey data missing!
    08-11 18:50:37.627: I/WifiHW(531): REPLY: RSSI=-33
    08-11 18:50:37.627: I/WifiHW(531): REPLY: LINKSPEED=65
    08-11 18:50:37.627: I/WifiHW(531): REPLY: NOISE=9999
    08-11 18:50:37.627: I/WifiHW(531): REPLY: FREQUENCY=2412






    Debug when I click to buy coins:

    08-11 18:57:28.337: I/Unity(28443): BuyProductID FAIL. Not initialized.
    08-11 18:57:28.337: I/Unity(28443):
    08-11 18:57:28.337: I/Unity(28443): (Filename: ./artifacts/generated/common/runtime/UnityEngineDebugBindings.gen.cpp Line: 42)


    Image In-app Products
     
  14. ramon_delmondo

    ramon_delmondo

    Joined:
    Aug 19, 2015
    Posts:
    22
    Hi @erika_d,

    Yes all those are true.

    Here is my PurchaseController script:

    using System;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.Purchasing;

    // Placing the Purchaser class in the CompleteProject namespace allows it to interact with ScoreManager,
    // one of the existing Survival Shooter scripts.
    //namespace CompleteProject
    //{
    // Deriving the Purchaser class from IStoreListener enables it to receive messages from Unity Purchasing.
    public class PurchaseController : MonoBehaviour, 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:
    // "convenience" 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 = "consumable";
    //public static string kProductIDNonConsumable = "nonconsumable";
    //public static string kProductIDSubscription = "subscription";

    // 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";

    private static string package500coins = "package500coins";
    //private static string package500coinsAppleStore = "package500coins";
    private static string package500coinsGoogleStore = "package500coins";

    private static string package1700coins = "package1700coins";
    //private static string package1700coinsAppleStore = "package1700coins";
    private static string package1700coinsGoogleStore = "package1700coins";

    private static string package2800coins = "package2800coins";
    //private static string package2800coinsAppleStore = "package2800coins";
    private static string package2800coinsGoogleStore = "package2800coins";

    private static string package6000coins = "package6000coins";
    //private static string package6000coinsAppleStore = "package6000coins";
    private static string package6000coinsGoogleStore = "package6000coins";

    private static string package13000coins = "package13000coins";
    //private static string package13000coinsAppleStore = "package13000coins";
    private static string package13000coinsGoogleStore = "package13000coins";

    private static string package35000coins = "package35000coins";
    //private static string package35000coinsAppleStore = "package35000coins";
    private static string package35000coinsGoogleStore = "package35000coins";

    void Start()
    {
    // If we haven't set up the Unity Purchasing reference
    if (m_StoreController == null)
    {
    Debug.Log("call InitializePurchasing");
    // Begin to configure our connection to Purchasing
    InitializePurchasing();
    }
    }

    public void InitializePurchasing()
    {
    // If we have already connected to Purchasing ...
    if (IsInitialized())
    {
    Debug.Log("already initialized");
    // ... 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.
    //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 },
    //});

    builder.AddProduct(package500coins, ProductType.Consumable, new IDs(){
    //{ package500coinsAppleStore, AppleAppStore.Name },
    { package500coinsGoogleStore, GooglePlay.Name },
    });

    builder.AddProduct(package1700coins, ProductType.Consumable, new IDs(){
    //{ package1700coinsAppleStore, AppleAppStore.Name },
    { package1700coinsGoogleStore, GooglePlay.Name },
    });

    builder.AddProduct(package2800coins, ProductType.Consumable, new IDs(){
    //{ package2800coinsAppleStore, AppleAppStore.Name },
    { package2800coinsGoogleStore, GooglePlay.Name },
    });

    builder.AddProduct(package6000coins, ProductType.Consumable, new IDs(){
    //{ package6000coinsAppleStore, AppleAppStore.Name },
    { package6000coinsGoogleStore, GooglePlay.Name },
    });

    builder.AddProduct(package13000coins, ProductType.Consumable, new IDs(){
    //{ package13000coinsAppleStore, AppleAppStore.Name },
    { package13000coinsGoogleStore, GooglePlay.Name },
    });

    builder.AddProduct(package35000coins, ProductType.Consumable, new IDs(){
    //{ package35000coinsAppleStore, AppleAppStore.Name },
    { package35000coinsGoogleStore, 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);
    }


    private bool IsInitialized()
    {
    // Only say we are initialized if both the Purchasing references are set.
    return m_StoreController != null && 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 Buy500coins()
    {
    BuyProductID(package500coins);
    }

    public void Buy1700coins()
    {
    BuyProductID(package1700coins);
    }

    public void Buy2800coins()
    {
    BuyProductID(package2800coins);
    }

    public void Buy6000coins()
    {
    BuyProductID(package6000coins);
    }

    public void Buy13000coins()
    {
    BuyProductID(package13000coins);
    }

    public void Buy35000coins()
    {
    BuyProductID(package35000coins);
    }


    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 && product.availableToPurchase)
    {
    Debug.Log(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, either is 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.");
    }
    }


    // 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.");
    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 ...");

    // 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.");
    });
    }
    // 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);
    }
    }


    //
    // --- IStoreListener
    //

    public void OnInitialized(IStoreController controller, IExtensionProvider extensions)
    {
    // Purchasing has succeeded initializing. Collect our Purchasing references.
    Debug.Log("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;
    }


    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);
    }


    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("ProcessPurchase: PASS. Product: '{0}'", args.purchasedProduct.definition.id));
    // // The consumable item has been successfully purchased, add 100 coins to the player's in-game score.
    // }
    // // 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("ProcessPurchase: PASS. Product: '{0}'", 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.
    if (String.Equals(args.purchasedProduct.definition.id, package500coins, StringComparison.Ordinal))
    {
    Debug.Log("COMPROU PACOTE COM 500 MOEDAS");
    MainController.Instance.playerData.moedas += 500;
    MainController.Instance.playerData.Save();
    }

    else if (String.Equals(args.purchasedProduct.definition.id, package1700coins, StringComparison.Ordinal))
    {
    Debug.Log("COMPROU PACOTE COM 1700 MOEDAS");
    MainController.Instance.playerData.moedas += 1700;
    MainController.Instance.playerData.Save();
    }

    else if (String.Equals(args.purchasedProduct.definition.id, package2800coins, StringComparison.Ordinal))
    {
    Debug.Log("COMPROU PACOTE COM 2800 MOEDAS");
    MainController.Instance.playerData.moedas += 2800;
    MainController.Instance.playerData.Save();
    }

    else if (String.Equals(args.purchasedProduct.definition.id, package6000coins, StringComparison.Ordinal))
    {
    Debug.Log("COMPROU PACOTE COM 6000 MOEDAS");
    MainController.Instance.playerData.moedas += 6000;
    MainController.Instance.playerData.Save();
    }

    else if (String.Equals(args.purchasedProduct.definition.id, package13000coins, StringComparison.Ordinal))
    {
    Debug.Log("COMPROU PACOTE COM 13000 MOEDAS");
    MainController.Instance.playerData.moedas += 13000;
    MainController.Instance.playerData.Save();
    }

    else if (String.Equals(args.purchasedProduct.definition.id, package35000coins, StringComparison.Ordinal))
    {
    Debug.Log("COMPROU PACOTE COM 35000 MOEDAS");
    MainController.Instance.playerData.moedas += 35000;
    MainController.Instance.playerData.Save();
    }

    // else if (String.Equals(args.purchasedProduct.definition.id, kProductIDSubscription, StringComparison.Ordinal))
    // {
    // Debug.Log(string.Format("ProcessPurchase: PASS. Product: '{0}'", 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("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;
    }


    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));
    }
    }
    //}





    Debug when I start scene with PurshaseController:

    08-11 19:09:59.257: D/AdsUnity(28443): Calling loadAd() on Android
    08-11 19:09:59.257: I/Unity(28443): call InitializePurchasing
    08-11 19:09:59.257: I/Unity(28443):
    08-11 19:09:59.257: I/Unity(28443): (Filename: ./artifacts/generated/common/runtime/UnityEngineDebugBindings.gen.cpp Line: 42)
    08-11 19:09:59.257: I/Ads(28443): Starting ad request.
    08-11 19:09:59.257: I/Ads(28443): Use AdRequest.Builder.addTestDevice("879748F98D189B3FBB6363BE9335747C") to get test ads on this device.
    08-11 19:09:59.267: I/Ads(28443): Please set theme of AdActivity to @android:style/Theme.Translucent to enable transparent background interstitial ad.
    08-11 19:09:59.267: D/Unity Purchasing Amazon(28443): RetrieveProducts 6
    08-11 19:09:59.267: D/c(28443): In App Purchasing SDK - Sandbox Mode: sendGetUserDataRequest
    08-11 19:09:59.267: W/ActivityManager(531): Unable to start service Intent { act=com.amazon.testclient.iap.appUserId flg=0x10000000 cmp=com.amazon.sdktestclient/.command.CommandBroker (has extras) } U=0: not found
    08-11 19:09:59.387: D/AdsUnity(28443): Calling show() on Android
    08-11 19:09:59.387: D/AwPasswordHandler(28443): setViewInfo, awcontents = com.android.org.chromium.android_webview.AwContents@21da34c0
    08-11 19:09:59.397: D/KeyguardViewMediator(658): setHidden false
    08-11 19:09:59.397: D/KeyguardUpdateMonitor(658): sendKeyguardVisibilityChanged(true)
    08-11 19:09:59.397: D/KeyguardUpdateMonitor(658): handleKeyguardVisibilityChanged(1)
    08-11 19:09:59.497: D/GassUtils(2601): Found app info for package com.intercode.coobo:15. Hash: f3f605fc811ece3a18a02e4c61f8bcc726aaaa43127bbed222fa37c72a658552
    08-11 19:09:59.497: D/GassSignalComposer(2601): Ad attest is not enabled
    08-11 19:09:59.637: W/Herrevad(2601): Invalid mccmnc
    08-11 19:09:59.637: W/Herrevad(2601): Invalid mccmnc
    08-11 19:09:59.727: I/Ads(2601): Received log message: <Google:HTML> You are using version 6.6 of the Google Play Services Ads SDK. Please consider updating your SDK to the most recent SDK version to get the latest features and bug fixes. See http://goo.gl/r2TRzC for instructions on how to get the latest version of Google Play services.
    08-11 19:09:59.987: D/WifiWatchdogStateMachine(531): CurrentRSSI:-16
    08-11 19:09:59.987: I/WifiHW(531): CMD: IFNAME=wlan0 SIGNAL_POLL
    08-11 19:09:59.987: D/wpa_supplicant(648): RX ctrl_iface - hexdump(len=11): 53 49 47 4e 41 4c 5f 50 4f 4c 4c
    08-11 19:09:59.987: D/wpa_supplicant(648): wlan0: Control interface command 'SIGNAL_POLL'
    08-11 19:09:59.997: D/wpa_supplicant(648): nl80211: survey data missing!
    08-11 19:09:59.997: I/WifiHW(531): REPLY: RSSI=-12
    08-11 19:09:59.997: I/WifiHW(531): REPLY: LINKSPEED=65
    08-11 19:09:59.997: I/WifiHW(531): REPLY: NOISE=9999
    08-11 19:09:59.997: I/WifiHW(531): REPLY: FREQUENCY=2412
    08-11 19:09:59.997: I/WifiHW(531): CMD: IFNAME=wlan0 PKTCNT_POLL
    08-11 19:09:59.997: D/wpa_supplicant(648): RX ctrl_iface - hexdump(len=11): 50 4b 54 43 4e 54 5f 50 4f 4c 4c
    08-11 19:09:59.997: D/wpa_supplicant(648): wlan0: Control interface command 'PKTCNT_POLL'
    08-11 19:10:00.007: I/WifiHW(531): REPLY: TXGOOD=297276
    08-11 19:10:00.007: I/WifiHW(531): REPLY: TXBAD=493
    08-11 19:10:00.007: I/WifiHW(531): REPLY: RXGOOD=628324
    08-11 19:10:00.007: I/WifiHW(531): CMD: IFNAME=wlan0 SIGNAL_POLL
    08-11 19:10:00.007: D/wpa_supplicant(648): RX ctrl_iface - hexdump(len=11): 53 49 47 4e 41 4c 5f 50 4f 4c 4c
    08-11 19:10:00.007: D/wpa_supplicant(648): wlan0: Control interface command 'SIGNAL_POLL'
    08-11 19:10:00.007: D/wpa_supplicant(648): nl80211: survey data missing!
    08-11 19:10:00.007: I/WifiHW(531): REPLY: RSSI=-11
    08-11 19:10:00.007: I/WifiHW(531): REPLY: LINKSPEED=65
    08-11 19:10:00.017: I/WifiHW(531): REPLY: NOISE=9999
    08-11 19:10:00.017: I/WifiHW(531): REPLY: FREQUENCY=2412
    08-11 19:10:00.017: D/KeyguardUpdateMonitor(658): received broadcast android.intent.action.TIME_TICK
    08-11 19:10:00.017: D/KeyguardUpdateMonitor(658): handleTimeUpdate
    08-11 19:10:00.407: D/dalvikvm(28443): GC_FOR_ALLOC freed 3783K, 24% free 13329K/17432K, paused 18ms, total 18ms
    08-11 19:10:00.427: W/Ads(28443): The webview is destroyed. Ignoring action.
    08-11 19:10:00.427: D/AwPasswordHandler(28443): getInstance, mInstance = com.android.org.chromium.android_webview.AwPasswordHandler@21ed5670
    08-11 19:10:00.427: W/chromium(28443): [WARNING:password_handler.cc(27)] create-->contents = 0x6a535460, delegate = 0x6a7c83b0
    08-11 19:10:00.427: W/chromium(28443): [WARNING:password_handler.cc(35)] attaching to web_contents
    08-11 19:10:00.437: D/AwPasswordHandler(28443): setSavePassword false
    08-11 19:10:00.467: D/dalvikvm(28443): GC_FOR_ALLOC freed 727K, 23% free 13425K/17432K, paused 10ms, total 10ms
    08-11 19:10:00.657: W/chromium(28443): [WARNING:password_handler.cc(202)] OnPasswordFormsRendered called
    08-11 19:10:00.657: D/AwPasswordHandler(28443): getSavePassword false
    08-11 19:10:00.657: W/chromium(28443): [WARNING:aw_password_handler_delegate_impl.cc(203)] IsSavePasswordEnabled ret is
    08-11 19:10:00.657: W/chromium(28443): [WARNING:password_handler.cc(204)] OnPasswordFormsRendered IsEnabledSavePassword is false
    08-11 19:10:00.717: W/chromium(28443): [WARNING:password_handler.cc(202)] OnPasswordFormsRendered called
    08-11 19:10:00.717: D/AwPasswordHandler(28443): getSavePassword false
    08-11 19:10:00.717: W/chromium(28443): [WARNING:aw_password_handler_delegate_impl.cc(203)] IsSavePasswordEnabled ret is
    08-11 19:10:00.717: W/chromium(28443): [WARNING:password_handler.cc(204)] OnPasswordFormsRendered IsEnabledSavePassword is false
    08-11 19:10:00.727: I/Ads(28443): Ad finished loading.
    08-11 19:10:00.747: W/AwContents(28443): nativeOnDraw failed; clearing to background color.
    08-11 19:10:00.757: W/AwContents(28443): nativeOnDraw failed; clearing to background color.



    Debug when I click to buy coins:

    08-11 19:10:45.417: I/Unity(28443): BuyProductID FAIL. Not initialized.
    08-11 19:10:45.417: I/Unity(28443):
    08-11 19:10:45.417: I/Unity(28443): (Filename: ./artifacts/generated/common/runtime/UnityEngineDebugBindings.gen.cpp Line: 42)



    Image in-app products:
     
  15. erika_d

    erika_d

    Joined:
    Jan 20, 2016
    Posts:
    413
    Hi @ramon_delmondo,

    Is this on Amazon or Google? I was under the impression it was google but in your logs it mentions Amazon...
    Also what version of IAP are you using? Can you make sure you're on the most up to date version of IAP by going to IAP in the services window and clicking Reimport or Update?
     
  16. ramon_delmondo

    ramon_delmondo

    Joined:
    Aug 19, 2015
    Posts:
    22
    Hi @erika_d,

    Yes, this is on google play developer console.
    I tried reimport IAP package after update unity to 5.4, but doesn't work.
    Before was working, idk exactly when stoped.
    Note: I deleted the Google AIDL.aar to get to the build
     
  17. erika_d

    erika_d

    Joined:
    Jan 20, 2016
    Posts:
    413
    @ramon_delmondo

    You're logs look like they are contacting Amazon to retrieve products. Try confirming that you are building to Google and not Amazon in the Window -> Unity IAP -> Android menu selection.

    What is the error you're getting that makes you think reimporting isn't working? What version are you on? You can find the version in the Changelog in UnityPurchasing folder.

    Deleting the AIDL is necessary when another plugin has imported it, so that makes sense.
     
  18. ramon_delmondo

    ramon_delmondo

    Joined:
    Aug 19, 2015
    Posts:
    22
    @erika_d

    I can reimport IAP package, but that doesn't solve the problem.

    When I click in Window -> Unity IAP -> Target Google Play:

    NullReferenceException: Object reference not set to an instance of an object
    UnityEngine.Purchasing.UnityPurchasingEditor.TargetAndroidStore (AndroidStore target)
    UnityEngine.Purchasing.UnityPurchasingEditor.TargetGooglePlay ()

    And still debug:

    08-12 15:10:32.338: W/ActivityManager(531): Unable to start service Intent { act=com.amazon.testclient.iap.appUserId flg=0x10000000 cmp=com.amazon.sdktestclient/.command.CommandBroker (has extras) } U=0: not found


    I need to put something on IAP Obfuscator?
     
    Last edited: Aug 12, 2016
  19. erika_d

    erika_d

    Joined:
    Jan 20, 2016
    Posts:
    413
    Hi @ramon_delmondo,

    You only need to use IAP Obfuscator if you are using Receipt Validation: https://docs.unity3d.com/Manual/UnityIAPValidatingReceipts.html Are you using this?

    One of my coworkers noticed that package1700coins is spelled differently on the screenshot of the console you attached than in the code you attached - that will need to be fixed in one of the places before that product will be available.

    Also make sure that if you're sideloading the app to a device for testing, that the app has the same build number as the one uploaded to the alpha/beta on the google developer console.

    The error switching targets is stumping us...we haven't seen that issue with anyone before, and nothing obvious is jumping out at us in our code. Does it give you any more information in those errors? Does it happen every time or just periodically? What's the full version of Unity you're using? i.e. is there anything after the 5.4.0...? Are you using one of the beta versions of 5.4 or the full release version (5.4.0f3)? How did you download the new version of the editor or was this the first version you downloaded? Do you ever build for Amazon?

    *Edit* Also make sure that the google account you're using for testing is not the one being used for the developer portal.

    And it looks like the logs you posted are missing some things from in between start up to when you make the purchase. Could you send us the full logs from all the way through (you can attach as a document if they're too long).
     
    Last edited: Aug 17, 2016
    nicholasr likes this.
Thread Status:
Not open for further replies.