Search Unity

Unity IAP Store Guides - Amazon, Apple, Google Play, Windows

Discussion in 'Unity IAP' started by nicholasr, Dec 8, 2015.

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

    kogen

    Joined:
    Dec 2, 2015
    Posts:
    6
    Is that the way to go? But what do I need the CrossPlattform Validator for then?
     
  2. Banderous

    Banderous

    Joined:
    Dec 25, 2011
    Posts:
    669
    @kogen take a trace using adb to see what is happening on the device, likely an exception.

    @LotusBlack receipt validation is not automatic; Unity IAP puts each receipt on each product according to the information it gets back from Google Play. You need to use the validator to determine if those receipts are genuine.
     
  3. LotusBlack

    LotusBlack

    Joined:
    Apr 9, 2015
    Posts:
    35
    I see, .hasReceipt only states that there IS a receipt then, not that it is validated, thanks for the clarification @Banderous.
     
  4. kogen

    kogen

    Joined:
    Dec 2, 2015
    Posts:
    6
    Hello again,

    I can feel we are almost done here, there is just one thing I cannot explain, when it comes to retrieving a product. On iOS I implemented the payment process and everything is fine, realized, that the app "forgets" the item, after I restart, and implemented a "retrieving" function. Which then works fine too. But now comes the headache:

    I set up everything again, with a new test user and started the app.

    Code (CSharp):
    1.     public void OnInitialized(IStoreController controller, IExtensionProvider extensions)
    2.     {
    3.         m_StoreController = controller;
    4.         m_StoreExtensionProvider = extensions;
    5.        
    6.         this.retrievePurchasedItems(); // after initializing retrieved possibly bought products
    7.     }
    (and the retrieving function)

    Code (CSharp):
    1.     private void retrievePurchasedItems()
    2.     {
    3.         if (Application.platform == RuntimePlatform.IPhonePlayer ||
    4.             Application.platform == RuntimePlatform.OSXPlayer)
    5.         {
    6.             var builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance());
    7.             var appleConfig = builder.Configure<IAppleConfiguration>();
    8.             this.transform.FindChild("TextDebug").GetComponent<Text>().text = "retrieving 1";
    9.             var receiptData = System.Convert.FromBase64String(appleConfig.appReceipt);
    10.             this.transform.FindChild("TextDebug").GetComponent<Text>().text = "retrieving 2";
    11.             try
    12.             {
    13.                 AppleReceipt receipt = new AppleValidator(AppleTangle.Data()).Validate(receiptData);
    14.                 this.transform.FindChild("TextDebug").GetComponent<Text>().text = "retrieving 3";
    15.                 this._checkAppleProducts(receipt.inAppPurchaseReceipts); // this is the function where each product is handled if it exists (could be retrieved).
    16.                 this.transform.FindChild("TextDebug").GetComponent<Text>().text = "retrieving 4";
    17.             }
    18.             catch (Exception e) // change this to "IAPSecurityExcepetion and test again later
    19.             {
    20.                 this.transform.FindChild("TextDebug").GetComponent<Text>().text = "Nothing retrieved";
    21.             }
    22.         }
    23.     }
    Obviously nothing was retrieved since the user has not bought anything, so the app starts telling me "Nothing retrieved" which is correct.
    I click on "buy" the item is bought BUT the ProcessPurchase method is not called. The event is not handled as process failed either.

    Code (CSharp):
    1.     public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs args)
    2.     {
    3.         this.transform.FindChild("TextDebug").GetComponent<Text>().text = "bought";
    4. }
    Now when I restart my app the bought item is there (since the retrieving function works).

    If I comment the retrieving function, it works like I can buy but not retrieve the products that have been bought at start up. I thought that the function may crash the app silently that's why I put in a general Try-Catch-Exception block, and obviously the function is executed completely. Cause it says "Nothing retrieved". To me this really does not make any sense.
    My last gues is that I may have to initialize "this.retrievePurchasedItems()" before setting the m_Controller variables in the Initialize function since there might be going on something asynchronally?

    Hope you can help me

    Best kogen
     
  5. LotusBlack

    LotusBlack

    Joined:
    Apr 9, 2015
    Posts:
    35
    Hey Kogen,

    I believe all those builder methods are supposed to be called before you call UnityPurchasing.Initialize, so even before your OnInitialize callback happens!
     
  6. Fattie

    Fattie

    Joined:
    Jul 5, 2012
    Posts:
    476
    Unity's amazing new receipt verification !

    I just had a hell of a time finding NicholasAR's and Banderous's link to the new receipt verification, here it is:



    and here's the link!

    Unity's amazing new built-in
    receipt validation and parsing library:


    https://docs.google.com/document/d/1dJzeoGPeUIUetvFCulsvRz1TwRNOcJzwTDVf23gk8Rg/edit


    Hope it saves someone some searching!!!
     
  7. Fattie

    Fattie

    Joined:
    Jul 5, 2012
    Posts:
    476
    Here's our current code for Unity IAP,

    First a base class. The only thing you have to personalise here is put in your actual store product codes around line 67.

    This keeps all the IAP code together and abstracts out your local code which (a) literally pays out the IAP and (b) handles interface issues.


    Code (CSharp):
    1. /*
    2.  
    3. example bridge to Unity IAP systems
    4.  
    5. You must provide the four overrides for the virtual functions in your own derive.
    6.  
    7. (Note, "when testing in the editor, PurchaseProcessing will always succeed")
    8.  
    9. */
    10.  
    11. using UnityEngine;
    12. using System.Collections;
    13. using System.Collections.Generic;
    14.  
    15. using UnityEngine.Purchasing;
    16.  
    17. public class IapBase:MonoBehaviour, IStoreListener
    18.   {
    19.  
    20.   protected virtual void SuccessNONConsumable(string pid)
    21.     {
    22.     Debug.Log("In your derive you must write the processing to actually pay-out for " +pid);
    23.     }
    24.   protected virtual void SuccessCONSUMABLE(string pid)
    25.     {
    26.     Debug.Log("In your derive you must write the processing to actually give the " +pid);
    27.     }
    28.   protected virtual void WaitShow()
    29.     {
    30.     Debug.Log("In your derive you must display an 'iap waiting' panel");
    31.     }
    32.   protected virtual void WaitHide()
    33.     {
    34.     Debug.Log("In your derive you must hide the 'iap waiting' panel");
    35.     }
    36.  
    37.   ///////////////////////////////////////////////////////////////////
    38.  
    39.   private void FinalResultProblem()
    40.     {
    41.     Debug.Log("IAP    FinalResultProblem in IapBase .......");
    42.     WaitHide();
    43.     }
    44.  
    45.   ///////////////////////////////////////////////////////////////////
    46.  
    47.   private static IStoreController iStoreController;
    48.   private static IExtensionProvider iExtensionProvider;
    49.  
    50.   void Start()
    51.         {
    52.         if (iStoreController == null) InitializePurchasing();
    53.  
    54.         RestorePurchases();
    55.         }
    56.  
    57.   public void InitializePurchasing()
    58.         {
    59.     if (IsInitialized())
    60.       {
    61.       Debug.Log("Couldn't initialize purchasing as IsInitialized failed.");
    62.       return;
    63.       }
    64.  
    65.     ConfigurationBuilder b = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance());
    66.  
    67.     b.Add(ProductType.Consumable, "COINSBIG");
    68.     b.Add(ProductType.Consumable, "COINSSMALL");
    69.     b.Add(ProductType.Consumable, "ETC");
    70.     b.Add(ProductType.Consumable, "ETC");
    71.     b.Add(ProductType.Consumable, "ETC");
    72.  
    73.     b.Add(ProductType.NonConsumable, "NEWMAP");
    74.     b.Add(ProductType.NonConsumable, "SPECIALMAP");
    75.     b.Add(ProductType.NonConsumable, "ETC");
    76.     b.Add(ProductType.NonConsumable, "ETC");
    77.     b.Add(ProductType.NonConsumable, "ETC");
    78.  
    79.     // note the small helper extension at the bottom of this file,
    80.     // to keep those tidy.
    81.  
    82.     UnityPurchasing.Initialize(this, b);
    83.         }
    84.  
    85.   private bool IsInitialized()
    86.     {
    87.     return iStoreController != null && iExtensionProvider != null;
    88.     }
    89.  
    90.   void tragicCommsFailuePIDS( string error )
    91.     {
    92.     Debug.Log( "productListRequestFailedEvent: " + error );
    93.     }
    94.  
    95.   // note Apple (alone) currently requires explicit purchase restoration
    96.   public void RestorePurchases()
    97.     {
    98.     if (!IsInitialized())
    99.       {
    100.       Debug.Log("RestorePurchases fail, not initialized. Try later?");
    101.       return;
    102.       }
    103.  
    104.     if ( Application.platform != RuntimePlatform.IPhonePlayer && Application.platform != RuntimePlatform.OSXPlayer)
    105.       {
    106.       //Debug.Log("RestorePurchases, it is NOT necessary to restore purchases on " + Application.platform);
    107.       return;
    108.       }
    109.  
    110.     Debug.Log("Since this is an Apple device, restoring purchases explicitly...");
    111.     var apple = iExtensionProvider.GetExtension<IAppleExtensions>();
    112.     apple.RestoreTransactions((result) =>
    113.       {
    114.       Debug.Log("RestorePurchases continuing: " + result + ". If no further messages, no purchases available to restore.");
    115.       });
    116.     }
    117.  
    118.   ////////////////////////////////////////////////////////////////////////////////
    119.  
    120.   public void Purchase(string code)
    121.     {
    122.     // this is "our" local Purchase call. use this from the project
    123.     // pass in the "unity" code. (in this project, always the same as IOS code.)
    124.  
    125.     Debug.Log("IAP    Purchase " +code);
    126.  
    127.     // bring up a waiting panel
    128.     WaitShow();
    129.     // ultimately match with WaitHide() no matter what path
    130.     // the three possible paths
    131.     // 1. error, exception
    132.     // 2. error, didn't work (various)
    133.     // 3. totally worked, paidout, consumable or nonconsumable
    134.  
    135.     // Buy the product using its general "Unity" identifier. (Not the store identifier.)
    136.     // We'll get a response either through ProcessPurchase or OnPurchaseFailed
    137.     // asynchronously.  Same for either consumables/nonconsumables.
    138.  
    139.  
    140.  
    141.     // right HERE you may wish to have a fake purchase for your ui:
    142.     // just wait a second (as if connecting) and then jump to either
    143.     // SuccessCONSUMABLE(PID) or SuccessNONConsumable(PID)
    144.  
    145.  
    146.     try {
    147.       // the heart of Unity's IAP system is this one line of code...
    148.       BuyProductID(code);
    149.       }
    150.     catch (System.Exception e)
    151.       {
    152.       Debug.Log ("BuyProductID: FAIL. Exception during purchase. " + e);
    153.       FinalResultProblem();
    154.       }
    155.     }
    156.  
    157.   private void BuyProductID(string productId)
    158.         {
    159.         if ( !IsInitialized() )
    160.           {
    161.           Debug.Log("BuyProductID fail, not initialized " +productId);
    162.           FinalResultProblem();
    163.           return;
    164.           }
    165.  
    166.         Product product = iStoreController.products.WithID(productId);
    167.  
    168.         if (product==null)
    169.       {
    170.       Debug.Log ("BuyProductID: fail, not found " +productId);
    171.       FinalResultProblem();
    172.       return;
    173.       }
    174.  
    175.         if (product.availableToPurchase==false)
    176.       {
    177.       Debug.Log ("BuyProductID: fail, not available for purchase (on this platform?) " +productId);
    178.       FinalResultProblem();
    179.       return;
    180.       }
    181.  
    182.     Debug.Log("Purchasing product async, thanks Unity... " +product.definition.id);
    183.     iStoreController.InitiatePurchase(product);
    184.     }
    185.  
    186.     // listeners...
    187.  
    188.   public void OnInitialized(IStoreController controller, IExtensionProvider extensions)
    189.     {
    190.     Debug.Log("OnInitialized: PASS");
    191.     iStoreController = controller; // Overall Purchasing system, configured with products for this application
    192.     iExtensionProvider = extensions; // Store specific subsystem, for accessing device-specific store features.
    193.     }
    194.  
    195.   public void OnInitializeFailed(InitializationFailureReason error)
    196.     {
    197.     // Purchasing set-up has not succeeded. Check error for reason. Consider sharing this reason with the user.
    198.     Debug.Log("OnInitializeFailed InitializationFailureReason:" + error);
    199.     }
    200.  
    201.   public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs args)
    202.     {
    203.     Product pp;
    204.     pp = args.purchasedProduct;
    205.     Debug.Log("calledback from store to ProcessPurchase! --> " +pp.definition.id);
    206.     PurchaseSUCCESSFUL( pp );
    207.     return PurchaseProcessingResult.Complete;
    208.     }
    209.  
    210.   public void OnPurchaseFailed(Product product, PurchaseFailureReason failureReason)
    211.     {
    212.     string PID = product.definition.id; // or product.definition.storeSpecificId
    213.     Debug.Log("FAILED buying ... " +PID);
    214.     Debug.Log("FAILED buying ... error is " +failureReason);
    215.  
    216.     FinalResultProblem();
    217.     }
    218.  
    219.   private void PurchaseSUCCESSFUL( Product pp )
    220.     {
    221.     Debug.Log("PurchaseSUCCESSFUL ....");
    222.  
    223.     //todo ... put in the new transaction verification right here
    224.  
    225.     if (pp.definition.type == ProductType.Consumable)
    226.       {
    227.       Debug.Log("actual PurchaseSUCCESSFUL, consumable, routing " +pp.definition.id);
    228.       SuccessCONSUMABLE(pp.definition.id);
    229.       WaitHide();
    230.       return;
    231.       }
    232.  
    233.     if (pp.definition.type == ProductType.NonConsumable)
    234.       {
    235.       Debug.Log("actual PurchaseSUCCESSFUL, NONconsumable, routing " +pp.definition.id);
    236.       SuccessNONConsumable(pp.definition.id);
    237.       WaitHide();
    238.       return;
    239.       }
    240.  
    241.     Debug.Log("Disaster?  " +pp.definition.id);
    242.     FinalResultProblem();
    243.     }
    244.  
    245.   }
    246.  
    247.  
    248. public static class _extensions
    249.   {
    250.   public static void Add(this ConfigurationBuilder cb, ProductType t, string code)
    251.     {
    252.     // slightly vary this per your system of product ids on the various stores.
    253.  
    254.     cb.AddProduct( code, t, new IDs() {
    255.       { code, AppleAppStore.Name },
    256.       // Android say ... { code, GooglePlay.Name },
    257.       // other stores ... { code, GooglePlay.Name },
    258.       });
    259.  
    260.     // note, per Unity forum, if you give one combination such as {"bigcoinbag", AppleAppStore.Name"}
    261.     // in fact it is NOT necessary to give the others >>if they are identical on all stores.<<
    262.     // (Don't forget PlayStore is only lower-case, so ideally make your AppStore all lower case.)
    263.     // It would definitely seem best to make your Unity identifiers same as your store identifiers.
    264.     }
    265.   }
    Note the four virtual classes.

    So you don't have to do anything to the above script. Copy and paste!

    Then you have your own derive. It has four simple calls which abstracts out everything about handing purchases in your own game...


    Code (CSharp):
    1. /*
    2. and heres "your" local code to handle the four overrides from IapBase
    3. */
    4.  
    5. using UnityEngine;
    6. using System.Collections;
    7. using System.Collections.Generic;
    8.  
    9. public class Iap:IapBase
    10.   {
    11.  
    12.   protected override void SuccessCONSUMABLE(string code)
    13.     {
    14.     Debug.Log("IAP    SuccessWithCode " +code);
    15.  
    16.     if(code=="COINSBIG")
    17.       {
    18.       something like ..
    19.       ActuallyIncreaseUsersBalance(6500);
    20.       }
    21.     // ... and so on
    22.     }
    23.  
    24.   public void ActuallyIncreaseLocalBalance(int c) // for example
    25.     {
    26.     // your own routine to (say) increase coin balance
    27.     Debug.Log("IAP    ActuallyIncreaseLocalBalance " +c);
    28.     // ... for example, using Anti-Cheat Toolkit,
    29.     // ... actually adjust the user's local balance using secure prefs
    30.     }
    31.  
    32.   ////////
    33.  
    34.   protected override void SuccessNONConsumable(string pid)
    35.     {
    36.     Debug.Log("IAP     SuccessNONConsumable .. " +pid);
    37.     RecordThatUserNowOwns(pid);
    38.     if (pid == "YELLOWDRAGONS")
    39.     //  ... actually unlock that feature, etc.
    40.     }
    41.  
    42.   private void SaveSecurePreferences(string blah)
    43.     {
    44.     //... your routines to, for example, save any local data (securely)
    45.     }
    46.  
    47.   ///////////////////////////////////////////////////////////////////////////////////////
    48.  
    49.   // the final two classes to override...
    50.   // in the example, we're expected to have a "WaitPanel"
    51.   // which is a script on a UIPanel (perhaps showing a waiting animation)
    52.  
    53.   protected override void WaitShow()
    54.     {
    55.     WaitPanel w = Object.FindObjectOfType<WaitPanel>();
    56.     // ie, in the present scene whatever that is.
    57.     if (w == null) { Debug.Log("drastic woe, give up"); return; }
    58.     w.Show = true;
    59.     }
    60.   protected override void WaitHide()
    61.     {
    62.     WaitPanel w = Object.FindObjectOfType<WaitPanel>();
    63.     // ie, in the present scene whatever that is.
    64.     if (w == null) { Debug.Log("drastic woe, give up"); return; }
    65.     w.Show = false;
    66.     }
    67.  
    68.   }
    69.  
    70.  
    Of course, your Iap component would just sit on a GameObject likely in your preload scene with DontDestroyOnLoad.

    To purchase from anywhere is simply
    .iap.Purchase( "BAGOFCOINS" ); and that's it.

    In the example of how UI is handled (you can do it any way you want in the derive) you just need a "WaitPanel" script (probably on a UI panel) in every scene, which will sit on top and block all.

    So, this script completely abstracts out the Unity IAP layer and you only need to write the functions of the four overrides for your game.

    Two of the functions literally payout the purchases (this could be as simple as using secure preferences with Anti-Cheat Toolkit) and the other two just show/hide some sort of modal "wait for store!" panel which blocks all other interface.
     
    Last edited: Mar 23, 2016
  8. Fattie

    Fattie

    Joined:
    Jul 5, 2012
    Posts:
    476
    Last edited: Mar 23, 2016
  9. Fattie

    Fattie

    Joined:
    Jul 5, 2012
    Posts:
    476
    And here's a basic way to do more receipt verification, particularly for ios store...this will combat the most common receipt cheating. (I mean beyond the fact that Unity already awesomely check all the hashes, etc.)

    In IapBase above there is
    PurchaseSUCCESSFUL() , only that routine need be changed...

    The basic idea is you look through all the receipts, find the relevant one, and check it has not been used on the device already.


    Code (CSharp):
    1. private void PurchaseSUCCESSFUL(Product pp)
    2.   {
    3.   Debug.Log("IAP    PurchaseSUCCESSFUL .... next up verify");
    4.  
    5.   // transaction verification right here using pp.receipt
    6.  
    7.   var validator = new CrossPlatformValidator(
    8.     GooglePlayTangle.Data(),
    9.     AppleTangle.Data(),
    10.     Application.bundleIdentifier);
    11.  
    12.   try
    13.     {
    14.     IPurchaseReceipt[] result = validator.Validate(pp.receipt);
    15.     Debug.Log("IAP    Receipt is valid...");
    16.  
    17.    // in the Android case, we are done
    18.      
    19.    bool androidCaseHenceDone = false;
    20.    #if UNITY_ANDROID
    21.    androidCaseHenceDone = true;
    22.    #endif
    23.    if (androidCaseHenceDone)
    24.       {
    25.      Debug.Log("ANDROID ... no need for further checking.");
    26.      PurchaseVERIFIED(pp);
    27.      return;
    28.      }
    29.  
    30.   // in the iOS case...........
    31.   // and knowing (cross fingers Unity) that the product was meant to be
    32.   // one of these: pp.definition.storeSpecificId
    33.   // and that it might be one of these pp.definition.type == ProductType.Consumable
    34.     string findThese = pp.definition.storeSpecificId;
    35.  
    36.     Debug.Log("IAP    looking through receipts for this one ... " +findThese);
    37.  
    38.     int k=0;
    39.     foreach (IPurchaseReceipt productReceipt in result)
    40.       {
    41.       // one for google, multiple for apple
    42.       Debug.Log("IAP    bunch of receipts: " +k +": " +productReceipt.productID);
    43.  
    44.       if ( productReceipt.productID != findThese )
    45.         {
    46.         Debug.Log("IAP    that one is not relevant to us, ignore for now");
    47.         continue;
    48.         }
    49.  
    50.       Debug.Log("IAP    bunch of receipts: " +k +": " +productReceipt.purchaseDate);
    51.       Debug.Log("IAP    bunch of receipts: " +k +": " +productReceipt.transactionID);
    52.  
    53.       if (pp.definition.type == ProductType.Consumable)
    54.         {
    55.         Debug.Log("IAP    Since this is a consumable, we do have to check if the ID is fresh");
    56.         string supposedTID = productReceipt.transactionID;
    57.         Debug.Log("IAP    Here's the ID to check if fresh... " +supposedTID);
    58.  
    59.         // you must use anti-cheat toolkit or some similar security
    60.         // to locally save transactionID which have been "used up" already
    61.  
    62.         if ( CodeStage.AntiCheat.ObscuredTypes.ObscuredPrefs.HasKey(supposedTID) )
    63.           {
    64.           Debug.Log("IAP    Some joker tried to reuse a consumable receipt. Ignore it.");
    65.           // make a note in your analytics
    66.           continue;
    67.           }
    68.  
    69.         // make a note in your analytics
    70.         CodeStage.AntiCheat.ObscuredTypes.ObscuredPrefs.SetInt(supposedTID,1);
    71.         CodeStage.AntiCheat.ObscuredTypes.ObscuredPrefs.Save();
    72.  
    73.         Debug.Log("IAP    We made a note that this tid has been used ... "+supposedTID);
    74.         Debug.Log("IAP    verified OK, leaving, for  ... "+findThese);
    75.         PurchaseVERIFIED(pp);
    76.  
    77.         return;
    78.         }
    79.  
    80.       if (pp.definition.type == ProductType.NonConsumable)
    81.         {
    82.         Debug.Log("IAP    Since this is a nonconsumable, we pay it out");
    83.         Debug.Log("IAP    There's no real reason to save and track the tids");
    84.         PurchaseVERIFIED(pp);
    85.  
    86.         return;
    87.         }
    88.       ++k;
    89.       }
    90.  
    91.  
    92.     }
    93.   catch (IAPSecurityException)
    94.     {
    95.     Debug.Log("IAP    Invalid receipt, not unlocking content");
    96.     Tics.H("IAPVerifyWoeA");
    97.     FinalResultProblem();
    98.     }
    99.   }
    Then in PurchaseProcessingResult() you should make this mod, to self-test...

    Code (CSharp):
    1. public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs args)
    2.   {
    3.   Product pp;
    4.   pp = args.purchasedProduct;
    5.   Debug.Log("IAP    calledback from store to ProcessPurchase! --> " +pp.definition.id);
    6.   Debug.Log("IAP    being careful to use the store-specific for verification: " +pp.definition.storeSpecificId);
    7.   PurchaseSUCCESSFUL( pp );
    8.   StartCoroutine(_DeliberatelyRepost(pp));
    9.   return PurchaseProcessingResult.Complete;
    10.   }
    11.  
    12. private IEnumerator _DeliberatelyRepost(Product pp)
    13.   {
    14.   // to ensure that your own security is working reasonably well
    15.   // deliberately again call "Purchase SUCCESSFUL" a couple of times,
    16.   // it should be that your verification system rejects the duplicates.
    17.  
    18.   yield return new WaitForSeconds(3f);
    19.   PurchaseSUCCESSFUL( pp );
    20.   yield return new WaitForSeconds(3f);
    21.   PurchaseSUCCESSFUL( pp );
    22.   }
    Here's a complete trace of someone going through a couple purchases with that.

    Code (CSharp):
    1. here's a consumable...
    2.  
    3. IAP        calledback from store to ProcessPurchase! --> STARSPAKA
    4. IAP        being careful to use the store-specific for verification: STARSPAKA
    5. IAP        PurchaseSUCCESSFUL .... next up verify
    6. IAP        Receipt is valid...
    7. IAP        looking through receipts for this one ... STARSPAKA
    8. IAP        bunch of receipts: 0: STARSPAKA
    9. IAP        bunch of receipts: 0: 03/23/2016 17:22:54
    10. IAP        bunch of receipts: 0: 1000000201135960
    11. IAP        Since this is a consumable, we do have to check if the ID is fresh
    12. IAP        Here's the ID to check if fresh... 1000000201135960
    13. IAP        We made a note that this tid has been used ... 1000000201135960
    14. IAP        verified OK, leaving, for  ... STARSPAKA
    15. IAP        actual PurchaseSUCCESSFUL, consumable, routing STARSPAKA
    16. IAP        SuccessWithCode STARSPAKA
    17. IAP        ActuallyIncreaseLocalBalance 6000
    18. IAP        Increased local balance by 6000 to 35702
    19. IAP        PurchaseSUCCESSFUL .... next up verify
    20. IAP        Receipt is valid...
    21. IAP        looking through receipts for this one ... STARSPAKA
    22. IAP        bunch of receipts: 0: STARSPAKA
    23. IAP        bunch of receipts: 0: 03/23/2016 17:22:54
    24. IAP        bunch of receipts: 0: 1000000201135960
    25. IAP        Since this is a consumable, we do have to check if the ID is fresh
    26. IAP        Here's the ID to check if fresh... 1000000201135960
    27. IAP        Some joker tried to reuse a consumable receipt. Ignore it.
    28. IAP        bunch of receipts: 0: COMBOSUPERPAK
    29. IAP        that one is not relevant to us, ignore for now
    30. IAP        bunch of receipts: 0: LEVELSUPER
    31. IAP        that one is not relevant to us, ignore for now
    32. IAP        bunch of receipts: 0: LEVELHYPER
    33. IAP        that one is not relevant to us, ignore for now
    34. IAP        bunch of receipts: 0: GALLERY
    35. IAP        that one is not relevant to us, ignore for now
    36. IAP        PurchaseSUCCESSFUL .... next up verify
    37. IAP        Receipt is valid...
    38. IAP        looking through receipts for this one ... STARSPAKA
    39. IAP        bunch of receipts: 0: STARSPAKA
    40. IAP        bunch of receipts: 0: 03/23/2016 17:22:54
    41. IAP        bunch of receipts: 0: 1000000201135960
    42. IAP        Since this is a consumable, we do have to check if the ID is fresh
    43. IAP        Here's the ID to check if fresh... 1000000201135960
    44. IAP        Some joker tried to reuse a consumable receipt. Ignore it.
    45. IAP        bunch of receipts: 0: ULTRAPAK
    46. IAP        that one is not relevant to us, ignore for now
    47. IAP        bunch of receipts: 0: LEVELSUPER
    48. IAP        that one is not relevant to us, ignore for now
    49. IAP        bunch of receipts: 0: LEVELHYPER
    50. IAP        that one is not relevant to us, ignore for now
    51. IAP        bunch of receipts: 0: GALLERY
    52. IAP        that one is not relevant to us, ignore for now
    53. IAP        Purchase LEVELSUPER
    54. IAP        Purchasing product async, thanks Unity... LEVELSUPER
    55.  
    56. and here's a NonConsumable...
    57.  
    58. IAP        calledback from store to ProcessPurchase! --> LEVELSUPER
    59. IAP        being careful to use the store-specific for verification: LEVELSUPER
    60. IAP        PurchaseSUCCESSFUL .... next up verify
    61. IAP        Receipt is valid...
    62. IAP        looking through receipts for this one ... LEVELSUPER
    63. IAP        bunch of receipts: 0: ULTRAPAK
    64. IAP        that one is not relevant to us, ignore for now
    65. IAP        bunch of receipts: 0: LEVELSUPER
    66. IAP        bunch of receipts: 0: 03/09/2016 18:02:02
    67. IAP        bunch of receipts: 0: 1000000198532338
    68. IAP        Since this is a nonconsumable, we pay it out
    69. IAP        There's no real reason to save and track the tids
    70. IAP        actual PurchaseSUCCESSFUL, NONconsumable, routing LEVELSUPER
    71. IAP        PurchaseSUCCESSFUL .... next up verify
    72. IAP        Receipt is valid...
    73. IAP        looking through receipts for this one ... LEVELSUPER
    74. IAP        bunch of receipts: 0: ULTRAPAK
    75. IAP        that one is not relevant to us, ignore for now
    76. IAP        bunch of receipts: 0: LEVELSUPER
    77. IAP        bunch of receipts: 0: 03/09/2016 18:02:02
    78. IAP        bunch of receipts: 0: 1000000198532338
    79. IAP        Since this is a nonconsumable, we pay it out
    80. IAP        There's no real reason to save and track the tids
    81. IAP        actual PurchaseSUCCESSFUL, NONconsumable, routing LEVELSUPER
    82. IAP        PurchaseSUCCESSFUL .... next up verify
    83. IAP        Receipt is valid...
    84. IAP        looking through receipts for this one ... LEVELSUPER
    85. IAP        bunch of receipts: 0: ULTRAPAK
    86. IAP        that one is not relevant to us, ignore for now
    87. IAP        bunch of receipts: 0: LEVELSUPER
    88. IAP        bunch of receipts: 0: 03/09/2016 18:02:02
    89. IAP        bunch of receipts: 0: 1000000198532338
    90. IAP        Since this is a nonconsumable, we pay it out
    91. IAP        There's no real reason to save and track the tids
    92. IAP        actual PurchaseSUCCESSFUL, NONconsumable, routing LEVELSUPER
    93.  
    94.  
     
    Last edited: Apr 4, 2016
    Gametyme and nicholasr like this.
  10. Fattie

    Fattie

    Joined:
    Jul 5, 2012
    Posts:
    476
    Confusion about PRICE of using Unity Analytics / IAP services

    Hey @Banderous and @nicholasr ...

    I'm completely confused about how much it costs to use Unity's Analytics/IAP services. Are they plain free like Parse was? What's the deal? Are they free for now, free with a limit, or ?
     
  11. mpinol

    mpinol

    Joined:
    Jul 29, 2015
    Posts:
    317
    Hey @Fattie,

    Unity Analytics and IAP are free services! We did announce our paid tiers of Analytics recently at GDC, which give you higher limits for things like the amount of data you can download from Raw Data Export and Analysis Points. I cannot really give you any more information about when these paid tiers will become available but there will always be a free tier of Analytics!
     
  12. Banderous

    Banderous

    Joined:
    Dec 25, 2011
    Posts:
    669
    @kogen if your code is throwing an exception you should log the exception details to see what is causing it.
     
  13. MarsGus

    MarsGus

    Joined:
    Mar 27, 2016
    Posts:
    5
    I was following the IAP Unity tutorial, made my version for android.
    I was struggling to get it working. I fixed it with adding the following but i couldnot find that in the tutorial, should this part not be added to the tutorial?

    Can you explain why I had to add the following in my purchase script?
    builder.Configure<IGooglePlayConfiguration>().SetPublicKey("my public key that I have in developer console");
    I thought it was enough to add this public key in unity services in the dashboard place. But it seems not to work, in my case it doesnot (or I do somehting wrong o_O).

    With adding above in my script it was finally working.
     
    Fattie likes this.
  14. Fattie

    Fattie

    Joined:
    Jul 5, 2012
    Posts:
    476
    hi @Banderous and @nicholasr

    "~/Desktop/691/Libraries/Plugins/UnityPurchasing/iOS/UnityPurchasing.m:106:62: 'transactionReceipt' is deprecated: first deprecated in iOS 7.0 - Use -[NSBundle appStoreReceiptURL]"

    what's up with that! ignoring won't help, I'll keep posting it :)
     
    nicholasr likes this.
  15. Banderous

    Banderous

    Joined:
    Dec 25, 2011
    Posts:
    669
    IGooglePlayConfiguration is obsolete now that the receipt validation library is included. You do not need to set your Google Play public key via IGooglePlayConfiguration, that was always optional.
     
    nicholasr likes this.
  16. MarsGus

    MarsGus

    Joined:
    Mar 27, 2016
    Posts:
    5
    @Fattie
    Code (CSharp):
    1. ConfigurationBuilder builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance());
    2. builder.AddProduct(myNewProduct, ProductType.NonConsumable, new IDs(){{myNewProductGoogle, GooglePlay.Name}});
    3. builder.Configure<IGooglePlayConfiguration>().SetPublicKey("MIIB..............");
    4.             UnityPurchasing.Initialize(this, builder);
    this last part with IGooglePlayConfiguration did it for me that I get a connection to the google play part en get the popup for buying myproduct.

    @Banderous I do not understand what you mean with receipt validation library is included Maybe I have to google it somewhere? Why is this not updated in the tutorial for IAP within Android, it is crucial for it getting working.
     
  17. Fattie

    Fattie

    Joined:
    Jul 5, 2012
    Posts:
    476
    Android - when to Initialize issue?

    @Banderous and @nicholasr dudes..

    Definitely a couple folks have reported having trouble calling initialize in the google play store case.

    Do you think we should just immediately call initialize when the app opens?

    Or should we wait a few seconds?

    Or should we keep trying it every few seconds?

    Any thoughts on this?
     
  18. Banderous

    Banderous

    Joined:
    Dec 25, 2011
    Posts:
    669
    @MarsGus you don't have to set your google play public key to use Unity IAP. Receipt validation is not a requirement of using Unity IAP, but it is documented here.

    @Fattie what specific initialisation problem are you having? You should initialise Unity IAP only once as soon as possible. You should never attempt to reinitialise it.
     
    nicholasr likes this.
  19. Fattie

    Fattie

    Joined:
    Jul 5, 2012
    Posts:
    476
    @MarsGus regarding doing this:

    Code (CSharp):
    1. builder.Configure<IGooglePlayConfiguration>().SetPublicKey("MIIB..............");
    2.             UnityPurchasing.Initialize(this, builder);
    FYI we tested very carefully and we found indeed you DO NOT have to do that. It does work perfectly without it. (The system appears to already know your ID from where you enter it.) It must have been some other problem you were having.

    @Banderous "You should initialise Unity IAP only once as soon as possible...." thanks for that clarification, good to know.
     
    nicholasr likes this.
  20. MarsGus

    MarsGus

    Joined:
    Mar 27, 2016
    Posts:
    5
    @Fattie that is interesting I will try working without (when I have time) it and test if i can also do it without.
    Thanks for the information because I currently also have some kind of same problem with gathering metadata (localized price). In the editor it works and I retrieve the dummy testdata. But on my android device it does not work. Looks like some kind of same mistake.

    Is it possible that building not always goes well if you are not running unity as an administrator on your laptop?
     
  21. Fattie

    Fattie

    Joined:
    Jul 5, 2012
    Posts:
    476
  22. xJupiter

    xJupiter

    Joined:
    Aug 6, 2015
    Posts:
    10
    var validator = new CrossPlatformValidator(GooglePlayTangle.Data(), AppleTangle.Data(), Application.bundleIdentifier);

    GooglePlayTangle.Data() and AppleTangle.Data() do not exist in the current context.Why?
     
  23. Banderous

    Banderous

    Joined:
    Dec 25, 2011
    Posts:
    669
  24. Cutier73098

    Cutier73098

    Joined:
    Apr 14, 2016
    Posts:
    7
    sorry for that but i m trying for 4 days but my iaps not working on android device when i click purchase button nothing happens...


    in unity editor when i hit play and click purchase it show it is working

    i check product id both are same in script and google iap

    any idea is i m missing something?

    or probably thats a bug?
     
  25. Banderous

    Banderous

    Joined:
    Dec 25, 2011
    Posts:
    669
    Make sure you have followed all the steps in the configuration guide, including publishing an alpha/beta.
     
  26. Cutier73098

    Cutier73098

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

    i check every thing now please tell me what else i can try!!!!!!!!!!!
     
  27. Banderous

    Banderous

    Joined:
    Dec 25, 2011
    Posts:
    669
    It is not about your code, it is about the way your products are configured on each store's publisher dashboard. Follow the exact steps in the guide, including publishing as an alpha/beta.
     
  28. Cutier73098

    Cutier73098

    Joined:
    Apr 14, 2016
    Posts:
    7
    hye i tracked down the main issue

    that type of issue i m also facing with every plugin

    thats the error

    AndroidJavaException: java.lang.ClassNotFoundException: com.unity.purchasing.googleplay.GooglePlayPurchasing
    java.lang.ClassNotFoundException: com.unity.purchasing.googleplay.GooglePlayPurchasing

    i think its a bug in unity 5.3.4
     
  29. erika_d

    erika_d

    Joined:
    Jan 20, 2016
    Posts:
    413
  30. kujo

    kujo

    Joined:
    Aug 19, 2013
    Posts:
    106
    Not sure if this is the best place or in Cloud services to post this, but I'm looking to release my game on GooglePlay and on Amazon and I'm very happy to see Amazon store is now supported by Unity IAP. I'm wondering how I can build my game on the cloud for both Amazon and Google in the cleanest possible way? For anything else that has required a split of the two platforms, I have created my own defines called UNITY_GOOGLEPLAY or UNITY_AMAZON to set the switch and then I've been able to configure those within the cloud and off we go, but I see in the Window menu there is a control to specify the target that then writes to a file.

    Do you have a suggestion to build both types from the same source control repo?
     
  31. Banderous

    Banderous

    Joined:
    Dec 25, 2011
    Posts:
    669
    You can use the static editor method UnityEngine.Purchasing.UnityPurchasingEditor.TargetAndroidStore() from a build script to switch android stores.
     
    nicholasr likes this.
  32. kujo

    kujo

    Joined:
    Aug 19, 2013
    Posts:
    106
    Cool - thanks for that - I'll give it a go.
     
  33. Fattie

    Fattie

    Joined:
    Jul 5, 2012
    Posts:
    476
    it's magic you guys even added Amazon IAP
     
    mpinol likes this.
  34. ThaderGamesSL

    ThaderGamesSL

    Joined:
    Jul 29, 2015
    Posts:
    3
    Hello guys. I'm building a test app on Unity for Amazon and I get this error OnInitializeFailed InitializationFailureReason: NoProductsAvailable when calling UnityPurchase.Initialize. I have checked and the JSON is configured properly with the name of the product. I'm using a Fire 7 (2015)
    There is also another minor problem when I try to set manually the store by using UnityEngine.Purchasing.UnityPurchasingEditor.TargetAndroidStore(), the editor gives me the following error The type or namespace name `UnityPurchasingEditor' does not exist in the namespace `UnityEngine.Purchasing'. even thought I didn't get any error when compiling on visual studio. I'm using Unity 5.3.4f1.

    The code is:
    Code (CSharp):
    1.  public void InitializePurchasing()
    2.     {
    3.         // If we have already connected to Purchasing ...
    4.         if (IsInitialized())
    5.         {
    6.             // ... we are done here.
    7.             return;
    8.         }
    9.  
    10.         // Create a builder, first passing in a suite of Unity provided stores.
    11.         var builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance());
    12.  
    13.         // Add a product to sell / restore by way of its identifier, associating the general identifier with its store-specific identifiers.
    14.         builder.AddProduct("package_of_coins", ProductType.Consumable);
    15.      
    16.         builder.Configure<IAmazonConfiguration>().WriteSandboxJSON(builder.products);
    17.  
    18.         UnityPurchasing.Initialize(this, builder);
    19.     }
    Update: I have update to Unity 5.4 and it supports now InApp for Amazon but when I deploy a really basic project (One scene and one script) for testing the InApp purchases I get the following errors (the project just opens to a black screen and closes in less than one second):
    -Still not able to use UnityEngine.Purchasing.UnityPurchasingEditor.TargetAndroidStore (Same error than before)
    -OpenGL error:
    D/ActivityManager(18130): default service binder = android.os.BinderProxy@3edaa3a7
    D/ActivityManager(18130): default service = android.app.ActivityManagerProxy@1b1ec054
    I/ActivityManager( 452): Displayed com.game.game/com.unity3d.player.UnityPlayerActivity: +244ms
    D/Unity (18130): GL_EXT_debug_marker GL_OES_texture_npot GL_OES_compressed_ETC1_RGB8_texture GL_OES_standard_derivatives GL_OES_EGL_image GL_OES_depth24 GL_ARM_rgba8 GL_ARM_mali_shader_binary GL_OES_depth_texture GL_OES_packed_depth_stencil GL_EXT_texture_format_BGRA8888 GL_OES_vertex_half_float GL_EXT_blend_minmax GL_OES_EGL_image_external GL_OES_EGL_sync GL_OES_rgb8_rgba8 GL_EXT_multisampled_render_to_texture GL_EXT_discard_framebuffer GL_OES_get_program_binary GL_ARM_mali_program_binary GL_EXT_shader_texture_lod GL_EXT_robustness GL_OES_depth_texture_cube_map GL_KHR_debug
    I/ActivityManager( 452): Process com.game.game (pid 18130) has died

    Update 2: The openGL bug is already reported for the beta version of Unity 5.4 it will be fixed in further releases and it is only happening to some Android devices (including my Fire 7)

    Thanks!
     
    Last edited: Apr 30, 2016
  35. Banderous

    Banderous

    Joined:
    Dec 25, 2011
    Posts:
    669
    Re: UnityPurchasingEditor have you updated to the latest version of Unity IAP? You can download and install the latest version by clicking 'import' in the IAP cloud service window.
     
    erika_d likes this.
  36. mankjon

    mankjon

    Joined:
    Feb 20, 2013
    Posts:
    3
    I have this "InitializationFailureReason.PurchasingUnavailable" when start android app.
    And link beta test is show:
    Offers in-app purchases
    x This app is incompatible with your device.

    What happen in my case.

    Thx.
    Saksit.
     
  37. sevensails

    sevensails

    Joined:
    Aug 22, 2013
    Posts:
    483
    "*NOTE: IL2CPP Windows builds using Unity IAP are not supported. Until support for Unity IAP with Windows IL2CPP is completed, using IL2CPP on Windows with Unity IAP will generate a compilation error."

    Is there any estimative to IL2CPP support? We will get it on time for final 5.4?

    Code (csharp):
    1.  
    2. Windows.LicenseInformation.isOnAppTrial
    3.  
    isOnAppTrial is also not supported on IL2CPP? I'm getting a crash when I try to use this.
     
  38. Banderous

    Banderous

    Joined:
    Dec 25, 2011
    Posts:
    669
    @mankjon google play states that the app is incompatible with your device?
    @Wagenheimer IL2CPP Windows plugin support will not make 5.4, but should be in 5.5.
     
  39. mankjon

    mankjon

    Joined:
    Feb 20, 2013
    Posts:
    3
    Yes, app is incompatible with my device.
    my Android phone is WIKO HIGHWAY.
    Android version : 4.2.2
    I look "Supported devices" at google play this model support.
    I can install apk But, I can't test IAP.

    Why?

    Thank you.
    Saksit
     
  40. Banderous

    Banderous

    Joined:
    Dec 25, 2011
    Posts:
    669
    I suggest you raise it with Google, Unity IAP can only tell you that purchasing is unavailable. It may be related to your device or your google account, or both.
     
    mankjon likes this.
  41. mankjon

    mankjon

    Joined:
    Feb 20, 2013
    Posts:
    3
    Hello , I do't understand the Restore Purchases.
    Code (CSharp):
    1.  
    2. var apple = m_StoreExtensionProvider.GetExtension<IAppleExtensions>();
    3.             // 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.
    4.             apple.RestoreTransactions((result) => {
    5.                 // The first phase of restoration. If no more responses are received on ProcessPurchase then no purchases are available to be restored.
    6.                 Debug.Log("RestorePurchases continuing: " + result + ". If no further messages, no purchases available to restore.");
    7.             });
    8.  
    This code is return boolean. But I want to know which IAP ID to finish Purchased.
    what should I do?

    Thank you.
    Saksit
     
  42. nicholasr

    nicholasr

    Joined:
    Aug 15, 2015
    Posts:
    183
    @mankjon Expect the IAP ID to be returned in the "args" of your IStoreListener.ProcessPurchase(PurchaseEventArgs args) implementation.
     
    mankjon and erika_d like this.
  43. HiddenJason

    HiddenJason

    Joined:
    Apr 18, 2016
    Posts:
    23
    I have an existing app using the iOS and Google purchasing, and that's all working fine. Now that it's available, I want to add in IAP support for Amazon, but the documentation on this seems to be pretty lacking still.

    I currently have some conditional code based on Application.platform, which obviously isn't enough to distinguish between the Google and Amazon app store implementations.

    Is there any way to access the information placed in BillingMode.json by the editor switch, or otherwise determine the currently-configured store from application code so the application can follow different paths depending on which store is configured - for things like verifying different product IDs, receipt validation, or extracting alternate receipt data that is specific to the store?
     
  44. Banderous

    Banderous

    Joined:
    Dec 25, 2011
    Posts:
    669
    Not currently though I can see why it should be easily accessible - I have raised a ticket to add some extended configuration to make it easy to read.

    For now you can read and parse the BillingMode.json file using the Resources API -

    Code (CSharp):
    1. var textAsset = (Resources.Load("BillingMode") as TextAsset);
    2. var dic = (Dictionary<string, object>) MiniJson.JsonDecode(json);
    3. var store = (AndroidStore) Enum.Parse(typeof(AndroidStore), (string) dic["androidStore"], true);
     
  45. pea

    pea

    Joined:
    Oct 29, 2013
    Posts:
    98
    Is it possible to further debug PurchaseFailureReason.Unknown? My purchasing works fine in testing and even during live testing (i.e. not using a test account, actual money changing hands). However some live Android devices are reporting back these unknown errors again and again.

    I'm a little at a loss on how to get to the bottom of this. Using Google Play.
     
  46. pea

    pea

    Joined:
    Oct 29, 2013
    Posts:
    98
    E.g. is it possible to access PurchaseFailureDescription.message or PurchaseFailedEventArgs.message to get more detailed info?

    https://docs.unity3d.com/ScriptRefe...nsion.PurchaseFailureDescription-message.html

    https://docs.unity3d.com/ScriptReference/Purchasing.PurchaseFailedEventArgs-message.html

    Edit: I'm trying to find the name of the Google Play IStoreExtension, so that I can extend its IStoreCallback.OnPurchaseFailed method, in order to access the error message. Can't figure out how to do that just yet.
     
    Last edited: Jun 24, 2016
  47. Fattie

    Fattie

    Joined:
    Jul 5, 2012
    Posts:
    476
    nicholasr likes this.
  48. erika_d

    erika_d

    Joined:
    Jan 20, 2016
    Posts:
    413
  49. erika_d

    erika_d

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

    Sorry you're experiencing this problem with your app! Currently, without reproducing the issue either locally or getting a full error log from one of the devices there isn't a way to get the full message. IStoreCallback is an internal implementation so it isn't accessible publicly. The IStoreExtension is public, but can't be used to access the callback. We are working on adding a way to pass more detailed error logging information to users, and hope we'll have it to release soon.

    Have the error logs you've gathered indicated whether it was occurring on a specific device or for a certain IAP purchase? We're looking at your Catchy Crystals game and will see if we can reproduce the error on our devices.
     
  50. pea

    pea

    Joined:
    Oct 29, 2013
    Posts:
    98
    Hi @erika_d,

    Thank you for the great reply. It seems to happen across a range of devices and for all IAP products (consumable or non-consumable). For example, from my logging:

    Screen Shot 2016-06-25 at 10.59.44 AM.png Screen Shot 2016-06-25 at 10.59.31 AM.png

    Another issue which may be related is that in Unity Analytics - which automatically tracks Unity IAP purchases - I have a bunch of verified Android purchases. Those purchases aren't actually showing up in my Google Merchant Console. Not sure if that's due to sandbox/test purchases being incorrectly logged in Analytics, or something else related to the PurchaseFailureReason.Unknown.
     
    Last edited: Jun 25, 2016
Thread Status:
Not open for further replies.