Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

How to implement single IAP to unlock full version of game

Discussion in 'Editor & General Support' started by Pi_3-14, Feb 16, 2014.

  1. Pi_3-14

    Pi_3-14

    Joined:
    May 9, 2012
    Posts:
    168
    I'm making a musical trainer game.

    I'm planning to release it free, with a single IAP option to remove the iAd banner and simultaneously unlock full gameplay.

    Would it be appropriate to use Soom-La (http://soom.la) to do this? Is there any better option?

    Can anyone link me to a step by step tutorial on how to do this (Soom-La or otherwise)?

    I'm looking for a "hello world" of IAP from Unity on an iOS device, where there is just one single thing to be purchased.

    I have gone into iTunes connect, created a new app and gone into IAP and created a single item priced at $1. But how to move forwards?

    (The Soom-La download appears to be a fully functional virtual store, which is overkill for my situation. Also I'm not sure if they are using their own store. And if they are, then doesn't Apple forbid their parties allowing money transactions? There are too many things I don't know... )

    π

    PS Also asking here: http://answers.unity3d.com/questions/641093/how-to-implement-single-iap-to-unlock-full-version.html

    PPS how can I test my solution before releasing the app?

    PPPS What happens if a user has the app installed on two devices, and IAPs on one? How will the other know to unlock as well?

    EDIT: I finally got this working, here is a screenshot from the iPhone to show it working:
    $Screen Shot 2014-02-23 at 13.03.33.png
     
    Last edited: Feb 23, 2014
  2. gurdotan

    gurdotan

    Joined:
    Feb 6, 2013
    Posts:
    15
    EDIT 2/15/2015: SOOMLA Store is available for free on the Unity Asset Store
    https://www.assetstore.unity3d.com/en/#!/content/6103


    For what you're trying to do you should use the SOOMLA open framework: http://project.soom.la

    This is an open source SDK for defining in app purchases in free to play games, without the full fledged store. Specifically have a look at the Unity3d project in Github - it has docs and examples which should help you.

    PS: Also answered there

    PPS: Testing in iOS requires that you have a device without any users signed in on it, so that you can perform test purchases with a test user that you define in iTunes Connect. See Apple's guide:
    https://developer.apple.com/library/ios/documentation/LanguagesUtilities/Conceptual/iTunesConnectInAppPurchase_Guide/Chapters/TestingInAppPurchases.html
    . For Android it's more straightforward - you just need to set "Soomla -> Edit Settings > Android Settings > Android Test Mode" to true. See the getting started section in https://github.com/soomla/unity3d-store
     
    Last edited: Feb 15, 2015
  3. Pi_3-14

    Pi_3-14

    Joined:
    May 9, 2012
    Posts:
    168
    Thankyou gurdotan!

    I've been reading https://github.com/soomla/unity3d-store/wiki

    Here is my process:

    * I go into iTunesConnect and create a new app with bundle ID com.pi.chromakey

    $app.png

    * give it a single nonconsumable IAP costing a dollar with ID com.pi.chromakey.unlock

    $iap.png

    * still in iTunesConnect I create a new test user giving a fake e-mail address (EDIT: I'm not completely sure this step is necessary)

    $Screen Shot 2014-02-20 at 18.37.12.png

    * log into my Apple developer account and create a provisioning profile for 'chromakey'. Download this profile and double click it.



    (now if you go into Xcode -> organiser -> Devices -> My iPod -> provisioning profiles, it should show up alongside the default "iOS team provisioning profile")

    $Screen Shot 2014-02-20 at 18.39.23.png

    * on my iPod touch I logout from my App Store user account (I don't attempt to login as the test user as per the last answer here: http://stackoverflow.com/questions/7866977/itunesconnect-test-user-verifying)

    In Unity
    * I open the Soomla-Unity demo project.
    * I create a new scene.
    * I drag drop Assets -> SoomLa -> Prefabs -> StoreEvents into my scene
    * I create an empty game object and attach the script listed below
    * I change the bundle-id of the Unity app (in Player settings -> iOS) to com.pi.chromakey
    * I build and run on my iPod touch

    But nothing happens when I hit the upgrade button. I'm expecting it to request I login. What's going wrong?

    EDIT: looks like I may need to instruct Xcode to use this provisioning profile. So stop running in Xcode, go into project settings -> Build Settings, make sure "Basic/All" is set to "All", type "code sign" into the search box, now "provisioning profile" should show up. And set it to the profile that has just been created above.

    Now even after the above edit, it still doesn't work!!!

    (error in screenshot)

    $Screen Shot 2014-02-20 at 18.30.34.png

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. using Soomla;
    6.  
    7. public class MyStoreAssets : IStoreAssets
    8. {
    9.  
    10.     public const string UNLOCK_PRODUCT_ID = "com.pi.chromakey.unlock"; // <-- create a NONCONSUMABLE IAP with this id in iTunesConnect
    11.  
    12.     static MarketItem marketItem_unlock = new MarketItem(
    13.         UNLOCK_PRODUCT_ID,
    14.         MarketItem.Consumable.NONCONSUMABLE,
    15.         1.99 // <-- Why do *we* set the price (iTunesConnect sets price, surely!)
    16.         );
    17.  
    18.     public static NonConsumableItem UNLOCK = new NonConsumableItem(
    19.         "No Ads",
    20.         "Test purchase of MANAGED item.",
    21.         marketItem_unlock.ProductId,
    22.         new PurchaseWithMarket( marketItem_unlock )
    23.         );
    24.    
    25.  
    26.     public int GetVersion() {
    27.         return 0;
    28.     }
    29.    
    30.     public VirtualCurrency[] GetCurrencies() {
    31.         return new VirtualCurrency[]{};
    32.     }
    33.    
    34.     public VirtualGood[] GetGoods() {
    35.         return new VirtualGood[] {};
    36.     }
    37.    
    38.     public VirtualCurrencyPack[] GetCurrencyPacks() {
    39.         return new VirtualCurrencyPack[] {};
    40.     }
    41.    
    42.     public VirtualCategory[] GetCategories() {
    43.         return new VirtualCategory[]{};
    44.     }
    45.    
    46.     public NonConsumableItem[] GetNonConsumableItems() {
    47.         return new NonConsumableItem[]{ UNLOCK };
    48.     }
    49. }
    50.  
    51. public class Foo : MonoBehaviour {
    52.  
    53.     // Use this for initialization
    54.     void Start () {
    55.         StoreController.Initialize( new MyStoreAssets());
    56.  
    57.         Events.OnMarketPurchaseStarted += OnMarketPurchaseStarted;
    58.         Events.OnMarketPurchase += OnMarketPurchase;
    59.  
    60.         Events.OnItemPurchaseStarted += OnItemPurchaseStarted;
    61.         Events.OnItemPurchased += OnItemPurchased;
    62.  
    63.         Events.OnStoreControllerInitialized += OnStoreControllerInitialized;
    64.  
    65.         Events.OnUnexpectedErrorInStore += OnUnexpectedErrorInStore;
    66.     }
    67.  
    68.     string s = "<nothing>";
    69.  
    70.     void OnGUI( )
    71.     {
    72.         if( GUILayout.Button( "Upgrade" ) ) {
    73.             s += "hit";
    74.             StoreInventory.BuyItem( MyStoreAssets.UNLOCK.ItemId );
    75.         }
    76.  
    77.         GUILayout.Label( s );
    78.     }
    79.  
    80.     public void OnMarketPurchaseStarted( PurchasableVirtualItem pvi ) {
    81.         Debug.Log( "OnMarketPurchaseStarted: " + pvi.ItemId );
    82.         s += "OnMarketPurchaseStarted: " + pvi.ItemId;
    83.     }
    84.     public void OnMarketPurchase( PurchasableVirtualItem pvi ) {
    85.         Debug.Log( "OnMarketPurchase: " + pvi.ItemId );
    86.         s += "OnMarketPurchase: " + pvi.ItemId;
    87.     }
    88.     public void OnItemPurchaseStarted( PurchasableVirtualItem pvi ) {
    89.         Debug.Log( "OnItemPurchaseStarted: " + pvi.ItemId );
    90.         s += "OnItemPurchaseStarted: " + pvi.ItemId;
    91.     }
    92.     public void OnItemPurchased( PurchasableVirtualItem pvi ) {
    93.         Debug.Log( "OnItemPurchased: " + pvi.ItemId );
    94.         s += "OnItemPurchased: " + pvi.ItemId;
    95.     }
    96.  
    97.     public void OnStoreControllerInitialized( ) {
    98.         Debug.Log( "OnStoreControllerInitialized" );
    99.         s += "OnStoreControllerInitialized";
    100.     }
    101.  
    102.     public void OnUnexpectedErrorInStore( string err ) {
    103.         Debug.Log( "OnUnexpectedErrorInStore" + err );
    104.         s += "OnUnexpectedErrorInStore" + err;
    105.     }
    106.  
    107. }
    108.  
    109.  
    There seems to be one contradiction: that code specifies the price, but iTunesConnect requires me to specify the price, what happens if they disagree? And what if I want to change the price on iTC after the app has gone live?
     
    Last edited: Feb 20, 2014
  4. Pi_3-14

    Pi_3-14

    Joined:
    May 9, 2012
    Posts:
    168
    I've had another go today, and found a missing step:

    In iTunes connect, it is not enough just to create the app, go into that app entry and create an IAP.

    You must also link the IAP to the app:

    $Screen Shot 2014-02-23 at 10.41.01.png

    Hit 'View Details'

    $Screen Shot 2014-02-23 at 10.41.17.png

    Hit 'Edit'

    $Screen Shot 2014-02-23 at 10.41.45.png


    Ok now something very strange is happening. Xcode is throwing out a ton of errors:

    $Screen Shot 2014-02-23 at 10.44.54.png


    These can be fixed by adding the SQLite3 library:

    $Screen Shot 2014-02-23 at 11.10.28.png
     
  5. Pi_3-14

    Pi_3-14

    Joined:
    May 9, 2012
    Posts:
    168
    I think there must be some limit on number of screenshots per post...

    Continuing, ...

    Still a few errors! These can be fixed by turning on IAP and enabling Game Centre in the project's capabilities tab (in Xcode):

    $Screen Shot 2014-02-23 at 11.02.53.png

    $Screen Shot 2014-02-23 at 11.03.43.png

    Note that both of these should be on; I may come back and replace those with a single screenshot.

    Now it actually runs on the device, but it gives some weird error. So I'm just about to reboot and see if that fixes anything.

    .. nope:

    $Screen Shot 2014-02-23 at 12.18.58.png

    foo



    This is what's killing it:

    -[NSConcreteData base64EncodedStringWithSeparateLines:]
    unrecognized selector sent to instance 0xab8f640
     

    Attached Files:

    Last edited: Feb 23, 2014
  6. Pi_3-14

    Pi_3-14

    Joined:
    May 9, 2012
    Posts:
    168
    W00t!

    Googling for base64EncodedStringWithSeparateLines soom-la shows a bunch of GitHub issues, with one of the SoomLa authors saying "switch to iOS 7" https://github.com/soomla/cocos2dx-store/issues/35

    But my iPod Touch won't support iOS 7 (this is really poor, I hate Apple for doing this, I cannot see any reason why it should not support iOS 7, and I suspect Apple are just doing this to push people to keep upgrading)

    Anyway, this is the issue: http://stackoverflow.com/questions/...sent-to-instance-when-calling-category-method

    And the solution is just to add a linker flag '-ObjC'

    $Screen Shot 2014-02-23 at 12.51.06.png

    Now it works:

    $Screen Shot 2014-02-23 at 12.52.18.png
    $Screen Shot 2014-02-23 at 12.52.42.png

    Choose "use existing ID", because I have already created a test user on iTunes Connect, I just have to remember the username / password

    $Screen Shot 2014-02-23 at 12.53.30.png
    - - -
    $Screen Shot 2014-02-23 at 12.53.56.png
     
    Last edited: Feb 23, 2014
  7. Pi_3-14

    Pi_3-14

    Joined:
    May 9, 2012
    Posts:
    168
    I still have a few unanswered questions about this process:

    1) I completely don't understand the Custom Secret / Soom Secret:
    $Screen Shot 2014-02-23 at 13.12.46.png


    2) I don't get why SoomLa expects me to supply an item price:


    Code (csharp):
    1.  
    2.     public const string UNLOCK_PRODUCT_ID = "com.pi.chromakey.unlock"; // <-- create a NONCONSUMABLE IAP with this id in iTunesConnect
    3.  
    4.  
    5.  
    6.     static MarketItem marketItem_unlock = new MarketItem(
    7.  
    8.         UNLOCK_PRODUCT_ID,
    9.  
    10.         MarketItem.Consumable.NONCONSUMABLE,
    11.  
    12.         1.99 // <-- Why do *we* set the price (iTunesConnect sets price, surely!)
    13.  
    14.         );
    15.  
    This item price should be set by iTC
    Also it is going to be different for different currencies.
    I don't get it.

    3) for some reason Xcode seems to have added an extra provisioning profile:
    $Screen Shot 2014-02-23 at 13.15.37.png
    Why, and does it make any difference which one I choose?
     
    Last edited: Feb 23, 2014
  8. gurdotan

    gurdotan

    Joined:
    Feb 6, 2013
    Posts:
    15
    Hi @Pi_3.14,

    First of all, I'd like to thank you for sharing this walkthrough with the community. We're working these days on improving the integration process of SOOMLA and creating better docs to support the process. That said, you're writing here is a great resource for developers who are just starting with IAP, and we'll definitely link it from our future community site.

    To answer your questions:

    1) The custom secret and SoomSec are special strings of your choice that the framework uses for encrypting end device data. The rational is that you want all the user's balances stored securely to prevent malicious users from hacking the numbers. The use of two secrets is for increased security. See more details in the Github repos:
    https://github.com/soomla/android-store
    https://github.com/soomla/unity3d-store

    2) All SOOMLA items require you to provide price by design. In the case of market purchase items (currency packs, remove ads etc.) this supports showing prices in situations of no internet connectivity where you can't query iTunes Connect \ Google Play. However, we do intend to improve this to have optional dynamic price fetching. If you'd like to try and implement this yourself, we'll be very glad to receive your contribution on Github and give you the credits.

    3) This is an Apple issue and isn't related to SOOMLA. You can look it up in their docs.
     
  9. Pi_3-14

    Pi_3-14

    Joined:
    May 9, 2012
    Posts:
    168
    Gur,

    Could you explain the custom secret and SoomSec in more detail?

    From the links that you gave, I can't locate any documentation for these. Is there any documentation?

    At the moment when my purchase is confirmed, I catch theOnItemPurchased event:

    Code (csharp):
    1.  
    2.     public void OnItemPurchased( PurchasableVirtualItem pvi ) {
    3.  
    4.         Debug.Log( "OnItemPurchased: " + pvi.ItemId );
    5.  
    6.         s += "OnItemPurchased: " + pvi.ItemId;
    7.  
    8.         PlayerPrefs.SetInt( "unlocked", 1 );
    9.  
    10. }
    Are you saying that SoomLa has an inbuilt version of this functionality, which has some additional security protection?

    And why two separate passwords?

    PS there are a lot of dead links in the documentation pages you linked!
     
  10. nostyleguy

    nostyleguy

    Joined:
    Jul 27, 2012
    Posts:
    16
    I am trying to do exactly the same thing as PI: add 1 non-consumable item to my iOS unity app. I already have the non-consumable item working for android.

    I've followed all the steps, and i'm still getting the NoVirtualItemFoundException with my id.

    What's not clear to me is if I have to upload a version of my app to iTunes Connect for this to start working? According to the OP's screenshots, his app is still in the 'ready to upload' state, but maybe he uploaded it before testing.

    Also, the link posted by gurdotan about having no logged-in users on the test device is a dead link.
     
  11. hadarhod

    hadarhod

    Joined:
    Jun 24, 2014
    Posts:
    2
  12. bboysil

    bboysil

    Joined:
    Jun 1, 2013
    Posts:
    11
    Hi, if I have a virtual item set up in google play store with 3.5 RON (romanian currency)
    1. do I set the PurchaseWithMarket() price parameter to 3.5 RON or 0.99 USD ?
    2. What happens if in other countries the price is conveted or displayed in other currency? Does it still work? Thanks