Receipt Call Back Function

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

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

Let me know if you need more information, thanks!

You can’t write any code in the same scope after you return something, as code automatically stops after you return an item.

1 Like

The initial problems I see are the following:

  1. 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.
  2. 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).

1 Like

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

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

Here it won’t increase my stage value:

	local productFunctions = {
		[1086221671] = function(player)
			player.leaderstats.Stage.Value = player.leaderstats.Stage.Value + 1
			player:LoadCharacter()	
		end;
	}

Move your callback outside of the ReceiptHandler function and simply just assign the callback to that function. Example:

game:GetService("MarketplaceService").ProcessReceipt = ReceiptHandler

Also, you don’t want to return whether or not the purchase has gone through because there’s still more code to be ran underneath that code block.

1 Like

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

Still having this issue, more details above in the main thread.

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.

2 Likes

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.

Ah, I think I see the issue. Add a parameter to ReceiptHandler, e.g:

function ReceiptHandler(receiptInfo)

And then every time you index it as ReceiptHandler replace it with receiptInfo.

Hope that helps :slight_smile:

1 Like

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

I’m noticing “DeveloperProductHandler” is disabled. Assuming that’s this code here, shouldn’t it be enabled??

1 Like

It gets activated on the server side I believe.

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.

1 Like

I’m confused. From this picture, you haven’t implemented the code that reads from the product functions table and calls it accordingly?:confused:

2 Likes

You mean this? I have added it just didn’t include it in the sc.

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)

1 Like