How to pass player as an argument in developer product script?

Hi! I’m writing a script where once you purchase a developer product, it adjusts the players stage count through a bindable event. The problem is that the player that bought the developer product isn’t being passed to the function, resulting in an “attempt to index nil with leaderstats”. However, I have no idea how to pass the player in my current script (from Roblox dev hub). Script:

local MarketplaceService = game:GetService("MarketplaceService")
local DataStoreService = game:GetService("DataStoreService")
local Players = game:GetService("Players")
local updateLevelEvent = game.ReplicatedStorage.BindableEvents.UpdateLevel

-- 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 = {}
productFunctions[1316145736] = function(receipt, player)
	updateLevelEvent:Fire(1, player)
	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 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]
	print(handler)
	print(player)
	print(receiptInfo)
	-- Call the handler function and catch any errors
	local success, result = pcall(handler(receiptInfo, player), 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

Help is appreciated!

You didn’t use the pcall function right.

New code:

local MarketplaceService = game:GetService("MarketplaceService")
local DataStoreService = game:GetService("DataStoreService")
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local updateLevelEvent = ReplicatedStorage.BindableEvents.UpdateLevel

-- 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 = {}

productFunctions[1316145736] = function(receipt, player)
	updateLevelEvent:Fire(1, player)
	
	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 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]
	print(handler)
	print(player)
	print(receiptInfo)
	
	-- 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

Also bindable events don’t accept instances, which is why they actually convert the player instance to a string.

I tried this code and it still gave me the attempt to index nil with leader stats bug

Show me your bindable event code, it’s most likely because you’re sending an instance and why that wouldn’t work is what I explained.

here you go:

local function updatePlayerLevelBy(num, player)
	print(player);
	print("Update Player Level Received!")
	player.leaderstats.stage.Value = player.leadersats.stage.Value + num;
	task.wait()
	player.Character:MoveTo(checkpoints[stageInt.Value].Position);
end

event.Event:Connect(function(player)
	character = player.Character;
	character:MoveTo(checkpoints[stageInt.Value].Position);
end)


uPLE.Event:Connect(updatePlayerLevelBy(num, player));

uPLE is the event that the dev product code triggered

Yep sending an event like this won’t work.

Change your product code to this:

local MarketplaceService = game:GetService("MarketplaceService")
local DataStoreService = game:GetService("DataStoreService")
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local updateLevelEvent = ReplicatedStorage.BindableEvents.UpdateLevel

-- 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 = {}

productFunctions[1316145736] = function(receipt, player)
	updateLevelEvent:Fire(1, player.Name)

	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 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]
	print(handler)
	print(player)
	print(receiptInfo)

	-- 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

And your bindable code to this:

local Players = game:GetService("Players")

local function updatePlayerLevelBy(num, playerName)
	local player = Players:WaitForChild(playerName)
	print(player);
	print("Update Player Level Received!")
	player.leaderstats.stage.Value = player.leadersats.stage.Value + num;
	task.wait()
	player.Character:MoveTo(checkpoints[stageInt.Value].Position);
end

event.Event:Connect(function(player)
	character = player.Character;
	character:MoveTo(checkpoints[stageInt.Value].Position);
end)

uPLE.Event:Connect(updatePlayerLevelBy(num, player));

After trying this code it gave me the error “Argument 1 missing or nil”
I’m assuming that the

uPLE.Event:Connect(updatePlayerLevelBy(num, player));

is an incorrect way of retrieving the passed arguments?

I just realized you can’t connect a local function like that.

Try changing your bindable code to this:

local Players = game:GetService("Players")

event.Event:Connect(function(player)
	character = player.Character;
	character:MoveTo(checkpoints[stageInt.Value].Position);
end)

uPLE.Event:Connect(function(num, playerName)
	local player = Players:WaitForChild(playerName)
	print(player);
	print("Update Player Level Received!")
	
	player.leaderstats.stage.Value += num;
	task.wait()
	player.Character:MoveTo(checkpoints[stageInt.Value].Position);
end)
1 Like

Thank you so much this worked!

1 Like

No problem. If you have any more questions, feel free to ask.

1 Like