What do you want to achieve?
I want to put all the game’s products in one function, because apparently there can only be on one call back throughout your entire game. Therefore I have the first major section which is for one product, and the second major section for the second product as seen below.
function ReceiptHandler()
warn("Donation Board: ProcessReceipt Activated")
game:GetService("MarketplaceService").ProcessReceipt = function(receiptInfo)
local playerProductKey = "player_" .. receiptInfo.PlayerId .. "_product_" .. receiptInfo.ProductId
local numberBought = game:GetService("DataStoreService"):GetDataStore("PurchaseHistory"):IncrementAsync(playerProductKey, 1)
local ProductBought = game:GetService("DataStoreService"):GetDataStore("PurchaseHistoryCount"):IncrementAsync(receiptInfo.ProductId, 1)
local PlayerFound = false
local players = game.Players.LocalPlayer
local done = 0
for i=1,#players do
if players[i].userId == receiptInfo.PlayerId then
if receiptInfo.ProductId == 1086221671 and done == 0 then
done = 1
players[i].leaderstats.Stage.Value = players[i].leaderstats.Stage.Value + 1
players[i]:LoadCharacter()
done = 0
end
end
end
return Enum.ProductPurchaseDecision.PurchaseGranted
for i, v in pairs (game.Players:GetChildren()) do
if v:IsA('Player') then
if v.userId == receiptInfo.PlayerId then
for _, p in pairs (Products.Products) do
if p.ProductId == receiptInfo.ProductId then
if v ~= nil then
PlayerFound = true
game:GetService("DataStoreService"):GetOrderedDataStore(GetData()):IncrementAsync(receiptInfo.PlayerId, p.ProductPrice)
end
end
end
end
end
end
if PlayerFound ~= true then
return Enum.ProductPurchaseDecision.NotProcessedYet
else
return Enum.ProductPurchaseDecision.PurchaseGranted
end
end
What is the issue?
The only issue I’m having is that for is underlined which means when buying these products they may not work; not quite sure if I did anything else correct, if not, please let me know
What solutions have you tried so far?
I’ve tried setting these call backs in completely scripts, which didn’t work because I was informed you had to do it in one (call backs as in the receipt function). I started today by trying to debug a perfectly working script to then realize it was this issue.
You seem to be trying to access the LocalPlayer from a server script, or you are handling the purchases in a client sided script, which you shouldn’t do.
You are trying to iterate through the local player with your “for” loop. This combination of code specifically:
local players = game.Players.LocalPlayer
for i = 1, # players
The # notation is supposed to be used with a table, not an instance (LocalPlayer).
What you can do instead, is store functions for each product in a dictionary that you can call from the same callback function, rather than stuffing all your code in the one function directly.
So for instance:
local productFunctions = {
[Put the ProductID in the brackets] = function(player)
--//Do stuff with 'player' according to what this product gives you
end;
}
And from your callback,
local productFunction = productFunctions[receiptInfo.ProductId]
productFunction(player)
Another thing I’m noticing is that you’re getting the LocalPlayer, which wouldn’t work as this code needs to run on the server.
To get the player who purchased the product, you can whip up a quick function to find the player in the game based on the purchaser’s UserId.
local function GetPlayerWhoBought(receiptInfo)
for _, player in pairs(game.Players:GetPlayers()) do
if player.UserId == receiptInfo.UserId then
return player
end
end
end
local player = GetPlayerWhoBought(receiptInfo)
if player then
productFunction(player)
return Enum.ProductPurchaseDecision.PurchaseGranted
else
return Enum.ProductPurchaseDecision.NotProcessedYet --//Player left the game
end
I tried, it works for the first half of the script, I’ve tested it. However it doesn’t seem to work for the second half, which is the modified half.
function ReceiptHandler()
warn("Donation Board: ProcessReceipt Activated")
game:GetService("MarketplaceService").ProcessReceipt = function(receiptInfo)
local playerProductKey = "player_" .. receiptInfo.PlayerId .. "_product_" .. receiptInfo.ProductId
local numberBought = game:GetService("DataStoreService"):GetDataStore("PurchaseHistory"):IncrementAsync(playerProductKey, 1)
local ProductBought = game:GetService("DataStoreService"):GetDataStore("PurchaseHistoryCount"):IncrementAsync(receiptInfo.ProductId, 1)
local PlayerFound = false
for i, v in pairs (game.Players:GetChildren()) do
if v:IsA('Player') then
if v.userId == receiptInfo.PlayerId then
for _, p in pairs (Products.Products) do
if p.ProductId == receiptInfo.ProductId then
if v ~= nil then
PlayerFound = true
game:GetService("DataStoreService"):GetOrderedDataStore(GetData()):IncrementAsync(receiptInfo.PlayerId, p.ProductPrice)
end
end
end
end
end
end
if PlayerFound ~= true then
return Enum.ProductPurchaseDecision.NotProcessedYet
else
return Enum.ProductPurchaseDecision.PurchaseGranted
end
end
local productFunctions = {
[1086221671] = function(player)
player.leaderstats.Stage.Value = player.leaderstats.Stage.Value + 1
player:LoadCharacter()
end;
}
local function GetPlayerWhoBought(receiptInfo)
for _, player in pairs(game.Players:GetPlayers()) do
if player.UserId == receiptInfo.UserId then
return player
end
end
local player = GetPlayerWhoBought(receiptInfo)
local productFunction = productFunctions[receiptInfo.ProductId]
productFunction(player)
if player then
productFunction(player)
return Enum.ProductPurchaseDecision.PurchaseGranted
else
return Enum.ProductPurchaseDecision.NotProcessedYet
end
end
end
I may have misinterpret what you meant, though here is what I changed. Also I was a little confused on what you meant by assign the callback to the function.
game:GetService("MarketplaceService").ProcessReceipt = ReceiptHandler
function ReceiptHandler()
local playerProductKey = "player_" .. ReceiptHandler.PlayerId .. "_product_" .. ReceiptHandler.ProductId
local numberBought = game:GetService("DataStoreService"):GetDataStore("PurchaseHistory"):IncrementAsync(playerProductKey, 1)
local ProductBought = game:GetService("DataStoreService"):GetDataStore("PurchaseHistoryCount"):IncrementAsync(ReceiptHandler.ProductId, 1)
local PlayerFound = false
for i, v in pairs (game.Players:GetChildren()) do
if v:IsA('Player') then
if v.userId == ReceiptHandler.PlayerId then
for _, p in pairs (Products.Products) do
if p.ProductId == ReceiptHandler.ProductId then
if v ~= nil then
PlayerFound = true
game:GetService("DataStoreService"):GetOrderedDataStore(GetData()):IncrementAsync(ReceiptHandler.PlayerId, p.ProductPrice)
end
end
end
end
end
end
local productFunctions = {
[1086221671] = function(player)
player.leaderstats.Stage.Value = player.leaderstats.Stage.Value + 1
player:LoadCharacter()
end;
}
local function GetPlayerWhoBought(receiptInfo)
for _, player in pairs(game.Players:GetPlayers()) do
if player.UserId == receiptInfo.UserId then
return player
end
end
local player = GetPlayerWhoBought(receiptInfo)
local productFunction = productFunctions[receiptInfo.ProductId]
productFunction(player)
if player then
productFunction(player)
return Enum.ProductPurchaseDecision.PurchaseGranted
else
return Enum.ProductPurchaseDecision.NotProcessedYet
end
end
end
What issue are you having now? The code looks a lot better. Although you could still simplify some aspects of it such as using the GetPlayerWhoBought method to find the player who purchased the product instead of defining another for loop to do that. I’d also advise making GetPlayerWhoBought a global function and not local to the scope (or the inside) of ReceiptHandler.
And I’m also not sure this matters but I’d probably move the top line (where you set the callback) to be at the bottom of the script, after ReceiptHandler is defined.
The bottom half of the script doesn’t seem to work, I’ve tried skipping a stage (which essentially is suppose to happen when u buy the skip stage product) and it didn’t work. Though the first half works, really strange. I’ve done what you said and placed the first line at the bottom.
Are you getting any output errors?? If not, you could debug this further by placing prints after each block of code to see where exactly it stops running.
No errors, and I was informed earlier that you can’t print stuff in a call back. I tested that just now printing in some areas and didn’t seem to work.
Didn’t seem to work for the second half (the skip stage section); what I found really strange was when I placed the boards in the MainModule, it’d work for the skip stage button, but it won’t work for the board side of things which is the second product. So basically when the board is out of the MainModule, It won’t work for the skip stage product, but when it’s inside it’ll work for the skip stage product but not the board product… (the board is a donation board)
I don’t know if this is related to the issue @megaficient is experiencing, but I decided to use your code because I’m working on something similar at the moment and am recieving this error:
ServerScriptService.Data.Credits:61: attempt to call a nil value
I tried printing the table to see what was going on and this was the result:
[1] = {
[1.12991e+09] = "function"
}
Don’t know if this is related or not but I decided I’d just drop by and let you know.
Edit: Turned out to be unrelated, sorry for the confusion.
Is there any particular reason why you’re iterating through every player in the game to find out who made the purchase instead of using the PlayerId provided through the receipt callback?
You could use Players:GetPlayerByUserId(Id_from_receipt)