The documentation for the PromptPurchaseFinished event does not mention that it can be spoofed by exploiters. It currently implies that clients cannot trigger this event manually, especially with isPurchased = true.
However, in practice, exploiters can fire this signal from the client, causing servers to believe that a purchase has occurred even if no transaction was processed.
This creates a serious vulnerability for UGC and free UGC games. My experience shows that attackers are using this exploit to unlock paid content without actually buying it
For context, I traced back spam of my game system notifying people of purchasing a specific UGC directly to this signal, where the argument isPurchased = true
Expected behavior
When everything is working correctly, the PromptPurchaseFinished event should only be fired by Roblox after a legitimate purchase flow has been completed.
The isPurchased parameter should be set by Roblox servers based on an actual successful transaction, and it should be impossible for client-side code or exploit tools to spoof this event or force isPurchased = true.
Additionally, the documentation should clearly indicate that developers should verify purchases using a secure server-side method (e.g., ProcessReceipt or other APIs) before granting assets or rewards. Currently there is no ProcessReceipt equivalent for asset purchases!
If you look at the documentation, it clearly says that it doesn’t accurately reflect if the purchase is actually processed. This has never been a safe event and is already known.
You can use PlayerOwnsAsset to check if someone actually bought the asset they’re buying.
What if they are doing a purchase request on an asset they already own? Say they create a t-shirt for 999999 robux and do this spoofed request that they purchased it.
If the asset is owned when the signal is called, that would be my indication that the purchase has succeeded right? But the exploit here is that they are firing this purchase success event for third-party assets that they already own.
To know if they already own a third-party t-shirt that they personally created, I would have to parse through their inventory when they first join the game (which requires prompting for permission of their inventory). And after that I may need to call :PlayerOwnsAsset for hundreds of items before I can begin processing any PromptPurchaseFinished events.
This process is unusable for third-party asset purchase validation given that this is NOT a safe event
Here’s a more fleshed-out example for what I meant.
Player prompts purchase for an asset via a remoteevent or clickdetector, or whatever you’re using (this code should be running on server!)
Server checks if player already owns the asset using :PlayerOwnsAsset()
– Already owned → notify player that asset is already owned and stop here
Get price of asset for comparison later.
– Not available for sale → notify player that asset cannot be bought and stop here,.
Add the assetid + its price to a dictionary for which assets are whitelisted for a player when promptpurchasefinished runs
When the event fires
Check if the assetid is whitelisted for the player
– Not whitelisted → very likely an exploiter who spoofed the event.
Check if player actually bought the asset using :PlayerOwnsAsset()
– Not owned → remove from whitelist & stop here.
Get price of the asset again and compare it to the original one
– Different price → remove from whitelist & stop here, player likely changed price mid-prompt.
Do whatever else you wanna do, should be safe now.
No, ProcessReceipt does not process asset purchases. Even in the documentation, it states it is used for “developer product purchases”. It is for in-game dev products, not assets (ugc avatar items).
This is not a duplicate topic. I think you misread PromptPurchaseFinished as PromptProductPurchaseFinished (these are not the same thing)
The exploiter code does not even run the prompt. The exploit that is currently happening is spamming the PromptPurchaseFinished signal without a corresponding PromptPurchase call associated to it. That’s why this is fundamentally not protected against exploits.
The server never could know about a custom t-shirt that a player made a minute ago that they are now spamming PromptPurchaseFinished with isPurchased=true on to the server.
I misinterpreted what you meant by whitelist. I think I understand what you mean and yeah that potentially can work. The only issue I see is if playerownsasset fails when the prompt appears (does sometimes happen unfortunately) and the server only gets a 2-5 second time window that its able to cleanly process the purchase before it turns into a false positive