Not purchasing one specific product before the other ones breaks it

I have a system where it is supposed to fire a Client event when it’s purchased. For some reason, not purchasing the Mini Storm before any other ones completely breaks it and doesn’t spawn it due to a “Transform function error”. None of the code for any of it is any different except for the product ids.

This is the code I have so far:

local MarketplaceService = game:GetService("MarketplaceService")
local DataStoreService = game:GetService("DataStoreService")
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local minievent = ReplicatedStorage:FindFirstChild("Mini")
local stormevent = ReplicatedStorage:FindFirstChild("Storm")
local omegaevent = ReplicatedStorage:FindFirstChild("Omega")

-- 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 = {}
-- ProductId 123123 for a full heal
-- ProductId 456456 for 100 gold

productFunctions[1298117676] = function(receipt, player)
	omegaevent:FireClient(player)
	return true
end

productFunctions[1298116021] = function(receipt, player)
	minievent:FireClient(player)
	return true 
end

productFunctions[1274234003] = function(receipt, player)
	stormevent:FireClient(player)
	return true 
end

productFunctions[1274234002] = function(receipt, player)
	player.leaderstats.Credits.Value += 100
	return true
end
productFunctions[1274234005] = function(receipt, player)
	player.leaderstats.Credits.Value += 250
	return true
end
productFunctions[1274234001] = function(receipt, player)
	player.leaderstats.Credits.Value += 1000
	return true
end
productFunctions[1274234004] = function(receipt, player)
	player.leaderstats.Credits.Value += 2500
	return true
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 success, isPurchaseRecorded = pcall(function()
		return purchaseHistoryStore:UpdateAsync(playerProductKey, function(alreadyPurchased)
			if alreadyPurchased then
				return true
			end

			-- Find the player who made the purchase in the server
			local player = Players:GetPlayerByUserId(receiptInfo.PlayerId)
			script.Parent.Buyer.Value = player.Name
			if not player then
				-- The player probably left the game
				-- If they come back, the callback will be called again
				return nil
			end

			local handler = productFunctions[receiptInfo.ProductId]

			local success, result = pcall(handler, receiptInfo, player)
			-- If granting the product failed, do NOT record the purchase in datastores.
			if not success or not result then
				error("Failed to process a product purchase for ProductId:", receiptInfo.ProductId, " Player:", player)
				return nil
			end

			-- Record the transcation in purchaseHistoryStore.
			return true
		end)
	end)

	if not success then
		error("Failed to process receipt due to data store error.")
		return Enum.ProductPurchaseDecision.NotProcessedYet
	elseif isPurchaseRecorded == nil then
		-- Didn't update the value in data store.
		return Enum.ProductPurchaseDecision.NotProcessedYet
	else	
		-- IMPORTANT: Tell Roblox that the game successfully handled the purchase
		return Enum.ProductPurchaseDecision.PurchaseGranted
	end
end

-- Set the callback; this can only be done once by one script on the server! 
MarketplaceService.ProcessReceipt = processReceipt

I don’t know why it only does it if I don’t buy the Mini storm first, and that it goes through for all the other ones if I buy the Mini storm first.

I don’t believe it’s a problem with processing the receipt; the client seems to be having the issue. The firing of remote events is working fine if you get errors from them. Could you show the client script?

The client script posts a chat message then fires an event to the server:

