ServerScript
local marketplaceService = game:GetService("MarketplaceService") local dataStoreService = game:GetService("DataStoreService") local purchaseHistory = dataStoreService:GetDataStore("PurchaseHistory") local replicatedStorage = game:GetService("ReplicatedStorage") local eventsFolder = replicatedStorage:WaitForChild("EventsFolder") local purchaseEvent = eventsFolder:WaitForChild("PurchaseEvent") local products = { ["Gold50"] = {ID = 100122331, Gold = 50}; ["Gold100"] = {ID = 111111111, Gold = 100}; ["Gold250"] = {ID = 11111112, Gold = 250}; ["Gold500"] = {ID = 111111113, Gold = 500}; ["Gold1000"] = {ID = 111111114, Gold = 1000}; ["Gold1500"] = {ID = 111111115, Gold = 1500}; } purchaseEvent.OnClientEvent:connect(function(...) local tuple = {...} if tuple then marketplaceService.ProcessReceipt = function(receiptInfo) for _, player in ipairs(game.Players:GetChildren()) do if player.userId == receiptInfo.PlayerId then if receiptInfo.ProductId == products["Gold" .. tuple[1][2]] then -- Give the gold end end end return Enum.ProductPurchaseDecision.PurchaseGranted end end end)
LocalScript:
for _, v in pairs(buttonFrame:GetChildren()) do if v:IsA("ImageButton") then v.MouseButton1Down:connect(function() player:FireClient(v.Gold.Value) end) end end
Gold.Value == 50
Original post moved to pastebin, as original question has been completely redone. Link: https://pastebin.com/Gmdc90wc
Note: for tables you can do products.Gold50
or products["Gold50"]
-- these are identical. You only need to use the []
for when you want to have a key that is not a string of the form of a variable. ex, products.50
is not allowed because 50
is not a legal variable name.
Based on your comments, I didn't explain the purpose of using all these classes/functions. The purpose is generally to let you make changes later without having to rewrite lots of bits of code. Ideally you make a change in one place and the rest of the script just works with it. To do this, the product table should have fields that are as general as you can make them -- ex, specifying "Amount" instead of "Gold". It's the difference between having 4 functions for granting Silver, Gold, XP, and Prestige (for example) and having just 1 function to do all four. On the other hand, if each product is completely unique (ex one grants gold, another gives an inventory item, another gives you the ability to run faster), then there's nothing wrong with being specific and using "Gold". Don't worry about getting this right the first time, as you may not have even invented all the products you want yet. Instead, as you continue to work with the script, keep on the look out for bits of code that are doing almost exactly the same thing and see if you can combine them into a single function. ex, say you did have a function for each of granting Silver, Gold, XP, and Prestige; here's how you'd simplify them:
--For simplicity I'll assume you're using IntValues to store everything function GiveGold(player, gold) player.Gold.Value = player.Gold.Value + gold end function GiveXP(player, xp) player.XP.Value = player.XP.Value + xp end --etc --In your Purchase function, say you've got the variable "name" referring to "Gold"/"XP"/etc and "amount" referring to the amount they're purchasing, you'd then have this code: if name == "Gold" then GiveGold(player, amount) elseif name == "XP" then GiveXP(player, amount) --etc end --Notice how, though the variable names differ, the format is the same. We can change the code to look like this: function GiveResource(player, resource, amount) player[resource].Value = player[resource].Value + amount end --In the purchase function, that 'if' block can be reduced to: GiveResource(player, name, amount)
In my opinion, you would ideally have three scripts:
products
table.However, for simplicity, I'll work with the scripts you've provided and I'll leave the LocalScript alone (though you'll need to change line 4 to player:FireClient("Gold", v.Gold.Value)
, and player
should be the PurchaseEvent
). Here's the server script:
(Too long for this post, see https://pastebin.com/HftR2qqe)
Note: Ideally the price is fetched using GetProductInfo instead of input manually (since then you can change it on the Roblox website and you don't have to remember to update your scripts). You don't even need it on the server, so if you are updating the GUI manually, you can delete Price entirely.
Note 2: The error
s in purchaseEvent could occur either because you set up something wrong in the LocalScripts or because an exploiter is sending invalid data.
Note 3: I took some of the code from Handling Multiple Developer Products. As such, _G.awardGold (or the ApplyFunc parameter) *must* return true if successful.
Note 4: I deleted purchaseHistory and replaced it with local storage. You could add it back in again if you ever might need to browse the global purchase history (but I doubt it would be useful). The local system I replaced it with only saves the information for 5 minutes, but the whole point of it is to prevent anything undesirable from happening should Roblox call ProcessReceipt multiple times. The Datastore method in the wiki would actually fail at preventing you from giving the player more than they purchased if ProcessReceipt was called quickly 2x in a row.