Search Unity

Feedback Unity IAP becomes broken after Init without Internet

Discussion in 'Unity IAP' started by makaka-org, Jun 8, 2020.

  1. makaka-org

    makaka-org

    Joined:
    Dec 1, 2013
    Posts:
    1,026
    So, official manual says that Unity IAP will try to initialize itself if there is no Internet.

    However, it fails in first time and further it's not possible to Initialize Unity IAP.

    It breaks purchasing workflow in terms of Local Caching:
    https://developer.android.com/google/play/billing/api#billing-security

    Environment.
    Unity 2019.3.15 with Unity IAP service enabled.
    Android 10 & Galaxy A71

    My Bug Report is ignored for a long time.

    P.S. Unity Ads has the same issue.
     
  2. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    IAP initialization requires the Internet, it needs to communicate with the Apple or Google servers to retrieve your purchase information. If you don't receive the Initialization Succeed callback, then you can assume there is likely a network issue and disable purchases. If there is no Internet, we will indeed try to connect but fail. This is expected behavior.
     
  3. makaka-org

    makaka-org

    Joined:
    Dec 1, 2013
    Posts:
    1,026
    If further internet is appeared then IAP Init doesn't work until app restart.

    ---

    So, if app uses Unity IAP then internet is needed constantly?
    So if there is no internet after app start, I need to show to the user a message that internet is required, right?
     
  4. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
  5. makaka-org

    makaka-org

    Joined:
    Dec 1, 2013
    Posts:
    1,026
    Unfortunately, this was a few month ago and I resolved it by validation of internet connection. But only thing that makes sense is that after this scenario Unity IAP System didn't work.

    No 3rd party code was used.

    I used code from here: https://learn.unity.com/tutorial/unity-iap
     
  6. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    Yes, IAP requires the Internet and won't successfully initialize without it.
     
  7. FoxFromPlayG

    FoxFromPlayG

    Joined:
    Jan 12, 2021
    Posts:
    8
    Hi. We encountered similar issue.
    Unity IAP docs says
    Code (CSharp):
    1. Note that initialization will not fail if the network is unavailable; Unity IAP will continue attempting to initialize in the background
    But we are getting
    OnInitializeFailed
    called with
    InitializationFailureReason.NoProductsAvailable


    Unity version 2019.4.18f1
    Unity IAP version 3.2.3
     
  8. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    The docs need updating.
     
  9. FoxFromPlayG

    FoxFromPlayG

    Joined:
    Jan 12, 2021
    Posts:
    8
    Thanks for clarifying. But I think at least you should add another error code for connection error and have an ability to reinitialize. And much better if you handle it out-of-box like it was in previous versions. It's common issue
     
  10. MousePods

    MousePods

    Joined:
    Jul 19, 2012
    Posts:
    811
    Wait, so it doesn’t continue initializing in the background anymore? That is very concerning!
     
    FoxFromPlayG likes this.
  11. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    What issue are you seeing?
     
  12. WilliamOD

    WilliamOD

    Unity Technologies

    Joined:
    May 31, 2021
    Posts:
    16
    Hi @makaka-org, I am happy to say that this issue will be resolved with the next release of IAP (version 4.0.1). IAP will try to reinitialize in the background if there is no internet connection.

    We are trying to release this update as soon as possible.

    Have a nice day.
     
  13. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    For those following this thread, the current behavior with 4.0.0 is as follows:

    * First time user A, no internet receives "NoProductsAvailable" in OnInitializedFailed
    * First time user B, with internet, successful initialization and product info is cached locally
    * User B launches app again, but without internet, successful initialization (because product info is cached). Purchase attempt for B without internet results in "UnknownError" in OnPurchaseFailed
     
  14. TJClifton_Headstart

    TJClifton_Headstart

    Joined:
    Nov 18, 2021
    Posts:
    1
    This solution seems to cause more issues than the original implementation which seemed to be correct.

    We are having an issue where we need to wait on the result of the store initialisation but with this new implementation there is no way to do this if the device has no network access. We can certainly check for network access before initialising Unity IAP but there is an edge case where the user has network access when the call to initialise is made, then loses connectivity for whatever reason before Unity IAP can successfully initialise. In this scenario, Unity IAP will continue to try and initialise unsuccessfully in the background but there will be no callback for either success or failure until/unless it successfully reconnects to the internet.

    Given that Unity IAP has failed to initialise because it can't access the internet, it seems to me that the behaviour SHOULD be to call OnInitializeFailed and let the user decide whether they want to reattempt initialisation or not to avoid getting stuck in an endless loop. If this behaviour is desired by some users then there should be an option to reattempt connection in the background, probably with some sort of timeout time or maximum number of attempts.

    (Tested with Unity IAP 4.3.0, Unity 2021.1.28f1)
     
    KeivanZayer likes this.
  15. WilliamOD

    WilliamOD

    Unity Technologies

    Joined:
    May 31, 2021
    Posts:
    16
    Hi!
    We agree that this was problematic as it removed some control from the devs.
    In IAP 4.3.0 we have added the method IGooglePlayConfiguration.SetQueryProductDetailsFailedListener which will be called when retry is attempted with the number of times the connection has been retried. We are also working on a fully-fledged feature that will allow complete control over the connection retry process.

    We really appreciate this kind of feedback, please feel free to mention other pain points you might have so we can keep improving the package.

    Thank you and have a nice day:)
     
    KeivanZayer and unityventures33 like this.
  16. bbridgesvb

    bbridgesvb

    Joined:
    Jan 4, 2019
    Posts:
    18
    Hi! Is there any plan to fix this? We recently found that this was an issue in our subscription-based app. We tested use-cases for this when originally implementing Unity IAP, and got a rude awakening when our app no longer gets past the loading screen after updating to IAP 4.3.0 when the device has no internet (we're currently on 4.5.0 and it's still an issue). We wait until the Unity IAP is initialized because it's a subscription app, and users aren't supposed to get access without an active subscription.
     
  17. spider853

    spider853

    Joined:
    Feb 16, 2018
    Posts:
    42
    For us even OnInitializedFailed is not called on no internet connection
     
  18. FIFTYTWO

    FIFTYTWO

    Joined:
    Oct 21, 2014
    Posts:
    49
    We fixed Unity IAP locally for Apple targets. IAP package should be modified, so copy it from Library/PackageCache/com.unity.purchasing@x.y to Packages/com.unity.purchasing and remove com.unity.purchasing from manifest. Navigate com.unity.purchasing/Plugins/UnityPurchasing/iOS/UnityPurchasing.m and find:

    Code (CSharp):
    1. // Store Kit returns a response from an SKProductsRequest.
    2. - (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
    3. {
    4.     UnityPurchasingLog(@"Received %lu products", (unsigned long)[response.products count]);
    5.     // Add the retrieved products to our set of valid products.
    6.     NSDictionary* fetchedProducts = [NSDictionary dictionaryWithObjects: response.products forKeys: [response.products valueForKey: @"productIdentifier"]];
    7.     [validProducts addEntriesFromDictionary: fetchedProducts];
    8.  
    9.     NSString* productJSON = [UnityPurchasing serializeProductMetadata: response.products];
    10.  
    11.     // Send the app receipt as a separate parameter to avoid JSON parsing a large string.
    12.     [self UnitySendMessage: @"OnProductsRetrieved" payload: productJSON];
    13. }
    It seems the problem is that sometimes Apple responses with above message containing empty product list instead of:
    Code (CSharp):
    1. - (void)request:(SKRequest *)request didFailWithError:(NSError *)error
    So, lets modify it to retry request in the case of empty product list using delay strategy from error handler:
    Code (CSharp):
    1. // Store Kit returns a response from an SKProductsRequest.
    2. - (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
    3. {
    4.     if ([response.products count] == 0)
    5.     {
    6.         delayInSeconds = MIN(MAX_REQUEST_PRODUCT_RETRY_DELAY, 2 * delayInSeconds);
    7.         UnityPurchasingLog(@"SKProductRequest::didReceiveResponse: 0 products. Unity Purchasing will retry in %i seconds", delayInSeconds);
    8.         [self initiateProductPoll: delayInSeconds];
    9.         return;
    10.     }
    11.  
    12.     UnityPurchasingLog(@"Received %lu products", (unsigned long)[response.products count]);    // Add the retrieved products to our set of valid products.
    13.     NSDictionary* fetchedProducts = [NSDictionary dictionaryWithObjects: response.products forKeys: [response.products valueForKey: @"productIdentifier"]];
    14.     [validProducts addEntriesFromDictionary: fetchedProducts];
    15.  
    16.     NSString* productJSON = [UnityPurchasing serializeProductMetadata: response.products];
    17.  
    18.     // Send the app receipt as a separate parameter to avoid JSON parsing a large string.
    19.     [self UnitySendMessage: @"OnProductsRetrieved" payload: productJSON];
    20. }
    This code fixed infinite uninitialized IAP state and IAP module initializes as soon as network appears.

    Next, you would like to rebuild com.unity.purchasing/Plugins/UnityPurchasing/unitypurchasing.bundle if you target macOS. Create Xcode project, choose macOS > Bundle
    Product name: unitypurchasing
    Organization identifier: com.unity.purchasing or your own corresponding to your main bundle ID.

    Add files UnityPurchasing.h and UnityPurchasing.m from folder com.unity.purchasing/Plugins/UnityPurchasing/iOS to the project.

    Navigate target's build settings, find Preprocessor Macros and append MAC_APPSTORE=1 to both Debug and Release.

    Also it requires some Base64.h which is missing in the package but it implements base64EncodedStringWithOptions method which was only added in OSX 10.9. We target since 11.0 so I just made Base64.h and Base64.m to implement required mgb64_base64EncodedString via base64EncodedStringWithOptions to satisfy compiler.

    Base64.h
    Code (CSharp):
    1. #import <Foundation/Foundation.h>
    2.  
    3.  
    4. NS_ASSUME_NONNULL_BEGIN
    5.  
    6. @interface NSData (mgb64_NSDataBase64Encoding)
    7.  
    8. - (NSString *)mgb64_base64EncodedString;
    9.  
    10. @end
    11.  
    12. NS_ASSUME_NONNULL_END
    Base64.m
    Code (CSharp):
    1. #import "Base64.h"
    2.  
    3.  
    4. @implementation NSData (mgb64_NSDataBase64Encoding)
    5.  
    6. - (NSString *)mgb64_base64EncodedString
    7. {
    8.     return [self base64EncodedStringWithOptions:0];
    9. }
    10.  
    11. @end
    Now bundle can be built and ready to replace original unitypurchasing.bundle
     
    Last edited: Dec 7, 2023