Search Unity

Resolved IAP Taking Payment before calling ConfirmPendingPurchase?

Discussion in 'Unity IAP' started by markmozza, Jul 15, 2022.

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

    markmozza

    Joined:
    Oct 16, 2015
    Posts:
    86
    So i might be getting this wrong but im trying to implement server side purchases using IAP.

    Currently sending the token to the server and confirming and sending the result back to the client to then confirm the transaction using ConfirmPendingPurchase. But if i comment out the line the payment is still taken and the purchase is Complete.


    Code (CSharp):
    1.  
    2. PurchaseProcessingResult IStoreListener.ProcessPurchase( PurchaseEventArgs purchaseEvent )
    3.         {
    4.             try
    5.             {
    6.                 Product product = purchaseEvent.purchasedProduct;
    7.                 if( IsPurchaseValid( product ) )
    8.                 {
    9.                     if( StandardPurchasingModule.Instance().appStore == AppStore.GooglePlay && storeExtensions.GetExtension<IGooglePlayStoreExtensions>().IsPurchasedProductDeferred( product ) )
    10.                     {
    11.                         // The purchase is deferred; therefore, we do not unlock the content or complete the transaction.
    12.                         // ProcessPurchase will be called again once the purchase is completed
    13.                         Debug.Log("DEFERRED");
    14.                         return PurchaseProcessingResult.Pending;
    15.                     }
    16.  
    17.                     OnPurchaseCompleted?.Invoke(product);
    18.                 }
    19.  
    20.                 return PurchaseProcessingResult.Pending;
    21.             }
    22.             finally
    23.             {
    24.                 OnNativeIAPWindowClosed();
    25.             }
    26.         }
    27.  
    OnPurchase Callback
    Code (CSharp):
    1.  
    2. Private void OnPurchase(Product product)
    3.         {
    4.             ValidateApi.Instance.CheckReceipt(product.receipt, s =>
    5.             {
    6.                 var response = JsonUtility.FromJson<ValidateStruct>(s.downloadHandler.text);
    7.                 if (response is { valid: true })
    8.                 {
    9.                     IAPManager.Instance.ConfirmPendingPurchase(product);
    10.                     PurchaseManager.Instance.SetPurchaseToken(response.token);
    11.                     PurchaseManager.Instance.LoadPurchaseToken();
    12.                 }
    13.                 else
    14.                 {
    15.                    // Show Failure
    16.                 }
    17.  
    18.                 GenerateTrackList(_currentMusicalId);
    19.             });
    20.         }

    I feel like im missing something or maybe i have misunderstood. Here is how i imagine it would work.

    Client (Make Purchase) -> Server (Confirm Purchase) -> Client (ConfirmPendingPurchase - Takes Payment)

    If the server step failures for whatever reason, it shouldnt take payment.
     
  2. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    I'm not clear on your "OnPurchase" callback, where is that being called? You are not using the IAP Catalog and an IAP Button, correct? How are you debugging, I don't see any Debug.Log calls. You'll want many of them to track your logic, they will show in the adb logcat logs. I might suggest you compare/start with the Sample IAP Project v3 here, it's working as expected https://forum.unity.com/threads/sample-iap-project.529555/#post-7922275 and https://forum.unity.com/threads/how-to-capturing-device-logs-on-android.528680/
     
  3. markmozza

    markmozza

    Joined:
    Oct 16, 2015
    Posts:
    86
    Hi Jeff, Thanks for the fast resposnse.

    The OnPurchase callback gets called in the first section of code. I removed the Debug.Log lines to keep the example smaller. We are using the IAP catalog but everything else we code via code.

    ```
    OnPurchaseCompleted?.Invoke(product);
    return PurchaseProcessingResult.Pending;
    ```

    Will check out the sample project thank you.
     
  4. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    But I mean, who is calling the callback? I believe that would be incorrect. And since you are using Scripted IAP, you should not be using the IAP Catalog either. Please compare to the Sample IAP Project, there is no such callback.
     
  5. markmozza

    markmozza

    Joined:
    Oct 16, 2015
    Posts:
    86
    Im not sure what you mean when you say who is calling the callback? The first function is called by the store, the callback then runs from within that function.

    We are definitely using the IAP Catalog. This side of things is working perfectly, all the DLC is purchasable on android. We export directly from IAP Catalog to Google Play. Im not sure how the Test Store would work without setting them in the catalog.

    From what your saying, im taking it that im doing everything wrong lol

    Update:
    Im using the below for a IAP Wrapper.
    https://gist.github.com/yasirkula/a709990882cb7376d4b2d8a06a5d70ca
     
  6. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    I meant, where exactly is OnPurchased called from. Is that a callback you wired up yourself? Do you mean your invoke? You don't want to explicitly call a callback, if so. Callbacks are async. Unity IAP does not have an OnPurchased method. Indeed, you are seeing issues. I'm also not familiar with your token handling methods. Also, how are you loading the IAP Catalog in your script? Regardless, please compare to the Sample IAP Project.
     
  7. markmozza

    markmozza

    Joined:
    Oct 16, 2015
    Posts:
    86
    OnPurchase() is a function i made that gets Invoked in PurchaseProcessingResult IStoreListener.ProcessPurchase().

    Code (CSharp):
    1. OnPurchaseCompleted?.Invoke(product);
    We attach in awake

    Code (CSharp):
    1. IAPManager.Instance.OnPurchaseCompleted += OnPurchase;
    Everything been fine, im able to purchase everything on Android, but in IStoreListener regardless of if we set Pending or Complete, the payment is still taken, is this normal?
     
  8. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    You don't need a callback if you are calling it explicitly. And apparently it's not normal if purchases are completed when you believe them to be in pending. I would not recommend your approach.
     
  9. markmozza

    markmozza

    Joined:
    Oct 16, 2015
    Posts:
    86
    The Invoked method is to connect to our API and confirm the purchase, this flow is described on the unity docs. OnPurchase confirm we validate server side and called Complete once we had the server validation back.

    This is also a commonly used wrapper for IAP (https://gist.github.com/yasirkula/a709990882cb7376d4b2d8a06a5d70ca).

    Im confused to how this is suppose to work and the demo app you sent doesnt really show much different then what im already doing.
     
  10. markmozza

    markmozza

    Joined:
    Oct 16, 2015
    Posts:
    86
    Looks like i have misunderstood after all.



    Payment is taken on step 3. I thought payment was taken on Step 9. This is bad because what if anything fails after step 3, the user wont have their own purchases. Are we going to have to code for this? I thought the idea of server side validation is to complete the purchase on the server (Similar to Stripe - Creates atoken client side to use later on server to take payment). We use our own accounts, so connecting purchases to our own accounts is important. We want purchases to be cross platform, meaning buy 1 DLC on one device, you have access on all other devices.
     
  11. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    It is different enough that it works properly. It doesn't use Invoke and the logic is quite different. Please test with it, create a new application in test on the store. You are welcome to use the ToggleComplete method, I would suggest to add another UI button to do so. This will set the return code to Pending at runtime. Then call CompletePurchase a minute later. This simulates the server call and is working for me.
     
  12. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    The image is old and is not accurate. You can test yourself. If a transaction is still in Pending, the credit card has not been charged. That would happen in Step 9
     
  13. chrismarch

    chrismarch

    Joined:
    Jul 24, 2013
    Posts:
    472
  14. John_Corbett

    John_Corbett

    Joined:
    May 17, 2019
    Posts:
    151
    Hi @chrismarch,

    That diagram may indeed need an update, and we'll look into it, but the credit card handling is done by GooglePlay, Apple or whatever store your app is using, and Credit Card info is indeed taken and validated at step 3.

    TL;DR: it's not 100% clear if the card is charged in step 3, or not, because that's opaque to us. The IAP SDK doesn't handle payment information. However it's only completed at step 9. If the transaction is never fulfilled, it won't be charged, or will be refunded.

    In the case of Google Play, they state that if the purchase isn't acknowledged or consumed, the purchase will be refunded, eventually. It could take a day. This implies it's charged in advance.

    In the case of Apple:

    From what we can tell, unfinished Apple Transactions are in limbo and it's not clear if the card is charged before or afterwards.
     
    chrismarch likes this.
  15. Arnaud_Gorain

    Arnaud_Gorain

    Unity Technologies

    Joined:
    Jun 28, 2022
    Posts:
    183
    This thread is now closed. Feel free to reach out via a new thread if you encounter further issues.
    Thanks!
     
Thread Status:
Not open for further replies.