mini.OnClientEvent:Connect(function(player)
	mini:FireServer(player)
		for _,Player in pairs(game.Players:GetPlayers()) do
			local StarterGui = game:GetService("StarterGui")
			StarterGui:SetCore("ChatMakeSystemMessage", {
				Text = "A mini storm is starting! Thank you, " ..workspace.Buyer.Value.. "!",
				Color = Color3.fromHSV(0.763889, 1, 0.8),
				TextSize = 18
			})
			wait(30)
			workspace.StormActive.Value = false
			StarterGui:SetCore("ChatMakeSystemMessage", {
				Text = "The mini storm has ended.",
				Color = Color3.fromHSV(0.763889, 1, 0.8),
				TextSize = 18
			})
		end
end)
storm.OnClientEvent:Connect(function(player)
	storm:FireServer(player)
	for _,Player in pairs(game.Players:GetPlayers()) do
		local StarterGui = game:GetService("StarterGui")

		StarterGui:SetCore("ChatMakeSystemMessage", {
			Text = "A credit storm is starting! Thank you, " ..workspace.Buyer.Value.. "!",
			Color = Color3.fromHSV(0.7915, 1, 1),
			TextSize = 20
		})
		wait(60)
		workspace.StormActive.Value = false
		StarterGui:SetCore("ChatMakeSystemMessage", {
			Text = "The credit storm has ended.",
			Color = Color3.fromHSV(0.7915, 1, 1),
			TextSize = 18
		})
	end
end)
omega.OnClientEvent:Connect(function(player)
	omega:FireServer(player)
	for _,Player in pairs(game.Players:GetPlayers()) do
		local StarterGui = game:GetService("StarterGui")
		StarterGui:SetCore("ChatMakeSystemMessage", {
			Text = "An OMEGA storm is starting! Thank you, " ..workspace.Buyer.Value.. "!",
			Color = Color3.fromHSV(0.935944, 1, 1),
			TextSize = 24
		})
		wait(120)
		workspace.StormActive.Value = false
		StarterGui:SetCore("ChatMakeSystemMessage", {
			Text = "The omega storm has ended.",
			Color = Color3.fromHSV(0.935944, 1, 1),
			TextSize = 18
		})
	end
end)

The receiving end of the Server event is as follows:

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local minievent = ReplicatedStorage:FindFirstChild("Mini")
local stormevent = ReplicatedStorage:FindFirstChild("Storm")
local omegaevent = ReplicatedStorage:FindFirstChild("Omega")
minievent.OnServerEvent:Connect(function(player)
	workspace.Lampshade.CoinDuper2.Enabled = true
	wait(30)
	workspace.Lampshade.CoinDuper2.Enabled = false
end)
stormevent.OnServerEvent:Connect(function(player)
	workspace.Lampshade.CoinDuper3.Enabled = true
	wait(60)
	workspace.Lampshade.CoinDuper3.Enabled = false
end)
omegaevent.OnServerEvent:Connect(function(player)
	workspace.Lampshade.CoinDuper4.Enabled = true
	wait(120)
	workspace.Lampshade.CoinDuper4.Enabled = false
end)

Sorry for the late reply, ended up starting work on my project and got caught up in it. For one, without even really reading the scripts, this script seems very exploitable. For two, you can’t set other people’s core GUIS through a local script; you would need to do that on theirs (this script would only be setting your core GUI). Also, you don’t need to provide the player variables inside of the FireServer; rather you shouldn’t even need the player in the server script, so you should be using a bindable event. Lastly, you are firing the singular player’s client (which I think is the main problem) which causes the player received in the client script to be nil. In the fixed version, you won’t need the Buyer Value. Here’s a fixed version of that:

local SSS = game:GetService("ServerScriptService") --put bindables in here
local mini = SSS:FindFirstChild("Mini") --BindableEvent
local storm = SSS:FindFirstChild("Storm") --BindableEvent
local omega = SSS:FindFirstChild("Omega") --BindableEvent

mini.Event:Connect(function()
	workspace.Lampshade.CoinDuper2.Enabled = true
	wait(30)
	workspace.Lampshade.CoinDuper2.Enabled = false
end)

storm.Event:Connect(function()
	workspace.Lampshade.CoinDuper3.Enabled = true
	wait(60)
	workspace.Lampshade.CoinDuper3.Enabled = false
end)

omega.Event:Connect(function()
	workspace.Lampshade.CoinDuper4.Enabled = true
	wait(120)
	workspace.Lampshade.CoinDuper4.Enabled = false
end)

Now, for the client script

local plr = game.Players.LocalPlayer

mini.OnClientEvent:Connect(function(buyer)
	local StarterGui = game:GetService("StarterGui")
	
	StarterGui:SetCore("ChatMakeSystemMessage", {
		Text = "A mini storm is starting! Thank you, " ..buyer.Name.. "!",
		Color = Color3.fromHSV(0.763889, 1, 0.8),
		TextSize = 18
	})
	
	task.wait(30)
	workspace.StormActive.Value = false
	
	StarterGui:SetCore("ChatMakeSystemMessage", {
		Text = "The mini storm has ended.",
		Color = Color3.fromHSV(0.763889, 1, 0.8),
		TextSize = 18
	})
end)

