Search Unity

[Solved] ProcessPurchase event get called multiple times

Discussion in 'Unity IAP' started by siddharth3322, Nov 28, 2017.

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

    siddharth3322

    Joined:
    Nov 29, 2013
    Posts:
    1,049
    Before I have purchased Weekly Subscription and now whenever I open my app again, ProcessPurchase event get called multiple times. Though this time I have purchased nothing.

    See below xCode log for more clearance:
    SubscriptionProcessPurchaseCall.png

    Now I have just open the app so no reason for event get called multiple times by itself.
    As per my thinking, it should get call only one time when ever you make purchase something.
    Then after there is no reason of this callback.

    Mainly I was following this document and its code:
    https://unity3d.com/learn/tutorials/topics/ads-analytics/integrating-unity-iap-your-game

    Here is my snippet of code:

    Code (CSharp):
    1. public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs args)
    2.     {
    3.         if (String.Equals(args.purchasedProduct.definition.id, kProductIDPick_099, StringComparison.Ordinal))
    4.         {
    5.             Debug.Log(string.Format("ProcessPurchase: PASS. Product: '{0}'", args.purchasedProduct.definition.id));
    6.         }
    7.         else if (String.Equals(args.purchasedProduct.definition.id, kProductIDPick_199, StringComparison.Ordinal))
    8.         {
    9.             Debug.Log(string.Format("ProcessPurchase: PASS. Product: '{0}'", args.purchasedProduct.definition.id));
    10.         }
    11.         else if (String.Equals(args.purchasedProduct.definition.id, kProductIDPick_299, StringComparison.Ordinal))
    12.         {
    13.             Debug.Log(string.Format("ProcessPurchase: PASS. Product: '{0}'", args.purchasedProduct.definition.id));
    14.         }
    15.         else if (String.Equals(args.purchasedProduct.definition.id, kProductIDPick_399, StringComparison.Ordinal))
    16.         {
    17.             Debug.Log(string.Format("ProcessPurchase: PASS. Product: '{0}'", args.purchasedProduct.definition.id));
    18.         }
    19.         else if (String.Equals(args.purchasedProduct.definition.id, kProductIDPick_499, StringComparison.Ordinal))
    20.         {
    21.             Debug.Log(string.Format("ProcessPurchase: PASS. Product: '{0}'", args.purchasedProduct.definition.id));
    22.         }
    23.         else if (String.Equals(args.purchasedProduct.definition.id, kProductIDWeeklySubscription, StringComparison.Ordinal))
    24.         {
    25.             Debug.Log(string.Format("ProcessPurchase: PASS. Product: '{0}'", args.purchasedProduct.definition.id));
    26.  
    27.             SubscriptionActivated();
    28.  
    29.             //if (DataStorage.RetrieveLoggedInStatus() == GameConstants.ON)
    30.             //    StartCoroutine(CheckForSubscriptionStatus());
    31.         }
    32.         else if (String.Equals(args.purchasedProduct.definition.id, kProductIDMonthlySubscription, StringComparison.Ordinal))
    33.         {
    34.             Debug.Log(string.Format("ProcessPurchase: PASS. Product: '{0}'", args.purchasedProduct.definition.id));
    35.  
    36.             SubscriptionActivated();
    37.             //if (DataStorage.RetrieveLoggedInStatus() == GameConstants.ON)
    38.             //    StartCoroutine(CheckForSubscriptionStatus());
    39.         }
    40.         else
    41.         {
    42.             Debug.Log(string.Format("ProcessPurchase: FAIL. Unrecognized product: '{0}'", args.purchasedProduct.definition.id));
    43.         }
    44.  
    45.         return PurchaseProcessingResult.Complete;
    46.     }
    47.  
    48. private void SubscriptionActivated()
    49.     {
    50.         GameManager.Instance.IsSubscriptionRunning = true;
    51.         if (paidPickParser != null)
    52.             paidPickParser.UnlockAllPaidPicks();
    53.         Camera.main.SendMessage("ActivateSubscriptionDialog", false, SendMessageOptions.DontRequireReceiver);
    54.     }
    Before I have made purchase of Weekly Subscription.
    Another point, why I was getting NullReferenceException through internal method? See above log image for more details.
     
    Last edited: Nov 28, 2017
  2. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    The IAP team is looking into this. The NullReference exception may imply that the transaction is not completing properly, and may explain the repeated attempts. If it turns out to be an issue on our end, we will try to get it into a future release.
     
    siddharth3322 likes this.
  3. siddharth3322

    siddharth3322

    Joined:
    Nov 29, 2013
    Posts:
    1,049
    Thank you sir for your side quick reply.

    Now please do something for this as early as possible because I have already launched the app into the market and its not functioning properly in subscription purchase.
    So I require to update soon app regarding this.

    One more question, why ProcessPurchase event get called multiple times ?
     
  4. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    @siddharth3322 We believe the original transaction never completes (evidence from the NullReference exception), so we receive a repeated callback hoping to clear the pending transaction.
     
  5. siddharth3322

    siddharth3322

    Joined:
    Nov 29, 2013
    Posts:
    1,049
    Now on every app launch ProcessPurchase method get called one time. So what is the reason behind this ??
    Because now I have not purchased anything.
     
  6. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    @siddharth3322 Can you elaborate? Are you logging in as a new user, or is this the same user with the weekly subscription? Please show your logs as before. What version of IAP are you using?
     
  7. siddharth3322

    siddharth3322

    Joined:
    Nov 29, 2013
    Posts:
    1,049
    Basically at present I was using Sandbox apple store account for testing purpose. Using this account I have purchased weekly subscription before and it was successful for me but multiple tries happen for this purpose so I got multiple NullReferenceException and multiple time called of ProcessPurchase success call rather than PurchaseFail method call.
    My iMac running little bit slow but I am giving you all details by attaching the device now.

    Unity IAP Version: 1.14.1
    Just 2 days ago, I have opened Unity and update related dialog box come so I have update that.
     
  8. siddharth3322

    siddharth3322

    Joined:
    Nov 29, 2013
    Posts:
    1,049
    Here you have xCode log data, at present I have just loaded main page of application.

    Screen Shot 2017-11-29 at 11.31.29 PM.png

    Now because of this problem - without any reason subscription purchase related code get executed.
    So in free, app user can take benefit of subscription purchase.

    Sir let me know any further help you want from me.
     
  9. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    @siddarth3322 This looks like the original issue that you reported with the NullReferenceException. We are looking into it.
     
    siddharth3322 likes this.
  10. siddharth3322

    siddharth3322

    Joined:
    Nov 29, 2013
    Posts:
    1,049
    Thank you sir :)
    Basically in above case all time, OnPurchaseFailed should require to get called.
    On whichever transaction was successful that should only call ProcessPurchase method.
    This is the actual solution for the problem.

    Lets give me reply for following points so I become more clear in implementation:
    1) if app user purchased weekly subscription and after 10 days he opened the app, then ProcessPurchase method get called or not ??
    2) If ProcessPurchase method get called then is it my responsibility to check subscription active or not? Before giving advantage of subscription.
    3) Giving subscription purchase advantage, at whichever place I have written activate subscription related code in above my mentioned snippet, is that correct place to write? because in real subscription purchase a person can't able to get success in unlocking things after subscription purchase was successful. Just payment get cut and no advantage he can able to get.
    SBK App Link
    https://itunes.apple.com/us/app/sports-bet-kingz/id1134216385?mt=8
     
  11. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    @siddarth3322 As mentioned, we are looking into this. Once we have more information, I will share it with you.
     
    siddharth3322 likes this.
  12. JayR

    JayR

    Joined:
    Aug 2, 2016
    Posts:
    84
    @siddarth3322 A null reference exception like you're seeing is typically a result of something being misconfigured in your application but we really can't assess that without additional information from you. First, what version of Unity and IAP plugin are you using? Second, are you using codeless IAP buttons or are you configuring your products manually using builder.AddProduct()? Are there any specific extension / plugin options you are setting in your application?

    Can you provide the complete log from xcode from when the app starts through the errors? The screen shots you've provided here don't provide sufficient context and don't show what kind of configuration issues may have been encountered earlier in the setup process. The full log would also answer the Unity and IAP version questions for us.

    What you're seeing with multiple process purchase calls is actually normal in the case of auto-renewing subscriptions. When you test in the sandbox those subscriptions renew very quickly (how fast depends on sub period) and you can often see several of those appear in the queue after an app restart. Also, if a purchase hasn't been successfully completed (which is likely given those exceptions) then they can remain in the queue and result in multiple calls to your ProcessPurchase on every app restart until the problem is resolved.
     
    siddharth3322 likes this.
  13. siddharth3322

    siddharth3322

    Joined:
    Nov 29, 2013
    Posts:
    1,049
    Thanks to whole Unity team for taking one request this much seriously and working into this.
    For me this is really really important because sooner I require to publish new version of application.
    Below I have mentioned one by one points reply those you mentioned above.

    1) I was using this versions:
    Unity 5.4.2p4
    Unity IAP Plugin 1.14.1

    2) Complete xCode log, from app launch to auto execution of ProcessPurchase method.
    Code (CSharp):
    1. 2017-11-30 16:18:32.125492 com[14940:3760623] [DYMTLInitPlatform] platform initialization successful
    2. 2017-11-30 16:18:32.162071 com[14940:3760446] UnityIAP UnityEarlyTransactionObserver: Created
    3. 2017-11-30 16:18:32.164843 com[14940:3760446] UnityIAP UnityEarlyTransactionObserver: Registered for lifecycle events
    4.  
    5. 2017-11-30 16:18:32.284895 com[14940:3760446] -> registered mono modules 0xfeddb0
    6. 2017-11-30 16:18:33.290654 com[14940:3760446] You've implemented -[<UIApplicationDelegate> application:didReceiveRemoteNotification:fetchCompletionHandler:], but you still need to add "remote-notification" to the list of your supported UIBackgroundModes in your Info.plist.
    7. -> applicationDidFinishLaunching()
    8. 2017-11-30 16:18:33.495688 com[14940:3760446] Metal GPU Frame Capture Enabled
    9. 2017-11-30 16:18:33.496629 com[14940:3760446] Metal API Validation Enabled
    10. 2017-11-30 16:18:33.938819 com[14940:3760446] UnityIAP UnityEarlyTransactionObserver: Added to the payment queue
    11. -> applicationDidBecomeActive()
    12. Renderer: PowerVR SGX 554
    13. Vendor:   Imagination Technologies
    14. Version:  OpenGL ES 2.0 IMGSGX554-128
    15. GLES:     2
    16. GL_OES_depth_texture GL_OES_depth24 GL_OES_element_index_uint GL_OES_fbo_render_mipmap GL_OES_mapbuffer GL_OES_packed_depth_stencil GL_OES_rgb8_rgba8 GL_OES_standard_derivatives GL_OES_texture_float GL_OES_texture_half_float GL_OES_texture_half_float_linear GL_OES_vertex_array_object GL_EXT_blend_minmax GL_EXT_color_buffer_half_float GL_EXT_debug_label GL_EXT_debug_marker GL_EXT_discard_framebuffer GL_EXT_draw_instanced GL_EXT_instanced_arrays GL_EXT_map_buffer_range GL_EXT_occlusion_query_boolean GL_EXT_pvrtc_sRGB GL_EXT_read_format_bgra GL_EXT_separate_shader_objects GL_EXT_shader_framebuffer_fetch GL_EXT_shader_texture_lod GL_EXT_shadow_samplers GL_EXT_sRGB GL_EXT_texture_filter_anisotropic GL_EXT_texture_rg GL_EXT_texture_storage GL_APPLE_clip_distance GL_APPLE_color_buffer_packed_float GL_APPLE_copy_texture_levels GL_APPLE_framebuffer_multisample GL_APPLE_rgb_422 GL_APPLE_sync GL_APPLE_texture_format_BGRA8888 GL_APPLE_texture_max_level GL_APPLE_texture_packed_float GL_IMG_read_format GL_IMG_texture_comp
    17.  
    18. ression_pvrtc
    19. OPENGL LOG: Creating OpenGL ES 2.0 graphics device ; Context level  <OpenGL ES 2.0> ; Context handle 358238688
    20. Initialize engine version: 5.4.2p4 (e59bdccd995f)
    21. -------- Shader compilation failed
    22. #version 100
    23. #extension GL_EXT_frag_depth : enable
    24. precision highp float;
    25. uniform highp vec4 _ProjectionParams;
    26. uniform highp vec4 _ZBufferParams;
    27. uniform highp mat4 unity_CameraToWorld;
    28. uniform highp mat4 _NonJitteredVP;
    29. uniform highp mat4 _PreviousVP;
    30. uniform highp sampler2D _CameraDepthTexture;
    31. varying highp vec2 xlv_TEXCOORD0;
    32. varying highp vec3 xlv_TEXCOORD1;
    33. void main ()
    34. {
    35.  highp vec4 tmpvar_1;
    36.  tmpvar_1 = texture2D (_CameraDepthTexture, xlv_TEXCOORD0);
    37.  mediump vec2 tmpvar_2;
    38.  highp vec4 tmpvar_3;
    39.  tmpvar_3.w = 1.0;
    40.  tmpvar_3.xyz = ((xlv_TEXCOORD1 * (_ProjectionParams.z / xlv_TEXCOORD1.z)) * (1.0/((
    41.    (_ZBufferParams.x * tmpvar_1.x)
    42.   + _ZBufferParams.y))));
    43.  highp vec4 tmpvar_4;
    44.  tmpvar_4 = (unity_CameraToWorld * tmpvar_3);
    45.  highp vec4 tmpvar_5;
    46.  tmpvar_5 = (_PreviousVP * tmpvar_4);
    47.  highp vec4 tmpvar_6;
    48.  tmpvar_6 = (_NonJitteredVP * tmpvar_4);
    49.  highp vec2 tmpvar_7;
    50.  tmpvar_7 = (((tmpvar_5.xy / tmpvar_5.w) + 1.0) / 2.0);
    51.  highp vec2 tmpvar_8;
    52.  tmpvar_8 = (((tmpvar_6.xy / tmpvar_6.w) + 1.0) / 2.0);
    53.  tmpvar_2 = (tmpvar_8 - tmpvar_7);
    54.  mediump vec4 tmpvar_9;
    55.  tmpvar_9.zw = vec2(0.0, 1.0);
    56.  tmpvar_9.xy = tmpvar_2;
    57.  gl_FragDepthEXT = tmpvar_1.x;
    58.  gl_FragData[0] = tmpvar_9;
    59. }
    60. -------- failed compiling:
    61. fragment evaluation shader
    62. WARNING: 0:4: extension 'GL_EXT_frag_depth' is not supported
    63. ERROR: 0:38: Use of undeclared identifier 'gl_FragDepthEXT'
    64.  
    65. Note: Creation of internal variant of shader 'Hidden/Internal-MotionVectors' failed.
    66. WARNING: Shader Unsupported: 'Hidden/Internal-MotionVectors' - Pass '' has no vertex shader
    67. WARNING: Shader Unsupported: 'Hidden/Internal-MotionVectors' - Setting to default shader.
    68. UnloadTime: 5.448750 ms
    69. Unloading 5 Unused Serialized files (Serialized files now loaded: 0)
    70. UnloadTime: 4.035500 ms
    71. 2017-11-30 16:18:37.194062 com[14940:3760446] You are using download over http. Currently unity adds NSAllowsArbitraryLoads to Info.plist to simplify transition, but it will be removed soon. Please consider updating to https.
    72. 2017-11-30 16:18:37.208518 com[14940:3760446] [core] SLComposeViewController isAvailableForServiceType com.apple.social.twitter
    73. 2017-11-30 16:18:37.243168 com[14940:3760446] [core] SLComposeViewController isAvailableForServiceType got serviceType com.apple.social.twitter isAvailable 1
    74. 2017-11-30 16:18:37.243241 com[14940:3760446] [core] SLComposeViewController isAvailableForServiceType com.apple.social.twitter returning 1
    75. 2017-11-30 16:18:37.256276 com[14940:3760634] [core] __42-[ACAccountStore accountsWithAccountType:]_block_invoke_2 (208) "Error returned from daemon: <private>"
    76. 2017-11-30 16:18:37.281493 com[14940:3760446] You are using download over http. Currently unity adds NSAllowsArbitraryLoads to Info.plist to simplify transition, but it will be removed soon. Please consider updating to https.
    77. 2017-11-30 16:18:37.282122 com[14940:3760446] You are using download over http. Currently unity adds NSAllowsArbitraryLoads to Info.plist to simplify transition, but it will be removed soon. Please consider updating to https.
    78. 2017-11-30 16:18:37.282936 com[14940:3760446] You are using download over http. Currently unity adds NSAllowsArbitraryLoads to Info.plist to simplify transition, but it will be removed soon. Please consider updating to https.
    79. 2017-11-30 16:18:37.295040 com[14940:3760446] You are using download over http. Currently unity adds NSAllowsArbitraryLoads to Info.plist to simplify transition, but it will be removed soon. Please consider updating to https.
    80. UnityIAP StandardPurchasingModule Version: 1.14.1
    81. PurchaseManager:InitializePurchasing()
    82. (Filename: /Users/builduser/buildslave/unity/build/artifacts/generated/common/runtime/UnityEngineDebugBindings.gen.cpp Line: 42)
    83.  
    84. 2017-11-30 16:18:37.352419 com[14940:3760446] UnityIAP:Requesting 7 products
    85. Using Facebook Unity SDK v7.8.0 with FBiOSSDK/4.14.0
    86. (Filename: /Users/builduser/buildslave/unity/build/artifacts/generated/common/runtime/UnityEngineDebugBindings.gen.cpp Line: 42)
    87.  
    88. SendMessage: object GameController does not have receiver for function UnityCallBack!
    89. FB.Init completed: Is user logged in? False
    90. Facebook.Unity.CallbackManager:TryCallCallback(Object, IResult)
    91. (Filename: /Users/builduser/buildslave/unity/build/artifacts/generated/common/runtime/UnityEngineDebugBindings.gen.cpp Line: 42)
    92.  
    93. 2017-11-30 16:18:37.393806 com[14940:3760446] UnityIAP:Requesting product data...
    94.  
    95. Unloading 8 unused Assets to reduce memory usage. Loaded Objects now: 2897.
    96. Total: 20.673250 ms (FindLiveObjects: 0.661416 ms CreateObjectMapping: 0.415500 ms MarkObjects: 17.668041 ms  DeleteObjects: 1.873833 ms)
    97.  
    98. OnTokenReived: 8af4ec2f018cd70f41dba5147aba7c9b50fcab2fd865b55cb6143f31be73c107
    99. (Filename: /Users/builduser/buildslave/unity/build/artifacts/generated/common/runtime/UnityEngineDebugBindings.gen.cpp Line: 42)
    100.  
    101. 2017-11-30 16:18:37.418493 com[14940:3760446] You are using download over http. Currently unity adds NSAllowsArbitraryLoads to Info.plist to simplify transition, but it will be removed soon. Please consider updating to https.
    102. 2017-11-30 16:18:39.392383 com[14940:3760446] You are using download over http. Currently unity adds NSAllowsArbitraryLoads to Info.plist to simplify transition, but it will be removed soon. Please consider updating to https.
    103. Device Registered...
    104. (Filename: /Users/builduser/buildslave/unity/build/artifacts/generated/common/runtime/UnityEngineDebugBindings.gen.cpp Line: 42)
    105.  
    106. 2017-11-30 16:18:46.070551 com[14940:3760446] UnityIAP:Received 7 products
    107. OnInitialized: PASS
    108. UnityEngine.Purchasing.PurchasingManager:CheckForInitialization()
    109. UnityEngine.Purchasing.PurchasingManager:OnProductsRetrieved(List`1)
    110. UnityEngine.Purchasing.AppleStoreImpl:OnProductsRetrieved(String)
    111. UnityEngine.Purchasing.Extension.UnityUtil:Update()
    112. (Filename: /Users/builduser/buildslave/unity/build/artifacts/generated/common/runtime/UnityEngineDebugBindings.gen.cpp Line: 42)
    113.  
    114. check for subscription active - logged in userid: 144 name: siddjio
    115. UnityEngine.Purchasing.PurchasingManager:OnProductsRetrieved(List`1)
    116. UnityEngine.Purchasing.AppleStoreImpl:OnProductsRetrieved(String)
    117. UnityEngine.Purchasing.Extension.UnityUtil:Update()
    118. (Filename: /Users/builduser/buildslave/unity/build/artifacts/generated/common/runtime/UnityEngineDebugBindings.gen.cpp Line: 42)
    119.  
    120. 2017-11-30 16:18:46.425561 com[14940:3760446] You are using download over http. Currently unity adds NSAllowsArbitraryLoads to Info.plist to simplify transition, but it will be removed soon. Please consider updating to https.
    121. 2017-11-30 16:18:46.426241 com[14940:3760446] UnityIAP:addTransactionObserver
    122. 2017-11-30 16:18:46.426484 com[14940:3760446] UnityIAP:UpdatedTransactions
    123. 2017-11-30 16:18:46.453577 com[14940:3760446] UnityIAP UnityEarlyTransactionObserver: Request to initiate queued payments
    124. ProcessPurchase: PASS. Product: 'weekly_unlimited_picks'
    125. UnityEngine.Purchasing.JSONStore:OnPurchaseSucceeded(String, String, String)
    126. UnityEngine.Purchasing.AppleStoreImpl:ProcessMessage(String, String, String, String)
    127. UnityEngine.Purchasing.Extension.UnityUtil:Update()
    128. (Filename: /Users/builduser/buildslave/unity/build/artifacts/generated/common/runtime/UnityEngineDebugBindings.gen.cpp Line: 42)
    129.  
    130. NullReferenceException: A null value was found where an object instance was required.
    131.  at UnityEngine.Purchasing.PurchasingManager.ProcessPurchaseIfNew (UnityEngine.Purchasing.Product product) [0x00000] in <filename unknown>:0
    132.  at UnityEngine.Purchasing.JSONStore.OnPurchaseSucceeded (System.String id, System.String receipt, System.String transactionID) [0x00000] in <filename unknown>:0
    133.  at UnityEngine.Purchasing.AppleStoreImpl.ProcessMessage (System.String subject, System.String payload, System.String receipt, System.String transactionId) [0x00000] in <filename unknown>:0
    134.  at UnityEngine.Purchasing.Extension.UnityUtil.Update () [0x00000] in <filename unknown>:0
    135. (Filename: currently not available on il2cpp Line: -1)
    136.  
    137. 2017-11-30 16:18:49.215610 com[14940:3760446] [MC] System group container for systemgroup.com.apple.configurationprofiles path is /private/var/containers/Shared/SystemGroup/systemgroup.com.apple.configurationprofiles
    138. 2017-11-30 16:18:49.220579 com[14940:3760446] [MC] Reading from public effective user settings.
    139. 2017-11-30 16:18:49.228606 com[14940:3760446] Could not successfully update network info during initialization.
    140. No Subscription active at present.
    141. (Filename: /Users/builduser/buildslave/unity/build/artifacts/generated/common/runtime/UnityEngineDebugBindings.gen.cpp Line: 42)
    142.  
    143.  
    3) I was using custom buttons for In App Purchase not using IAP buttons.
    Here I have shown exact purchase related code too:

    Code (CSharp):
    1. using System;
    2. using System.Collections.Generic;
    3. using System.Collections;
    4. using UnityEngine;
    5. using UnityEngine.Purchasing;
    6. using UnityEngine.Purchasing.Security;
    7.  
    8.  
    9. public class PurchaseManager : MonoBehaviour, IStoreListener
    10. {
    11.  
    12.     private const string TAG_SUCCESS = "success";
    13.     private const string TAG_MESSAGE = "message";
    14.     private const string TAG_DATA = "data";
    15.     private const string VALUE_TRUE = "true";
    16.     private const string VALUE_FALSE = "false";
    17.  
    18.  
    19.     private static string kProductIDPick_099 = "pick.099";
    20.     private static string kProductIDPick_199 = "pick.199";
    21.     private static string kProductIDPick_299 = "pick.299";
    22.     private static string kProductIDPick_399 = "pick.399";
    23.     private static string kProductIDPick_499 = "pick.499";
    24.     private static string kProductIDMonthlySubscription = "monthly_unlimited_picks";
    25.     private static string kProductIDWeeklySubscription = "weekly_unlimited_picks";
    26.  
    27.     private static PurchaseManager instance;
    28.     private static IStoreController m_StoreController;
    29.     private static IExtensionProvider m_StoreExtensionProvider;
    30.     private PaidPickItem currentPaidPickItem;
    31.     private string appReceipt;
    32.     private bool showLoader;
    33.  
    34.     //
    35.     public static PurchaseManager Instance
    36.     {
    37.         get
    38.         {
    39.             return instance;
    40.         }
    41.     }
    42.  
    43.     public PaidPickParser paidPickParser;
    44.  
    45.  
    46.     void Awake()
    47.     {
    48.         instance = this;
    49.     }
    50.  
    51.     void Start()
    52.     {
    53.         if (m_StoreController == null)
    54.         {
    55.             InitializePurchasing();
    56.         }
    57.     }
    58.  
    59.     public void InitializePurchasing()
    60.     {
    61.         if (IsInitialized())
    62.         {
    63.             return;
    64.         }
    65.  
    66.         var builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance());
    67.  
    68.         builder.AddProduct(kProductIDPick_099, ProductType.Consumable);
    69.         builder.AddProduct(kProductIDPick_199, ProductType.Consumable);
    70.         builder.AddProduct(kProductIDPick_299, ProductType.Consumable);
    71.         builder.AddProduct(kProductIDPick_399, ProductType.Consumable);
    72.         builder.AddProduct(kProductIDPick_499, ProductType.Consumable);
    73.         builder.AddProduct(kProductIDWeeklySubscription, ProductType.Subscription);
    74.         builder.AddProduct(kProductIDMonthlySubscription, ProductType.Subscription);
    75.  
    76.  
    77.         UnityPurchasing.Initialize(this, builder);
    78.     }
    79.  
    80.  
    81.     private bool IsInitialized()
    82.     {
    83.         return m_StoreController != null && m_StoreExtensionProvider != null;
    84.     }
    85.  
    86.  
    87.     public void PurchasePaidPick(string responsePickPrice, PaidPickItem paidPickItem)
    88.     {
    89. #if UNITY_ANDROID
    90.         var productId = kProductIDPick_099;
    91. #elif UNITY_IPHONE || UNITY_IOS
    92.         var productId = kProductIDPick_099;
    93.  
    94.         switch (responsePickPrice)
    95.         {
    96.             case GameConstants.RESPONSE_PICK_199:
    97.                 productId = kProductIDPick_199;
    98.                 break;
    99.             case GameConstants.RESPONSE_PICK_299:
    100.                 productId = kProductIDPick_299;
    101.                 break;
    102.             case GameConstants.RESPONSE_PICK_399:
    103.                 productId = kProductIDPick_399;
    104.                 break;
    105.             case GameConstants.RESPONSE_PICK_499:
    106.                 productId = kProductIDPick_499;
    107.                 break;
    108.         }
    109.  
    110. #endif
    111.  
    112.         currentPaidPickItem = paidPickItem;
    113.         BuyProductID(productId);
    114.     }
    115.  
    116.     public void PurchaseWeeklySubscription()
    117.     {
    118. #if UNITY_ANDROID
    119.         var productId = GameConstants.SKU_WEEKLY_SUBSCRIPTION;
    120. #elif UNITY_IPHONE || UNITY_IOS
    121.         var productId = kProductIDWeeklySubscription;
    122. #endif
    123.  
    124.         BuyProductID(productId);
    125.  
    126.     }
    127.  
    128.     public void PurchaseMonthlySubscription()
    129.     {
    130. #if UNITY_ANDROID
    131.         var productId = GameConstants.SKU_MONTHLY_SUBSCRIPTION;
    132. #elif UNITY_IPHONE || UNITY_IOS
    133.         var productId = kProductIDMonthlySubscription;
    134. #endif
    135.  
    136.         BuyProductID(productId);
    137.  
    138.     }
    139.  
    140.     void BuyProductID(string productId)
    141.     {
    142.         // show loading...
    143.         Camera.main.SendMessage("ActivateLoadingDialog", true, SendMessageOptions.DontRequireReceiver);
    144.         showLoader = true;
    145.  
    146.         if (IsInitialized())
    147.         {
    148.             Product product = m_StoreController.products.WithID(productId);
    149.  
    150.             if (product != null && product.availableToPurchase)
    151.             {
    152.                 Debug.Log(string.Format("Purchasing product asychronously: '{0}'", product.definition.id));
    153.                 m_StoreController.InitiatePurchase(product);
    154.             }
    155.             else
    156.             {
    157.                 Debug.Log("BuyProductID: FAIL. Not purchasing product, either is not found or is not available for purchase");
    158.  
    159.                 // hide loading...
    160.                 showLoader = false;
    161.                 Camera.main.SendMessage("ActivateLoadingDialog", false, SendMessageOptions.DontRequireReceiver);
    162.             }
    163.         }
    164.         else
    165.         {
    166.             Debug.Log("BuyProductID FAIL. Not initialized.");
    167.  
    168.             // hide loading...
    169.             showLoader = false;
    170.             Camera.main.SendMessage("ActivateLoadingDialog", false, SendMessageOptions.DontRequireReceiver);
    171.         }
    172.     }
    173.  
    174.  
    175.     public void RestorePurchases()
    176.     {
    177.         if (!IsInitialized())
    178.         {
    179.             Debug.Log("RestorePurchases FAIL. Not initialized.");
    180.             return;
    181.         }
    182.  
    183.         if (Application.platform == RuntimePlatform.IPhonePlayer ||
    184.             Application.platform == RuntimePlatform.OSXPlayer)
    185.         {
    186.             Debug.Log("RestorePurchases started ...");
    187.  
    188.             var apple = m_StoreExtensionProvider.GetExtension<IAppleExtensions>();
    189.             apple.RestoreTransactions((result) =>
    190.             {
    191.                 Debug.Log("RestorePurchases continuing: " + result + ". If no further messages, no purchases available to restore.");
    192.             });
    193.         }
    194.         else
    195.         {
    196.             Debug.Log("RestorePurchases FAIL. Not supported on this platform. Current = " + Application.platform);
    197.         }
    198.     }
    199.  
    200.  
    201.     //
    202.     // --- IStoreListener
    203.     //
    204.  
    205.     public void OnInitialized(IStoreController controller, IExtensionProvider extensions)
    206.     {
    207.         Debug.Log("OnInitialized: PASS");
    208.  
    209.         m_StoreController = controller;
    210.         m_StoreExtensionProvider = extensions;
    211.  
    212.         CheckIfSubscriptionIsActive();
    213.  
    214.     }
    215.  
    216.     public void CheckIfSubscriptionIsActive()
    217.     {
    218.         ConfigurationBuilder builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance());
    219.  
    220.         IAppleConfiguration appleConfig = builder.Configure<IAppleConfiguration>();
    221.  
    222.         if (!string.IsNullOrEmpty(appleConfig.appReceipt))
    223.         {
    224.             //            Debug.Log (appleConfig.appReceipt);
    225.             appReceipt = appleConfig.appReceipt;
    226.             StartCoroutine(CheckForSubscriptionStatus());
    227.  
    228.             //            var receiptData = System.Convert.FromBase64String (appleConfig.appReceipt);
    229.             //            AppleReceipt receipt = new AppleValidator (AppleTangle.Data ()).Validate (receiptData);
    230.             //            foreach (AppleInAppPurchaseReceipt productReceipt in receipt.inAppPurchaseReceipts) {
    231.             //                Debug.Log ("PRODUCTID: " + productReceipt.productID);
    232.             //                Debug.Log ("PURCHASE DATE: " + productReceipt.purchaseDate);
    233.             //                Debug.Log ("EXPIRATION DATE: " + productReceipt.subscriptionExpirationDate);
    234.             //                Debug.Log ("CANCELDATE DATE: " + productReceipt.cancellationDate);
    235.             //            }
    236.         }
    237.     }
    238.  
    239.     IEnumerator CheckForSubscriptionStatus()
    240.     {
    241.         // hide loading...
    242.         showLoader = false;
    243.         Camera.main.SendMessage("ActivateLoadingDialog", false, SendMessageOptions.DontRequireReceiver);
    244.  
    245.         Debug.Log("check for subscription active - logged in userid: " + DataStorage.RetrieveLoggedInUserId() + " name: " + DataStorage.RetrieveLoggedInUserName());
    246.         if (DataStorage.RetrieveLoggedInStatus() == GameConstants.OFF)
    247.             yield break;
    248.  
    249.         WWWForm form = new WWWForm();
    250.         form.AddField(GameConstants.SUBSCRIPTION_VERIFICATION_USERID_ARG, DataStorage.RetrieveLoggedInUserId());
    251.         form.AddField(GameConstants.SUBSCRIPTION_VERIFICATION_RECEIPT_DATA_ARG, appReceipt);
    252.  
    253.         Dictionary<string, string> headerDisc = form.headers;
    254.         headerDisc.Add("Api-Key", "ZSqGguknzXP4zXxudeVXg2fzHJIYFaPbGgy6Eo4U3PLsheygXUJ54OWmO2Tk5Gw0WgF6whiMwrrV");
    255.  
    256.  
    257.         WWW www = new WWW(GameConstants.SUBSCRIPTION_VERIFICATION_BASE_URL, form.data, headerDisc);
    258.         yield return www;
    259.  
    260.         if (www.error == null)
    261.         {
    262.  
    263.             //            Debug.Log ("Data: " + www.text);
    264.             JSONObject jsonObj = new JSONObject(www.text);
    265.             JSONObject messageObj = jsonObj[TAG_MESSAGE];
    266.  
    267.             string successValueStr = jsonObj[TAG_SUCCESS].ToString();
    268.             if (successValueStr.Equals(VALUE_TRUE))
    269.             {
    270.                 // response success
    271.  
    272.                 SubscriptionActivated();
    273.                 Debug.Log("Subscription activated....");
    274.  
    275.             }
    276.             else
    277.             {
    278.                 // response fail
    279.                 GameManager.Instance.IsSubscriptionRunning = false;
    280.                 Debug.Log("No Subscription active at present.");
    281.             }
    282.         }
    283.         else
    284.         {
    285.             Debug.Log("Error: " + www.error);
    286.         }
    287.  
    288.     }
    289.  
    290.     private void SubscriptionActivated()
    291.     {
    292.         GameManager.Instance.IsSubscriptionRunning = true;
    293.         if (paidPickParser != null)
    294.             paidPickParser.UnlockAllPaidPicks();
    295.         Camera.main.SendMessage("ActivateSubscriptionDialog", false, SendMessageOptions.DontRequireReceiver);
    296.     }
    297.  
    298.  
    299.     public void OnInitializeFailed(InitializationFailureReason error)
    300.     {
    301.         Debug.Log("OnInitializeFailed InitializationFailureReason:" + error);
    302.     }
    303.  
    304.  
    305.     public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs args)
    306.     {
    307.         if (String.Equals(args.purchasedProduct.definition.id, kProductIDPick_099, StringComparison.Ordinal))
    308.         {
    309.  
    310.             Debug.Log(string.Format("ProcessPurchase: PASS. Product: '{0}'", args.purchasedProduct.definition.id));
    311.             UnlockPaidPick();
    312.  
    313.         }
    314.         else if (String.Equals(args.purchasedProduct.definition.id, kProductIDPick_199, StringComparison.Ordinal))
    315.         {
    316.  
    317.             Debug.Log(string.Format("ProcessPurchase: PASS. Product: '{0}'", args.purchasedProduct.definition.id));
    318.             UnlockPaidPick();
    319.  
    320.         }
    321.         else if (String.Equals(args.purchasedProduct.definition.id, kProductIDPick_299, StringComparison.Ordinal))
    322.         {
    323.  
    324.             Debug.Log(string.Format("ProcessPurchase: PASS. Product: '{0}'", args.purchasedProduct.definition.id));
    325.             UnlockPaidPick();
    326.  
    327.         }
    328.         else if (String.Equals(args.purchasedProduct.definition.id, kProductIDPick_399, StringComparison.Ordinal))
    329.         {
    330.  
    331.             Debug.Log(string.Format("ProcessPurchase: PASS. Product: '{0}'", args.purchasedProduct.definition.id));
    332.             UnlockPaidPick();
    333.  
    334.         }
    335.         else if (String.Equals(args.purchasedProduct.definition.id, kProductIDPick_499, StringComparison.Ordinal))
    336.         {
    337.  
    338.             Debug.Log(string.Format("ProcessPurchase: PASS. Product: '{0}'", args.purchasedProduct.definition.id));
    339.             UnlockPaidPick();
    340.  
    341.         }
    342.         else if (String.Equals(args.purchasedProduct.definition.id, kProductIDWeeklySubscription, StringComparison.Ordinal))
    343.         {
    344.  
    345.             Debug.Log(string.Format("ProcessPurchase: PASS. Product: '{0}'", args.purchasedProduct.definition.id));
    346.  
    347.             SubscriptionActivated();
    348.  
    349.             //if (DataStorage.RetrieveLoggedInStatus() == GameConstants.ON)
    350.             //    StartCoroutine(CheckForSubscriptionStatus());
    351.  
    352.         }
    353.         else if (String.Equals(args.purchasedProduct.definition.id, kProductIDMonthlySubscription, StringComparison.Ordinal))
    354.         {
    355.  
    356.             Debug.Log(string.Format("ProcessPurchase: PASS. Product: '{0}'", args.purchasedProduct.definition.id));
    357.  
    358.             SubscriptionActivated();
    359.             //if (DataStorage.RetrieveLoggedInStatus() == GameConstants.ON)
    360.             //    StartCoroutine(CheckForSubscriptionStatus());
    361.  
    362.         }
    363.         else
    364.         {
    365.             // hide loading...
    366.             showLoader = false;
    367.             Camera.main.SendMessage("ActivateLoadingDialog", false, SendMessageOptions.DontRequireReceiver);
    368.  
    369.             Debug.Log(string.Format("ProcessPurchase: FAIL. Unrecognized product: '{0}'", args.purchasedProduct.definition.id));
    370.         }
    371.  
    372.         return PurchaseProcessingResult.Complete;
    373.     }
    374.  
    375.     private void UnlockPaidPick()
    376.     {
    377.         // hide loading...
    378.         showLoader = false;
    379.         Camera.main.SendMessage("ActivateLoadingDialog", false, SendMessageOptions.DontRequireReceiver);
    380.  
    381.         if (currentPaidPickItem != null)
    382.             currentPaidPickItem.UnlockPaidPick();
    383.         Camera.main.SendMessage("ActivateFreePaidPicksAccessDialog", false, SendMessageOptions.DontRequireReceiver);
    384.         Camera.main.SendMessage("ActivatePickDetailPanel", true, SendMessageOptions.DontRequireReceiver);
    385.     }
    386.  
    387.     public void OnPurchaseFailed(Product product, PurchaseFailureReason failureReason)
    388.     {
    389.         // hide loading...
    390.         Camera.main.SendMessage("ActivateLoadingDialog", false, SendMessageOptions.DontRequireReceiver);
    391.  
    392.         Debug.Log(string.Format("OnPurchaseFailed: FAIL. Product: '{0}', PurchaseFailureReason: {1}", product.definition.storeSpecificId, failureReason));
    393.     }
    394.  
    395.     void OnApplicationPause(bool state)
    396.     {
    397.         if (!showLoader)
    398.             return;
    399.  
    400.         if (state)
    401.         {
    402.             // hide loading...
    403.             Camera.main.SendMessage("ActivateLoadingDialog", false, SendMessageOptions.DontRequireReceiver);
    404.         }
    405.         else
    406.         {
    407.             // show loading...
    408.             Camera.main.SendMessage("ActivateLoadingDialog", true, SendMessageOptions.DontRequireReceiver);
    409.         }
    410.     }
    411. }
    4) I understand your side provide information about subscription renewal process.
    Basically queue was managed for all background or past transactions and when you launch the application, it get executed. But what to do if subscription time duration was already over??

    In past transaction was happened so it will definitely call ProcessPurchase method, but in this method - I have written subscription benefit related code. So situation happen like, subscription time was over then also person get benefit of subscription.
    So what to do in this case?? Any kind of manually checking is necessary.

    Sorry for my so many questions but these are just for my clearance. I hope - I have provided your side required information. Waiting for your side new message.
     
  14. siddharth3322

    siddharth3322

    Joined:
    Nov 29, 2013
    Posts:
    1,049
    I have given above all information properly and I hope you have check those.
    When I get any reply from your side about this? Because I require to release new app version soon.
    Also reply for my asked questions too.
     
  15. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    As mentioned previously, we are investigating this. Once we have reproduced the issue in-house, we will create the task to address the issue and begin the code development. After that is done, the update will move to QA/Testing, and finally into a released updated version of IAP. We cannot guarantee which release this will be in, but hopefully within a few weeks. If sooner, we will certainly let you know.
     
    siddharth3322 likes this.
  16. siddharth3322

    siddharth3322

    Joined:
    Nov 29, 2013
    Posts:
    1,049
    Thank you sir for your quick reply :)
    Few questions from my side:

    • Basically you will concentrate on NullReferenceException into new plugin realise, Am I right?
    • What to do for ProcessPurchase event get called multiple times for all renewable transactions in past?
    • For this multiple method calls of ProcessPurchase event, I manage to deal with my custom server regarding the status of subscription by querying Apple server. But this IEnumerator didn't get called in real purchase so that I can get information through web service call. Though in Sandbox mode its working properly and getting response from my custom server but in real purchase IEnumerator didn't get called as per my thinking. Can you share your point of view about this?
     
  17. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    Yes, those are the questions that we are investigating. I will keep you updated.
     
  18. JayR

    JayR

    Joined:
    Aug 2, 2016
    Posts:
    84
    @siddarth3322 a few comments / questions for you:
    1. Does your game continue to operate after you see the exception message in the logs? There are some exceptions that are caught and just printed to indicate potentially degraded performance. In particular, problems accessing the device file system could be an issue here.
    2. If the game continues to run, can you successfully make a test purchase after seeing the exception?
    3. If you disable your receipt checking (in your OnInitialized()) does that change behavior?
    4. You may want to adjust your subscription checking process to include using the receipt provided to your ProcessPurchase() since that is arriving with the transaction. You can also perform on-device receipt validation at this point.
    5. At a minimum try adding some debug to your ProcessPurchase() to print args.purchasedProduct.transactionID and possibly args.purchasedProduct.receipt to help match up what you're seeing on-device with the servers
     
  19. siddharth3322

    siddharth3322

    Joined:
    Nov 29, 2013
    Posts:
    1,049
    Let me reply for above each point:

    1. Yes game remain continue normally as like before.
    2. Basically I have done both real and sandbox purchases and with both purchase process remain successful.
    3. This point, I can't able to reply because I am not aware what exactly I require to do for this step!
    4. Basically I have already implemented on device receipt validation, check my updated below code:
      Code (CSharp):
      1. public void OnInitialized(IStoreController controller, IExtensionProvider extensions)
      2.     {
      3.         Debug.Log("OnInitialized: PASS");
      4.  
      5.         m_StoreController = controller;
      6.         m_StoreExtensionProvider = extensions;
      7.  
      8.         CheckIfSubscriptionIsActive(false);
      9.  
      10.     }
      11.  
      12.     public void CheckIfSubscriptionIsActive(bool validateReceipt)
      13.     {
      14.         ConfigurationBuilder builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance());
      15.  
      16.         IAppleConfiguration appleConfig = builder.Configure<IAppleConfiguration>();
      17.         bool isSubscriptionRunning = false;
      18.  
      19.         if (!string.IsNullOrEmpty(appleConfig.appReceipt))
      20.         {
      21.             if (validateReceipt)
      22.             {
      23.                 // local receipt verification
      24.                 var receiptData = System.Convert.FromBase64String(appleConfig.appReceipt);
      25.                 AppleReceipt receipt = new AppleValidator(AppleTangle.Data()).Validate(receiptData);
      26.                 foreach (AppleInAppPurchaseReceipt productReceipt in receipt.inAppPurchaseReceipts)
      27.                 {
      28.  
      29.                     Debug.Log("# server date: " + GameManager.Instance.ServerDate + " expire date: " + productReceipt.subscriptionExpirationDate);
      30.                     int result = DateTime.Compare(GameManager.Instance.ServerDate, productReceipt.subscriptionExpirationDate);
      31.                     if (result <= 0)
      32.                     {
      33.                         isSubscriptionRunning = true;
      34.                         Debug.Log(" === ==== === Subscription Running: curr date: " + GameManager.Instance.ServerDate + " expire date: " + productReceipt.subscriptionExpirationDate);
      35.                     }
      36.  
      37.                     //Debug.Log("PRODUCTID: " + productReceipt.productID);
      38.                     //Debug.Log("PURCHASE DATE: " + productReceipt.purchaseDate);
      39.                     //Debug.Log("EXPIRATION DATE: " + productReceipt.subscriptionExpirationDate);
      40.                     //Debug.Log("CANCELDATE DATE: " + productReceipt.cancellationDate);
      41.                 }
      42.  
      43.                 if (isSubscriptionRunning)
      44.                     SubscriptionActivated();
      45.  
      46.                 // hide loading...
      47.                 showLoader = false;
      48.                 Camera.main.SendMessage("ActivateLoadingDialog", false, SendMessageOptions.DontRequireReceiver);
      49.  
      50.             }
      51.             else
      52.             {
      53.                 // server side receipt vecification
      54.                 appReceipt = appleConfig.appReceipt;
      55.                 StartCoroutine(CheckSubscriptionStatusServerSideVerification());
      56.             }
      57.         }
      58.     }
      59.  
      60.     IEnumerator CheckSubscriptionStatusServerSideVerification()
      61.     {
      62.  
      63.         // hide loading...
      64.         showLoader = false;
      65.         Camera.main.SendMessage("ActivateLoadingDialog", false, SendMessageOptions.DontRequireReceiver);
      66.  
      67.         if (DataStorage.RetrieveLoggedInStatus() == GameConstants.OFF)
      68.             yield break;
      69.  
      70.         WWWForm form = new WWWForm();
      71.         form.AddField(GameConstants.SUBSCRIPTION_VERIFICATION_USERID_ARG, DataStorage.RetrieveLoggedInUserId());
      72.         form.AddField(GameConstants.SUBSCRIPTION_VERIFICATION_RECEIPT_DATA_ARG, appReceipt);
      73.  
      74.         Dictionary<string, string> headerDisc = form.headers;
      75.         headerDisc.Add("Api-Key", "ZSqGguknzXP4zXxudeVXg2fzHJIYFaPbGgy6Eo4U3PLsheygXUJ54OWmO2Tk5Gw0WgF6whiMwrrV");
      76.  
      77.  
      78.         WWW www = new WWW(GameConstants.SUBSCRIPTION_VERIFICATION_BASE_URL, form.data, headerDisc);
      79.         yield return www;
      80.  
      81.         if (www.error == null)
      82.         {
      83.  
      84.             //            Debug.Log ("Data: " + www.text);
      85.             JSONObject jsonObj = new JSONObject(www.text);
      86.             JSONObject messageObj = jsonObj[TAG_MESSAGE];
      87.  
      88.             string successValueStr = jsonObj[TAG_SUCCESS].ToString();
      89.             if (successValueStr.Equals(VALUE_TRUE))
      90.             {
      91.                 // response success
      92.  
      93.                 SubscriptionActivated();
      94.                 Debug.Log("Subscription activated....");
      95.  
      96.             }
      97.             else
      98.             {
      99.                 // response fail
      100.                 GameManager.Instance.IsSubscriptionRunning = false;
      101.                 Debug.Log("No Subscription active at present.");
      102.             }
      103.         }
      104.         else
      105.         {
      106.             Debug.Log("Error: " + www.error);
      107.         }
      108.  
      109.     }
      110.  
      111.     private void SubscriptionActivated()
      112.     {
      113.         GameManager.Instance.IsSubscriptionRunning = true;
      114.         if (paidPickParser != null)
      115.             paidPickParser.UnlockAllPaidPicks();
      116.         Camera.main.SendMessage("ActivateSubscriptionDialog", false, SendMessageOptions.DontRequireReceiver);
      117.     }
      118.  
      119. public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs args)
      120.     {
      121.         if (String.Equals(args.purchasedProduct.definition.id, kProductIDPick_099, StringComparison.Ordinal))
      122.         {
      123.  
      124.             Debug.Log(string.Format("ProcessPurchase: PASS. Product: '{0}'", args.purchasedProduct.definition.id));
      125.             UnlockPaidPick();
      126.  
      127.         }
      128.         else if (String.Equals(args.purchasedProduct.definition.id, kProductIDPick_199, StringComparison.Ordinal))
      129.         {
      130.  
      131.             Debug.Log(string.Format("ProcessPurchase: PASS. Product: '{0}'", args.purchasedProduct.definition.id));
      132.             UnlockPaidPick();
      133.  
      134.         }
      135.         else if (String.Equals(args.purchasedProduct.definition.id, kProductIDPick_299, StringComparison.Ordinal))
      136.         {
      137.  
      138.             Debug.Log(string.Format("ProcessPurchase: PASS. Product: '{0}'", args.purchasedProduct.definition.id));
      139.             UnlockPaidPick();
      140.  
      141.         }
      142.         else if (String.Equals(args.purchasedProduct.definition.id, kProductIDPick_399, StringComparison.Ordinal))
      143.         {
      144.  
      145.             Debug.Log(string.Format("ProcessPurchase: PASS. Product: '{0}'", args.purchasedProduct.definition.id));
      146.             UnlockPaidPick();
      147.  
      148.         }
      149.         else if (String.Equals(args.purchasedProduct.definition.id, kProductIDPick_499, StringComparison.Ordinal))
      150.         {
      151.  
      152.             Debug.Log(string.Format("ProcessPurchase: PASS. Product: '{0}'", args.purchasedProduct.definition.id));
      153.             UnlockPaidPick();
      154.  
      155.         }
      156.         else if (String.Equals(args.purchasedProduct.definition.id, kProductIDWeeklySubscription, StringComparison.Ordinal))
      157.         {
      158.  
      159.             Debug.Log(string.Format("ProcessPurchase: PASS. Product: '{0}'", args.purchasedProduct.definition.id));
      160.  
      161.             CheckIfSubscriptionIsActive(true);
      162.  
      163.             //if (DataStorage.RetrieveLoggedInStatus() == GameConstants.ON)
      164.             //    StartCoroutine(CheckForSubscriptionStatus());
      165.  
      166.         }
      167.         else if (String.Equals(args.purchasedProduct.definition.id, kProductIDMonthlySubscription, StringComparison.Ordinal))
      168.         {
      169.  
      170.             Debug.Log(string.Format("ProcessPurchase: PASS. Product: '{0}'", args.purchasedProduct.definition.id));
      171.  
      172.             CheckIfSubscriptionIsActive(true);
      173.             //if (DataStorage.RetrieveLoggedInStatus() == GameConstants.ON)
      174.             //    StartCoroutine(CheckForSubscriptionStatus());
      175.  
      176.         }
      177.         else
      178.         {
      179.             // hide loading...
      180.             showLoader = false;
      181.             Camera.main.SendMessage("ActivateLoadingDialog", false, SendMessageOptions.DontRequireReceiver);
      182.  
      183.             Debug.Log(string.Format("ProcessPurchase: FAIL. Unrecognized product: '{0}'", args.purchasedProduct.definition.id));
      184.         }
      185.  
      186.         return PurchaseProcessingResult.Complete;
      187.     }
      At above I have divided whole thing into two process. i) At actual purchase time, used local device receipt verification through DateTime object. ii) Then rest all game loading time, performed receipt validation through web server passing actual receipt data. In sandbox mode, I was getting time related data wrong so at present I can't able to say, I got success in implementation because 3 mins time interval is for Weekly subscription and 5 mins time duration for Monthly subscription purchase.
    5. Okay I will deliver you a log information within short time.
     
  20. siddharth3322

    siddharth3322

    Joined:
    Nov 29, 2013
    Posts:
    1,049
    One more point for discussion, why I was getting wrong time related entry in apple receipt response??
    Above I have mentioned code that I was using but here you have complete log information too:

    sandbox_mode_wrong_autorenew_time.png

    Usually Weekly subscription has 3 mins time duration so if I am not getting exact time then how can I do proper testing!!

    In above image, I have mention both times, my local time is 15:01:04 and apple receipt response time is 09:37:18.
    Using local device receipt validation at present I can't able to test the work. so what way exist for me for testing??
     
  21. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    Regarding the Apple receipt times, you would want to contact Apple. They may be using artificial dates in the testing environment. Are the times always off by the same amount? If so, you could just subtract the proper time interval.
     
    siddharth3322 likes this.
  22. siddharth3322

    siddharth3322

    Joined:
    Nov 29, 2013
    Posts:
    1,049
    Thank you sir - I was waiting for your this reply.
    Is this possible time difference related to GMT ??
    I am at India so GMT time difference is 5:30.
    Please check following two examples:

    Weekly subscription purchase screenshot
    Screen Shot 2017-12-11 at 10.52.56 PM.png

    Another different day purchase example.
    Monthly subscription purchase screenshot
    Screen Shot 2017-12-11 at 10.53.08 PM.png

    Share your opinion regarding this.
     
  23. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    As mentioned, you would want to contact Apple regarding this. If the time is off by the same amount each time, you could adjust accordingly.
     
  24. JayR

    JayR

    Joined:
    Aug 2, 2016
    Posts:
    84
    On-device the apple receipt times are GMT. Server validation returns both GMT and local times in the JSON. Sandbox subscriptions only auto-renew five times so your testing window on weekly subs in particular can be tight. Also worth noting that the expired subscriptions are not removed from the receipt so your previous tests will continue to appear as chunks of six expired entries.

    What's the source of your server time and how is it synchronized? A significant difference in two server times would not be terribly surprising.
     
  25. siddharth3322

    siddharth3322

    Joined:
    Nov 29, 2013
    Posts:
    1,049
    Hello Sir, after using Unity 2017.1.2 and latest Unity IAP plugin, multiple time calling of ProcessPurchase method get stopped. Now without any reason its not calling if in past background transaction occurred then only its get called.

    But in Unity 5.4.2 version with latest Unity IAP plugin 100% there is some mistake exist because I have several proof too and few I have posted above. At present its okay, I have shifted my project to higher Unity version.

    Now if you want to close this thread then you can because now I am satisfied with the output :)
    Thank you for giving so much attention to this single thread!!!
     
  26. MorenoBralts

    MorenoBralts

    Joined:
    May 23, 2017
    Posts:
    19
    This is very crucial information for auto-renewing subscriptions in the UnityIAP library. Thanks for explaining this, are there any more things we need to take into consideration with auto-renewable subscriptions?

    Also an update to the documentation would be very helpful.
     
  27. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    We are releasing an update soon that will include improved subscription support including an isSubscriptionActive boolean flag.
     
  28. gregory_igromatic

    gregory_igromatic

    Joined:
    Feb 1, 2014
    Posts:
    25
    Is there any way to remove these hundreds of test subscriptions from the queue? (we have 830)
     
  29. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    You would want to reach out to Google for your suggestion. We are a pass through service for the various stores. We would not want to remove their receipts from the store processing. You can configure a new test user who would not have any subscriptions yet.
     
Thread Status:
Not open for further replies.