Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

OnPurchaseCompleted called on App Launch

Discussion in 'Unity IAP' started by Draikz, May 31, 2021.

  1. Draikz

    Draikz

    Joined:
    Dec 20, 2016
    Posts:
    12
    Hello,

    We recently enabled our game globally both for Android and iOS. Our game, of course, includes IAPs, which most of the time seem to be working as expected.

    The problem we're facing is that we've seen some sort of hacking or maybe bug exploit being used by a couple of users in Android, which based on our analytics follow this flow:

    User goes to the store -> Clicks on an IAP, so the IAP pop up is triggered -> User closes the app -> User relaunches app -> IAP is completed on app launch.



    We have checked our script several times, we do not cache anything related to store or IAPS, so it looks like the user leaves the IAP as pending and then it is completed automatically when the IAP are initialized for some unknown reason.

    This in an example of a Mexican user who did this exploit, where we can see his payments are stuck on pending status but he has received all the rewards.



    To be honest we don't have much security nor are we sure how to improve it, as for now we're just using the recommended receipt validation.

    What can we do about this ? We're using last Unity IAP Sdk and Unity 2020.3 LTS
     
    Last edited: May 31, 2021
  2. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    14,446
    Are you able to reproduce? Which version of IAP? Are you using Scripted or Codeless IAP?
     
  3. Draikz

    Draikz

    Joined:
    Dec 20, 2016
    Posts:
    12
    Hello Jeff, thanks for your interest in the matter.

    We tried to reproduce it but we weren't able.

    SDK Version where this happened was the previous to the latest, 3.1 I think? We just updated to 3.2.1 and we're pending to launch an update, but we saw this with older version of the SDK as well

    We're using Scripted IAP, do you need us to share the code?
     
  4. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    14,446
    Yes, please share your code here as an attachment
     
  5. Draikz

    Draikz

    Joined:
    Dec 20, 2016
    Posts:
    12
    Here is our manager and the button which starts the shopping process.
     

    Attached Files:

  6. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    14,446
    Last edited: Jun 2, 2021
  7. Draikz

    Draikz

    Joined:
    Dec 20, 2016
    Posts:
    12
    We do receive ProcessPurchase in this scenario, since we are receiving the corresponding analytic which only happens in that function.

    upload_2021-6-2_21-33-38.png

    As you see we don't have the deferred purchases enabled nor we want them enabled.

    What we are really curious about, is this pattern behaviour, where users may know of some API vulnerability because, why would you even try to: Intent purchase -> Force close -> Re-open app

    We don't imagine a specific scenario where our game has lead the user to behave like this. Have you not heard of this exploit before?

    Either way we're comparing with the Sample project if you don't see anything weird...

    Thanks!!
     

    Attached Files:

  8. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    14,446
    Are you guessing at an exploit, perhaps I'm not quite following. I have not heard of this. Are you able to consistently reproduce? If you can reproduce, it might be an exploit. Also, deferred purchases are a Google requirement. I would suggest that you want to at least rule it out, you need to check the ProcessPurchase callback and check the receipt. I suspect you are seeing legitimate deferred purchases that you'll need to properly handle. You will need to parse the receipt to confirm. If purchaseState = 4, you need to leave it in Pending until purchaseState = 1 (4 = waiting for the user to make a physical payment), then return Complete. We plan to expose purchaseState as a product property in a future release.
     
  9. Draikz

    Draikz

    Joined:
    Dec 20, 2016
    Posts:
    12
    What I meant, is that we find really weird such pattern behaviour by several users like they knew how to force a fail on the API. As I said we couldn't reproduce but maybe because we have some uncontrolled step, we've also seen users trying to do this exploit but without any luck.

    If I understand correctly, they could be legitimate, unhadled deferred purchases? But, if that were the case, why are all these guys following the same steps? (we've seen 3 doing this exploit and 1 trying)

    To clarify when this happen, the users open the google's shop popup, then force close the app and relaunch it. that's what we've seen, doing if for every purchase.

    Here we have a user trying to hack the purchase, it looks like he's trying for the purchase to be in a specific state and hence the several clicks on the purchase?

     
  10. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    14,446
    Then you will want to code against this. As mentioned, you'll want to inspect the receipt. It would be easy to tell if this ProcessPurchase wasn't from a button click, for example. So it's either a restore, or an exploit. You'll want to look at the receipt properties to look for differences between the two scenarios.
     
  11. Draikz

    Draikz

    Joined:
    Dec 20, 2016
    Posts:
    12
    Thank you!! Checking if purchase comes from a button click sounds like an easy-straight forward solution...

    About the receipt, any tip on what parameters we should be looking at? We already have the recommended receipt validation, so, what else?
     
  12. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    14,446
    purchaseState
     
  13. Darktemplar1

    Darktemplar1

    Joined:
    Apr 26, 2021
    Posts:
    6
    We are also seeing this issue as well where Unit’s IAP framework thinks the player confirmed the Google Play purchase, but no payment was received. (No payment was verified in Google Play Console, but Unity IAP logs indicated a purchase). Note that even after, with Unity IAP's provided product.reciept - validation passes server side.

    This looks to be malicious player behaviour - right after installing the game, he proceeds to perform this exploit as one of his early user event steps.

    Similar to @Draikz, we see similar behaviour in our metric logs.



    This happens on Android. We are using IAP version v3.1.0
     

    Attached Files:

    Last edited: Jul 22, 2021
  14. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    14,446
    What is the purchaseState on these transactions? Are you sure it's not just a Restore operation? If it's just a consumable, a Restore would not be expected. Are you able to reproduce?
     
  15. Darktemplar1

    Darktemplar1

    Joined:
    Apr 26, 2021
    Posts:
    6
    We are selling "Gems" IAP that are consumables. After processPurchase, we validate server side and confirm the purchase via Unity's StoreController.ConfirmPendingPurchase. Like @Draikz we are not able to reproduce the conditions under which it had occurred to reproduce the behaviour.

    My understanding is that processPurchase would also be triggered upon Unity IAP initialization if a previous purchase payment process was completed but the app is not notified (Google credit card payment dialog confirms payment, but the app is restarted before anything else happens) - please correct me if i'm wrong here.

    Thus, from our logs, the player InitiatePurchase, but the restarts. Upon restart, processPurchase is triggered with the receipt containing purchaseState = 4. To which we then use Playfab to verify (server-side verification).

    One interesting thing is that the purchase state on these transactions are "4".
    That seems to correspond to this thread where an exploit is mentioned in the case of deferred purchases with Google Billing Library v3:

    "It appears that some users may be exploiting a new feature offered by Google Billing v3 where they indeed choose a deferred payment method. If you are using server site validation you will need to accommodate purchaseState = 2 (or 4)."

    Perhaps they sound related?
     
  16. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    14,446
    Possibly, are you looking for purchaseState = 4 in your code? If so, continue to return Pending from ProcessPurchase.
     
  17. Darktemplar1

    Darktemplar1

    Joined:
    Apr 26, 2021
    Posts:
    6
    Can you please point to us Unity's documentation and guidance on what to do when purchaseState is 4 and deferred purchases? This sounds like a specific behaviour to Android and we want to ensure we aren't missing anything.

    It does looks like we are seeing the same behaviour as some of the users on that thread. So it sounds like irregardless adding a deferred listener is required to bypass the exploit?

    @mrm83 where he mentions:
    "Our case:
    no deferred listener, ProcessPurchase gets called for these unpaid purchases and it gets validated. FREE IAP."

    @roointan where he mentions:
    "We see purchases in Pending payment status in Google Play console, and they get cancelled automatically after some time.
    But they are accepted as complete purchases in the game."
     
  18. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    14,446
    This has been discussed elsewhere on this forum. This link has some good code (I haven't tested specifically) https://forum.unity.com/threads/google-play-iap-problem.1140367/#post-7351220 Google has a new payment method where users can choose to purchase now but defer the payment until later at an approved physical store. Until they actually pay, purchaseState is 4. If you receive this purchaseState, you want to continue to return Pending from ProcessPurchase until they pay (purchaseState = 1) , then return Complete.
     
  19. Darktemplar1

    Darktemplar1

    Joined:
    Apr 26, 2021
    Posts:
    6
    Thanks @JeffDUnity3D, can you provide an example of how users defer the payment until later at an approved physical store? How can we test this case?
     
  20. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    14,446
    I believe you can use the testing "Slow credit card with success" method. That should be an option on the device purchase popup. I haven't used or tested on a live production app however.