I recently created a DevProduct script AND a Datastore script with a shop. The shop works fine, no problems. But the real problem is that I’ve been trying to update my leaderstats.Points.Value variable without while true do since it would just go on forever and never return true. The reason I’m trying to update my variable? When I buy some points from the store, it takes the first values from the datastore. Even if the value changed after that.
In simple words:
I have 16 points.
I buy something for 4 points.
I have 12 points.
I buy 10 points.
I have 26 points.
It just takes the old Points values and never takes the current ones.
local MarketplaceService = game:GetService("MarketplaceService")
local DataStoreService = game:GetService("DataStoreService")
local Players = game:GetService("Players")
-- Data store for tracking purchases that were successfully processed
local purchaseHistoryStore = DataStoreService:GetDataStore("PurchaseHistory")
-- Table setup containing product IDs and functions for handling purchases
local productFunctions = {1228461943, 1228464400}
-- ProductId 456456 for 100 gold
productFunctions[1228464400] = function(receipt, player)
-- Logic/code for player buying 500 gold (may vary)
local stats = player:FindFirstChild("leaderstats")
local gold = stats and stats:FindFirstChild("Points")
if gold then
gold.Value = gold.Value + 500
-- Indicate a successful purchase
return true
end
end
productFunctions[1228461943] = function(receipt, player)
-- Logic/code for player buying 100 gold (may vary)
local stats = player:FindFirstChild("leaderstats")
local gold = game.ReplicatedStorage.Player.Points
if gold then
gold.Value = gold.Value + 100
-- Indicate a successful purchase
return true
end
end
-- The core 'ProcessReceipt' callback function
local function processReceipt(receiptInfo)
-- Determine if the product was already granted by checking the data store
local playerProductKey = receiptInfo.PlayerId .. "_" .. receiptInfo.PurchaseId
local purchased = false
local success, errorMessage = pcall(function()
purchased = purchaseHistoryStore:GetAsync(playerProductKey)
end)
-- If purchase was recorded, the product was already granted
if success and purchased then
return Enum.ProductPurchaseDecision.PurchaseGranted
elseif not success then
error("Data store error:" .. errorMessage)
end
-- Find the player who made the purchase in the server
local player = Players:GetPlayerByUserId(receiptInfo.PlayerId)
if not player then
-- The player probably left the game
-- If they come back, the callback will be called again
return Enum.ProductPurchaseDecision.NotProcessedYet
end
-- Look up handler function from 'productFunctions' table above
local handler = productFunctions[receiptInfo.ProductId]
-- Call the handler function and catch any errors
local success, result = pcall(handler, receiptInfo, player)
if not success or not result then
warn("Error occurred while processing a product purchase")
print("\nProductId:", receiptInfo.ProductId)
print("\nPlayer:", player)
return Enum.ProductPurchaseDecision.NotProcessedYet
end
-- Record transaction in data store so it isn't granted again
local success, errorMessage = pcall(function()
purchaseHistoryStore:SetAsync(playerProductKey, true)
end)
if not success then
error("Cannot save purchase data: " .. errorMessage)
end
-- IMPORTANT: Tell Roblox that the game successfully handled the purchase
return Enum.ProductPurchaseDecision.PurchaseGranted
end
-- Set the callback; this can only be done once by one script on the server!
MarketplaceService.ProcessReceipt = processReceipt
Heres the shop:
local function Exchange()
local Sounds = script.Parent.Parent.Parent.Parent.Parent.Sounds
local Cola = game.ReplicatedStorage.ToolsGUI.CocaCola
local Points = game.Players.LocalPlayer.leaderstats.Points
if Points.Value >= 400 then
Points.Value = Points.Value - 400
local NCola = Cola:Clone()
NCola.Parent = game.Players.LocalPlayer.Backpack
Sounds.ching:Play()
print(game.Players.LocalPlayer.Name.." has just bought a Cola!")
else
Sounds.fail:Play()
script.Parent.Parent.Failed.Visible = true
print(game.Players.LocalPlayer.Name.." tried to buy a cola but didn't have enough points. RIP : (")
end
end
script.Parent.Activated:Connect(Exchange)
(also, remember to add a RemoteEvent into ReplicatedStorage)
Shop LocalScript:
--//Services
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")
--//Variables
local RemoteEvent = ReplicatedStorage.RemoteEvent
local LocalPlayer = Players.LocalPlayer
--//Functions
script.Parent.Activated:Connect(function()
local Sounds = script.Parent.Parent.Parent.Parent.Parent.Sounds
if Points.Value >= 400 then
RemoteEvent:FireServer("Cola")
Sounds.ching:Play()
else
Sounds.fail:Play()
script.Parent.Parent.Failed.Visible = true
return
end
end)
ServerScript:
--//Services
local ReplicatedStorage = game:GetService("ReplicatedStorage")
--//Variables
local RemoteEvent = ReplicatedStorage.RemoteEvent
local Cola = ReplicatedStorage.ToolsGUI.CocaCola
--//Functions
RemoteEvent.OnServerConnect(function(player, item)
if item == "Cola" then
if player.leaderstats.Points.Value >= 400 then -- Sanity check
player.leaderstats.Points.Value -= 400
local NCola = Cola:Clone()
NCola.Parent = player.Backpack
print(player.Name .. " has just bought a Cola!")
else
print(player.Name.." tried to buy a cola but didn't have enough points. RIP : (")
end
end
end)
Change the numbering, as @katrist said " (this should work) " there isn’t a full guarantee it will use the fixed code and skim through it to figure out numbering and receive coins/points as needed.
You’re modifying the “Points” stat from the client (through code executed inside local scripts) which means that these changes won’t replicate to the server (won’t be seen/observed/noticed by the server), this consequently means that the server script which handles DataStore saving will not realise these changes and only save the stat which is currently known to the server.