I cannot test it right now, but what is firing first? And what should you use in what scenarios? As far as I understand, ProcessReceipt is vaguely the same as PromptProductPurchaseFinished with the difference being that in ProcessReceipt you should confirm that you processed a purchase, but it can be called mutliple times so adding currency in this function seems a bad idea. According to their code snippet for ProcessReceipt, they are adding gold every time this callback is fired, but that doesn’t make any sense since it can be fired multiple times. And why would I record purchase id if I want to save player’s data? (to grant them that their purchase has been saved since DataStoreService may ocasionally fail)
I might misunderstood the purpose of ProcessReceipt, but in my opinion it is used to save the player’s data after they made a purchase (currencies, boosts, you name it). Or is it used just to make that 1 day limited offer not taking robux if a player attempts to buy it when they are not supposed to. I want to know a clear definition and purpose of ProcessReceipt and why would I use it over a PromptProductPurchaseFinished (and can they both be used at the same time)
Process receipt should be used for every single game dev product. PromptProductPurchaseFinished is only used for detecting when the box is closed. So after pressing Ok after success, would trigger this, or cancelling. ProcessReceipt is called when its actually processing the robux. That’s what you want. Also, that code snippet is calling a specific function based on what product the user bought. It’s best to add currency there, as that is where the product is actually being purchased, and robux is being taken. The other function is just the box closing.
you are actually wrong since there is a boolean argument in PromptProductPurchaseFinished which tells you if they hit cancel or actually bought a product succesfully
Actually bought a product successfully is in there yes, but once again its only after the box closes, meanwhile it says processing while its running ProcessReceipt.
Properly setting up your ProcessReceipt callback is required for selling developer products. Even though both options tell you whether or not the user made the purchase, only the response provided in your ProcessReceipt callback can inform ROBLOX as to whether or not you have fulfilled the expectations outlined by your product.
In the code sample on the developer hub, a receipt is sent to the callback, and the first thing it does is check whether or not the receipt has already been processed. It does this by checking a DataStore. If the receipt is present, the code returns Enum.ProductPurchaseDecision.PurchaseGranted. The ProcessReceipt callback can be invoked more than one time per purchase, so checking to see whether or not the receipt has already been processed prevents unintentionally granting services additional times.
If the receipt is not present, that means the user has not yet received what they paid for. At that point, the code provides the service in accordance to the developer product purchased (either healing the player or giving them money in the example). This takes place in an DataStore:UpdateAsync() call, which ensures that both the service being provided and the recording of the receipt happen in the same step and so that both must succeed for either to happen.
If the service is successfully provided and the receipt is saved, the code returns Enum.ProductPurchaseDecision.PurchaseGranted to inform ROBLOX that the transaction has been fulfilled. If the service could not be provided and recorded, the code returns Enum.ProductPurchaseDecision.NotProcessedYet to inform ROBLOX that the transaction has not been fulfilled, which will prompt the callback to be invoked again at another point to try again.
ROBLOX uses these return values to determine if people get what they paid for, so it is imperative that you use this method for handling developer product transactions.
That’s a good explonation, however I still have some questions left.
So I get it, you check datastore for the present of purchase id, if there is none, then you do whatever your product is advertised for (alongside recording a purchase id), and then return purchasegranted enum, I kinda had that idea in mind.
However, updateAsync function may fail (you know, roblox’s backend error) and we are adding gold inside of updateAsync’s callback function before we return a value. As I understand, when you return a value in a callback function, it will basically perform a setAsync operation, meaning it may fail. Since we are catching errors using pcall, we will tell roblox that we have not provided a service yet we have actually provided one, we just failed to record purchase id, meaning if we succesfully save player’s current data with services provided to them and they come back after some time, we will add gold again
If I understand how updateAsync works, roblox’s code isn’t good or ProcessReceipt shouldn’t be used in a scenario where we just add gold since providing paid services for free is the last thing a developer wants to do if they want money. This is why PromptProductPurchaseFinished is better since it’s guaranteed that it will fire once for every succesfull purchase and I still see no reason why would I use ProcessReceipt to handle purchases if logically my code can never fail.
Again, like I originally said, I thought of using it for special time limited deals and such. And ProcessReceipt doesn’t protect players from failing to save their data upon disconnecting a game yet takes robux either way (just like with PromptProductPurchaseFinished).
What about telling roblox that we have succesfully provided a service… might as well instantly return purchaseGranted because logically my code can never fail and all we do there is saving purchase id
To sumup this sh*tpost, processReceipt can be called multiple times despite succesfully providing services and data loss is still a posibility
Using PromptProductPurchaseFinished for processing sales is deprecated behavior–the only reason the function isn’t outright deprecated is because it is the only function that handles the closing of the prompt.
What I will say is that the code sample is not perfect. It does a poor job of handling the case where the DataStore call fails; however, whether or not the service was provided and whether or not the save succeeded are both known states. In the case where the service is provided but the save fails, you could write a handler to address the failed save state that undoes the service until another attempt can be made, avoiding the issue of providing the service for free. This cannot be addressed properly with PromptProductPurchaseFinished because it will only ever try once. Yes, you can keep retrying on the server the player made the purchase on, but it provides no reliability beyond that
If you’re storing your player’s data and receipts in the same DataStore entry (which, admittedly, the code sample also does not do), you can guarantee by running the code in an UpdateAsync call that the service was only provided once and that it was successfully recorded.
Ultimately, without a record of the transaction being stored somewhere, you cannot confidently prove that you did provide the service you claimed to provide. While it is understandable to want to avoid providing paid services for free, it is much worse to be liable for taking payment for services you did not provide. Setting up this callback correctly resolves that issue.
ProcessReceipt is a function used to handle the processing of purchase transactions and updating player data, PromptProductPurchaseFinished is responsible for handling user responses and interactions after a purchase prompt, such as updating the user interface or triggering further actions. Xx