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

Tracking Monetization: Receipt Verification

Discussion in 'Unity Analytics' started by kentunity, Mar 27, 2015.

  1. kentunity

    kentunity

    Unity Technologies

    Joined:
    Sep 16, 2014
    Posts:
    55
    Tracking monetization in our SDK requires you to call the Transaction method and pass in the necessary parameters.

    The Transaction method looks like this:
    Code (csharp):
    1. UnityAnalytics.Transaction(string productId, decimal price, string currency, string receipt, string signature);
    example:
    Code (csharp):
    1. UnityAnalytics.Transaction("PRODUCT_ID", 0.99m, "USD", null, null);
    We currently support both iOS and Android platforms and also support receipt verification.
    With receipt verification, you'll have the ability to differentiate fraud and real transactions on your dashboard.
    We've had a lot of questions regarding the receipt verification and how to do it when using popular In-App-Purchase plugins like Prime31 or Unibill.

    So we've come up with the following receipt guide:
     
    Last edited: Apr 28, 2015
  2. andymads

    andymads

    Joined:
    Jun 16, 2011
    Posts:
    1,614
    Well I was doing just this but after querying why I didn't get any verified transactions I was told by Angelo Ferro that the receipt could not be base 64 encoded and I had to decode it myself before passing it to you.

    The receipt can't be Base64 encoded. It needs to be decoded first before being passed as a parameter, here is some sample code to decode it:

    byte[] data = System.Convert.FromBase64String("IOS_RECEIPT_GOES_HERE");
    string receipt = System.Text.Encoding.UTF8.GetString(data);
     
  3. angeloferro

    angeloferro

    Moderator

    Joined:
    Sep 12, 2014
    Posts:
    105
    Hi @andymads,

    We recently made a change to our backend infrastructure to accept Base64 encoding for Prime31 transactions. The original instructions I sent you were correct at the time, but we wanted to simplify the receipt verification process by accepting Base64 encoded transactions (so no need to decode it!) :)

    Either way, for Prime31 transactions on iOS, whether you send it decoded or encoded, our backend will still process it correctly. Please let us know if you have any questions :)

    Thanks!
    -Angelo
     
  4. michael_voltage

    michael_voltage

    Joined:
    Feb 27, 2015
    Posts:
    30
    How is the data handled if a duplicate receipt is sent for multiple transactions? Is it treated as just one (under both total revenue and verified)?
     
  5. kentunity

    kentunity

    Unity Technologies

    Joined:
    Sep 16, 2014
    Posts:
    55
    Currently, we don't validate for duplicate receipt, but we do checks based on the timestamp of the receipt and when the transaction event was triggered. Both timestamps have to be within 1 hour.
     
  6. Lotti

    Lotti

    Joined:
    Apr 15, 2013
    Posts:
    18
    Good to know. Time to leave Flurry and save time verifying all those F***ing receipt that no-one implements in the same way.
    I will surely try this service on next game.
     
  7. michael_voltage

    michael_voltage

    Joined:
    Feb 27, 2015
    Posts:
    30
    Thanks Kent for the further details!
     
  8. Splipic

    Splipic

    Joined:
    May 18, 2015
    Posts:
    2
    Hi, Im trying to set my Google Api Key but always I recieved "There was an error updating your project."

    Any guess??
     
  9. sschan

    sschan

    Moderator

    Joined:
    Oct 8, 2014
    Posts:
    87
    Hi @Splipic - We apologize for the error message when you attempted entering your Google API Key. Please try this again as the error is now fixed. Let us know if you need any assistance.
     
  10. Splipic

    Splipic

    Joined:
    May 18, 2015
    Posts:
    2
    It work thank you, the Google Api Key is the Base64-encoded RSA public key right?
     
  11. Antony-Blackett

    Antony-Blackett

    Joined:
    Feb 15, 2011
    Posts:
    1,778
    Hey, I was just implementing this and I was wondering if you think it's worthwhile and/or possible to track refunds? How would I go about doing this? I was thinking of just tracking a negative purchase value... but I wasn't sure if that was a good idea or not.
     
  12. mbirk

    mbirk

    Unity Technologies

    Joined:
    Apr 25, 2014
    Posts:
    18
    Hi @Splipic, Yes that is the correct Google API key, as described in Google's documentation. Our receipt verification for Google Play apps works by base64-decoding the key and using it to verify the signature in the receipt.
     
  13. eezSZI

    eezSZI

    Joined:
    Nov 16, 2012
    Posts:
    121
    Is there currently a way to check the response of verified or unverified - or is that a future API? Is it foolproof enough to disable future IAP for a user with unverified transactions - assuming they have a tampered version of the app?

    My instinct says no, but isn't that the point of monitoring if the transactions are verified?
     
  14. mpinol

    mpinol

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

    Currently there is no returned value on a Transaction method so determining if an in app purchase was verified or unverified is not possible. Our goal is to accurately display your transaction histories, by separating your verified transaction from fraudulent ones, and not to prevent fraudulent transactions from occurring.
     
  15. eezSZI

    eezSZI

    Joined:
    Nov 16, 2012
    Posts:
    121
    mpinol likes this.
  16. vladrybak

    vladrybak

    Joined:
    Aug 30, 2013
    Posts:
    108
    Hello, I'm using Unity 5.2.1p2 and build games for windows, android, ios and web. Does purchase verification exist now only for android and ios? Did you plan to integrate it for windows store/phone 8.1/10 and for facebook?
     
  17. mpinol

    mpinol

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

    Yes, we currently only support purchase verification for Android and iOS. Windows store is certainly on our roadmap but I cannot give you any real information about when it will be available. I do not believe Facebook has a purchase verification system in place, if you have any information on this you could link me that would be greatly appreciated, but I will look into the possibility of adding Facebook to our future plans as well.
     
  18. fpomerleau

    fpomerleau

    Joined:
    Jun 22, 2015
    Posts:
    1
    Hi everyone,

    We are using Unibill in Unity 5.1.1p2 and everything seems to work properly, excepted that we do not see the transactions in Unity Analytics. Even though the transactions are processed in iTunes Connect.

    Any guess?
     
  19. mpinol

    mpinol

    Joined:
    Jul 29, 2015
    Posts:
    317
  20. ailen

    ailen

    Joined:
    Mar 4, 2014
    Posts:
    6
    Hi @kentunity

    can we filter our test devices with purchase data or it will mix the test data and real data.
     
  21. mpinol

    mpinol

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

    As of right now there is no way to keep the test data and real data separated on the Dashboard. There are two kinds of approaches you can use to keep your test data separated from your real data.

    1. When building your projects check 'Development Build' in the Build Settings window. This will send all of the Analytics data directly to the Validator on the integration pages. The only downside to this is that you will only be able to view this data as it is coming in and not be able to reference it at a later date so this would be most appropriate for testing whether or not your events are formatted correctly with the correct monetary values.

    2. You could create a new Analytics project and then link that project id to your Unity project only when you are creating test builds. The main issue with this is that you will need to be rather diligent about making sure that the appropriate Analytics project is linked to your Unity project when creating builds but it is much more useful being able to reference the data at a later date.
     
  22. levius

    levius

    Joined:
    Jun 18, 2016
    Posts:
    19
    Hi @mpinol . Can you help me?
    I don't understand what insert in Analytics.Transaction(productId, amount, currency, --->receiptPurchaseData<---, --->signature<---)
    from Unity IAP. I have IPurchaseReceipt, than I cast it to GooglePlayReceipt & AppleInAppPurchaseReceipt.

    Can I use Analytics.Transition >>OK return's value<< to identify the receipt is valid?

    Thank's. Best regards, Andrew
     
  23. ap-unity

    ap-unity

    Unity Technologies

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

    If you are using Unity IAP then you do not need to use the Transaction events. This method is used internally in the Unity IAP purchase code. If you were to use Unity IAP and the Transaction event, then you will be double counting your revenue and it could cause issues and discrepancies in your data.
     
  24. levius

    levius

    Joined:
    Jun 18, 2016
    Posts:
    19
    This isn't true. I think you don't test this or don't realy know how it works.

    Yes, I'm using Unity IAP. But, I don't see Revenue, only unverified revenue - but this is not correct information.

    For iOS I fixed this problem.

    AppleInAppPurchaseReceipt apple = productReceipt as AppleInAppPurchaseReceipt;
    Analytics.Transaction(productId, amount, currency, apple.ToString(), null);

    It works very well!

    But for Google Play, I don't know where I can get signature for my transaction.
    Please ask about it in those who develop analytics.
     
  25. levius

    levius

    Joined:
    Jun 18, 2016
    Posts:
    19
    Local receipt verification doesn't work too. I have many fake purchases.
     
  26. ap-unity

    ap-unity

    Unity Technologies

    Joined:
    Aug 3, 2016
    Posts:
    1,519
  27. andymads

    andymads

    Joined:
    Jun 16, 2011
    Posts:
    1,614
    In the Receipt Validation section of the docs it says if you are using the Prime31 StoreKit iOS plugin to pass in the base64EncodedTransactionReceipt property. This property was removed in plugin version 2.17 (September 2016) "as it has been deprecated by Apple since iOS 7".

    FYI the AppsFlyer plugin we use has a similar method but it takes the transaction id.
     
  28. dragonfoundry

    dragonfoundry

    Joined:
    Jun 18, 2014
    Posts:
    4
    To confirm something I think is right: If I'm using Unity Analytics for a Steam game, I should be calling "Analytics.Transaction(itemId, price, currency)" for every successful purchase. But for an iOS/Android game using Unity Purchasing, I should not call that, as Unity Purchasing already handles that
     
  29. ap-unity

    ap-unity

    Unity Technologies

    Joined:
    Aug 3, 2016
    Posts:
    1,519
    Yes, that is correct.
     
    sfilo likes this.
  30. krisventure

    krisventure

    Joined:
    Mar 24, 2016
    Posts:
    118
    @ap-unity Could you point me to the documentation of this, saying that we don't need to add the transaction code if we use Unity Analytics on iOS? I've been reading Unity analytics integrations tutorials for a while and this detail wasn't mentioned.
     
  31. ap-unity

    ap-unity

    Unity Technologies

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

    That is definitely something we should make more clear in the documentation. I think it's meant to be implied by the fact that Analytics is required for Unity IAP, but we should update our docs.

    I did find a reference to it in the Manual, under Monetization Metrics:

    >IAP transactions include Unity IAP purchases and purchases reported using the Analytics.Transaction() function.

    https://docs.unity3d.com/Manual/UnityAnalyticsTerminology.html
     
    krisventure likes this.
  32. krisventure

    krisventure

    Joined:
    Mar 24, 2016
    Posts:
    118
    @ap-unity Awesome thanks! On another note, we're submitting our iOS app tomorrow with Unity IAP/analytics/ads. The IAP transaction currently takes me like 15 seconds before it returns payment success (but this is on TestFlight, app isn't released yet). Is the extreme slow response time related to Unity IAP - iTunes Store interface, is it always going to be that slow or perhaps related to TestFlight?

    Sorry about the unrelated question.
     
  33. ap-unity

    ap-unity

    Unity Technologies

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

    Once you initiate a purchase, we call the native purchase API, so the OS takes over after that. We have definitely heard reports of slow and weird behavior with sandbox purchases that are cleared up once the app is released. However, I can't really make any guarantees. For the most part, Unity IAP is a wrapper for native functionality, so we try to be as efficient as we can.
     
  34. krisventure

    krisventure

    Joined:
    Mar 24, 2016
    Posts:
    118
    @ap-unity I'm seeing another weird behavior with IAP in Testflight: I've been bombarded with lots of Sign in alerts in the game, continuously, like every 10 seconds. Every time it asks me to type in my password but when I do so it comes back again later. It started when I signed in from my iPhone with a test account created in iTunes Connect, and then (after testing with test account finished) I signed out and back into my own personal account to continue testing with my own account. So ever since I'm back to my own account (the one that worked fine before), I just can't silence this pop up message.

    I've read on multiple Apple forums that 'This usually means that your app failed to call finishTransaction for one or more transactions sent to updatedTransactions. IStoreKit wants the appropriate user to log in so it can resend the transactions.' But I'm just using the IAP script provided by Unity, with little modification.

    Is it possible that Unity IAP isn't taking care of calling finishTransaction in some cases (somehow related to Testflight and sandbox users)?
     
  35. Feelnside

    Feelnside

    Joined:
    Sep 30, 2016
    Posts:
    83
    Sorry for posting into this thread, but looks like the issue is related to this one.

    I'm getting the error below once I'm trying to perform any purchase in Unity Editor:

    So the main issue with the UnityEngine.Analytics.Analytics.Transaction method. Due to some reason it always breaks once I click 'purchase' for any item.

    It works perfectly on Android, but in Editor it always breaks and as result I can't perform any purchase in Unity Editor.

    I used Unity 2018.1.0f2

    Could someone take a look at this one?
     
  36. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    Can you provide your purchase script and show where you are calling this method? Have you checked to see which parameter is null? You could use Debug.Log. So you know, there are no "real" purchases in the Editor as you are using the fake store, but might point to another issue.
     
  37. Feelnside

    Feelnside

    Joined:
    Sep 30, 2016
    Posts:
    83
    The issue is reproducible in both ways:
    - Codeless (just IAP Button and IAP catalog with products)
    - Example script of Survival Shooter

    Once I'm calling:
    "controller.InitiatePurchase(product);"
    I'm receiving the issue. I can't check which parameter is null because it's Unity Analytics scripts, as far as I know I have no access to it. All I have it's the following exception below:

    Here is test copy of script:

    Code (CSharp):
    1.  
    2.     IStoreController controller;
    3.     IExtensionProvider extensions;
    4.  
    5.     gameID = "com.test.test";
    6.  
    7.     void Start()
    8.     {
    9.         InitializePurchasing();
    10.     }
    11.  
    12.     public void InitializePurchasing()
    13.     {
    14.         if (IsInitialized())
    15.         {
    16.             return;
    17.         }
    18.         var builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance());
    19.  
    20.         builder.AddProduct(gameID + "lives1", ProductType.NonConsumable);
    21.         builder.AddProduct(gameID + "2x", ProductType.NonConsumable);
    22.         builder.AddProduct(gameID + "gems1", ProductType.Consumable);
    23.  
    24.         UnityPurchasing.Initialize(this, builder);
    25.     }
    26.  
    27.     public void OnInitialized(IStoreController controller, IExtensionProvider extensions)
    28.     {
    29.         this.controller = controller;
    30.         this.extensions = extensions;
    31.     }
    32.  
    33.     public void BuyProductID(string productId)
    34.     {
    35.         productId = gameID + productId;
    36.         if (IsInitialized())
    37.         {
    38.             Product product = controller.products.WithID(productId);
    39.  
    40.             if (product != null && product.availableToPurchase)
    41.             {
    42.                 Debug.Log(string.Format("Purchasing product asychronously: '{0}'", product.definition.id));
    43.                 controller.InitiatePurchase(product);
    44.             }
    45.             else
    46.             {
    47.                 Debug.Log("BuyProductID: FAIL. Not purchasing product, either is not found or is not available for purchase");
    48.             }
    49.         }
    50.         else
    51.         {
    52.             Debug.Log("BuyProductID FAIL. Not initialized.");
    53.         }
    54.     }
    55.  
    56.     public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs e)
    57.     {
    58.         bool validPurchase = true;
    59.  
    60.         #if UNITY_ANDROID || UNITY_IOS || UNITY_STANDALONE_OSX
    61.         var validator = new CrossPlatformValidator(GooglePlayTangle.Data(),
    62.             AppleTangle.Data(), Application.identifier);
    63.  
    64.         try
    65.         {
    66.             var result = validator.Validate(e.purchasedProduct.receipt);
    67.             Debug.Log("Receipt is valid. Contents:");
    68.             foreach (IPurchaseReceipt productReceipt in result)
    69.             {
    70.                 Debug.Log(productReceipt.productID);
    71.                 Debug.Log(productReceipt.purchaseDate);
    72.                 Debug.Log(productReceipt.transactionID);
    73.             }
    74.         }
    75.         catch (IAPSecurityException)
    76.         {
    77.             Debug.Log("Invalid receipt, not unlocking content");
    78.             validPurchase = false;
    79.         }
    80.         #endif
    81.  
    82.         if (validPurchase)
    83.         {
    84.             //purchase logic
    85.         }
    86.  
    87.         return PurchaseProcessingResult.Complete;
    88.     }
    89.  
    90.     public void OnPurchaseFailed(Product p, PurchaseFailureReason r)
    91.     {
    92.         Debug.Log(string.Format("OnPurchaseFailed: FAIL. Product: '{0}', PurchaseFailureReason: {1}", p.definition.storeSpecificId, r));
    93.     }
    94.  
    I can use workaround (just remove any purchase verification in Editor and run 'purchase logic' manually), but it's a bit strange, previously it was worked (in older versions, ~Unity 2017.1-2).
     
    Last edited: May 4, 2018
  38. dhitz666

    dhitz666

    Joined:
    Sep 20, 2017
    Posts:
    3
    ArgumentException: Cannot set currency to an empty or null string
    UnityEngine.Analytics.Analytics.Transaction (System.String productId, Decimal amount, System.String currency, System.String receiptPurchaseData, System.String signature, Boolean usingIAPService) (at /Users/builduser/buildslave/unity/build/Modules/UnityAnalytics/UnityAnalytics.cs:134)
    UnityEngine.Purchasing.UnityAnalytics.Transaction (System.String productId, Decimal price, System.String currency, System.String receipt, System.String signature) (at /Users/stevenb/unity-src3/Extensions/UnityPurchasing/Runtime/Purchasing/UnityAnalytics.cs:14)
    UnityEngine.Purchasing.AnalyticsReporter.OnPurchaseSucceeded (UnityEngine.Purchasing.Product product) (at /Users/stevenb/unity-src3/Extensions/UnityPurchasing/Runtime/Purchasing/AnalyticsReporter.cs:27)
    UnityEngine.Purchasing.StoreListenerProxy.ProcessPurchase (UnityEngine.Purchasing.PurchaseEventArgs e) (at /Users/stevenb/unity-src3/Extensions/UnityPurchasing/Runtime/Purchasing/StoreListenerProxy.cs:33)
    UnityEngine.Purchasing.PurchasingManager.ProcessPurchaseIfNew (UnityEngine.Purchasing.Product product) (at /Users/stevenb/unity-src3/Extensions/UnityPurchasing/Runtime/Purchasing/PurchasingManager.cs:211)
    UnityEngine.Purchasing.PurchasingManager.OnPurchaseSucceeded (System.String id, System.String receipt, System.String transactionId) (at /Users/stevenb/unity-src3/Extensions/UnityPurchasing/Runtime/Purchasing/PurchasingManager.cs:115)
    UnityEngine.Purchasing.JSONStore.OnPurchaseSucceeded (System.String id, System.String receipt, System.String transactionID)
    UnityEngine.Purchasing.FakeStore.<>n__0 (System.String id, System.String receipt, System.String transactionID)
    UnityEngine.Purchasing.FakeStore+<>c__DisplayClass15_0.<FakePurchase>b__0 (Boolean allow, PurchaseFailureReason failureReason)
    UnityEngine.Purchasing.FakeStore.FakePurchase (UnityEngine.Purchasing.ProductDefinition product, System.String developerPayload)
    UnityEngine.Purchasing.FakeStore.Purchase (System.String productJSON, System.String developerPayload)
    UnityEngine.Purchasing.JSONStore.Purchase (UnityEngine.Purchasing.ProductDefinition product, System.String developerPayload)
    UnityEngine.Purchasing.PurchasingManager.InitiatePurchase (UnityEngine.Purchasing.Product product, System.String developerPayload) (at /Users/stevenb/unity-src3/Extensions/UnityPurchasing/Runtime/Purchasing/PurchasingManager.cs:60)
    UnityEngine.Purchasing.PurchasingManager.InitiatePurchase (UnityEngine.Purchasing.Product product) (at /Users/stevenb/unity-src3/Extensions/UnityPurchasing/Runtime/Purchasing/PurchasingManager.cs:38)
    IAPManager.BuyProductID (System.String productId) (at Assets/Scripts/Manager/IAPManager.cs:85)
    UnityEngine.Events.InvokableCall`1[System.String].Invoke (System.String args0) (at /Users/builduser/buildslave/unity/build/Runtime/Export/UnityEvent.cs:207)
    UnityEngine.Events.CachedInvokableCall`1[System.String].Invoke (System.Object[] args) (at /Users/builduser/buildslave/unity/build/Runtime/Export/UnityEvent.cs:345)
    UnityEngine.Events.UnityEvent.Invoke () (at /Users/builduser/buildslave/unity/build/Runtime/Export/UnityEvent_0.cs:70)
    UnityEngine.UI.Button.Press () (at /Users/builduser/buildslave/unity/build/Extensions/guisystem/UnityEngine.UI/UI/Core/Button.cs:36)
    UnityEngine.UI.Button.OnPointerClick (UnityEngine.EventSystems.PointerEventData eventData) (at /Users/builduser/buildslave/unity/build/Extensions/guisystem/UnityEngine.UI/UI/Core/Button.cs:45)
    UnityEngine.EventSystems.ExecuteEvents.Execute (IPointerClickHandler handler, UnityEngine.EventSystems.BaseEventData eventData) (at /Users/builduser/buildslave/unity/build/Extensions/guisystem/UnityEngine.UI/EventSystem/ExecuteEvents.cs:50)
    UnityEngine.EventSystems.ExecuteEvents.Execute[IPointerClickHandler] (UnityEngine.GameObject target, UnityEngine.EventSystems.BaseEventData eventData, UnityEngine.EventSystems.EventFunction`1 functor) (at /Users/builduser/buildslave/unity/build/Extensions/guisystem/UnityEngine.UI/EventSystem/ExecuteEvents.cs:261)
    UnityEngine.EventSystems.EventSystem:Update()




    you same with me
    right now down grade to unity 2017
     
  39. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    @dhitz666 Is this error when running in the Editor? What version of Unity?
     
  40. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
  41. Feelnside

    Feelnside

    Joined:
    Sep 30, 2016
    Posts:
    83
    I found the issue. Previously I was used the codeless method of the IAP. As result I have added several Products into the IAP Catalog. So, to resolve the issue you just need to remove the all items from the IAP Catalog and then it works as expected without any errors.

    But it's not the fix if you are using the codeless method. So here are the steps to reproduce the issue (which is relevant for users who are using codeless method):
    1) Open the sample project: https://forum.unity.com/threads/sample-iap-project.529555/
    2) Go to Window > Unity IAP > IAP Catalog
    3) Add the Product with the "ID" = "noads" (this is NonConsumable purchase of the sample project)
    4) Press the Play button
    5) Click the Non-Consumable button
    Actual Result: "ArgumentException: Cannot set currency to an empty or null string" issue appears. Looks like there is some problem with IAP Catalog price configuration.
     
    Last edited: May 11, 2018
  42. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    I have reproduced the issue in the sample project. But I believe you meant to say "Which is not relevant.." correct?
     
  43. Feelnside

    Feelnside

    Joined:
    Sep 30, 2016
    Posts:
    83
    I meant this issue is a problem for users who are using the codeless method (IAP Buttons + IAP Catalog). In my case I just removed all products from IAP Catalog and the issue disappeared for me.
     
  44. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    Got it, we will address this issue!
     
  45. Feelnside

    Feelnside

    Joined:
    Sep 30, 2016
    Posts:
    83
    Thank you for your reply!
     
  46. dhitz666

    dhitz666

    Joined:
    Sep 20, 2017
    Posts:
    3
    2018.1.0f2
    only error in editor