I have a game like “pls donate” where players can purchase game passes from each other. If a player changes the price of their game pass before another person buys it, using MarketplaceService:GetProductInfo(13195518, Enum.InfoType.GamePass) will get the old price. This allows players to trick the system into thinking they bought a game pass for millions of robux.
Since MarketplaceService seems to update every 60 seconds, what’s a quicker way of getting the price of a game pass?
Just cache the original price of the devproduct/gamepass on the callback when the player purchases it. Then save it to a datastore and when you’re displaying the price, get from the datastore and boom ez
local MPS = game:GetService("MarketplaceService")
ID = "your Id"
wait(2)
MPS:PromptPurchase(game.Players.LocalPlayer,ID)
MPS.PromptPurchaseFinished:Connect(function()
if MPS:PlayerOwnsAsset(game.Players.LocalPlayer,ID) then
local A = MPS:GetProductInfo(ID, Enum.InfoType.GamePass)
print(A)
end
end)
this should work when the player purchases the item
Confusing? Yes. It has to be implemented as a single callback (You cannot connect more, it’s a property.) You can always make a function call other callbacks to make it more organized but that’s complex.
ProcessReceipt is the only reliable way to get the amount of currency spent on any given purchase prompt in a game. Don’t rely on cached info for prices in a multi-variable purchase, that’s just silly. And a good way to go broke/undercut people.
Not only that, what if the funds run out or the donation amount we’re processing is more than the funds we have? What if we want to implement a max donation amount, and stop purchases that go above it?
Well, ProcessReceipt allows us to effectively decline a transaction like a bank can decline a card.
-- Here's a snippet of a ProcessReceipt callback I wrote for a club.
local function HandleReceipt(Info)
local UserID = Info.PlayerId
local ProductID = Info.ProductId
if DevProducts["CurrencyAmt"][ProductID] ~= nil then
if not FindPlayer(UserID) then return Enum.ProductPurchaseDecision.NotProcessedYet end
local Player = FindPlayer(UserID)
local Profile = Profiles[Player]
if not Profile then return Enum.ProductPurchaseDecision.NotProcessedYet end
local Test = GrantCurrency(Player, Profile, DevProducts["CurrencyAmt"][ProductID])
if not Test then
return Enum.ProductPurchaseDecision.NotProcessedYet -- If this is returned, the purchase is effectively cancelled and the user has no robux deducted from their balance.
else
return Enum.ProductPurchaseDecision.PurchaseGranted
end
else
DonorDataStore:UpdateAsync(UserID, function(oldValue)
if (oldValue ~= nil) then
return oldValue + Info.CurrencySpent -- See this? It's the main use for ProcessReceipt in donation boards.
end
return Info.CurrencySpent
end)
return Enum.ProductPurchaseDecision.PurchaseGranted
end
end
And:
Technically yes, but also no. They don’t have any method of returning how much was spent other than through the receipt processing callback. Funnily enough, the result is also dependent on that callback! Woohoo.
So, you’d need to find out how to read the text on the PurchasePrompt (a CoreGui), then you can rely on that workaround until Roblox patches it since it’s technically an exploit.
Reminder: We’re using a script here to make a game like Donate pls; I can read but I’d dare say it’s rather hard to script that on Roblox.
I don’t believe it is. Edit: No, it doesn’t work for game passes, but seemingly only gamepasses. It works for both developer products and clothing, and possibly even more.
Maybe, except it’ll be one less API call and not cached (which will result in it giving the actual price instead of an older one).
Edit: It would be the same if the alternative actually worked.
The ProcessReceipt callback, for whatever reason in the name of Builderman, does not work with gamepasses! I feel like this is almost a bug, it is never explicitly mentioned anywhere on ProcessReceipt's section of the API that it doesn’t work with gamepasses!
ProcessReceipt callback (only PromptGamePassPurchase will work due to gamepasses having different IDs, my gamepass ID as an asset ID points to a Classic Shirt):
So, unfortunately, it seems like the only way to get a gamepasses’ price is to use :GetProduceInfo.
Back to OP’s problem, I don’t think you should be using gamepasses in the first place. Why not use developer products or t-shirts (like that “Starving Artists” game does) instead if this is such a big issue? I’m not entirely sure there’s a fix for this issue.
Ah my draft didn’t send in reply to @Play_MazeOfHeck: This was my bad. I don’t know how I overlooked the literal reason ProcessReceipt exists (to reward players with in-game items), perhaps I was just too sleepy.
Either way, I’m not entirely positive how Pls Donate sanity checks their donations, but I’ll see if I can find anything relevant and let you know if I do. (If this isn’t solved by then.)
Edit: Also, possible solutions include donation cooldowns, sanity checks, etc.
Another note I have (that I think would be most helpful) is only using :GetProductInfo() after they’ve bought it, since the API should be quicker than they can swap prices.