storm.OnClientEvent:Connect(function(buyer)
	local StarterGui = game:GetService("StarterGui")
	
	StarterGui:SetCore("ChatMakeSystemMessage", {
		Text = "A credit storm is starting! Thank you, " ..buyer.Name.. "!",
		Color = Color3.fromHSV(0.7915, 1, 1),
		TextSize = 20
	})
	
	task.wait(60)
	workspace.StormActive.Value = false
	
	StarterGui:SetCore("ChatMakeSystemMessage", {
		Text = "The credit storm has ended.",
		Color = Color3.fromHSV(0.7915, 1, 1),
		TextSize = 18
	})
end)

omega.OnClientEvent:Connect(function(buyer)
	local StarterGui = game:GetService("StarterGui")
	
	StarterGui:SetCore("ChatMakeSystemMessage", {
		Text = "An OMEGA storm is starting! Thank you, " ..buyer.Name.. "!",
		Color = Color3.fromHSV(0.935944, 1, 1),
		TextSize = 24
	})
	
	task.wait(120)
	workspace.StormActive.Value = false
	
	StarterGui:SetCore("ChatMakeSystemMessage", {
		Text = "The omega storm has ended.",
		Color = Color3.fromHSV(0.935944, 1, 1),
		TextSize = 18
	})
end)

and lastly for the processReceipt script

local MarketplaceService = game:GetService("MarketplaceService")
local DataStoreService = game:GetService("DataStoreService")
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local minievent = ReplicatedStorage:FindFirstChild("Mini")
local stormevent = ReplicatedStorage:FindFirstChild("Storm")
local omegaevent = ReplicatedStorage:FindFirstChild("Omega")
local SSS = game:GetService("ServerScriptService")
local minibind = SSS:FindFirstChild("Mini")
local stormbind = SSS:FindFirstChild("Storm")
local omegabind = SSS:FindFirstChild("Omega")


-- 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 = {}
-- ProductId 123123 for a full heal
-- ProductId 456456 for 100 gold

productFunctions[1298117676] = function(receipt, player)
	omegaevent:FireAllClients(player)
	omegabind:Fire()
	return true
end

productFunctions[1298116021] = function(receipt, player)
	minievent:FireAllClients(player)
	minibind:Fire()
	return true 
end

productFunctions[1274234003] = function(receipt, player)
	stormevent:FireAllClients(player)
	storm:Fire()
	return true 
end

productFunctions[1274234002] = function(receipt, player)
	player.leaderstats.Credits.Value += 100
	return true
end
productFunctions[1274234005] = function(receipt, player)
	player.leaderstats.Credits.Value += 250
	return true
end
productFunctions[1274234001] = function(receipt, player)
	player.leaderstats.Credits.Value += 1000
	return true
end
productFunctions[1274234004] = function(receipt, player)
	player.leaderstats.Credits.Value += 2500
	return true
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 success, isPurchaseRecorded = pcall(function()
		return purchaseHistoryStore:UpdateAsync(playerProductKey, function(alreadyPurchased)
			if alreadyPurchased then
				return true
			end

			-- Find the player who made the purchase in the server
			local player = Players:GetPlayerByUserId(receiptInfo.PlayerId)
			script.Parent.Buyer.Value = player.Name
			if not player then
				-- The player probably left the game
				-- If they come back, the callback will be called again
				return nil
			end

			local handler = productFunctions[receiptInfo.ProductId]

			local success, result = pcall(handler, receiptInfo, player)
			-- If granting the product failed, do NOT record the purchase in datastores.
			if not success or not result then
				error("Failed to process a product purchase for ProductId:", receiptInfo.ProductId, " Player:", player)
				return nil
			end

			-- Record the transcation in purchaseHistoryStore.
			return true
		end)
	end)

	if not success then
		error("Failed to process receipt due to data store error.")
		return Enum.ProductPurchaseDecision.NotProcessedYet
	elseif isPurchaseRecorded == nil then
		-- Didn't update the value in data store.
		return Enum.ProductPurchaseDecision.NotProcessedYet
	else	
		-- IMPORTANT: Tell Roblox that the game successfully handled the purchase
		return Enum.ProductPurchaseDecision.PurchaseGranted
	end
end

-- Set the callback; this can only be done once by one script on the server! 
MarketplaceService.ProcessReceipt = processReceipt

Edit: to elaborate on the exploitable, the server script allowed it to be fired by anyone and had no checks. I changed that into bindable events so it could only be interacted with by the server.