Developer Product - process receipt function happens in increasing amounts per purchase

Hello, I have a script that should give players different amounts of cash depending on what develop product they purchased. However, each time they buy it, they get given more and more cash. (e.g. if they buy a 1k product, then they will get 1k, 2k, 3k, 4k ect. for each purpose.

I have determined that this issue is the looping of the process reciept function, but I can’t figure out what exactly is causing it.

I saw some other posts about this same issue, but couldn’t really find a solution within it.

My code:

local DS2 = require(1936396537)
local MarketplaceService = game:GetService("MarketplaceService")

local playerMoney = game.ServerStorage:WaitForChild("PlayerMoney")
local RemoteEvent = game.ReplicatedStorage:WaitForChild("MoneyPurchase")

local event = game.ReplicatedStorage:WaitForChild("Donate")
local Marketplace = game:GetService("MarketplaceService")
local DatastoreService = game:GetService("DataStoreService")
local Donations = DatastoreService:GetOrderedDataStore("Donations")
local ChatService = require(game:GetService("ServerScriptService"):WaitForChild("ChatServiceRunner"):WaitForChild("ChatService"))



local IDs =  {

	[1208476645] = 1000, 
	[1208476646] = 2500,
	[1208476644] = 10000, 
	[1208476642] = 100000,
	[1208476643] = 1000000

}

local DonationIDs = {
	[1208496944] = 25,
	[1208496945] = 50,
	[1208496946] = 75,
	[1208496947] = 100,
	[1208496948] = 250,
	[1208497389] = 500,
	[1208497480] = 750,
	[1208497648] = 1000,
	[1208497649] = 2500,
	[1208497718] = 5000
}



if not ChatService:GetChannel("All") then
	while true do
		local ChannelName = ChatService.ChannelAdded:Wait()
		if ChannelName == "All" then
			break
		end
	end
end

local DonationAnnouncement = ChatService:AddSpeaker("Donations")
DonationAnnouncement:JoinChannel("All")

DonationAnnouncement:SetExtraData("NameColor", Color3.fromRGB(255, 170, 0))
DonationAnnouncement:SetExtraData("ChatColor", Color3.fromRGB(255, 255, 0))


RemoteEvent.OnServerEvent:Connect(function(plr, amount)

	MarketplaceService:PromptProductPurchase(plr, IDs[amount])

end)



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

local function processReceipt(receiptInfo)
	print("Purchase Processing")
	-- 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 = function()
		if IDs[receiptInfo.ProductId] then
			playerMoney[player.Name].Value = playerMoney[player.Name].Value + IDs[receiptInfo.ProductId] 
			print("Awarding money")
			DS2("Money", player):Set(playerMoney[player.Name].Value)
			print("Money awarded")
			DS2("Money", player).Save()
			return true
		elseif DonationIDs[receiptInfo.ProductId] then
			local success, err = pcall(function()
				Donations:UpdateAsync(player.UserId, function(old)
					local oldValue = old or 0
					for key, value in pairs(DonationIDs) do
						if receiptInfo.ProductId == key then		
							spawn(function()DonationAnnouncement:SayMessage(player.Name.." just donated R$"..receiptInfo.CurrencySpent.."! Thank you!", "All")end)
							return oldValue + tonumber(value)
						end
					end

				end)

			end)
			return true

		else
			warn("ID did not match list")
		end

	end

	-- Call the handler function and catch any errors
	local success, result = pcall(handler, receiptInfo, player)
	if not success then --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


2 Likes

So, I did some testing and it turn out here is the problem :

DS2("Money", player).Save()

also you can put print or warn(result) to see the result from the ouput here :

local success, result = pcall(handler, receiptInfo, player)

How would you suggest I fix the .Save() thing? Should I put it as a seperate function and just call it instead?

Actually, I still don’t know how to use datastore2, but if we change to :Save() it should fix the problem.

Well, I just found a solution on how to prevent the function get call many times that can prevent playerMoney get double every time code inside pcall function throw an error,
just remove this code return Enum.ProductPurchaseDecision.NotProcessedYet

YES THANK YOU SO MUCH. I still don’t get why returning this continues to recall the function but I’m glad I fixed my code. Again THANK YOU SO MUCH

1 Like

Alright cool. :slightly_smiling_face:

1 Like