How to make this free ugc script more secure?

Hello. I am a UGC creator and wanted to make a free item in my game where when a player has enough in- game currency, they can purchase an item.

this is how the script works.

local storage = game:GetService("ReplicatedStorage")
local re = storage.RemoteEvent
local players = game:GetService("Players")
local player = players.LocalPlayer or players.PlayerAdded:Wait()
local item = script.Parent
local value = 40000
local id = 14147323794 -- ID


item.MouseButton1Click:Connect(function()
	if player.leaderstats.FairyDust.Value >= value then
		re:FireServer(value)
		game:GetService("MarketplaceService"):PromptPurchase(player, id)
		value = 0
	else
		item.Text = "Insuffiecetn Funds"
		task.wait(2)
		item.Text = "Purchase"
	end
end)

I was just wondering if there was a way to make this more secure? is it already secure enough? i dont want a bunch of exploiers getting the free item.

9 Likes

here is my server script service script too.

4 Likes

I was originally going to reply with updated security, but taking another look at this, it doesn’t seem like any form of security will really help in this case as exploiters can just call MarketplaceService:PromptPurchase() from their own scripts.

The only time you can add security to this kind of script is if the product in question is a Limited

2 Likes

It is a limited. I just want to make it more secure. Sadly the only way we can do it is with a purchase prompt ( the id isnt i was just testing it for now but i will change to limited later)

1 Like

In that case, to secure it, you can just move the MarketplaceService:PromptPurchase() call to the server

1 Like

Hello I did that, but there is an issue. if the player tries to buy it again without having enough money, it kicks them form the game.
image

Is there anyway to fix this or do i need to remove it?

You can either remove the kick or remove this line from the client script:

value = 0

does the kick even do anything? the one in the server script service

Don’t kick for trying to fire the remote without enough currency, just don’t do the expected action.

so can i just remove the kick then

Yes there’s no need for it, instead you can show an error message on the player’s screen

For starters, this won’t work because the PromptPurchase call for UGC must occur on the server. The reasoning? So exploiters can’t bypass the in-game tasks associated with winning the UGC item.

The correct way to award UGC items is using remotes and checking if the user has the requirements(and also awarding the item) on the server:

--Server(Script in ServerScriptService)
local MarketplaceService = game:GetService("MarketplaceService")

local remote = Instance.new("RemoteEvent", game.ReplicatedStorage)
remote.Name = "AwardUGC"

local price = 40000
local ugcID = 14147323794

--This function removes the price of the UGC from the user if the purchase is successful
MarketplaceService.PromptPurchaseFinished:Connect(function(player, id, isPurchased)
	if not isPurchased or id ~= ugcID then return end
	player.leaderstats.FairyDust.Value -= price 
end)

remote.OnServerEvent:Connect(function(player)
	--Why check on the server? So exploiters can't bypass it
	if player.leaderstats.FairyDust.Value >= price then
		MarketplaceService:PromptPurchase(player, ugcID)
	else
		--because they bypassed the client price check
		warn(player.Name.." may be exploiting!")
	end
end)
--Client(LocalScript inside some UI)
local remote: RemoteEvent = game.ReplicatedStorage:WaitForChild("AwardUGC")
local player = game.Players.LocalPlayer
local item: TextButton = script.Parent

local price = 40000 --make sure this is same for server as well

item.MouseButton1Click:Connect(function()
	if player.leaderstats.FairyDust.Value >= price then
		remote:FireServer()
	else
		item.Text = "Insufficient Funds"
		task.wait(2)
		item.Text = "Purchase"
	end
end)
2 Likes

Roblox patched this case specifically for UGC limiteds:

“past exploits” here is just a more fancy way to say exploiters were running PromptPurchase for a limited on the client because there was no limitation.

Anyway… I think I’m late, but I will try.

So… Make that in LocalScript:

local storage = game:GetService("ReplicatedStorage")
local re = storage.RemoteEvent
local players = game:GetService("Players")
local player = players.LocalPlayer or players.PlayerAdded:Wait()
local item = script.Parent
local value = 40000
local id = 14147323794 -- ID


item.MouseButton1Click:Connect(function()
   re:FireServer()
end)

and in ServerScript:

local storage = game:GetService("ReplicatedStorage")
local re = storage.RemoteEvent
local players = game:GetService("Players")
local item = script.Parent
local value = 40000
local id = 14147323794 -- ID

re.OnServerEvent:Connect(function(player)
    if player.leaderstats.FairyDust.Value >= value then
		game:GetService("MarketplaceService"):PromptPurchase(player, id)
		value = 0
	else
		item.Text = "Insuffiecetn Funds"
		task.wait(2)
		item.Text = "Purchase"
	end
end)

1 Like